How Firefox Renders PDFs Using PDF.js and HTML5 Canvas

This article explains the technical process of how the Mozilla Firefox web browser natively renders Portable Document Format (PDF) files without external plugins. It covers the architecture of the built-in PDF.js viewer, detailing how it fetches, parses, and translates complex PDF binary data into visual elements using the HTML5 Canvas API, Web Workers, and DOM overlays for optimal performance.

The Two-Layer Architecture

PDF.js operates on a split architecture to keep the browser responsive while handling heavy document files. The system is divided into two main parts:

  1. The Core Layer (Web Worker): Handles the heavy lifting of downloading, decompressing, and parsing the raw PDF binary data in a background thread.
  2. The Display Layer (Main Thread): Receives processed instructions from the Core layer and paints the visual output onto the screen using the HTML5 <canvas> element.

Step 1: Parsing Data in the Web Worker

PDF files are complex binary documents containing nested objects, compressed streams, font definitions, and cross-reference tables. When you open a PDF in Firefox, PDF.js immediately spawns a Web Worker.

This background worker fetches the PDF data in chunks and parses the internal structure of the document. By isolating this CPU-intensive parsing process in a separate thread, Firefox ensures that the browser's main user interface never freezes or stutters, even when loading massive, graphics-heavy documents.

Step 2: Converting PDF Operators to JavaScript Instructions

Inside a PDF, the visual layout of a page is not stored as a static image. Instead, it is described using a sequence of PostScript-like drawing commands called "operators." These operators dictate coordinates for lines, color fills, text placement, and image masks.

The HTML5 Canvas cannot read native PDF operators directly. Therefore, the PDF.js Web Worker reads the PDF's content stream and translates these native operators into a streamlined array of JavaScript drawing instructions, known internally as the Operator List. Once a page's Operator List is compiled, the Web Worker sends it to the main browser thread via message passing.

Step 3: Drawing on the HTML5 Canvas

When the main thread receives the Operator List, the Display layer takes over. Firefox dynamically generates an HTML5 <canvas> element for the pages currently in the user's viewport.

PDF.js initializes a standard 2D rendering context (CanvasRenderingContext2D) on the canvas and iterates through the Operator List. It maps each translated PDF command directly to its corresponding HTML5 Canvas API method:

  • Vector Graphics: Shapes, tables, and lines are drawn by translating PDF path operators into Canvas methods like beginPath(), moveTo(), lineTo(), and bezierCurveTo().
  • Colors and Styles: PDF color spaces (such as CMYK) are converted by the worker into standard RGB values, which the canvas applies using the fillStyle and strokeStyle properties.
  • Images: Embedded JPEGs or raw image streams within the PDF are decoded into pixel arrays (Uint8ClampedArray), converted into ImageData objects, and painted onto the canvas using drawImage().
  • Typography: Embedded PDF fonts are extracted, sanitized, and converted on the fly into web-compatible OpenType fonts. They are loaded into the browser's document environment using the CSS FontFace API, allowing the canvas to draw the characters accurately using fillText() or by tracing the font glyphs as vector paths.

Step 4: Memory Management and Virtual Rendering

Rendering every page of a 500-page PDF onto individual HTML5 canvases simultaneously would crash the browser due to excessive RAM and GPU memory consumption.

To prevent this, PDF.js uses a virtualized scrolling mechanism. It only renders the <canvas> elements for the pages currently visible on the screen, plus a small buffer of adjacent pages. As the user scrolls, canvases that move far out of the viewport are destroyed to free up memory, and new canvases are dynamically generated and painted for the incoming pages.

Step 5: The Invisible Text Layer Overlay

A major limitation of the HTML5 Canvas is that it outputs a flat bitmap of pixels. Once text is drawn onto a canvas, the browser no longer recognizes it as selectable text.

To allow users to highlight, copy, and search text within a PDF, PDF.js constructs an invisible HTML layer directly over the <canvas> element. Using the exact geometric coordinates and font metrics extracted during the worker's parsing phase, PDF.js generates transparent <span> elements containing the document's text. When you select text in Firefox's PDF viewer, you are interacting with this invisible DOM overlay, while visually viewing the crisp typography rendered on the HTML5 Canvas directly beneath it.