| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 |
- /**
- * Regenerates src/features/invoice-designer/Pdf/fontWidths.ts from the TTFs
- * the PDFs embed, so text can be wrapped identically on server and client
- * without loading font files at runtime. Run: node scripts/generate-font-widths.mjs
- */
- import { createRequire } from 'node:module'
- import { readFileSync, writeFileSync } from 'node:fs'
- import path from 'node:path'
- const require = createRequire(import.meta.url)
- const fontkit = require('fontkit')
- const FONTS = [
- ['Helvetica', 'Roboto-Regular.ttf', 'Roboto-Bold.ttf'],
- ['Times-Roman', 'NotoSerif-Regular.ttf', 'NotoSerif-Bold.ttf'],
- ['Courier', 'NotoSansMono-Regular.ttf', 'NotoSansMono-Bold.ttf'],
- ['Open Sans', 'OpenSans-Regular.ttf', 'OpenSans-Bold.ttf'],
- ['Lato', 'Lato-Regular.ttf', 'Lato-Bold.ttf'],
- ['Montserrat', 'Montserrat-Regular.ttf', 'Montserrat-Bold.ttf'],
- ['PT Sans', 'PTSans-Regular.ttf', 'PTSans-Bold.ttf'],
- ]
- // ASCII plus the Latin-1/Latin Extended and Cyrillic ranges the app's locales use.
- const CHARS = []
- for (let c = 0x20; c <= 0x7e; c++) CHARS.push(c)
- for (let c = 0xa0; c <= 0x17f; c++) CHARS.push(c)
- for (const c of [0x2013, 0x2014, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x20ac, 0xb7]) CHARS.push(c)
- for (let c = 0x400; c <= 0x45f; c++) CHARS.push(c)
- const dir = path.resolve('src/assets/fonts')
- const table = {}
- for (const [family, regular, bold] of FONTS) {
- for (const [weight, file] of [['regular', regular], ['bold', bold]]) {
- const font = fontkit.create(readFileSync(path.join(dir, file)))
- const upm = font.unitsPerEm
- const widths = {}
- for (const code of CHARS) {
- if (!font.hasGlyphForCodePoint(code)) continue
- const glyph = font.glyphForCodePoint(code)
- widths[code] = Math.round((glyph.advanceWidth / upm) * 1000)
- }
- // A representative width for anything outside the table.
- const fallback = widths[0x6e] ?? 500 // 'n'
- table[`${family}|${weight}`] = { fallback, widths }
- }
- }
- const out = `/**
- * Character advance widths, in thousandths of the font size, for the exact
- * font files the PDFs embed. Generated by scripts/generate-font-widths.mjs —
- * do not edit by hand. This is what lets the server wrap text the same way
- * the printed page will, without opening a font file at runtime.
- */
- export interface WidthTable {
- fallback: number
- widths: Record<number, number>
- }
- export const FONT_WIDTHS: Record<string, WidthTable> = ${JSON.stringify(table)}
- `
- writeFileSync('src/features/invoice-designer/Pdf/fontWidths.ts', out)
- console.log('written', Object.keys(table).join(', '))
|