Add bundled TTF fonts, page geometry, column allocator and render model

Static TTF fonts (IBM Plex Sans/Mono, Noto Devanagari/Kannada) with a
fontkit-generated manifest, a verifier and OFL licences. Every family
registers a normal and italic source so fallback stacks cannot throw.
Adds A4/Letter geometry with margin presets, an exact column allocator,
deterministic Indian-grouping money and date formatting, text sanitising
and a pure buildRenderModel.
This commit is contained in:
2026-10-04 04:38:03 +05:30
parent 12d80efcb8
commit bf4b85563b
40 changed files with 2162 additions and 49 deletions
+29
View File
@@ -0,0 +1,29 @@
// Regenerates public/fonts/manifest.json from the TTFs under public/fonts/<folder>/.
// Run: node scripts/fonts/build-manifest.mjs
import { existsSync, writeFileSync } from "node:fs";
import path from "node:path";
import { FONTS_DIR, MANIFEST_PATH, familyFolders, inspectFont, ttfFiles } from "./lib.mjs";
const families = [];
for (const folder of familyFolders()) {
const files = ttfFiles(folder);
if (files.length === 0) continue;
const inspected = files.map((file) => inspectFont(folder, file));
const names = new Set(inspected.map((f) => f.familyName));
if (names.size !== 1) {
throw new Error(`${folder}: mixed family names ${[...names].join(", ")}`);
}
const licenseFile = "OFL.txt";
if (!existsSync(path.join(FONTS_DIR, folder, licenseFile))) {
throw new Error(`${folder}: missing ${licenseFile}`);
}
const fonts = inspected
.map((f) => f.info)
.sort((a, b) => a.weight - b.weight || a.style.localeCompare(b.style) || a.file.localeCompare(b.file));
families.push({ family: [...names][0], folder, license: "OFL-1.1", licenseFile, fonts });
}
writeFileSync(MANIFEST_PATH, `${JSON.stringify({ version: 1, families }, null, 2)}\n`);
console.log(
`manifest.json: ${families.length} families, ${families.reduce((n, f) => n + f.fonts.length, 0)} fonts`,
);