* Add roof surface placement support for items Items (e.g. solar panels) can now be placed on sloped roof surfaces. The placement system computes euler rotation from the roof surface normal so items sit flush on the slope instead of going inside. - Add roofStrategy to placement-strategies with enter/move/click/leave - Wire roof:enter/move/click/leave events in the placement coordinator - Add calculateRoofRotation in placement-math using surface normals - Support full 3D cursor rotation for sloped surfaces - Items on roofs are parented to the level with world-space rotation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fixed conflict * feat(floorplan): add construction dimension strings * feat(floorplan): coordinate opening dimensions * feat(floorplan): add opening documentation * feat(floorplan): add construction dimensions and notes * feat(floorplan): add interior dimensions and curved note leaders * feat(floorplan): improve construction dimensions and document plan * feat(floorplan): harden construction document output * feat(floorplan): add annotation collision diagnostics * feat(floorplan): size export annotations in paper space * feat(floorplan): automatically separate overlapping labels * fix(floorplan): resolve dense label overlaps * fix(floorplan): remove stale collision warning overlays * fix(floorplan): treat mark pills as collision obstacles * feat(floorplan): place short dimension values outside * fix(floorplan): preserve dimension string order * fix(floorplan): avoid architectural geometry in label layout * feat(floorplan): add dimension side fallback leaders * docs(floorplan): update chapter 17 implementation status * fix(floorplan): dimension subdivided interior walls * feat: add associative floor plan dimensions * feat: add continuous construction dimension strings * feat: add structural floor plan grids * feat: coordinate columns with structural grids Snap column placement and movement to structural axes and intersections, derive associative grid references, and preserve floor-plan rotation by allowing secondary-button pointer moves through the grid drafting layer. * feat: add architectural room documentation Add room-role metadata, editable documentation fields, centered room labels, and persisted live/PDF visibility while preserving generic zone behavior. * feat: generate architectural room schedules Add registry-driven room schedule rows with unit-aware areas and heights, natural room ordering, enclosure resolution, and document-quality warnings. * feat: add reliable room clear dimensions Derive unit-aware clear dimensions from proven modeled inside wall faces for straight rectangular rooms, including rotated and split-wall enclosures, while suppressing unproven datums. * feat: add architectural stair documentation Add level-aware UP/DN graphics, derived flight and rail notes, plan break and overhead conventions, linked destination-level projection, and persisted live/PDF visibility. * feat: add typed specialty construction notes Add schema-validated specialty payloads, standardized plan notation, contract-scope metadata, configurable overhead outlines, and editor authoring controls. * feat: add curved and circular dimensions Add associative radius, diameter, center, chord, arc-length, angular, and coordinate modes with unit-aware notation, repeated-feature labels, 2D authoring, and document controls. * feat: coordinate floor plan drawing types Add persistent floor, foundation, reflected-ceiling, roof, and site plan views with per-dimension show, omit, reference, and foundation-controller behavior across live and PDF output. * feat: add associative curved wall dimensions Bind radius, center, chord, arc-length, and angular construction dimensions directly to curved wall geometry so annotations update when the host curve changes. * fix: render automatic curved wall dimensions The wall floor-plan builder explicitly skipped curved walls, leaving the associative authoring workflow as the only dimension path. Render a concentric arc-length dimension automatically and keep it governed by automatic-dimension visibility. * fix: use radius callout for curved walls Replace the automatic arc-length annotation with the source-standard radius method: computed center mark, radial leader, curve arrow, and R value. Keep adjacent linear strings responsible for locating the curve tangencies and depth. * Implement construction dimension string editing * Add construction dimension standards controls * Apply drawing standards to automatic dimensions * Add floorplan overhead and reference visibility controls * Add view-specific dimension segment suppression * Add persistent drawing sheet model * Plot floorplan exports at fixed scale * Apply paper-space annotation profiles * Compose floorplan PDF sheets * Support sheet paper sizes and preflight * Persist pinned annotation layout overrides * Expand annotation collision obstacles * Add floorplan annotation preflight surface * Add reusable drawing sheet general notes * Add drawing sheet keyed note instances * Add drawing sheet document markers * Expand construction note leader terminators * Add wall assembly layer model * Resolve wall assembly datum references * Add wall assembly floorplan graphics * Add opening documentation dimension policies * Add finish-face room clear dimensions * Extend room clear dimensions to rectilinear rooms * Add construction module advisories * Add clearance advisory profiles * Add dimension completeness audit * Expand dimension completeness audit * Include preflight issues in completeness audit * feat: complete floorplan construction documentation * refactor: remove construction note node * feat: refine floorplan documentation and unit display * fix(editor): improve floorplan PDF dimensions * fix(floorplan): refresh annotation collision layout * fix(floorplan): keep annotations clear and restore registry boundaries * feat(floorplan): refine construction dimension references * fix(floorplan): align documentation tools with architecture --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
Floor-plan PDF export library research
Date: 2026-07-21
Decision summary
The missing dimension values are a conversion-boundary problem, not a limitation of PDF text.
The current export builds an SVG in the DOM and asks svg2pdf.js to reinterpret that SVG as PDF.
That makes the result depend on how the converter handles nested transforms, inherited SVG styles,
font discovery, text baselines, paint order, and non-scaling strokes. Replacing svg2pdf.js with a
second automatic SVG converter leaves those same risks in place.
The reliable design is to render the existing semantic FloorplanGeometry directly into PDF
primitives. Dimension values must be emitted with the PDF library's native text API, and dimension
lines/ticks must be emitted with explicit point widths. That preserves selectable vector text and
removes SVG/CSS interpretation from the critical path.
Recommended choices:
- Smallest and lowest-risk: keep jsPDF but stop sending dimension annotations through
svg2pdf.js. Draw dimensions as a native jsPDF overlay withdoc.text,doc.line, anddoc.rect. This is the best implementation choice even though it is not a library replacement. - If a different library is required: use PDFKit directly, rendering from
FloorplanGeometry. It has the strongest current combination of browser support, native vector drawing, transformation support, font embedding, and active maintenance. - Do not choose another automatic SVG converter as the primary fix. In particular,
SVG-to-PDFKithas been inactive since 2022 and documents unsupported features and browser font loading caveats.
No production code was changed as part of this research.
Current architecture and why it matters
The current code already has the right source model for a direct PDF backend:
- Node builders return semantic
FloorplanGeometry, includingdimension,dimension-string,dimension-label, lines, paths, polygons, circles, and groups. floorplan-dimension-renderer.tsxresolves each dimension's line endpoints, ticks, label point, label angle, font size, and label placement.floorplan-export.tsxcurrently mounts a React SVG off-screen and converts it with jsPDF +svg2pdf.js.
That means a new export backend does not need to infer measurements from DOM nodes. It can traverse the same geometry tree and emit native PDF operations deterministically.
The current converter itself says that custom fonts must be registered before conversion, calls
itself "by no means perfect," and notes that its visual tests can vary because of text measurement.
Those are material warnings for small, rotated architectural labels.
Official svg2pdf.js repository
Requirements
The selected approach should provide:
- visible dimension values at every rotation;
- selectable/searchable PDF text;
- embedded or otherwise deterministic fonts;
- explicit thin vector strokes in PDF points;
- lines, curves, polygons, circles, fills, clips, and nested transforms;
- browser-side generation and Blob/download support;
- compatibility with React and TypeScript in this monorepo;
- an API that can be tested without visual browser automation.
Comparison
| Rank | Approach | Native/selectable text | Fonts | Transforms and thin vectors | Browser/TypeScript fit | Maintenance | Integration cost |
|---|---|---|---|---|---|---|---|
| 1 | Direct jsPDF drawing, optionally as a hybrid overlay | Yes; text() emits PDF text and supports an angle or matrix |
Custom TTF through VFS + addFont |
Explicit setLineWidth; advanced mode exposes transformation matrices |
Already installed and browser-first; official typings | Active; current 4.x docs and releases | Low for annotation overlay, medium for full renderer |
| 2 | Direct PDFKit renderer | Yes; native PDF text | TTF, OTF, WOFF, WOFF2, TTC, dfont; subsetting | Canvas-like vectors, SVG path data, save/restore, translate/rotate/scale/transform, explicit line width | Browser supported, but Blob stream/bundling and separate TS types add work | Active; current 0.19.x releases | Medium-high |
| 3 | Chromium print-to-PDF | Browser's own text/SVG renderer; generally preserves vector text | Uses loaded web fonts; Puppeteer waits for fonts by default | Browser-native SVG/CSS transforms and strokes | Not a pure browser-side library: needs print UI or a headless-browser service | Very active | Low rendering rewrite, high operational cost |
| 4 | @react-pdf/renderer |
Native <Text> and SVG <Text> |
Font.register; TTF and WOFF |
SVG primitives, group transforms, explicit strokes; Canvas wraps PDFKit operations |
Browser + server React APIs, Blob provider, bundled typings | Active releases and commits | High because DOM SVG is not reusable as-is |
| 5 | pdf-lib direct renderer |
Native drawText with rotation |
Standard fonts; custom fonts through @pdf-lib/fontkit |
Lines, shapes, individual SVG path data, explicit thickness; lower-level transform work | Browser-compatible and TypeScript-native | Stable but inactive upstream since November 2021 | High |
| 6 | SVG-to-PDFKit automatic conversion |
Supports SVG text/tspan/textPath | Requires pre-registration or a callback; does not wait for async browser font loading | Supports common transforms, but documents unsupported vector-effect |
Browser possible through PDFKit; has a declaration file | Last commit August 2022; no published GitHub releases | Medium |
| 7 | Canvg raster fallback | No; text becomes pixels | Whatever the canvas resolved at rasterization time | Visually faithful at sufficient resolution, but all output is raster | Browser-friendly and TypeScript-based | Maintained; 4.0.3 released in 2025 | Low-medium |
Approach details
1. Direct jsPDF primitives — recommended incremental implementation
jsPDF already exposes all operations needed for dimension annotations:
text(text, x, y, { angle, align, baseline })for actual PDF text;- transformation matrices in advanced mode;
setLineWidth(width)in the document's declared units;- custom font registration with
addFileToVFS,addFont, andsetFont.
The official source documentation shows that text() writes a PDF text object (BT, font
selection, text position, Tj, ET) rather than rasterizing the label. It also documents angle and
matrix transforms. jsPDF text and line-width documentation,
font and advanced-mode guide
Practical design:
- Keep the current
svg2pdf.jspass temporarily for non-annotation geometry. - Exclude all
dimension,dimension-string, anddimension-labelgeometry from that SVG pass. - Resolve them into a small
PdfDimensionAnnotationdisplay list containing witness lines, dimension line, tick segments, label text, label anchor, angle, font size, and background box. - Transform model coordinates into page points once.
- Draw the background plate, lines, ticks, then
doc.text()with explicit fill color and embedded font. - Later, move walls and other geometry to the same native backend if desired.
Why this is ranked first: it eliminates the observed failure path while retaining the installed PDF engine, page setup, headers, schedules, and save flow. It also provides a narrow regression-test surface: generated PDF content can be inspected for each expected label string.
2. Direct PDFKit — recommended full replacement library
PDFKit runs in both Node and the browser. Its official documentation includes:
- selectable text and embedded font support for TTF, OTF, WOFF, WOFF2, TTC, and dfont;
- vector
moveTo,lineTo, Bézier and quadratic curves; - parsing of SVG path data (not an entire SVG DOM);
- save/restore, translate, rotate, scale, and arbitrary transform operations;
- explicit stroke widths and Blob output in the browser.
PDFKit browser setup, vector and transform APIs, text and font APIs
PDFKit 0.19 raised its documented browser floor to Firefox 115 and Safari/iOS 16, and its current release line continues to include text, font, SVG-path, and browser fixes. Official PDFKit releases
Practical design:
- Add an exhaustive
renderFloorplanGeometryToPdfKitvisitor. - Give the visitor a coordinate transform from model metres to PDF points, including the page's Y-axis inversion and plan rotation.
- Draw every dimension label using
doc.text()aftersave/translate/rotate. - Register an exact project font before rendering and use explicit point sizes.
- Pipe to a browser Blob stream and keep the existing download UX.
Tradeoffs: this is a cleaner long-term backend but a larger initial migration. PDFKit's npm package
does not currently advertise bundled declarations in its package manifest, so the TypeScript package
would normally also use @types/pdfkit. Browser output uses a Node-style stream, commonly adapted
with blob-stream. Both add integration weight compared with the existing jsPDF save flow.
3. Chromium/browser printing
Printing a dedicated page lets the browser render the same SVG, CSS, transforms, and fonts that it
renders on screen. window.print() is widely available, and print-specific CSS can control the
page. MDN window.print,
MDN printing guide
For automatic downloads, Puppeteer's Page.pdf() uses Chromium print output. Its documented
options include CSS page-size preference, background graphics, and waiting for
document.fonts.ready (enabled by default).
Puppeteer PDF guide,
Puppeteer PDF options
This is the best path when exact browser-rendering parity outweighs infrastructure cost. It is not a pure client library: either the user must use the print dialog, or the application needs a trusted server/desktop process running Chromium. That is a substantial architectural change for the current browser-side download.
4. @react-pdf/renderer
React-pdf produces PDFs in the browser and server and offers browser Blob/download components. It
has its own PDF primitives, including SVG Line, Path, Text, Tspan, and G. Its documented
presentation attributes include strokeWidth, transform, textAnchor, and
dominantBaseline. Group transforms apply to children. Its Canvas painter wraps PDFKit methods,
including text, path, rotate, lineWidth, translate, and scale.
React-pdf SVG APIs,
components and Canvas API,
font registration
This is viable and actively maintained. It is not a drop-in renderer for the existing React DOM SVG: the floor plan must be rebuilt with React-pdf's component types or drawn through its Canvas painter. For a CAD-like plan, that gives no decisive rendering advantage over direct PDFKit while adding a second React renderer and layout engine. It is more attractive if the broader drawing-sheet document will be rebuilt declaratively.
5. pdf-lib
pdf-lib runs in browsers and is written in TypeScript. It provides native drawText with rotation,
explicit line thickness, rectangles/circles/ellipses, and individual SVG path drawing. Custom fonts
are embedded through @pdf-lib/fontkit.
Official examples,
PDFPage drawing API,
DrawTextOptions
It does not parse a complete SVG document; its SVG support is for one path-data string at a time.
Consequently, it requires the same complete FloorplanGeometry visitor as PDFKit, with a less
convenient graphics-state/transform API for this use case. The upstream repository's latest commit
and release are from November 2021, so it is not the preferred new dependency for a renderer being
introduced in 2026. Official commit history,
official releases
6. SVG-to-PDFKit
SVG-to-PDFKit is the only credible alternate JavaScript full-SVG converter found. Its documented
coverage includes SVG text/tspan/textPath, transforms, paths, clips, masks, fonts, gradients, and
patterns. However, it explicitly does not support vector-effect, warns that browser fonts must be
registered before conversion because it does not wait for asynchronous loading, warns that bugs
remain, and has not received a commit since August 2022.
Official repository and support table,
official commit history
It is therefore not a sensible replacement for svg2pdf.js. It changes the converter without
removing the converter boundary.
7. Canvg raster fallback
Canvg parses SVG and renders it to Canvas; its stated purpose includes SVG rasterization. Official repository, official API
Rendering the plan at high device-pixel density and embedding the canvas as PNG would make missing
text unlikely after document.fonts.ready, because the browser/canvas has already converted the
glyphs to pixels. It is an acceptable emergency fallback or diagnostic control. It does not meet the
core deliverable: dimension text is not selectable, all geometry becomes raster, thin lines depend
on export resolution, and large plans produce larger PDFs.
Proposed implementation sequence
If implementation is approved, use this order:
- Add a library-independent PDF display list or visitor over
FloorplanGeometry. - Implement dimensions first: lines, ticks, background plates, and native text.
- Embed one exact non-variable TTF font and wait for/load it explicitly.
- Preserve all document sizes in PDF points; do not use CSS pixels for line weights.
- Keep the existing SVG conversion only for unported geometry during the transition.
- Add structural tests that inspect the generated PDF for expected text strings and page count.
- Add fixture coverage for horizontal, vertical, diagonal, rotated-plan, short/outside-label, metric, and imperial dimensions.
- Only after the annotation path is proven, decide whether to port the remaining geometry and
remove
svg2pdf.js.
For a mandated new library, substitute a direct PDFKit backend at steps 2–5 and port geometry kinds
incrementally. Do not introduce SVG-to-PDFKit as an intermediate layer.
Acceptance criteria for the eventual implementation
- Every dimension value in the source geometry is present as extractable text in the generated PDF.
- Horizontal, vertical, and diagonal values remain readable at plan rotations of 0°, 45°, 90°, and arbitrary building rotations.
- Dimension lines render at an explicit target such as 0.5 pt and ticks at 0.75 pt regardless of plan scale.
- The chosen font is embedded or a deliberate standard PDF font is used.
- Label backing plates are drawn before text and do not obscure glyphs.
- Text extraction verifies representative metric and imperial labels.
- Geometry remains vector except for explicitly documented raster-only assets.
Primary sources
- jsPDF repository
- jsPDF documentation
- jsPDF source/API documentation
svg2pdf.jsrepositorysvg2pdf.jsreleases- PDFKit repository
- PDFKit browser setup
- PDFKit vector graphics
- PDFKit text and fonts
- PDFKit releases
- React-pdf components
- React-pdf SVG primitives
- React-pdf fonts
- React-pdf releases
pdf-libdocumentationpdf-libPDFPageAPIpdf-librepositorySVG-to-PDFKitrepository- Canvg repository
- Puppeteer PDF generation
- Puppeteer PDF options
- MDN printing guide