Give each og-card screenshot its own port, and Japanese a font to fall back to
Two bugs in flake.nix's og-card build step, both silent because Chromium and the static file server it screenshots against have their stderr thrown away: - All 5 languages shared one port (8971) across the build loop. When the previous language's server hadn't released it in time, the next `python3 -m http.server` failed to bind and every screenshot after that point silently landed on the *previous* language's still-running server instead - fr.mykiwi.blog/.../og-card.png ended up rendering the Spanish page. Each language now gets its own port, so there is nothing left to race. - The sandboxed Chromium has no system fonts of its own, so ja's kicker text (JetBrains Mono has no CJK glyphs) rendered as tofu boxes instead of falling back the way a real visitor's browser does. ipaexfont (~9MB, Japanese-only) is now registered via a generated fonts.conf, reached only for codepoints neither self-hosted font covers - no change for en/es/fr/pt.
8804fce parent: 45af4c3 modified
flake.nix +44 -1 | @@ -15,6 +15,16 @@ | ||
| 15 | 15 | chromium = pin.version "chromium" "153.0.8010.36"; |
| 16 | 16 | imagemagick = pkgs.imagemagick; |
| 17 | 17 | python3 = pkgs.python3; |
| 18 | + # A visitor's own OS always supplies *some* CJK fallback font for the | |
| 19 | + # ja site's kicker text (JetBrains Mono itself has no CJK glyphs at | |
| 20 | + # all), so this never came up until a real page was rendered by a | |
| 21 | + # browser with no system fonts of its own to fall back to: the ja | |
| 22 | + # og-card.png showed tofu boxes instead of "1分で読了" and friends. | |
| 23 | + # IPAex Gothic is small (~9MB) and just for that fallback - it is | |
| 24 | + # never the requested font-family, only what fontconfig reaches for | |
| 25 | + # once Newsreader/JetBrains Mono (self-hosted, already covering every | |
| 26 | + # other language here) come up short on a codepoint. | |
| 27 | + ipaexfont = pin.version "ipaexfont" "004.01"; | |
| 18 | 28 | in |
| 19 | 29 | { |
| 20 | 30 | packages.${system}.default = pkgs.stdenvNoCC.mkDerivation { |
| @@ -73,8 +83,41 @@ | ||
| 73 | 83 | # this is one more, not a new problem. |
| 74 | 84 | langs="en es fr pt ja" |
| 75 | 85 | export HOME="$TMPDIR" |
| 76 | - port=8971 | |
| 86 | + | |
| 87 | + # Chromium's font fallback goes through fontconfig, which has | |
| 88 | + # nothing to search by default in this sandbox - not even the | |
| 89 | + # site's own self-hosted webfonts, which are @font-face rules the | |
| 90 | + # page fetches over HTTP, unrelated to what the OS/fontconfig | |
| 91 | + # supplies. ipaexfont is registered here, once, for every | |
| 92 | + # language's screenshots: it only ever gets reached for a | |
| 93 | + # codepoint neither self-hosted font covers (CJK), so it changes | |
| 94 | + # nothing for en/es/fr/pt. | |
| 95 | + mkdir -p "$TMPDIR/fontconfig-cache" | |
| 96 | + # Unindented on purpose: a heredoc terminator has to start in | |
| 97 | + # column 0 (bash strips leading *tabs* with <<-, not the spaces | |
| 98 | + # this file otherwise uses throughout). | |
| 99 | + cat > "$TMPDIR/fonts.conf" <<FONTCONF | |
| 100 | +<?xml version="1.0"?> | |
| 101 | +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> | |
| 102 | +<fontconfig> | |
| 103 | +<dir>${ipaexfont}/share/fonts/truetype</dir> | |
| 104 | +<cachedir>$TMPDIR/fontconfig-cache</cachedir> | |
| 105 | +</fontconfig> | |
| 106 | +FONTCONF | |
| 107 | + export FONTCONFIG_FILE="$TMPDIR/fonts.conf" | |
| 108 | + | |
| 109 | + # A distinct port per language, not one reused across the loop: | |
| 110 | + # reusing one, an earlier build hit a silent race where the next | |
| 111 | + # language's `python3 -m http.server` failed to bind (the previous | |
| 112 | + # one not yet torn down) and every screenshot after that point | |
| 113 | + # silently landed on the *previous* language's still-running | |
| 114 | + # server instead - fr.mykiwi.blog's card came out in Spanish. | |
| 115 | + # stderr is thrown away below because Chromium is noisy on stdout | |
| 116 | + # even on success, so that failure mode raised no error either. | |
| 117 | + # One port per language means there is nothing left to race. | |
| 118 | + port=8970 | |
| 77 | 119 | for lang in $langs; do |
| 120 | + port=$((port + 1)) | |
| 78 | 121 | root="$out/$lang" |
| 79 | 122 | python3 -m http.server "$port" --bind 127.0.0.1 --directory "$root" \ |
| 80 | 123 | >/dev/null 2>&1 & |
| @@ -15,6 +15,16 @@ | |||
| 15 | chromium = pin.version "chromium" "153.0.8010.36"; | 15 | chromium = pin.version "chromium" "153.0.8010.36"; |
| 16 | imagemagick = pkgs.imagemagick; | 16 | imagemagick = pkgs.imagemagick; |
| 17 | python3 = pkgs.python3; | 17 | python3 = pkgs.python3; |
| 18 | + # A visitor's own OS always supplies *some* CJK fallback font for the | ||
| 19 | + # ja site's kicker text (JetBrains Mono itself has no CJK glyphs at | ||
| 20 | + # all), so this never came up until a real page was rendered by a | ||
| 21 | + # browser with no system fonts of its own to fall back to: the ja | ||
| 22 | + # og-card.png showed tofu boxes instead of "1分で読了" and friends. | ||
| 23 | + # IPAex Gothic is small (~9MB) and just for that fallback - it is | ||
| 24 | + # never the requested font-family, only what fontconfig reaches for | ||
| 25 | + # once Newsreader/JetBrains Mono (self-hosted, already covering every | ||
| 26 | + # other language here) come up short on a codepoint. | ||
| 27 | + ipaexfont = pin.version "ipaexfont" "004.01"; | ||
| 18 | in | 28 | in |
| 19 | { | 29 | { |
| 20 | packages.${system}.default = pkgs.stdenvNoCC.mkDerivation { | 30 | packages.${system}.default = pkgs.stdenvNoCC.mkDerivation { |
| @@ -73,8 +83,41 @@ | |||
| 73 | # this is one more, not a new problem. | 83 | # this is one more, not a new problem. |
| 74 | langs="en es fr pt ja" | 84 | langs="en es fr pt ja" |
| 75 | export HOME="$TMPDIR" | 85 | export HOME="$TMPDIR" |
| 76 | - port=8971 | 86 | + |
| 87 | + # Chromium's font fallback goes through fontconfig, which has | ||
| 88 | + # nothing to search by default in this sandbox - not even the | ||
| 89 | + # site's own self-hosted webfonts, which are @font-face rules the | ||
| 90 | + # page fetches over HTTP, unrelated to what the OS/fontconfig | ||
| 91 | + # supplies. ipaexfont is registered here, once, for every | ||
| 92 | + # language's screenshots: it only ever gets reached for a | ||
| 93 | + # codepoint neither self-hosted font covers (CJK), so it changes | ||
| 94 | + # nothing for en/es/fr/pt. | ||
| 95 | + mkdir -p "$TMPDIR/fontconfig-cache" | ||
| 96 | + # Unindented on purpose: a heredoc terminator has to start in | ||
| 97 | + # column 0 (bash strips leading *tabs* with <<-, not the spaces | ||
| 98 | + # this file otherwise uses throughout). | ||
| 99 | + cat > "$TMPDIR/fonts.conf" <<FONTCONF | ||
| 100 | +<?xml version="1.0"?> | ||
| 101 | +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> | ||
| 102 | +<fontconfig> | ||
| 103 | +<dir>${ipaexfont}/share/fonts/truetype</dir> | ||
| 104 | +<cachedir>$TMPDIR/fontconfig-cache</cachedir> | ||
| 105 | +</fontconfig> | ||
| 106 | +FONTCONF | ||
| 107 | + export FONTCONFIG_FILE="$TMPDIR/fonts.conf" | ||
| 108 | + | ||
| 109 | + # A distinct port per language, not one reused across the loop: | ||
| 110 | + # reusing one, an earlier build hit a silent race where the next | ||
| 111 | + # language's `python3 -m http.server` failed to bind (the previous | ||
| 112 | + # one not yet torn down) and every screenshot after that point | ||
| 113 | + # silently landed on the *previous* language's still-running | ||
| 114 | + # server instead - fr.mykiwi.blog's card came out in Spanish. | ||
| 115 | + # stderr is thrown away below because Chromium is noisy on stdout | ||
| 116 | + # even on success, so that failure mode raised no error either. | ||
| 117 | + # One port per language means there is nothing left to race. | ||
| 118 | + port=8970 | ||
| 77 | for lang in $langs; do | 119 | for lang in $langs; do |
| 120 | + port=$((port + 1)) | ||
| 78 | root="$out/$lang" | 121 | root="$out/$lang" |
| 79 | python3 -m http.server "$port" --bind 127.0.0.1 --directory "$root" \ | 122 | python3 -m http.server "$port" --bind 127.0.0.1 --directory "$root" \ |
| 80 | >/dev/null 2>&1 & | 123 | >/dev/null 2>&1 & |