import { PDFFont, PDFDocument, StandardFonts } from 'pdf-lib'; type FontSource = | { type: 'standard'; name: StandardFonts } | { type: 'remote'; url: string }; const FONT_SOURCES: Record = { roman: { type: 'standard', name: StandardFonts.Helvetica }, arabic: { type: 'remote', url: '/static/fonts/NotoSansArabic-Regular.ttf' }, japanese: { type: 'remote', url: '/static/fonts/Meiryo.ttf' }, korean: { type: 'remote', url: '/static/fonts/malgun.ttf' }, chinese: { type: 'remote', url: '/static/fonts/SimSun.ttf' }, thai: { type: 'remote', url: '/static/fonts/NotoSansThai-Regular.ttf' }, }; const fontBytesCache = new Map(); const embeddedFontCache = new WeakMap>>(); const FALLBACK_FONT = { type: 'standard', name: StandardFonts.Helvetica } as const; async function fetchFontBytes(url: string): Promise { const cached = fontBytesCache.get(url); if (cached) { return cached; } const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch font from ${url}`); } const arrayBuffer = await response.arrayBuffer(); const bytes = new Uint8Array(arrayBuffer); fontBytesCache.set(url, bytes); return bytes; } export async function loadFontForAlphabet( pdfDoc: PDFDocument, alphabet: string | undefined ): Promise { const key = alphabet && FONT_SOURCES[alphabet] ? alphabet : 'roman'; const source = FONT_SOURCES[key] ?? FALLBACK_FONT; let perDocCache = embeddedFontCache.get(pdfDoc); if (!perDocCache) { perDocCache = new Map>(); embeddedFontCache.set(pdfDoc, perDocCache); } const cacheKey = source.type === 'standard' ? source.name : source.url; const existing = perDocCache.get(cacheKey); if (existing) { return existing; } let fontPromise: Promise; if (source.type === 'standard') { fontPromise = pdfDoc.embedFont(source.name, { subset: true }); } else { fontPromise = fetchFontBytes(source.url) .then(bytes => pdfDoc.embedFont(bytes, { subset: true })) .catch(async () => { // Fall back to a standard font if remote font loading fails return pdfDoc.embedFont(StandardFonts.Helvetica, { subset: true }); }); } perDocCache.set(cacheKey, fontPromise); return fontPromise; }