Rendering Parity & Dual-Engine Pipeline
A central technical challenge in client-side resume generation is achieving exact visual parity between what the user sees in the browser and what is exported to the final PDF file. Standard browser printing (window.print()) suffers from variable browser margins, font rendering discrepancies and page break clipping.
Resume Constructor solves this problem through a dual-engine rendering pipeline:
- Compilation Engine:
@react-pdf/rendererdeclaratively constructs an A4 document according to strict Yoga Flexbox layout rules and generates an in-memory binary PDFBlob. - Pre-Download Inspection Engine: Mozilla’s
pdfjs-distconsumes the generated PDF buffer and rasterises the exact document pages onto an HTML<canvas>element inside an on-demand modal preview before downloading.
flowchart TD
State["ResumeData (Immer State)"] --> Document["ResumeDocument.tsx<br/>(@react-pdf/renderer Primitives)"]
Document --> Engine["Yoga Layout Engine<br/>(Flexbox Rules & Font Subsets)"]
Engine --> PDFBlob["In-Memory PDF Blob<br/>(application/pdf)"]
PDFBlob --> DownloadLink["Instant Download<br/>(Sanitised #quot;[Name] - Resume.pdf#quot;)"]
PDFBlob --> PDFWorker["pdfjs-dist Web Worker<br/>(pdf.worker.bundle.js)"]
PDFWorker --> CanvasRaster["HTML Canvas Render<br/>(Crisp High-DPI 2D Viewport)"]
1. Declarative Document Construction
Section titled “1. Declarative Document Construction”The exported document is defined in src/components/Preview/ResumeDocument.tsx and ResumeSection.tsx using @react-pdf/renderer primitive components (Document, Page, View, Text, Link, Svg, Path):
- Physical Page Formatting: The root
<Page size="A4">enforces standard international A4 dimensions (595.28pt by 841.89pt, an aspect ratio of approximately 0.707). - Page Break Control: Multi-line items (such as individual job entries or degree cards) are configured with
wrap={false}. This directs the Yoga layout engine to prevent breaking an accomplishment block awkwardly across page boundaries. - Font Subsetting:
src/App/loadFonts.tsregisters TTF font binaries (Font.register({ family: 'EBGaramond', fonts: [...] })) rather than WOFF. WOFF formats require runtime decompression in JavaScript (or fontkit), which introduced noticeable latency when compiling the PDF blob and rendering the preview modal or preparing downloads. Pre-registering raw TTF binaries eliminates this decompression overhead.
2. Yoga Layout Engine Constraints
Section titled “2. Yoga Layout Engine Constraints”Unlike standard web CSS, layout in @react-pdf/renderer is driven entirely by the Yoga layout engine. This imposes strict architectural constraints:
- No CSS Grid or Floats: All layout arrangements must be composed strictly using Flexbox (
flexDirection: 'row',justifyContent: 'space-between',alignItems: 'baseline'). - No CSS Variables or Dynamic Functions: CSS custom properties,
clamp(),calc()andfirst baselineare unsupported in Yoga. All measurements must be resolved to static point (pt) or pixel values. - SVG Path Rendering: Brand icons (GitHub, LinkedIn, Website, Telegram) are rendered using vector
<Svg viewBox="0 0 640 640">with explicit<Path>definitions rather than external image files or CSS font glyphs.
3. On-Demand Modal Canvas Rasterization
Section titled “3. On-Demand Modal Canvas Rasterization”The pre-download inspection modal in src/components/Preview/Preview.tsx visualises the real document compiled from @react-pdf/renderer without relying on inconsistent native browser PDF reader plug-ins:
- Worker Offloading: PDF parsing and font decoding run off the main thread inside
pdfjs-dist/build/pdf.worker.mjs, bundled via webpack 5 as an independent chunk (pdf.worker.bundle.js). - Scale Factor Calculation: The preview calculates the available container width inside the modal dialog and applies a high-DPI scaling factor (
scale: 2.0) to guarantee razor-sharp typography on Retina displays. - Race Condition Prevention: When navigating between pages or re-rendering with updated data, in-flight render operations are cancelled via
renderTask.cancel()to prevent outdated frames from overwriting newer renders. - Window Resize Debouncing:
useDebouncedWindowSizedebounces viewport adjustments by 1000ms (DEBOUNCE_DELAY_MS = 1000) before re-measuring canvas dimensions.
4. Headless Testing Parity (JSDOM)
Section titled “4. Headless Testing Parity (JSDOM)”Standard JSDOM environments do not implement the HTML Canvas 2D rendering context (HTMLCanvasElement.prototype.getContext('2d') returns null).
To maintain test execution speed without native binary canvas dependencies:
- In
jest.setup.tsx,pdfjs-dist/webpackis comprehensively mocked. - The mocked
render()lifecycle returns a resolved promise without attempting to access raw 2D contexts, ensuring test suites execute in milliseconds while testing pure component lifecycle and prop propagation.