@embedpdf/plugin-measurement 3.0.0-next.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,469 @@
1
+ import { t as MeasurementToken } from "./contract-DO0emN-T.js";
2
+ import { definePlugin } from "@embedpdf/core";
3
+ import { AnnotationToken } from "@embedpdf/plugin-annotation/contract/host";
4
+ import { InteractionToken } from "@embedpdf/plugin-interaction/contract";
5
+ import { AnnotationToken as AnnotationToken$1 } from "@embedpdf/plugin-annotation/contract";
6
+ import { EngineError, EngineErrorCode, METRES, PermissionDenied, areaUnitLabel, assertWritableMeasure, measureFromKnownLength, measureFromRatio, measurementPoint, measurementReadout, serializeError, squareMetres, squareOf, viewportForPoint } from "@embedpdf/engine-core/runtime";
7
+ //#region src/scale.ts
8
+ const DEFAULT_PRESETS = [
9
+ ...[
10
+ 1,
11
+ 10,
12
+ 20,
13
+ 50,
14
+ 100,
15
+ 200
16
+ ].map((real) => ({
17
+ id: `metric-${real}`,
18
+ label: `1:${real}`,
19
+ paper: 1,
20
+ real,
21
+ unit: "m"
22
+ })),
23
+ {
24
+ id: "imperial-quarter",
25
+ label: "1/4 in = 1 ft",
26
+ paper: 1,
27
+ real: 48,
28
+ unit: "ft"
29
+ },
30
+ {
31
+ id: "imperial-eighth",
32
+ label: "1/8 in = 1 ft",
33
+ paper: 1,
34
+ real: 96,
35
+ unit: "ft"
36
+ }
37
+ ];
38
+ const defaultMeasure = (config, userUnit = 1) => typeof config.defaultScale === "object" ? config.defaultScale : measureFromRatio(1, 1, config.defaultScale === "imperial" ? "ft" : "m", userUnit);
39
+ function selectPageScale(viewports, crop, fallback, persistent) {
40
+ const selected = viewports.find((v) => v.owned) ?? viewportForPoint(viewports, {
41
+ x: (crop.left + crop.right) / 2,
42
+ y: (crop.top + crop.bottom) / 2
43
+ });
44
+ return {
45
+ measure: selected ? selected.measure ?? null : fallback,
46
+ source: selected ? selected.owned ? "owned" : "foreign" : "default",
47
+ ready: true,
48
+ persistent
49
+ };
50
+ }
51
+ function withUnit(m, unit, area = squareOf(unit)) {
52
+ const basis = m.x[0]?.unit.trim();
53
+ if (!(basis in METRES)) throw new RangeError("This scale has an unknown base unit; calibrate it first");
54
+ const previous = m.distance[m.distance.length - 1];
55
+ const precision = previous?.fraction === "fraction" ? 100 : previous?.precision ?? 100;
56
+ return {
57
+ ...m,
58
+ distance: [{
59
+ unit,
60
+ conversion: METRES[basis] / METRES[unit],
61
+ precision,
62
+ fraction: "decimal"
63
+ }],
64
+ area: [{
65
+ unit: areaUnitLabel(area),
66
+ conversion: METRES[basis] ** 2 / squareMetres(area),
67
+ precision,
68
+ fraction: "decimal"
69
+ }]
70
+ };
71
+ }
72
+ function withPrecision(m, precision) {
73
+ if (!Number.isInteger(Math.log10(precision)) || precision < 1 || precision > 1e9) throw new RangeError("Invalid decimal precision");
74
+ const update = (formats) => formats.map((f, i) => i === formats.length - 1 ? {
75
+ ...f,
76
+ precision,
77
+ fraction: "decimal"
78
+ } : f);
79
+ return {
80
+ ...m,
81
+ distance: update(m.distance),
82
+ area: update(m.area)
83
+ };
84
+ }
85
+ /** Change the area display unit while preserving imported distance formatting. */
86
+ function withAreaUnit(measure, unit) {
87
+ const basis = measure.x[0]?.unit.trim();
88
+ if (!(basis in METRES)) throw new RangeError("This scale has an unknown base unit; calibrate it first");
89
+ const previous = measure.area[measure.area.length - 1];
90
+ const precision = previous?.fraction === "fraction" ? 100 : previous?.precision ?? 100;
91
+ return {
92
+ ...measure,
93
+ area: [{
94
+ unit: areaUnitLabel(unit),
95
+ conversion: METRES[basis] ** 2 / squareMetres(unit),
96
+ precision,
97
+ fraction: "decimal"
98
+ }]
99
+ };
100
+ }
101
+ //#endregion
102
+ //#region src/capability.ts
103
+ const LOADING = {
104
+ measure: null,
105
+ source: "default",
106
+ ready: false,
107
+ persistent: false
108
+ };
109
+ const UNITS = Object.keys(METRES);
110
+ function createMeasurementCapability(ctx, config, effects) {
111
+ const anno = ctx.get(AnnotationToken$1);
112
+ const interaction = ctx.get(InteractionToken);
113
+ const canCalibrate = () => ctx.doc?.security.allows("doc.annotate.modify") ?? false;
114
+ const pons = (pon, opts) => pon === "all" || opts?.allPages ? (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber) : [pon];
115
+ const scaleOf = (pon) => {
116
+ const scale = ctx.getState().pages[pon]?.scale.measure;
117
+ if (!scale || scale.subtype !== "RL") throw new RangeError("Calibrate this page before changing its units or precision");
118
+ return scale;
119
+ };
120
+ const presets = config.presets ?? DEFAULT_PRESETS;
121
+ return {
122
+ canCalibrate,
123
+ canMeasure: (pon) => anno.canCreate() && !!ctx.getState().pages[pon]?.scale.ready,
124
+ pageScale: (pon) => ctx.getState().pages[pon]?.scale ?? LOADING,
125
+ isBusy: () => ctx.getState().pending > 0,
126
+ lastReports: () => ctx.getState().reports,
127
+ prepare: effects.prepare,
128
+ setPageScale: (pon, measure, opts = {}) => effects.change(pons(pon, opts), () => measure, opts),
129
+ calibrate: (pon, from, to, real, opts = {}) => {
130
+ const a = measurementPoint(from);
131
+ const b = measurementPoint(to);
132
+ const scale = measureFromKnownLength(Math.hypot(b.x - a.x, b.y - a.y), real);
133
+ return effects.change(pons(pon, opts), () => scale, opts);
134
+ },
135
+ setUnit: (pon, unit, areaUnit, opts = {}) => effects.change(pons(pon, opts), (p) => withUnit(scaleOf(p), unit, areaUnit), {
136
+ ...opts,
137
+ allPages: pon === "all" || opts.allPages
138
+ }),
139
+ setPrecision: (pon, precision, opts = {}) => effects.change(pons(pon, opts), (p) => withPrecision(scaleOf(p), precision), {
140
+ ...opts,
141
+ allPages: pon === "all" || opts.allPages
142
+ }),
143
+ setPreset: (pon, id, opts = {}) => {
144
+ const preset = presets.find((p) => p.id === id);
145
+ if (!preset) return Promise.reject(/* @__PURE__ */ new RangeError("Unknown scale preset"));
146
+ return effects.change(pons(pon, opts), (p) => measureFromRatio(preset.paper, preset.real, preset.unit, ctx.document()?.pages.find((page) => page.pageObjectNumber === p)?.userUnit ?? 1), opts);
147
+ },
148
+ presets: () => presets,
149
+ units: () => UNITS,
150
+ areaUnits: () => [
151
+ ...UNITS.map(squareOf),
152
+ "ha",
153
+ "acre"
154
+ ],
155
+ setAreaUnit: (pon, unit, opts = {}) => effects.change(pons(pon, opts), (p) => withAreaUnit(scaleOf(p), unit), opts),
156
+ readout: (ref) => {
157
+ const dto = anno.get(ref);
158
+ return dto ? measurementReadout(dto) : { unavailable: "not-dimension" };
159
+ },
160
+ startCalibration: () => {
161
+ if (canCalibrate()) {
162
+ ctx.dispatch({
163
+ type: "CALIBRATION",
164
+ request: null
165
+ });
166
+ interaction.activateTool("calibrate");
167
+ }
168
+ },
169
+ calibrationRequest: () => ctx.getState().calibration,
170
+ dismissCalibration: () => ctx.dispatch({
171
+ type: "CALIBRATION",
172
+ request: null
173
+ }),
174
+ onCalibrationRequested: (cb) => {
175
+ effects.calibrationListeners.add(cb);
176
+ return () => {
177
+ effects.calibrationListeners.delete(cb);
178
+ };
179
+ },
180
+ onScaleChanged: (cb) => {
181
+ effects.scaleListeners.add(cb);
182
+ return () => {
183
+ effects.scaleListeners.delete(cb);
184
+ };
185
+ }
186
+ };
187
+ }
188
+ //#endregion
189
+ //#region src/effects.ts
190
+ /** All engine IO and cross-plugin lifecycle work lives here. Calls serialize
191
+ * per page; refresh epochs prevent an old read from replacing a newer scale. */
192
+ function createMeasurementEffects(ctx, config) {
193
+ const anno = ctx.get(AnnotationToken);
194
+ const interaction = ctx.get(InteractionToken);
195
+ let disposed = false;
196
+ const epochs = /* @__PURE__ */ new Map();
197
+ const reading = /* @__PURE__ */ new Map();
198
+ const queues = /* @__PURE__ */ new Map();
199
+ const calibrationListeners = /* @__PURE__ */ new Set();
200
+ const scaleListeners = /* @__PURE__ */ new Set();
201
+ const meta = (pon) => ctx.document()?.pages.find((p) => p.pageObjectNumber === pon);
202
+ const live = () => {
203
+ if (disposed || !ctx.doc) throw new EngineError(EngineErrorCode.DocNotOpen);
204
+ };
205
+ const assertAllowed = () => {
206
+ live();
207
+ if (!ctx.doc.security.allows("doc.annotate.modify")) throw new PermissionDenied("doc.annotate.modify", "measurement");
208
+ };
209
+ const publish = (pon, viewports) => {
210
+ if (disposed) return;
211
+ const page = meta(pon);
212
+ if (!page) return;
213
+ const fallback = defaultMeasure(config, page.userUnit);
214
+ anno.setPageViewports(pon, viewports, fallback);
215
+ ctx.dispatch({
216
+ type: "PAGE_SCALE",
217
+ pon,
218
+ viewports,
219
+ scale: selectPageScale(viewports, page.boxes.crop, fallback, !!ctx.doc?.page(pon).measure)
220
+ });
221
+ };
222
+ const refresh = (pon) => {
223
+ live();
224
+ const page = meta(pon);
225
+ if (!page) return Promise.reject(new EngineError(EngineErrorCode.NotFound, "Page not found"));
226
+ const epoch = (epochs.get(pon) ?? 0) + 1;
227
+ epochs.set(pon, epoch);
228
+ anno.setPageViewports(pon, void 0, defaultMeasure(config, page.userUnit));
229
+ const previous = ctx.getState().pages[pon];
230
+ ctx.dispatch({
231
+ type: "PAGE_SCALE",
232
+ pon,
233
+ viewports: previous?.viewports ?? [],
234
+ scale: {
235
+ ...previous?.scale ?? {
236
+ measure: null,
237
+ source: "default",
238
+ persistent: !!ctx.doc.page(pon).measure
239
+ },
240
+ ready: false
241
+ }
242
+ });
243
+ const service = ctx.doc.page(pon).measure;
244
+ const request = (async () => {
245
+ try {
246
+ const viewports = service ? await service.viewports() : ctx.getState().pages[pon]?.viewports ?? [];
247
+ if (!disposed && epochs.get(pon) === epoch) publish(pon, viewports);
248
+ } catch (error) {
249
+ if (!disposed && epochs.get(pon) === epoch) ctx.dispatch({
250
+ type: "PAGE_SCALE",
251
+ pon,
252
+ viewports: [],
253
+ scale: {
254
+ measure: null,
255
+ source: "default",
256
+ ready: false,
257
+ persistent: !!service,
258
+ error: serializeError(error)
259
+ }
260
+ });
261
+ throw error;
262
+ } finally {
263
+ if (epochs.get(pon) === epoch) reading.delete(pon);
264
+ }
265
+ })();
266
+ reading.set(pon, request);
267
+ return request;
268
+ };
269
+ const prepare = (pon) => reading.get(pon) ?? (ctx.getState().pages[pon]?.scale.ready ? Promise.resolve() : refresh(pon));
270
+ const enqueue = (pon, action) => {
271
+ const result = (queues.get(pon) ?? Promise.resolve()).catch(() => {}).then(action);
272
+ queues.set(pon, result);
273
+ result.finally(() => {
274
+ if (queues.get(pon) === result) queues.delete(pon);
275
+ }).catch(() => {});
276
+ return result;
277
+ };
278
+ const save = async (pon, scale, opts) => {
279
+ assertAllowed();
280
+ await prepare(pon);
281
+ assertAllowed();
282
+ const service = ctx.doc.page(pon).measure;
283
+ if (service) {
284
+ await service.setScale(scale);
285
+ live();
286
+ await refresh(pon);
287
+ } else {
288
+ const page = meta(pon);
289
+ const old = ctx.getState().pages[pon]?.viewports ?? [];
290
+ publish(pon, [...old.filter((v) => !v.owned), {
291
+ bbox: page.boxes.crop,
292
+ name: "EmbedPDF",
293
+ owned: true,
294
+ measure: scale
295
+ }]);
296
+ }
297
+ live();
298
+ const report = opts.recalculate === false ? {
299
+ pon,
300
+ scale,
301
+ updated: [],
302
+ skipped: [],
303
+ failed: []
304
+ } : await anno.remeasurePage(pon, scale);
305
+ live();
306
+ for (const cb of scaleListeners) try {
307
+ cb({
308
+ pon,
309
+ report
310
+ });
311
+ } catch {}
312
+ return report;
313
+ };
314
+ const change = async (pons, measure, opts = {}) => {
315
+ assertAllowed();
316
+ ctx.dispatch({
317
+ type: "PENDING",
318
+ delta: 1
319
+ });
320
+ try {
321
+ const reports = await Promise.all(pons.map((pon) => enqueue(pon, async () => {
322
+ try {
323
+ await prepare(pon);
324
+ const scale = measure(pon);
325
+ assertWritableMeasure(scale);
326
+ return await save(pon, scale, opts);
327
+ } catch (error) {
328
+ if (!opts.allPages) throw error;
329
+ return {
330
+ pon,
331
+ updated: [],
332
+ skipped: [],
333
+ failed: [],
334
+ scaleError: serializeError(error)
335
+ };
336
+ }
337
+ })));
338
+ live();
339
+ ctx.dispatch({
340
+ type: "REPORTS",
341
+ reports
342
+ });
343
+ return reports;
344
+ } finally {
345
+ if (!disposed) ctx.dispatch({
346
+ type: "PENDING",
347
+ delta: -1
348
+ });
349
+ }
350
+ };
351
+ const start = () => {
352
+ const doc = ctx.doc;
353
+ if (!doc) return;
354
+ ctx.cleanup(() => {
355
+ disposed = true;
356
+ epochs.clear();
357
+ queues.clear();
358
+ calibrationListeners.clear();
359
+ scaleListeners.clear();
360
+ });
361
+ ctx.cleanup(anno.onDraftCaptured((draft) => {
362
+ if (draft.tool !== "calibrate" || !doc.security.allows("doc.annotate.modify")) return;
363
+ const a = measurementPoint(draft.from);
364
+ const b = measurementPoint(draft.to);
365
+ const userSpaceLength = Math.hypot(b.x - a.x, b.y - a.y);
366
+ if (!(userSpaceLength > 0)) return;
367
+ const request = {
368
+ pon: draft.pon,
369
+ from: a,
370
+ to: b,
371
+ userSpaceLength
372
+ };
373
+ interaction.activateTool("pointer");
374
+ ctx.dispatch({
375
+ type: "CALIBRATION",
376
+ request
377
+ });
378
+ for (const cb of calibrationListeners) try {
379
+ cb(request);
380
+ } catch {}
381
+ }));
382
+ const hydrate = () => {
383
+ for (const page of ctx.document()?.pages ?? []) refresh(page.pageObjectNumber).catch(() => {});
384
+ };
385
+ ctx.cleanup(doc.events.subscribe((event) => {
386
+ if (event.type === "page.viewportsChanged") refresh(event.pageObjectNumber).catch(() => {});
387
+ else if (event.type === "stream.desynced" || event.type === "document.versioned") hydrate();
388
+ }));
389
+ let pages = "";
390
+ const reconcile = () => {
391
+ const next = (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber).join(",");
392
+ if (next === pages) return;
393
+ pages = next;
394
+ for (const page of ctx.document()?.pages ?? []) if (!ctx.getState().pages[page.pageObjectNumber]) prepare(page.pageObjectNumber).catch(() => {});
395
+ };
396
+ ctx.cleanup(ctx.subscribe(reconcile));
397
+ reconcile();
398
+ };
399
+ return {
400
+ start,
401
+ prepare,
402
+ change,
403
+ calibrationListeners,
404
+ scaleListeners
405
+ };
406
+ }
407
+ //#endregion
408
+ //#region src/reducer.ts
409
+ const initialMeasurementState = {
410
+ pages: {},
411
+ pending: 0,
412
+ calibration: null,
413
+ reports: []
414
+ };
415
+ function measurementReducer(s, a) {
416
+ switch (a.type) {
417
+ case "PAGE_SCALE": return {
418
+ ...s,
419
+ pages: {
420
+ ...s.pages,
421
+ [a.pon]: {
422
+ viewports: a.viewports,
423
+ scale: a.scale
424
+ }
425
+ }
426
+ };
427
+ case "PENDING": return {
428
+ ...s,
429
+ pending: Math.max(0, s.pending + a.delta)
430
+ };
431
+ case "CALIBRATION": return {
432
+ ...s,
433
+ calibration: a.request
434
+ };
435
+ case "REPORTS": return {
436
+ ...s,
437
+ reports: a.reports
438
+ };
439
+ }
440
+ }
441
+ //#endregion
442
+ //#region src/measurement.plugin.ts
443
+ const measurementPlugin = (config = {}) => {
444
+ const drivers = /* @__PURE__ */ new Map();
445
+ return definePlugin({
446
+ id: "measurement",
447
+ scope: "document",
448
+ token: MeasurementToken,
449
+ requires: [AnnotationToken, InteractionToken],
450
+ initialState: initialMeasurementState,
451
+ reduce: measurementReducer,
452
+ capability: (ctx) => {
453
+ const effects = createMeasurementEffects(ctx, config);
454
+ drivers.set(ctx.documentId, effects);
455
+ ctx.cleanup(() => {
456
+ drivers.delete(ctx.documentId);
457
+ });
458
+ return createMeasurementCapability(ctx, config, effects);
459
+ },
460
+ effects: (ctx) => {
461
+ ctx.get(MeasurementToken);
462
+ drivers.get(ctx.documentId).start();
463
+ }
464
+ });
465
+ };
466
+ //#endregion
467
+ export { MeasurementToken, measurementPlugin };
468
+
469
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["AnnotationToken"],"sources":["../src/scale.ts","../src/capability.ts","../src/effects.ts","../src/reducer.ts","../src/measurement.plugin.ts"],"sourcesContent":["import {\n areaUnitLabel,\n measureFromRatio,\n METRES,\n squareMetres,\n squareOf,\n viewportForPoint,\n} from '@embedpdf/engine-core/runtime';\nimport type {\n AreaUnit,\n LengthUnit,\n PageMeasurementViewport,\n PdfMeasure,\n PdfRect,\n} from '@embedpdf/engine-core/runtime';\nimport type { MeasurementConfig, PageScale, ScalePreset } from './types';\n\nexport const DEFAULT_PRESETS: ScalePreset[] = [\n ...[1, 10, 20, 50, 100, 200].map((real) => ({\n id: `metric-${real}`,\n label: `1:${real}`,\n paper: 1,\n real,\n unit: 'm' as const,\n })),\n { id: 'imperial-quarter', label: '1/4 in = 1 ft', paper: 1, real: 48, unit: 'ft' },\n { id: 'imperial-eighth', label: '1/8 in = 1 ft', paper: 1, real: 96, unit: 'ft' },\n];\n\nexport const defaultMeasure = (config: MeasurementConfig, userUnit = 1): PdfMeasure =>\n typeof config.defaultScale === 'object'\n ? config.defaultScale\n : measureFromRatio(1, 1, config.defaultScale === 'imperial' ? 'ft' : 'm', userUnit);\n\nexport function selectPageScale(\n viewports: PageMeasurementViewport[],\n crop: PdfRect,\n fallback: PdfMeasure,\n persistent: boolean,\n): PageScale {\n const selected =\n viewports.find((v) => v.owned) ??\n viewportForPoint(viewports, {\n x: (crop.left + crop.right) / 2,\n y: (crop.top + crop.bottom) / 2,\n });\n return {\n measure: selected ? (selected.measure ?? null) : fallback,\n source: selected ? (selected.owned ? 'owned' : 'foreign') : 'default',\n ready: true,\n persistent,\n };\n}\n\nexport function withUnit(\n m: PdfMeasure,\n unit: LengthUnit,\n area: AreaUnit = squareOf(unit),\n): PdfMeasure {\n const basis = m.x[0]?.unit.trim() as LengthUnit;\n if (!(basis in METRES)) {\n throw new RangeError('This scale has an unknown base unit; calibrate it first');\n }\n const previous = m.distance[m.distance.length - 1];\n const precision = previous?.fraction === 'fraction' ? 100 : (previous?.precision ?? 100);\n return {\n ...m,\n distance: [{ unit, conversion: METRES[basis] / METRES[unit], precision, fraction: 'decimal' }],\n area: [\n {\n unit: areaUnitLabel(area),\n conversion: METRES[basis] ** 2 / squareMetres(area),\n precision,\n fraction: 'decimal',\n },\n ],\n };\n}\n\nexport function withPrecision(m: PdfMeasure, precision: number): PdfMeasure {\n if (!Number.isInteger(Math.log10(precision)) || precision < 1 || precision > 1e9) {\n throw new RangeError('Invalid decimal precision');\n }\n const update = (formats: PdfMeasure['distance']) =>\n formats.map((f, i) =>\n i === formats.length - 1 ? { ...f, precision, fraction: 'decimal' as const } : f,\n );\n return { ...m, distance: update(m.distance), area: update(m.area) };\n}\n\n/** Change the area display unit while preserving imported distance formatting. */\nexport function withAreaUnit(measure: PdfMeasure, unit: AreaUnit): PdfMeasure {\n const basis = measure.x[0]?.unit.trim() as LengthUnit;\n if (!(basis in METRES)) {\n throw new RangeError('This scale has an unknown base unit; calibrate it first');\n }\n const previous = measure.area[measure.area.length - 1];\n const precision = previous?.fraction === 'fraction' ? 100 : (previous?.precision ?? 100);\n return {\n ...measure,\n area: [\n {\n unit: areaUnitLabel(unit),\n conversion: METRES[basis] ** 2 / squareMetres(unit),\n precision,\n fraction: 'decimal',\n },\n ],\n };\n}\n","import type { PluginContext } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/contract';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract';\nimport {\n measureFromKnownLength,\n measureFromRatio,\n measurementPoint,\n measurementReadout,\n METRES,\n squareOf,\n} from '@embedpdf/engine-core/runtime';\nimport type { PdfMeasure } from '@embedpdf/engine-core/runtime';\nimport type { MeasurementEffects } from './effects';\nimport { DEFAULT_PRESETS, withAreaUnit, withPrecision, withUnit } from './scale';\nimport type {\n MeasurementAction,\n MeasurementCapability,\n MeasurementConfig,\n MeasurementState,\n PageScale,\n SetScaleOptions,\n} from './types';\n\nconst LOADING: PageScale = { measure: null, source: 'default', ready: false, persistent: false };\nconst UNITS = Object.keys(METRES) as Array<keyof typeof METRES>;\n\nexport function createMeasurementCapability(\n ctx: PluginContext<MeasurementState, MeasurementAction>,\n config: MeasurementConfig,\n effects: MeasurementEffects,\n): MeasurementCapability {\n const anno = ctx.get(AnnotationToken);\n const interaction = ctx.get(InteractionToken);\n const canCalibrate = () => ctx.doc?.security.allows('doc.annotate.modify') ?? false;\n const pons = (pon: number | 'all', opts?: SetScaleOptions) =>\n pon === 'all' || opts?.allPages\n ? (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber)\n : [pon];\n const scaleOf = (pon: number): PdfMeasure => {\n const scale = ctx.getState().pages[pon]?.scale.measure;\n if (!scale || scale.subtype !== 'RL') {\n throw new RangeError('Calibrate this page before changing its units or precision');\n }\n return scale;\n };\n const presets = config.presets ?? DEFAULT_PRESETS;\n return {\n canCalibrate,\n canMeasure: (pon) => anno.canCreate() && !!ctx.getState().pages[pon]?.scale.ready,\n pageScale: (pon) => ctx.getState().pages[pon]?.scale ?? LOADING,\n isBusy: () => ctx.getState().pending > 0,\n lastReports: () => ctx.getState().reports,\n prepare: effects.prepare,\n setPageScale: (pon, measure, opts = {}) => effects.change(pons(pon, opts), () => measure, opts),\n calibrate: (pon, from, to, real, opts = {}) => {\n const a = measurementPoint(from);\n const b = measurementPoint(to);\n const scale = measureFromKnownLength(Math.hypot(b.x - a.x, b.y - a.y), real);\n return effects.change(pons(pon, opts), () => scale, opts);\n },\n setUnit: (pon, unit, areaUnit, opts = {}) =>\n effects.change(pons(pon, opts), (p) => withUnit(scaleOf(p), unit, areaUnit), {\n ...opts,\n allPages: pon === 'all' || opts.allPages,\n }),\n setPrecision: (pon, precision, opts = {}) =>\n effects.change(pons(pon, opts), (p) => withPrecision(scaleOf(p), precision), {\n ...opts,\n allPages: pon === 'all' || opts.allPages,\n }),\n setPreset: (pon, id, opts = {}) => {\n const preset = presets.find((p) => p.id === id);\n if (!preset) {\n return Promise.reject(new RangeError('Unknown scale preset'));\n }\n return effects.change(\n pons(pon, opts),\n (p) =>\n measureFromRatio(\n preset.paper,\n preset.real,\n preset.unit,\n ctx.document()?.pages.find((page) => page.pageObjectNumber === p)?.userUnit ?? 1,\n ),\n opts,\n );\n },\n presets: () => presets,\n units: () => UNITS,\n areaUnits: () => [...UNITS.map(squareOf), 'ha', 'acre'],\n setAreaUnit: (pon, unit, opts = {}) =>\n effects.change(pons(pon, opts), (p) => withAreaUnit(scaleOf(p), unit), opts),\n readout: (ref) => {\n const dto = anno.get(ref);\n return dto ? measurementReadout(dto) : { unavailable: 'not-dimension' };\n },\n startCalibration: () => {\n if (canCalibrate()) {\n ctx.dispatch({ type: 'CALIBRATION', request: null });\n interaction.activateTool('calibrate');\n }\n },\n calibrationRequest: () => ctx.getState().calibration,\n dismissCalibration: () => ctx.dispatch({ type: 'CALIBRATION', request: null }),\n onCalibrationRequested: (cb) => {\n effects.calibrationListeners.add(cb);\n return () => {\n effects.calibrationListeners.delete(cb);\n };\n },\n onScaleChanged: (cb) => {\n effects.scaleListeners.add(cb);\n return () => {\n effects.scaleListeners.delete(cb);\n };\n },\n };\n}\n","import type { PluginContext } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/contract/host';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract';\nimport {\n assertWritableMeasure,\n EngineError,\n EngineErrorCode,\n PermissionDenied,\n measurementPoint,\n serializeError,\n} from '@embedpdf/engine-core/runtime';\nimport type { PdfMeasure, PageMeasurementViewport } from '@embedpdf/engine-core/runtime';\nimport { defaultMeasure, selectPageScale } from './scale';\nimport type {\n CalibrationRequest,\n MeasurementAction,\n MeasurementConfig,\n MeasurementState,\n ScaleChangeReport,\n SetScaleOptions,\n} from './types';\n\ntype Context = PluginContext<MeasurementState, MeasurementAction>;\n/** All engine IO and cross-plugin lifecycle work lives here. Calls serialize\n * per page; refresh epochs prevent an old read from replacing a newer scale. */\nexport function createMeasurementEffects(ctx: Context, config: MeasurementConfig) {\n const anno = ctx.get(AnnotationToken);\n const interaction = ctx.get(InteractionToken);\n let disposed = false;\n const epochs = new Map<number, number>();\n const reading = new Map<number, Promise<void>>();\n const queues = new Map<number, Promise<unknown>>();\n const calibrationListeners = new Set<(r: CalibrationRequest) => void>();\n const scaleListeners = new Set<(e: { pon: number; report: ScaleChangeReport }) => void>();\n const meta = (pon: number) => ctx.document()?.pages.find((p) => p.pageObjectNumber === pon);\n const live = () => {\n if (disposed || !ctx.doc) {\n throw new EngineError(EngineErrorCode.DocNotOpen);\n }\n };\n const assertAllowed = () => {\n live();\n if (!ctx.doc!.security.allows('doc.annotate.modify')) {\n throw new PermissionDenied('doc.annotate.modify', 'measurement');\n }\n };\n const publish = (pon: number, viewports: PageMeasurementViewport[]) => {\n if (disposed) {\n return;\n }\n const page = meta(pon);\n if (!page) {\n return;\n }\n const fallback = defaultMeasure(config, page.userUnit);\n anno.setPageViewports(pon, viewports, fallback);\n ctx.dispatch({\n type: 'PAGE_SCALE',\n pon,\n viewports,\n scale: selectPageScale(viewports, page.boxes.crop, fallback, !!ctx.doc?.page(pon).measure),\n });\n };\n const refresh = (pon: number): Promise<void> => {\n live();\n const page = meta(pon);\n if (!page) {\n return Promise.reject(new EngineError(EngineErrorCode.NotFound, 'Page not found'));\n }\n const epoch = (epochs.get(pon) ?? 0) + 1;\n epochs.set(pon, epoch);\n // Disable creation until calibration is known; never race a default into an imported viewport.\n anno.setPageViewports(pon, undefined, defaultMeasure(config, page.userUnit));\n const previous = ctx.getState().pages[pon];\n ctx.dispatch({\n type: 'PAGE_SCALE',\n pon,\n viewports: previous?.viewports ?? [],\n scale: {\n ...(previous?.scale ?? {\n measure: null,\n source: 'default',\n persistent: !!ctx.doc!.page(pon).measure,\n }),\n ready: false,\n },\n });\n const service = ctx.doc!.page(pon).measure;\n const request = (async () => {\n try {\n const viewports = service\n ? await service.viewports()\n : (ctx.getState().pages[pon]?.viewports ?? []);\n if (!disposed && epochs.get(pon) === epoch) {\n publish(pon, viewports);\n }\n } catch (error) {\n if (!disposed && epochs.get(pon) === epoch) {\n ctx.dispatch({\n type: 'PAGE_SCALE',\n pon,\n viewports: [],\n scale: {\n measure: null,\n source: 'default',\n ready: false,\n persistent: !!service,\n error: serializeError(error),\n },\n });\n }\n throw error;\n } finally {\n if (epochs.get(pon) === epoch) {\n reading.delete(pon);\n }\n }\n })();\n reading.set(pon, request);\n return request;\n };\n const prepare = (pon: number) =>\n reading.get(pon) ?? (ctx.getState().pages[pon]?.scale.ready ? Promise.resolve() : refresh(pon));\n const enqueue = (\n pon: number,\n action: () => Promise<ScaleChangeReport>,\n ): Promise<ScaleChangeReport> => {\n const result = (queues.get(pon) ?? Promise.resolve()).catch(() => {}).then(action);\n queues.set(pon, result);\n void result\n .finally(() => {\n if (queues.get(pon) === result) {\n queues.delete(pon);\n }\n })\n .catch(() => {});\n return result;\n };\n const save = async (\n pon: number,\n scale: PdfMeasure,\n opts: SetScaleOptions,\n ): Promise<ScaleChangeReport> => {\n assertAllowed();\n await prepare(pon);\n assertAllowed();\n const service = ctx.doc!.page(pon).measure;\n if (service) {\n await service.setScale(scale);\n live();\n await refresh(pon);\n } else {\n const page = meta(pon)!;\n const old = ctx.getState().pages[pon]?.viewports ?? [];\n publish(pon, [\n ...old.filter((v) => !v.owned),\n { bbox: page.boxes.crop, name: 'EmbedPDF', owned: true, measure: scale },\n ]);\n }\n live();\n const report =\n opts.recalculate === false\n ? { pon, scale, updated: [], skipped: [], failed: [] }\n : await anno.remeasurePage(pon, scale);\n live();\n for (const cb of scaleListeners) {\n try {\n cb({ pon, report });\n } catch {\n /* observers do not roll back a committed scale */\n }\n }\n return report;\n };\n const change = async (\n pons: number[],\n measure: (pon: number) => PdfMeasure,\n opts: SetScaleOptions = {},\n ): Promise<ScaleChangeReport[]> => {\n assertAllowed();\n ctx.dispatch({ type: 'PENDING', delta: 1 });\n try {\n const reports = await Promise.all(\n pons.map((pon) =>\n enqueue(pon, async () => {\n try {\n await prepare(pon);\n const scale = measure(pon);\n assertWritableMeasure(scale);\n return await save(pon, scale, opts);\n } catch (error) {\n if (!opts.allPages) {\n throw error;\n }\n return {\n pon,\n updated: [],\n skipped: [],\n failed: [],\n scaleError: serializeError(error),\n };\n }\n }),\n ),\n );\n live();\n ctx.dispatch({ type: 'REPORTS', reports });\n return reports;\n } finally {\n if (!disposed) {\n ctx.dispatch({ type: 'PENDING', delta: -1 });\n }\n }\n };\n const start = () => {\n const doc = ctx.doc;\n if (!doc) {\n return;\n }\n ctx.cleanup(() => {\n disposed = true;\n epochs.clear();\n queues.clear();\n calibrationListeners.clear();\n scaleListeners.clear();\n });\n ctx.cleanup(\n anno.onDraftCaptured((draft) => {\n if (draft.tool !== 'calibrate' || !doc.security.allows('doc.annotate.modify')) {\n return;\n }\n const a = measurementPoint(draft.from);\n const b = measurementPoint(draft.to);\n const userSpaceLength = Math.hypot(b.x - a.x, b.y - a.y);\n if (!(userSpaceLength > 0)) {\n return;\n }\n const request = { pon: draft.pon, from: a, to: b, userSpaceLength };\n interaction.activateTool('pointer');\n ctx.dispatch({ type: 'CALIBRATION', request });\n for (const cb of calibrationListeners) {\n try {\n cb(request);\n } catch {\n /* observers */\n }\n }\n }),\n );\n const hydrate = () => {\n for (const page of ctx.document()?.pages ?? []) {\n void refresh(page.pageObjectNumber).catch(() => {});\n }\n };\n ctx.cleanup(\n doc.events.subscribe((event) => {\n if (event.type === 'page.viewportsChanged') {\n void refresh(event.pageObjectNumber).catch(() => {});\n } else if (event.type === 'stream.desynced' || event.type === 'document.versioned') {\n hydrate();\n }\n }),\n );\n // Covers pages inserted after this document-scoped instance was created.\n let pages = '';\n const reconcile = () => {\n const next = (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber).join(',');\n if (next === pages) {\n return;\n }\n pages = next;\n for (const page of ctx.document()?.pages ?? [])\n if (!ctx.getState().pages[page.pageObjectNumber]) {\n void prepare(page.pageObjectNumber).catch(() => {});\n }\n };\n ctx.cleanup(ctx.subscribe(reconcile));\n reconcile();\n };\n return { start, prepare, change, calibrationListeners, scaleListeners };\n}\n\nexport type MeasurementEffects = ReturnType<typeof createMeasurementEffects>;\n","import type { MeasurementAction, MeasurementState } from './types';\n\nexport const initialMeasurementState: MeasurementState = {\n pages: {},\n pending: 0,\n calibration: null,\n reports: [],\n};\n\nexport function measurementReducer(s: MeasurementState, a: MeasurementAction): MeasurementState {\n switch (a.type) {\n case 'PAGE_SCALE':\n return { ...s, pages: { ...s.pages, [a.pon]: { viewports: a.viewports, scale: a.scale } } };\n case 'PENDING':\n return { ...s, pending: Math.max(0, s.pending + a.delta) };\n case 'CALIBRATION':\n return { ...s, calibration: a.request };\n case 'REPORTS':\n return { ...s, reports: a.reports };\n }\n}\n","import { definePlugin } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/contract/host';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract';\nimport { createMeasurementCapability } from './capability';\nimport { createMeasurementEffects, type MeasurementEffects } from './effects';\nimport { initialMeasurementState, measurementReducer } from './reducer';\nimport { MeasurementToken } from './types';\nimport type {\n MeasurementAction,\n MeasurementCapability,\n MeasurementConfig,\n MeasurementState,\n} from './types';\n\nexport const measurementPlugin = (config: MeasurementConfig = {}) => {\n // A plugin definition is reused across documents. Drivers are keyed by the\n // bound instance, never one mutable closure shared between document tabs.\n const drivers = new Map<string, MeasurementEffects>();\n return definePlugin<MeasurementState, MeasurementAction, MeasurementCapability>({\n id: 'measurement',\n scope: 'document',\n token: MeasurementToken,\n requires: [AnnotationToken, InteractionToken],\n initialState: initialMeasurementState,\n reduce: measurementReducer,\n capability: (ctx) => {\n const effects = createMeasurementEffects(ctx, config);\n drivers.set(ctx.documentId!, effects);\n ctx.cleanup(() => {\n drivers.delete(ctx.documentId!);\n });\n return createMeasurementCapability(ctx, config, effects);\n },\n effects: (ctx) => {\n ctx.get(MeasurementToken); // Capabilities are lazy; construct this document's driver first.\n drivers.get(ctx.documentId!)!.start();\n },\n });\n};\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@embedpdf/plugin-measurement",
3
+ "version": "3.0.0-next.14",
4
+ "private": false,
5
+ "description": "Page calibration and distance measurement workflows for EmbedPDF.",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./contract": {
16
+ "import": "./dist/contract.js",
17
+ "require": "./dist/contract.cjs"
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "dependencies": {
25
+ "@embedpdf/core": "3.0.0-next.14",
26
+ "@embedpdf/plugin-annotation": "3.0.0-next.14",
27
+ "@embedpdf/plugin-interaction": "3.0.0-next.14",
28
+ "@embedpdf/engine-core": "3.0.0-next.14"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.0.0",
32
+ "vitest": "^2.1.9",
33
+ "@embedpdf/tooling-build": "0.0.0"
34
+ },
35
+ "license": "Apache-2.0",
36
+ "sideEffects": false,
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/embedpdf/embed-pdf-viewer.git",
40
+ "directory": "packages/plugin/measurement"
41
+ },
42
+ "scripts": {
43
+ "build": "epdf-build",
44
+ "clean": "rimraf dist",
45
+ "typecheck": "tsc -p tsconfig.json --noEmit",
46
+ "test": "vitest run --passWithNoTests"
47
+ }
48
+ }