Engineering Journal
Schema Editor
Schema Editor

Stop importing through a renderer when you own an extraction model

2026-07-17

TLDR

When two of your tools need to share document geometry, the tempting integration is whatever serialized output a library already emits, in our case pdf.js's SVGGraphics. The right integration is your own extraction model: the typed, transform-resolved primitives your pipeline already produces. Renderer output is a picture of the data; the extraction model is the data. Build the bridge on the data.

The position

Any pipeline that turns a source format into structure (a PDF parser, a DXF reader, a design-file importer) should expose its intermediate model as the integration surface for other tools, even when a library offers a ready-made "export as SVG/canvas/PNG" that looks like a shortcut. Rendering APIs answer "what should this look like"; extraction models answer "what is this." Downstream tools that analyze, rather than display, need the second answer.

What the shortcut looks like, and why teams take it

pdf.js ships (shipped) SVGGraphics: hand it a page, get an SVG. Our original plan for PDF-to-diagram import was exactly that: render the page to SVG in one tool, ship the string to the diagram editor, re-parse it there. Zero new code on the producing side. Most cross-tool integrations start this way because the renderer's output format is the one both sides already understand.

Why it fails for analysis consumers

Renderer output is structure-blind by design. SVGGraphics emits draw-order soup: paths in painting sequence, text as positioned glyph outlines, transforms nested however the renderer's state machine happened to push them. The consuming tool must then reverse-engineer structure out of a format built to discard it: re-parse d strings back into coordinates, re-flatten transform chains, re-guess which strokes are meaningful.

You inherit the library's lifecycle. SVGGraphics was deprecated and then removed in pdf.js v4. An integration built on it has a forced migration on someone else's schedule. Our extraction path (a CTM adapter that walks the operator list, multiplies transform matrices, and emits world-space subpaths with stroke metadata) is ours; it deprecates when we say so.

You do the same work twice, worse. Our pipeline already resolved every transform and classified segments into horizontal, vertical, diagonal, and closed rectangles. Piping through a renderer throws that away at the boundary and forces the consumer to approximate it from path strings. Numbers in, path-string archaeology out.

The better approach

Serialize the model, not the picture. Our bridge builds a minimal SVG from the classified primitives, with the semantics attached as data attributes the consumer's own analyzer natively reads:

segs.map(s => <line x1="${s.x1}" y1="${s.y1}" x2="${s.x2}" y2="${s.y2}"
  data-geo-class="wire"/>)
rects.map(r => <rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}"
  data-geo-class="component"/>)

The consuming editor skips its heuristics entirely: classification arrives as ground truth, endpoints arrive as numbers, and the whole thing flows into topology analysis deterministically. The transport happens to be SVG because both tools speak it, but the payload is the extraction model wearing an SVG costume, not a rendering.

What you give up

Fidelity. A renderer reproduces everything: gradients, images, text styling, clipping. The model-based bridge carries only what the extraction understood, so a page with content your pipeline does not model arrives incomplete. For an analysis consumer that is correct behavior (it could not have used what the pipeline cannot type), but it makes this the wrong bridge for display use cases.

When the renderer is right

If the consumer just needs to show the document (thumbnails, previews, print), use the rendering API; that is its job, and re-deriving pixels from your model would be reinventing it badly. The split is clean: renderers for eyes, extraction models for algorithms. The mistake is only ever using the first to feed the second.

Read this post in the full Engineering Journal →