feat: measurement tools (distance, perimeter, area) - #654
Conversation
Add calibrated measurement tools (distance, perimeter, polygon/rectangle/ ellipse area) with live, zoom-stable labels, scale calibration (incl. draw-to-calibrate), units (mm/cm/m/in/ft/yd/pt), decimal/fractional precision, and an optional secondary unit. Implementation: measurements are ordinary Line/Polyline/Polygon/Square/Circle annotations carrying typed `measurement` metadata; pure math lives in @embedpdf/models; persisted via the /EPDFCustom channel + spec /IT intent (native /Measure dictionary deferred — needs a pdfium WASM rebuild). Rendered across React/Preact/Vue/Svelte, with snippet-viewer UI (Measure mode, toolbar, calibrate dialog, property panel) and hold-Shift angle constraint.
|
@MathiasWP is attempting to deploy a commit to the OpenBook Team on Vercel. A member of the Team first needs to authorize it. |
Fill rectangle/ellipse/polygon area measurements with a subtle 45° diagonal hatch (mode === 'area' only) so the enclosed region reads as the measured area. Sized in px/scale so density stays constant across zoom; shared AreaHatch helper with Vue/Svelte mirrors.
|
@bobsingor thoughts on adding this? This is the missing piece for us to start using EmbedPDF |
|
Also, i am not sure if this is something that should be deeper integrated into PDFIUM engine or if it's correct to solve it at this layer. LKM and i'll make any needed changes |
|
Hi @bobsingor, i do not want to disturb you if you're busy with other things, but our license for the current PDF editor we're using is expiring in the next month, and this is the only feature we're missing to be able to use EmbedPDF. Do you plan on looking at this implementation? And is EmbedPDF going to keep being maintained? I've seen that there's been a while since the last update, but i'm assuming you're probably on summer vacation 😄 Would love to know what your plan is! It's okay if it cannot be added within the next month or two - then we'll just buy another year with the license we have. Also one thing i want to add is that i would love with helping reporting bugs from real usage if we manage to get EmbedPDF into our app. We have a lot of PDF usage with weird PDF's, so i would definitely help with reporting any bugs we get from Sentry to help make this library as robust as can be! |
main reorganized the repo into the v3 package layout and rewrote the annotation plugin, so every file the measure work touched was deleted upstream (42 modify/delete conflicts). Resolved by taking main's side throughout and dropping the v2 measure implementation; it is re-applied against the v3 architecture in the follow-up commit.
Re-applies the measure tool against v3, where the shape of the codebase
made most of the v2 implementation unnecessary rather than portable.
What a measurement IS changed. In v2 it was five bespoke tools with their
own handlers, patch entries and per-framework label/hatch components
(React, Preact, Vue, Svelte — four copies of the same drawing). In v3 it
is a `measurement` calibration on an ordinary geometry annotation:
- `measurement` joins the flat props vocabulary, so it is set, edited
and cleared through the same setDefaults/updateSelection paths as
colour or opacity. Setting it promotes a shape into a dimension;
`null` demotes it. No new gesture, handler or subtype.
- The five tools are `extends` presets of line/polyline/polygon/
square/circle carrying that default, plus `clickCreate: false` —
a fixed-size shape from a bare click measures nothing the user chose.
- The read-out is painted by the shared `scene()`, so every framework
renderer gets it for free. The four per-framework components are gone.
The read-out is DERIVED from the geometry on every render rather than
cached on the annotation, so a vertex drag or resize can never leave a
stale number behind. Zoom-stable sizing rides the ViewEnv `pageItems`
already threads through.
Layout:
- engine-core `annotation/measurement.ts` — units, scale, rounding,
formatting, and a zod-free `parseMeasurementInfo` guard (the engine
read path ships to the browser, so it must not pull in zod).
- engine-services — persists `/EMBD_Metadata/Measurement` plus the spec
`/IT` intent, derived from subtype+mode so the two cannot drift.
- core-annotation `measure.ts` — the geometry half: which formula a
shape uses, where the label sits, and a scanline-clipped hatch (the
SceneNode vocabulary has no patterns or clip paths and needs none).
- viewer-chrome — a Measure mode and a Measurement section in the style
panel that calibrates the armed tool or the selected annotation.
A committed annotation renders as the engine's baked /AP raster, which
never runs `scene()` — and the engine cannot bake a read-out. So
`measureScene()` is exposed separately and the painter draws it over the
raster; without this the label vanished the moment you finished drawing.
Tests: 34 in core-annotation (geometry, hatch clipping, scene layering,
overlay parity), 21 in engine-core (units, formatting, intent mapping),
14 in plugin-annotation (tool registry, repository round-trip).
The PDFium round-trip is NOT verified here: the engine test suite needs
@embedpdf/engine-runtime-wasm32, whose compiled lib/ is absent in this
workspace — 32 engine test files already fail on main for that reason.
`caret.geom` widened to `Geom` through the `Annot` annotation, so reading `.rect` off it for the upright variant did not compile. Bind the geom separately and narrowly instead. Pre-existing on main, fixed here because it fails plugin-annotation's typecheck, which blocks every downstream package's typecheck in the turbo graph — including the ones this branch touches.
ISO 32000 defines measurement intents for Line, PolyLine and Polygon only. Stamping `PolygonDimension` on a Square or Circle invents spec vocabulary, and a foreign viewer that trusted it would go looking for `/Vertices` that are not there. Those two are an EmbedPDF extension and are recognised the same way every measurement is — by the calibration itself. A demote no longer clears `/IT` on those shapes either: we never wrote it, so it may belong to whoever authored the annotation. This restores the deliberate spec-honesty call from the original v2 work, which the port had flattened into "every area is a PolygonDimension".
Adds calibrated measurement tools — distance (line), perimeter (polyline), and area (polygon / rectangle / ellipse) — with live, zoom-stable labels, scale calibration (including draw-to-calibrate), units (mm/cm/m/in/ft/yd/pt), decimal/fractional precision, and an optional secondary unit.
Implementation: measurements are ordinary Line/Polyline/Polygon/Square/Circle annotations carrying typed
measurementmetadata; the unit/geometry math is a pure module in@embedpdf/models. Calibration is persisted via the/EPDFCustomdata channel plus the spec/ITintent (a fully spec-compliant native/Measuredictionary for cross-viewer interop is deferred — it needs a pdfium WASM rebuild). Labels render across React/Preact/Vue/Svelte, and the snippet viewer gets a Measure mode (toolbar, draw-to-calibrate dialog, property panel) plus hold-Shift angle constraint.Resolves #223