Archilyzer · Source

archilyzer

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

commit d03048ae1c310948faecd556507851569d49b69b
parent 7716753f67ea051610c6894826d75d04cfa85939
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon, 28 Sep 2026 03:23:59 -0400

umtool: test that the preset's rsvg-convert children get FONTCONFIG_FILE; fc-match's escaped paths read back

IBM Plex Sans is not a system font, so a branded SVG rasterized without
the preset's FONTCONFIG_FILE falls back to some other sans, silently. A
stub RSVG_BIN records the variable each child is given and runs the real
rsvg-convert: an unbranded scroll card's child gets the system's config,
a branded one's the vendored fonts.conf. Bites: with rasterize() calling
rsvg-convert without childOpts, it fails.

fc-match backslash-escapes a comma in a path (`\,`), which a checkout path
with one would trip; the fonts.conf test unescapes before comparing, and
checks Plex Sans's variable file by its full path.

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

Diffstat:
Mumtool/report-to-video/brand.test.mjs | 43++++++++++++++++++++++++++++++++++++++++---
1 file changed, 40 insertions(+), 3 deletions(-)

diff --git a/umtool/report-to-video/brand.test.mjs b/umtool/report-to-video/brand.test.mjs @@ -9,10 +9,11 @@ import assert from "node:assert/strict"; import test from "node:test"; import { execFileSync, spawnSync } from "node:child_process"; -import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { mkdir } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; import { BRAND_IDS, END_CARD_DEFAULT_SECONDS, END_CARD_DEFAULT_URL, END_CARD_ID, FACES, FONTCONFIG_FILE, @@ -25,6 +26,7 @@ import { markPng, renderThumbnail, THUMB } from "./brand-cards.mjs"; import { IBM_PLEX_SANS } from "./svg-faces.mjs"; const mkdirP = (d) => mkdir(d, { recursive: true }); +const HERE = path.dirname(fileURLToPath(import.meta.url)); const PLAIN_RENDER = { width: 1920, height: 1080, fps: 30, headerHeight: 56, @@ -172,11 +174,14 @@ test("the kit: the lockup and the mark are outlines, not text", () => { const hasFcMatch = spawnSync("fc-match", ["--version"], { stdio: "ignore" }).status === 0; test("fonts.conf: the preset's children resolve the vendored faces, bold mono included", { skip: !hasFcMatch && "fc-match not available" }, () => { + // fc-match escapes its separators in a path (`\,`), so they are unescaped. const match = (pattern) => - execFileSync("fc-match", [pattern, "file"], { encoding: "utf8", env: { ...process.env, FONTCONFIG_FILE } }).trim(); + execFileSync("fc-match", [pattern, "file"], { encoding: "utf8", env: { ...process.env, FONTCONFIG_FILE } }) + .trim() + .replace(/\\(.)/g, "$1"); assert.equal(match("IBM Plex Mono:bold"), `:file=${MONO_BOLD_FONT_FILE}`); assert.equal(match("IBM Plex Mono"), `:file=${MONO_FONT_FILE}`); - assert.match(match("IBM Plex Sans:bold"), /IBMPlexSans\[wdth\\?,wght\]\.ttf$/); + assert.equal(match("IBM Plex Sans:bold"), `:file=${path.join(FONTS_DIR, "IBMPlexSans[wdth,wght].ttf")}`); }); // Rendering needs ImageMagick with Pango and rsvg-convert; a machine without @@ -253,3 +258,35 @@ test("rendered: the scroll's SVG text takes the preset's face and its fit; unbra rmSync(dir, { recursive: true, force: true }); } }); + +test("rendered: the preset's rsvg-convert children get the vendored fonts; unbranded ones the system's", { skip: !hasTools && "magick / rsvg-convert not available" }, () => { + // IBM Plex Sans is not a system font, so a branded SVG rasterized without + // FONTCONFIG_FILE would fall back to some other sans -- silently. A stub + // RSVG_BIN records the variable each child is given, then runs the real one. + const dir = mkdtempSync(path.join(tmpdir(), "rtv-brand-env-")); + try { + const log = path.join(dir, "env.log"); + const stub = path.join(dir, "rsvg-stub.sh"); + const real = process.env.RSVG_BIN ?? "rsvg-convert"; + writeFileSync(stub, `#!/bin/sh\nprintf '%s\\n' "\${FONTCONFIG_FILE:-system}" >> '${log}'\nexec ${real} "$@"\n`, { mode: 0o755 }); + const url = (f) => JSON.stringify(pathToFileURL(path.join(HERE, f)).href); + const script = ` + import { mkdirSync } from "node:fs"; + import { renderScrollCard } from ${url("render-cards.mjs")}; + import { resolveBrandRender } from ${url("brand.mjs")}; + const render = ${JSON.stringify({ ...PLAIN_RENDER, rail: { tracks: [{ key: "q", label: "Q", color: "#2bb38a" }] } })}; + const ledger = [{ id: "a", date: "2023-06-30", scope: "q", value: 6, display: "6", label: "six", entryId: "c" }]; + for (const [sub, r] of [["plain", render], ["brand", resolveBrandRender({ ...render, brand: "archilyzer-media" })]]) { + mkdirSync(${JSON.stringify(dir)} + "/" + sub + "/cards", { recursive: true }); + await renderScrollCard({ type: "scroll", id: "z", hideRail: true }, r, ledger, ${JSON.stringify(dir)} + "/" + sub); + } + `; + const env = { ...process.env, RSVG_BIN: stub }; + delete env.FONTCONFIG_FILE; + const r = spawnSync(process.execPath, ["--input-type=module", "-e", script], { env, encoding: "utf8" }); + assert.equal(r.status, 0, r.stderr); + assert.deepEqual(readFileSync(log, "utf8").trim().split("\n"), ["system", FONTCONFIG_FILE]); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +});