{"record":{"id":"5d258976956e2fe9","repo":"santifer/career-ops","slug":"font-file-not-found-keeping-original-referenc","errorCode":null,"errorMessage":"⚠️  Font file not found, keeping original reference: fonts/${name}","messagePattern":"⚠️  Font file not found, keeping original reference: fonts/(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"generate-pdf.mjs","lineNumber":1409,"sourceCode":"    const fontPath = resolve(fontsDir, name);\n    const rel = relative(fontsDir, fontPath);\n    if (rel.startsWith('..') || isAbsolute(rel)) {\n      console.warn(`⚠️  Font reference escapes fonts/, keeping original reference: ${name}`);\n      continue;\n    }\n    if (_fontDataUrlCache.has(fontPath)) {\n      dataUrls.set(name, _fontDataUrlCache.get(fontPath));\n      continue;\n    }\n    try {\n      const buf = await readFile(fontPath);\n      const ext = name.slice(name.lastIndexOf('.') + 1).toLowerCase();\n      const dataUrl = `url('data:${MIME[ext] || 'application/octet-stream'};base64,${buf.toString('base64')}')`;\n      _fontDataUrlCache.set(fontPath, dataUrl);\n      dataUrls.set(name, dataUrl);\n    } catch (err) {\n      if (err?.code !== 'ENOENT') throw err;\n      console.warn(`⚠️  Font file not found, keeping original reference: fonts/${name}`);\n    }\n  }\n  return html.replace(FONT_REF, (match, _quote, name) => dataUrls.get(name) || match);\n}\n\n/**\n * Render an HTML string to a PDF file via headless Chromium.\n *\n * Writes the HTML to a temporary file in the baseDir and loads it via\n * page.goto() to give the page a file:// origin. This allows relative\n * resources (images, fonts) to load — setContent() runs from about:blank\n * and Chromium blocks file:// subresource loads from non-file origins.\n *\n * Local url('./fonts/...') references are inlined as data: URLs first so\n * fonts also survive the ATS normalization pass (which may strip font refs).\n *\n * @param {string} html - Full HTML document to render.\n * @param {string} outputPath - Absolute path to write the PDF to.","sourceCodeStart":1391,"sourceCodeEnd":1427,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/generate-pdf.mjs#L1391-L1427","documentation":"Graceful-degradation warning while inlining fonts for PDF rendering in generate-pdf.mjs: a CSS font reference matching fonts/<name> could not be read with ENOENT, so instead of embedding a data: URL the original 'fonts/<name>' reference is kept and Chromium falls back to system fonts. Non-ENOENT errors still propagate; only a genuinely missing file warns.","triggerScenarios":"Rendering a CV whose template references fonts/<name> while the fonts/ directory (resolved against baseDir) lacks that file — e.g. template updated to a new font family, fonts/ never populated, or rendering from a cwd/baseDir where fonts/ does not exist. _fontDataUrlCache means the miss is retried per process, not cached.","commonSituations":"Customized templates/cv-template.html referencing webfonts never downloaded; cloning without the (gitignored) fonts/ dir; renaming a font file but not the CSS; batch renders from a different baseDir than interactive ones.","solutions":["Put the missing font file at fonts/<name> relative to the render baseDir (exact name from the warning)","Or remove/replace the @font-face reference in the template if the font is not needed","Verify the PDF afterwards — layout metrics (page count, spacing) can shift under fallback fonts"],"exampleFix":"# before\ncp template-with-fonts.html my-cv.html && node generate-pdf.mjs my-cv.html   # fonts/Inter.ttf absent\n# after\nmkdir -p fonts && cp ~/Downloads/Inter.ttf fonts/Inter.ttf && node generate-pdf.mjs my-cv.html","handlingStrategy":"fallback","validationCode":"import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nfor (const name of fontNamesReferencedBy(html)) {\n  if (!existsSync(join(baseDir, 'fonts', name))) {\n    console.warn(`font missing, expect fallback metrics: fonts/${name}`);\n  }\n}","typeGuard":"function hasAllFonts(html, baseDir) {\n  const names = [...html.matchAll(FONT_REF)].map((m) => m[2]);\n  return names.every((n) => existsSync(join(baseDir, 'fonts', n)));\n}","tryCatchPattern":"// Already warn-and-fallback by design; callers should react to the warning:\nif (!hasAllFonts(html, baseDir)) {\n  // either populate fonts/ or accept layout drift and re-check pageCount\n  console.warn('font fallback will be used; verify page count and spacing');\n}","preventionTips":["Ship the exact font files referenced by your template in fonts/ (and commit or vendor them)","After any template font change, sync fonts/ to match the @font-face names","Review rendered PDF page count after this warning — fallback fonts change text metrics"],"tags":["fonts","pdf","fallback","missing-asset"],"backgroundTag":"missing-font-file","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}