← All docs

Managed vs Native (pdfium) — fidelity, performance, prod-readiness

Head-to-head of the two build flavors converting the same PDFs to HTML, each page scored against the pdfium page render (ground truth) with bench/fidelity_bench.py. Both flavors run over an identical 39-document representative sample (text-heavy, chart, photo-cover, multi-language + a few pdf.js edge cases), sampling the cover page + 10 body pages per document — 408 pages compared.

Fidelity

metric Managed (SVG + live text) Native (pdfium raster fallback + live text)
mean SSIM 0.850 0.859
median SSIM 0.871 0.877
mean structural cosine (photo/AA-robust) 0.958 0.978
confirmed suspects (SSIM<0.72 and coverage off) 5 2
pages where managed ≥ native 392 / 408 (96 %) —
pages where native beats managed by >0.10 SSIM 11 / 408 (3 %) —

The two flavors are within ~1 % mean SSIM, and managed equals or beats native on 96 % of pages. Native's advantage is concentrated on the 3 % of pages that are design-heavy graphics — full-bleed marketing covers and infographic spreads — where pdfium's raster is more robust than the managed SVG path.

Where native wins (the gap)

All the >0.10-SSIM wins are graphics-heavy pages. The design-heavy-cover defects surfaced here:

On text, tables and charts — the body of an ESEF financial report — the two flavors are indistinguishable (managed's #20 / #18 / soft-mask / JPX / #21 / stencil-mask fixes closed the confirmed defects).

SSIM under-scores managed's faithful vector photos (pdfium and Blink rasterize JPEGs differently), so the raw-SSIM gap overstates the real difference. The coverage-ratio + structural-cosine columns and the confirmed-suspect count are the fair signal.

Performance (convert, 30 pages, -f pages)

document managed native
SAP 2024 (text) 1.2 s 1.4 s
Continental 2024 (charts) 4.5 s 4.3 s
Novo Nordisk 2024 1.1 s 1.1 s

Essentially identical. The native raster fallback only adds pdfium-render time on the specific pages it rasters; everywhere else both take the same managed extraction path.

Deployment

managed native
pdfium native binaries 0 (native-free) 12
output size on disk 90 MB 160 MB
runs on any .NET 10 target, no native libs needs the pdfium binaries per platform
output pure inline SVG + live selectable HTML text same, but hard pages are an inline raster PNG with live text on top

Managed is native-free — deploys anywhere .NET 10 runs, no per-platform native libraries, and every page stays machine-readable vector + live text (the ESEF requirement).

Verdict — is managed production-ready?

For ESEF / born-digital financial reports (text, tables, charts, photos): yes. Managed is fidelity-equivalent to native (mean SSIM within 1 %, ≥ native on 96 % of pages, identical performance), fully machine-readable, and native-free. The confirmed fidelity defects surfaced by the bench in this area were fixed this cycle (chart strokes #20, soft-masked images #18 + Merlin/Kofola).

The remaining gap is design-heavy marketing covers (~1–3 % of pages), where managed occasionally drops a complex cover image or subset-font glyph that native's pdfium raster handles.

The pure-managed flavor reproduces page content as vector SVG with live text. Where a page contains content it cannot reproduce faithfully, it emits a per-page warning identifying the page and the feature, and keeps the vector background — it never silently degrades. The native flavor additionally carries an independent renderer (pdfium) that can substitute a raster background for such pages; text stays live HTML in both flavors. Because that rescue depends on a second, independent PDF implementation, it is inherently native-only: a managed renderer would be driven by the same extraction that flagged the page, and would reproduce the same gap.

Font-embedding policy parity (#45)

Both engines build glyph outlines themselves rather than delegating rendering to pdfium/Skia — the native engine reads FPDFFont_GetGlyphPath per captured code point into the same PathCommand shape the managed engine emits from the vendored parser, and both feed the result into the same shared, engine-agnostic decision logic (DynDyn.Pdf.Core.Fonts.FontEmbeddingRights / FontSubstitution, DynDyn.Pdf.Fonts' vendored OFL Liberation faces). There is no pdfium-vs-managed architectural fork here: FontEmbeddingPolicy.SubstituteFree now behaves identically on both engines for a restricted-embedding font (fsType Restricted License / No Subsetting / Bitmap-Only) — the source's glyph advances are kept and every glyph is redrawn from the bundled OFL substitute, gated the same way behind ConversionOptions.EmbeddingRightsAttestation. PdfToHtmlConverter.BuildFontAssets (native) was ported line-for-line from DocumentConverter.BuildFontAssets (managed) to use the full FontEmbeddingRights.Decide overload instead of the old Strict-only Skip check.

Fixed (#193): the native FontRegistry used to build a FontSpec only for a font it had captured an embedded program from — a PDF font dictionary with no FontFile/FontFile2/FontFile3 at all (e.g. a bare reference to a standard-14 font) never reached the font pipeline, so FontSpec.NoEmbeddedProgram was hardcoded false on the native path and SubstituteFree's "never embedded to begin with" branch could never fire there. FontRegistry.GetOrRegister now asks pdfium directly (FPDFFont_GetIsEmbedded) rather than inferring embedding from whether font-data bytes were readable, and registers an accumulator for every font it sees — embedded or not — so FontSpec.NoEmbeddedProgram carries the real signal and SubstituteFree behaves identically on both engines for this case too. See FontRegistryNonEmbeddedTests in DynDyn.Tests.Native for the end-to-end regression (a real pdfium round trip through a bare /BaseFont /Helvetica reference).