Files
jobtrackingapp/docs/redesign/phase-9/p9-remed-02/PDF_PIPELINE.md
T

8.6 KiB
Raw Blame History

PDF pipeline and preservation investigation

Read-only source/raw-evidence assessment, 24 October 2026, baseline3727a72. This is a root-cause hypothesis and architecture map, not a claim that a repair or new runtime qualification passed.

Actual owners

  1. CvVariantService.RenderAsync reads the owner-scoped saved variant settings and current canonical Career Profile. RenderSettingsAsync renders unsaved settings against the same profile; RenderPublicAsync reads only a public slug and its owner's profile. All three call RenderInternal, then CvVariantResolver.Build, CvThemeCatalog.Resolve and the same IThemedCvRenderer.Render.
  2. CvVariantResolver produces the model from canonical profile plus variant lens: section order/visibility, language labels and presentation remain variant concerns. Rendering is not a canonical write. Save/restore revision checks and history persistence are separate owners and should remain unchanged.
  3. ThemedCvRenderer.Render normalizes settings, inherits theme values for null overrides and builds one self-contained HTML/CSS document. Nine themes are data in CvThemeCatalog, not separate renderer implementations. The renderer chooses single/sidebar/header layouts, fixed A4/Letter geometry, margins/density and flow classifications for long entries/items. Existing frozen PDF bytes are stored artifacts; rerendering HTML must not rewrite those historical bytes.
  4. CvVariantController exposes saved preview, live unsaved preview and saved export. The export wraps the same render HTML in TailoredCvRenderResult; PublicCvController uses the same exporter. Template thumbnails also use ThemedCvRenderer. A fix at this shared HTML owner reaches all these flows, so any intended font change needs thumbnail/public/export checks too.
  5. cvPreview.ts:previewDocumentHtml inserts a pagination script into the HTML. It already awaits document.fonts.ready and image decoding, copies the renderer's print rules into screen media, and uses columns as screen fragmentainers. It converts avoid-page to avoid-column and publishes readiness, physical dimensions, page count and overflow state. CvBuilderEditor waits for that state and displays individual iframe pages. A font-loading race is therefore not established by simply observing different fonts.
  6. PlaywrightCvPdfExporter writes rendered HTML to an owned temporary local file and launches a configured/native Chromium CLI. It sets headless, no PDF browser header/footer and print-to-PDF, drains diagnostics, bounds execution/admission and verifies process termination. It does not embed fonts, choose a fixed font package or pin Chromium itself. Existing process/admission/security behavior is independent of a typography repair.

Font and geometry evidence

CvThemeCatalog uses operating-system font stacks: Segoe UI/Roboto/Arial; Helvetica Neue/Arial; Georgia/Times New Roman; Roboto/Arial; Poppins/Segoe UI/Arial; and utility Roboto Mono/Consolas/Courier New. The six allowed custom font stack strings are normalized by CvVariantSettingsJson; persisted strings/null-as-inherit must remain compatible.

There is no @font-face in baseline ThemedCvRenderer. CSS family names are requests, not proof of the actual face used. The baseline Linux image installs Chromium but no corresponding document-font package. The recorded Linux environment has DejaVu Sans/Serif/Mono regular/bold; exported PDF font names confirm these substitutions. Windows reference PDFs use different families. Root independently inventories the actual Linux image/runtime/locale in the reproduction record.

The original raw oracle shows seven fidelity and four settings failures. Their shared pattern is accumulating vertical drift over long prose, including line/page reflow. This strongly supports font metric/wrapping differences as the first controlled hypothesis. It does not yet establish that every failing fixture has only that cause. Browser versions differ too; screen-column versus print fragmentation remains a distinct possibility if equal embedded fonts do not clear a fixture.

Baseline failing fixture Observed consequence
long-en-minimal Accumulating5–85mm marker shifts; markers cross page boundaries
long-nb-code Accumulating shifts and different page membership
compact-8mm-letter Accumulating shifts despite Letter dimensions being accepted
roomy-28mm-nb Preview5 versusPDF6 pages; large cumulative shifts
grid-and-bubbles Roughly5mm marker displacement after wrapping/layout
long-sidebar-nb Cumulative wrapping/page membership drift in a narrower column
unbreakable-and-boundary Long-content/page membership drift, including boundary paragraphs
light-en-after / light-nb-after Preview3 versusPDF4 pages; cumulative shifts
dark-en-after / dark-nb-after Same geometry consequence in independent theme/language settings cases

In these retained eleven failure arrays there are no missing/extra marker, blank-page, physical-page-dimension, font-size, RGB or vertical-safe-area findings. That does not prove complete arbitrary text/glyph fidelity: the oracle checks designated text markers, page text and safe bounds rather than every source character. Raw comparisons/PDF page images remain available for visual review. Missing-content, clipping, colour and nondeterminism must not be invented as established causes.

Minimal controlled repair strategy

First test identical retained HTML with an embedded, licensed font face in both preview and Linux export. Hold fixture text/settings, layout CSS, browser versions and oracle constant. Compare line/page markers before making application changes. A same-font residual must be investigated independently for print/column fragmentation, weights, fallback glyphs, browser version and timing.

If the experiment confirms missing-font metrics, package exact legally distributable font bytes with version/source/license/hash, embed the required font data into the shared render HTML, and resolve effective font choices consistently. This avoids network dependencies for srcDoc previews, exported local files and public documents. Support normal/bold/italic/bold-italic where content/weights require them; Markdown italics are actually supported. Assert Norwegian glyphs and no unintended fallback. Preserve six supported custom choices and utility mono meaning rather than silently collapsing every setting to one sans face. Avoid fake claims that a substituted face is the proprietary original.

No proprietary system font copying is justified. Candidate replacement provenance must be verified against the actual distributed files: Gelasio upstream states Georgia metric compatibility; Google Fonts metadata declares OFL and records source commit9228e69b160d79f33950e026293f6e13ba9780d0. Carlito upstream targets Calibri metrics, not Segoe UI. Neither an upstream compatibility statement nor an OFL declaration substitutes for the actual fixture measurements, retained license and bundled-byte hash.

Any new deterministic open-font choice is an intentional visual change for environments previously using another native font. Record original and new images/page counts and explain that change; do not claim identical historical typography or rewrite stored PDFs. Keep profile/settings/revision/API contracts unchanged.

Original oracle versus supplemental historical totals

scripts/verify-cv-fidelity.py compares each exported PDF's page count to that fixture's live previewPages. It checks A4/Letter dimensions, marker membership and page/position/font-size/colour agreement, hidden text, blank pages and vertical safe bounds, then renders actual PDF bytes independently with PDFium and writes comparison images. Tolerances are2mm position,0.5mm dimensions,0.5pt font size and1/255 RGB.

It does not hardcode36/24 corpus page totals. Those totals were an extra p9-requal-01/pdf-proof.py collection assertion for that historical font environment. If an authorized deterministic-font change alters both preview and PDF pagination, preserve historical results and explicitly reconcile the intended new totals while keeping the original oracle and fixture membership/tolerances unchanged. A changed total is not automatically a defect or automatically acceptable: content, layout intent and actual page images still need evidence. Do not weaken the oracle or replace reference images to conceal a mismatch.

No tests, services, browsers or rendering commands were run by this read-only investigator. Root owns reproduction, visual inspection, implementation and final qualification.