Engineering Journal
Pdf Processor
Pdf Processor

Font Rendering Hottake

2026-07-11

You Don't Need Font Files to Render a PDF

TLDR: Standard PDF viewers load megabytes of embedded TrueType and OpenType fonts to render pixel-perfect text onto a canvas. For a responsive, semantic layout engine, this is pure overhead. By normalizing font metadata and mapping it to a standard CSS font registry, we get clean, lightweight web output without downloading a single binary font file.


The Canvas Rendering Illusion

Standard PDF.js was designed for a single goal: reproducing a printed page on a screen.

To do this, it takes a brute-force approach. It downloads the PDF, extracts the raw binary font subsets, registers them with the browser font-face API, and draws characters at precise, absolute coordinates on a canvas.

This is perfect for digital print previews, but it is the wrong model for responsive reading. The resulting layout is completely rigid. It does not wrap, it does not adapt to mobile viewports, and it relies on an invisible, absolutely positioned text layer just to support basic copy-paste operations.

If your goal is to extract structure and render a readable document, trying to preserve the original binary fonts is a trap.


The Metadata Shortcut

You do not need the actual font binaries to know what the text should look like. The PDF already contains the styling intent in its metadata.

PDF.js exposes this metadata in two ways. The first is through text item properties, which are messy. Exporters subset fonts and generate synthetic names like ABCDEF+Arial-BoldMT.

The second, better source is page.commonObjs. This cache stores parsed metadata about the font itself, including explicit flags for bold and italic.

Our custom extraction pipeline bypasses the font binaries entirely. We read the raw font names, clean them, and map them to a small set of standard web fallbacks:

function _normalizeFontFamily(rawName) {
    if (!rawName) return { family: 'inherit', bold: false, italic: false };
    const name = rawName.replace(/^[A-Z]{6}\+/, '');
    const bold = /bold|heavy|black/i.test(name);
    const italic = /italic|oblique|slanted/i.test(name);
    // ...

By resolving the font names to a few generic families (such as Arial, Times New Roman, and Georgia) and combining them with the parsed style flags, we capture the typography intent without the file weight.


The Style Registry Pattern

If you write style attributes directly onto every single paragraph and heading, you end up with massive HTML documents. It also makes editor styling and user customization impossible.

The correct approach is a CSS registry:

  1. As we process each region, we calculate the dominant font properties (family, size, bold, italic) for the text.
  2. We register this font footprint in a global registry map.
  3. The registry generates a unique class name (like .f0, .f1) and saves the corresponding CSS rule.
  4. After all pages are processed, we compile the registry into a single <style> block.
function _registerFont(fontRegistry, family, sizePt, bold, italic) {
    const size = Math.round(sizePt) || 10;
    const key = ${family}|${size}|${bold ? 'b' : ''}${italic ? 'i' : ''};
    if (!fontRegistry.has(key)) {
        const cls = f${fontRegistry._counter++};
        // Generate CSS rule
        fontRegistry.set(key, { className: cls, cssLine: .pdf-doc .${cls} { ... } });
    }
    return fontRegistry.get(key).className;
}

The output HTML contains clean class declarations instead of verbose styles. This is not just a size optimization: it means a user can override the styling of the entire document by editing a single class in the editor, something that is impossible in a standard canvas-based viewer.


Reflowable Typography

Once you let go of absolute pixel positioning, the browser handles the hard part.

By mapping font metrics to standard CSS classes and injecting semantic wrappers (such as <strong> for bold and <em> for italics), we get text that flows naturally. Columns adjust to screen size, tables scale gracefully, and search engines index the text directly.

The standard PDF.js canvas viewer is a remarkable piece of engineering for pixel-perfect fidelity. But if you want to turn static documents into dynamic web content, the first step is throwing away the font files.

Read this post in the full Engineering Journal →