Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 4132b68c9c04c7df92ddd5b4de3668f06c2cbb10
parent d03048ae1c310948faecd556507851569d49b69b
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon, 28 Sep 2026 03:29:17 -0400

umtool: README — the preset sets the rail, ledger, scroll and chart in Plex Sans, "Why a table, not an average", Plex Mono Bold vendored

The preset's "owns" list gains the SVG text and `fontBold`; the "Not
branded yet" paragraph goes; the fonts paragraph names Plex Mono Bold
(about 1.5 MB in all). A new subsection says why a face carries its
metric, Fira's 0.50 em average as measured (0.457, p95 0.492) and why it
stays, Plex's table, the numbers that rule out an average (Fira's own
0.50 overruns the scroll head at 152 px in 140 given real text; Plex 0
over, widest 98.9 %), kerning (the plain sum was at or above the
rendered width of every run), the 1.3 em fallback, and how to
regenerate face-metrics.mjs. svg-faces.mjs's header states the same
measurements as they were rendered.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/report-to-video/README.md | 56++++++++++++++++++++++++++++++++++++++++++++++----------
Mumtool/report-to-video/svg-faces.mjs | 20++++++++++----------
2 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/umtool/report-to-video/README.md b/umtool/report-to-video/README.md @@ -821,10 +821,11 @@ What the preset **owns**, whatever the manifest says: - `render.palette` — the slate of the parent mark lit in Signal (`#151b20` / `#e7edf1` / `#8496a2` / `#5fa8a0`, amber `#e3b15c`); -- the faces — Archivo at wdth 118 for display, IBM Plex Sans for body, IBM Plex - Mono for the header and meta lines; `render.fontRegular` becomes the vendored - Plex Mono. (`fontBold` is left alone: only compose-chrome's HyperFrames band - reads it.) +- the faces — Archivo at wdth 118 for display, IBM Plex Sans for body (the + rail, `ledger`, `scroll` and `chart` SVG text included), IBM Plex Mono for the + header and meta lines; `render.fontRegular` becomes the vendored Plex Mono + Regular and `render.fontBold` the vendored Plex Mono Bold (compose-chrome's + HyperFrames band sets its bold in it). What it **adds**: @@ -836,10 +837,10 @@ What it **adds**: | thumbnail | 1280 × 720: a still from a cached clip window (or `thumbnail.still`), the headline in Archivo 800, the mark top-left, the duration-badge corner clear. Made after a full build when `thumbnail` is set, or alone with `--thumbnail` | `out/<slug>.thumbnail.png` | The other card styles (`chapter`, `bullets`, `sources`, `timeline`) keep their -layouts and take the preset's faces and palette. **Not branded yet:** the rail, -`ledger`, `scroll` and `chart` still set their SVG text in Fira Sans — their -truncation (`fit()`) is measured against Fira's average advance, and moving the -face without re-measuring would overrun columns. +layouts and take the preset's faces and palette. So do the rail, `ledger`, +`scroll` and `chart`: their SVG text is set in IBM Plex Sans, and rsvg-convert +finds it through the same `FONTCONFIG_FILE`, measured by its own advances +(below, "Why a table, not an average"). **The drawings are not drawn here.** The mark and the lockup (its letters as outlines, so no font is needed to draw it) are `common/lib/brandMedia.ts`'s, @@ -848,9 +849,9 @@ exported by `common/bin/brand-media.ts --video-kit` into A common test fails when that file is stale; re-run the CLI, never edit it. **Fonts are vendored, not installed.** `fonts/` holds Archivo[wdth,wght], IBM Plex -Sans[wdth,wght] and IBM Plex Mono Regular — unmodified from google/fonts +Sans[wdth,wght] and IBM Plex Mono Regular and Bold — unmodified from google/fonts (`ofl/archivo` at `95f4904f`, `ofl/ibmplexsans` / `ofl/ibmplexmono` at -`0b58fb37`), about 1.3 MB — with `OFL.txt` (Plex carries a Reserved Font Name, so +`0b58fb37`), about 1.5 MB — with `OFL.txt` (Plex carries a Reserved Font Name, so it may not be shipped modified, subset included) and a `fonts.conf`. The preset sets `FONTCONFIG_FILE` to that conf on **its own** `magick` and `rsvg-convert` children only, so Pango sees the variable faces (`Archivo @wght=720,wdth=118`); @@ -869,6 +870,41 @@ Two measurements the code depends on: of the same code never produce the same PNG bytes. A byte-identity check has to drop those three chunks and compare the rest — IHDR and IDAT included. +### Why a table, not an average + +The rail, `ledger`, `scroll` and `chart` assets are **fitted, not flowed**: a +label that would overrun its column is cut short with an ellipsis by `fit()` +(and a ledger quote wrapped by `wrapPx()`) before it is drawn, because an +overflowing row runs under the value column beside it rather than wrapping. So +a face carries the metric it is fitted by (`svg-faces.mjs`), and swapping the +face without it would overrun. + +- **Fira Sans** (no brand) keeps its average: 0.50 em a character, a character + count, exactly as it always cut. Measured over the strings `fit()` actually + truncates in the real manifests, Fira averages 0.457 em (p95 of a string's own + average 0.492), so 0.50 is the hair of margin it was chosen with. It cannot + move — an unbranded render is byte-identical by rule — and it is the system's + font, so a table of its advances could not be pinned anyway. +- **IBM Plex Sans** (the preset) is measured per character: + `face-metrics.mjs` holds the vendored variable font's advance for all 891 + characters it maps, at the two weights these assets draw (400 and 700, wdth + 100). `fit()` keeps the longest prefix whose width — ellipsis, weight and + letter-spacing included — is inside the budget; `wrapPx()` wraps by width and + fits every line; the ledger's scope pill hugs its label. + +An average cannot promise a column. Measured the same way, Plex Sans averages +0.456 em regular (Fira's own number) but 0.479 bold and 0.59–0.61 in capitals; +and pushing the manifests' longest real text through each call site, even +Fira's 0.50 lets the scroll's bold, letter-spaced column head overrun (152 px +rendered in 140). The table does not: over the same runs, every Plex one +renders inside its budget (widest 98.9 %). Kerning is ignored: the plain sum of +advances came out at or above the rendered width of every one of those runs, so +it is the conservative side. A character the face does not map (emoji, CJK, +drawn by fontconfig's fallback) counts 1.3 em. + +`face-metrics.mjs` is generated: `python3 fonts/gen-face-metrics.py` (fontTools) +rewrites it from the vendored font, and a test fails when it is stale. + ## Things that cost time to find out **yt-dlp picks VP9 at `height<=720`, and that is a trap.** `--download-sections` diff --git a/umtool/report-to-video/svg-faces.mjs b/umtool/report-to-video/svg-faces.mjs @@ -23,17 +23,17 @@ // face-metrics.mjs, generated by fonts/gen-face-metrics.py. An // average cannot promise a run fits: measured the same way, // Plex averages 0.456 em regular (Fira's own number) but 0.479 -// em bold, 0.59-0.61 em in capitals, and even Fira's 0.50 lets -// a run of long real labels in bold, letter-spaced capitals -// overrun by 11 %. A table does not: fit() keeps the longest -// prefix whose measured width, ellipsis included, is inside -// the budget. +// em bold, 0.59-0.61 em in capitals, and even Fira's 0.50, +// given the manifests' longest real text, lets the scroll's +// bold, letter-spaced column head overrun (152 px rendered in +// 140). A table does not: fit() keeps the longest prefix whose +// measured width, ellipsis included, is inside the budget. // -// The measurement sums advances and ignores kerning: kerning only tightens a -// run of these faces, so the sum is the conservative side (rendered ink widths -// came in 0.3-1.5 % under it). A character the face does not map is drawn by -// fontconfig's fallback (emoji, CJK) and counted at FALLBACK_EM, wider than -// either. +// The measurement sums advances and ignores kerning: over the manifests' +// longest real text fitted at every call site, the rendered ink width of every +// Plex run came out at or under the sum, so the sum is the conservative side. +// A character the face does not map is drawn by fontconfig's fallback (emoji, +// CJK) and counted at FALLBACK_EM, wider than either. import { IBM_PLEX_SANS_METRICS } from "./face-metrics.mjs";