HH-708: align container fonts with fingerprint maps #4

Merged
rogee merged 4 commits from agent/hh-708-font-fingerprint into main 2026-08-26 21:42:11 +08:00
3 changed files with 114 additions and 0 deletions
+11
View File
@@ -25,8 +25,19 @@ LABEL org.opencontainers.image.revision="$OCI_REVISION" \
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
fonts-croscore \
fonts-crosextra-caladea \
fonts-crosextra-carlito \
fonts-dejavu-core \
fonts-dejavu-extra \
fonts-freefont-ttf \
fonts-liberation \
fonts-liberation-sans-narrow \
fonts-noto-core \
fonts-noto-cjk \
fonts-noto-color-emoji \
fonts-ubuntu \
fonts-urw-base35 \
libasound2t64 \
libatk-bridge2.0-0t64 \
libatk1.0-0t64 \
+56
View File
@@ -30,3 +30,59 @@ DOCKER_LOG="$docker_log" OCI_REVISION=test PUSH=true PATH="$tmpdir:$PATH" \
./scripts/build.sh registry.example/fingerprint-chromium > "$tmpdir/output"
test "$(cat "$docker_log")" = "manifest inspect registry.example/fingerprint-chromium:1.2.3.4"
grep -q 'skipping build and push' "$tmpdir/output"
font_packages=(
fonts-croscore
fonts-crosextra-caladea
fonts-crosextra-carlito
fonts-dejavu-core
fonts-dejavu-extra
fonts-freefont-ttf
fonts-liberation
fonts-liberation-sans-narrow
fonts-noto-cjk
fonts-noto-color-emoji
fonts-noto-core
fonts-ubuntu
fonts-urw-base35
)
version="$(curl --fail --location --retry 3 --silent --show-error \
https://api.github.com/repos/adryfish/fingerprint-chromium/releases/latest \
| jq -er '.tag_name | select(test("^[0-9]+(\\.[0-9]+){3}$"))')"
image="fingerprint-chromium-test:$version"
CHROME_VERSION="$version" ./scripts/build.sh fingerprint-chromium-test
docker run --rm --entrypoint sh "$image" -c '
for package do
test "$(dpkg-query -W -f="\${Status}" "$package" 2>/dev/null)" = "install ok installed" || exit 1
done
' sh "${font_packages[@]}"
docker run --rm --entrypoint sh "$image" -c '
for font do
fc-match --format "%{family}\n" "$font" | grep -Fq "$font" || exit 1
done
' sh Arimo Tinos Cousine Caladea Carlito \
'Noto Sans CJK JP' 'Noto Sans CJK SC' 'Noto Sans CJK TC' \
'Noto Serif CJK JP' 'Noto Serif CJK SC' 'Noto Serif CJK TC'
probe() {
local platform="$1" threshold="$2" output count
output="$(docker run --rm -i --entrypoint sh "$image" -c '
cat >/tmp/font-probe.html
exec /opt/chromium/chrome --no-sandbox --headless=new --disable-gpu --disable-dev-shm-usage \
--fingerprint=1000 --fingerprint-platform="$1" \
--dump-dom "file:///tmp/font-probe.html#$1"
' sh "$platform" <tests/font-probe.html 2>/dev/null)"
count="$(sed -n 's/.*data-count="\([0-9][0-9]*\)".*/\1/p' <<<"$output")"
[[ "$count" =~ ^[0-9]+$ && "$count" -ge "$threshold" ]] || {
printf '%s font probe: expected >= %s, got %s\n' "$platform" "$threshold" "${count:-none}" >&2
return 1
}
printf '%s font probe: %s\n' "$platform" "$count"
}
probe windows 22
probe macos 24
probe linux 27
+47
View File
@@ -0,0 +1,47 @@
<!doctype html>
<output id="result"></output>
<script>
const fonts = {
windows: [
'Arial Black', 'Bahnschrift', 'Cambria', 'Candara', 'Cascadia Code',
'Comic Sans MS', 'Consolas', 'Constantia', 'Corbel', 'Ebrima',
'Franklin Gothic Medium', 'Gabriola', 'Gadugi', 'Georgia', 'Impact',
'Ink Free', 'Javanese Text', 'Lucida Console', 'Malgun Gothic',
'Microsoft YaHei', 'Palatino Linotype', 'Segoe UI', 'SimSun', 'Tahoma',
'Trebuchet MS', 'Verdana', 'Webdings', 'Wingdings', 'Yu Gothic'
],
macos: [
'Academy Engraved LET', 'American Typewriter', 'Andale Mono',
'Apple Chancery', 'Apple Color Emoji', 'Apple Symbols', 'Avenir',
'Avenir Next', 'Baskerville', 'Big Caslon', 'Bradley Hand', 'Chalkboard',
'Chalkduster', 'Charter', 'Cochin', 'Copperplate', 'Didot', 'Futura',
'Geneva', 'Gill Sans', 'Helvetica Neue', 'Herculanum', 'Hoefler Text',
'Lucida Grande', 'Marker Felt', 'Menlo', 'Monaco', 'Optima', 'Papyrus',
'PingFang SC', 'Rockwell', 'SF Pro Display', 'Skia', 'Snell Roundhand',
'Songti SC', 'Times', 'Trattatello', 'Zapfino'
],
linux: [
'Arimo', 'Caladea', 'Carlito', 'Cousine', 'DejaVu Math TeX Gyre',
'DejaVu Sans', 'DejaVu Sans Mono', 'DejaVu Serif', 'FreeMono', 'FreeSans',
'FreeSerif', 'Liberation Mono', 'Liberation Sans', 'Liberation Sans Narrow',
'Liberation Serif', 'Nimbus Mono PS', 'Nimbus Roman', 'Nimbus Sans',
'Nimbus Sans Narrow', 'Noto Color Emoji', 'Noto Sans', 'Noto Sans CJK JP',
'Noto Sans CJK SC', 'Noto Serif', 'Noto Serif CJK JP', 'Noto Serif CJK SC',
'Tinos', 'Ubuntu', 'Ubuntu Mono'
]
};
const platform = location.hash.slice(1);
const sample = 'mmmmmmmmmmlli';
const bases = ['monospace', 'sans-serif', 'serif'];
const context = document.createElement('canvas').getContext('2d');
const widths = bases.map(base => {
context.font = `72px ${base}`;
return context.measureText(sample).width;
});
const detected = fonts[platform].filter(font => bases.some((base, index) => {
context.font = `72px "${font}",${base}`;
return context.measureText(sample).width !== widths[index];
}));
result.dataset.count = detected.length;
result.textContent = `${platform}:${detected.join('|')}`;
</script>