import { readFileSync, writeFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { Buffer } from 'node:buffer'; import subsetFont from 'subset-font'; import opentype from 'opentype.js'; const sourcePath = resolve('assets/fonts/Roboto-Regular.ttf'); const outputPath = resolve('src/pdf/pdf-font.generated.ts'); const ranges = [ [0x20, 0x7e], [0xa0, 0x17f], [0x400, 0x52f], [0x2000, 0x206f], ]; const required = new Set(['°', '²', '′', '″', '×', '≈', '€', '₽', '—', '→']); for (const [start, end] of ranges) { for (let code = start; code <= end; code++) required.add(String.fromCodePoint(code)); } const characters = [...required].join(''); const original = readFileSync(sourcePath); const subset = Buffer.from(await subsetFont(original, characters, { targetFormat: 'truetype' })); const arrayBuffer = subset.buffer.slice(subset.byteOffset, subset.byteOffset + subset.byteLength); const font = opentype.parse(arrayBuffer); const glyphs = {}; const widths = {}; for (const char of characters) { const code = char.codePointAt(0); const glyph = font.charToGlyph(char); if (!code || glyph.index === 0) continue; glyphs[code] = glyph.index; widths[glyph.index] = Math.round(glyph.advanceWidth || font.unitsPerEm * 0.5); } const generated = `/* Generated by scripts/generate-pdf-font.mjs. DO NOT EDIT. */ export const PDF_FONT_BASE64 = ${JSON.stringify(subset.toString('base64'))}; export const PDF_FONT_UNITS_PER_EM = ${font.unitsPerEm}; export const PDF_FONT_ASCENT = ${Math.round(font.ascender)}; export const PDF_FONT_DESCENT = ${Math.round(font.descender)}; export const PDF_FONT_BBOX = ${JSON.stringify([ Math.round(font.tables.head.xMin), Math.round(font.tables.head.yMin), Math.round(font.tables.head.xMax), Math.round(font.tables.head.yMax), ])} as const; export const PDF_FONT_GLYPHS: Readonly> = ${JSON.stringify(glyphs)}; export const PDF_FONT_WIDTHS: Readonly> = ${JSON.stringify(widths)}; `; if (process.argv.includes('--check')) { let current = ''; try { current = readFileSync(outputPath, 'utf8'); } catch {} if (current !== generated) { console.error('src/pdf/pdf-font.generated.ts is stale; run npm run pdf-font:generate'); process.exit(1); } } else { writeFileSync(outputPath, generated); console.log(`Generated ${outputPath} (${subset.length} bytes)`); }