@reekon-tools/boldr-utils 1.26.1 → 1.26.2

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.
Files changed (74) hide show
  1. package/dist/annotation/canvas/pdfCanvasPages.d.ts +7 -4
  2. package/dist/annotation/canvas/pdfCanvasPages.js +93 -14
  3. package/dist/annotation/canvas/tileCalibration.d.ts +16 -0
  4. package/dist/annotation/canvas/tileCalibration.js +91 -0
  5. package/dist/annotation/canvas/useAnnotationCanvasState.js +7 -67
  6. package/dist/annotation/data/hooks/usePdfCanvasDoc.js +24 -7
  7. package/dist/export/pdf/renderCanvasToPdf.js +7 -5
  8. package/dist/export/pdf/types.d.ts +11 -0
  9. package/dist/exports.d.ts +2 -1
  10. package/dist/exports.js +2 -1
  11. package/dist/server.d.ts +2 -1
  12. package/dist/server.js +2 -1
  13. package/dist/types/pdfCanvas.d.ts +12 -1
  14. package/package.json +1 -1
  15. package/dist/canvas/AnnotationCanvas.d.ts +0 -11
  16. package/dist/canvas/AnnotationCanvas.js +0 -10
  17. package/dist/canvas/AnnotationCanvas.native.d.ts +0 -8
  18. package/dist/canvas/AnnotationCanvas.native.js +0 -6
  19. package/dist/canvas/AnnotationCanvasInner.d.ts +0 -39
  20. package/dist/canvas/AnnotationCanvasInner.js +0 -219
  21. package/dist/canvas/AnnotationCanvasInner.native.d.ts +0 -35
  22. package/dist/canvas/AnnotationCanvasInner.native.js +0 -138
  23. package/dist/canvas/AnnotationCanvasSkia.d.ts +0 -27
  24. package/dist/canvas/AnnotationCanvasSkia.js +0 -20
  25. package/dist/canvas/Tool.d.ts +0 -38
  26. package/dist/canvas/Tool.js +0 -1
  27. package/dist/canvas/elements/BackgroundImageElement.d.ts +0 -9
  28. package/dist/canvas/elements/BackgroundImageElement.js +0 -37
  29. package/dist/canvas/elements/MeasurementStampElement.d.ts +0 -13
  30. package/dist/canvas/elements/MeasurementStampElement.js +0 -30
  31. package/dist/canvas/elements/ShapeElement.d.ts +0 -7
  32. package/dist/canvas/elements/ShapeElement.js +0 -62
  33. package/dist/canvas/elements/StrokeElement.d.ts +0 -7
  34. package/dist/canvas/elements/StrokeElement.js +0 -18
  35. package/dist/canvas/measurementPicker.d.ts +0 -10
  36. package/dist/canvas/measurementPicker.js +0 -1
  37. package/dist/canvas/measurementStampOverlay.d.ts +0 -11
  38. package/dist/canvas/measurementStampOverlay.js +0 -1
  39. package/dist/canvas/pointerAdapter.d.ts +0 -3
  40. package/dist/canvas/pointerAdapter.js +0 -19
  41. package/dist/canvas/stampLayout.d.ts +0 -5
  42. package/dist/canvas/stampLayout.js +0 -14
  43. package/dist/canvas/tools/measurementStampTool.d.ts +0 -9
  44. package/dist/canvas/tools/measurementStampTool.js +0 -37
  45. package/dist/canvas/tools/panTool.d.ts +0 -5
  46. package/dist/canvas/tools/panTool.js +0 -25
  47. package/dist/canvas/tools/penTool.d.ts +0 -13
  48. package/dist/canvas/tools/penTool.js +0 -68
  49. package/dist/canvas/tools/selectTool.d.ts +0 -2
  50. package/dist/canvas/tools/selectTool.js +0 -182
  51. package/dist/canvas/useAnnotationCanvasState.d.ts +0 -54
  52. package/dist/canvas/useAnnotationCanvasState.js +0 -210
  53. package/dist/canvas/viewport.d.ts +0 -16
  54. package/dist/canvas/viewport.js +0 -54
  55. package/dist/data/AnnotationDataContext.d.ts +0 -8
  56. package/dist/data/AnnotationDataContext.js +0 -11
  57. package/dist/data/AnnotationDataProvider.d.ts +0 -65
  58. package/dist/data/AnnotationDataProvider.js +0 -4
  59. package/dist/data/InMemoryAnnotationProvider.d.ts +0 -30
  60. package/dist/data/InMemoryAnnotationProvider.js +0 -197
  61. package/dist/data/canvasPersistence.d.ts +0 -3
  62. package/dist/data/canvasPersistence.js +0 -26
  63. package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +0 -33
  64. package/dist/data/hooks/useAnnotationCanvasDoc.js +0 -314
  65. package/dist/data/hooks/useAnnotationDoc.d.ts +0 -7
  66. package/dist/data/hooks/useAnnotationDoc.js +0 -33
  67. package/dist/data/hooks/useAnnotationList.d.ts +0 -7
  68. package/dist/data/hooks/useAnnotationList.js +0 -26
  69. package/dist/data/hooks/useAnnotationMutations.d.ts +0 -9
  70. package/dist/data/hooks/useAnnotationMutations.js +0 -11
  71. package/dist/hooks/useParseMeasurement.d.ts +0 -4
  72. package/dist/hooks/useParseMeasurement.js +0 -14
  73. package/dist/utils/evaluateFormula.d.ts +0 -20
  74. package/dist/utils/evaluateFormula.js +0 -31
@@ -1,5 +1,5 @@
1
- import { type AnnotationCanvasState, type AnnotationPdfDocument, type Vec2 } from '../../types/annotation.js';
2
- import type { PdfCanvasData, PdfCanvasPage, PdfCanvasPageState, PdfCanvasRev, PdfPageRotation } from '../../types/pdfCanvas.js';
1
+ import { type AnnotationCanvasState, type AnnotationPdfDocument, type PlacedMeasurementRef, type Vec2 } from '../../types/annotation.js';
2
+ import type { PdfCanvasData, PdfCanvasPage, PdfCanvasPageState, PdfCanvasPlacement, PdfCanvasTileScales, PdfCanvasRev, PdfPageRotation } from '../../types/pdfCanvas.js';
3
3
  export interface PdfPageDisplaySize {
4
4
  width: number;
5
5
  height: number;
@@ -10,16 +10,19 @@ export declare const pdfPageDisplaySize: (page: Pick<PdfCanvasPage, "width" | "h
10
10
  export declare const pdfCanvasPageDocId: (pageIndex: number) => string;
11
11
  export declare const emptyPdfCanvasPageState: (pageIndex: number) => PdfCanvasPageState;
12
12
  export declare const pdfCanvasPageDocument: (data: PdfCanvasData, pageIndex: number) => AnnotationPdfDocument;
13
- type PdfCanvasKnobs = Pick<PdfCanvasData, 'tileScaleFactor' | 'tileScaleFactorMobile' | 'headerTileScaleFactor' | 'headerTileScaleFactorMobile' | 'tileDocSize'>;
13
+ type PdfCanvasKnobs = PdfCanvasTileScales;
14
14
  export declare const pdfCanvasKnobsOf: (source: Partial<PdfCanvasKnobs>) => PdfCanvasKnobs;
15
15
  export declare const pdfCanvasKnobsEqual: (a: Partial<PdfCanvasKnobs>, b: Partial<PdfCanvasKnobs>) => boolean;
16
+ export declare const pdfCanvasPageKnobs: (data: Pick<PdfCanvasData, "pageTileScales"> & PdfCanvasKnobs, pageIndex: number) => PdfCanvasKnobs;
16
17
  export declare const pageStateToCanvasState: (data: PdfCanvasData, pageState: PdfCanvasPageState | null, pageIndex: number) => AnnotationCanvasState;
17
18
  export declare const canvasStateToPageState: (canvas: AnnotationCanvasState, pageIndex: number, stamp: {
18
19
  updatedAt: number;
19
20
  rev?: PdfCanvasRev;
20
21
  }) => PdfCanvasPageState;
22
+ export declare const pdfCanvasPagePlacements: (placed: readonly PlacedMeasurementRef[]) => PdfCanvasPlacement[];
23
+ export declare const pdfCanvasPlacementsOf: (pdfCanvas: Pick<PdfCanvasData, "placementsByPage"> | null | undefined) => PdfCanvasPlacement[];
21
24
  export type PdfRotationDelta = 90 | -90 | 180;
22
25
  export declare const rotatePointInPage: (p: Vec2, size: PdfPageDisplaySize, delta: PdfRotationDelta) => Vec2;
23
- export declare const rotatePageState: <T extends Pick<PdfCanvasPageState, "strokes" | "shapes" | "placedMeasurements" | "planes">>(state: T, currentSize: PdfPageDisplaySize, delta: PdfRotationDelta) => T;
26
+ export declare const rotatePageState: <T extends Pick<PdfCanvasPageState, "strokes" | "shapes" | "placedMeasurements" | "planes" | "backgroundImages">>(state: T, currentSize: PdfPageDisplaySize, delta: PdfRotationDelta) => T;
24
27
  export declare const rotatePdfCanvasPage: (page: PdfCanvasPage, delta: PdfRotationDelta) => PdfCanvasPage;
25
28
  export {};
@@ -3,6 +3,7 @@
3
3
  // AnnotationCanvasState the engine mounts, and the coordinate transform that
4
4
  // turns a page's annotations with it when the page is rotated.
5
5
  import { createDefaultLayer, createEmptyCanvasState, } from '../../types/annotation.js';
6
+ import { backgroundLayerFullDocRect, backgroundLayersOf, } from './backgroundLayers.js';
6
7
  import { textShapeBounds } from './textGeometry.js';
7
8
  // Snap any degree value onto the four legal rotations, modulo 360.
8
9
  export const normalizePdfRotation = (deg) => {
@@ -58,7 +59,8 @@ const KNOB_KEYS = [
58
59
  'headerTileScaleFactorMobile',
59
60
  'tileDocSize',
60
61
  ];
61
- // The file-level tile-scale knobs present on a canvas (undefined keys omitted).
62
+ // The tile-scale knobs present on a canvas or knob record (undefined keys
63
+ // omitted).
62
64
  export const pdfCanvasKnobsOf = (source) => {
63
65
  const out = {};
64
66
  for (const key of KNOB_KEYS) {
@@ -69,6 +71,13 @@ export const pdfCanvasKnobsOf = (source) => {
69
71
  return out;
70
72
  };
71
73
  export const pdfCanvasKnobsEqual = (a, b) => KNOB_KEYS.every((key) => a[key] === b[key]);
74
+ // One page's tile-scale knobs: its own `pageTileScales` entry, else the
75
+ // legacy file-wide knobs (files written before scale went per-page, Asana
76
+ // 1218877420681571 — they keep their look until a page's knobs change).
77
+ export const pdfCanvasPageKnobs = (data, pageIndex) => {
78
+ const own = data.pageTileScales?.[String(pageIndex)];
79
+ return pdfCanvasKnobsOf(own && typeof own === 'object' ? own : data);
80
+ };
72
81
  // The AnnotationCanvasState the editor mounts for one page. A null page state
73
82
  // (never annotated) yields an empty canvas over the page.
74
83
  export const pageStateToCanvasState = (data, pageState, pageIndex) => {
@@ -81,7 +90,15 @@ export const pageStateToCanvasState = (data, pageState, pageIndex) => {
81
90
  });
82
91
  return {
83
92
  ...base,
84
- ...pdfCanvasKnobsOf(data),
93
+ ...pdfCanvasPageKnobs(data, pageIndex),
94
+ ...(pageState?.backgroundImages?.length
95
+ ? {
96
+ viewport: {
97
+ ...base.viewport,
98
+ backgroundImages: pageState.backgroundImages,
99
+ },
100
+ }
101
+ : {}),
85
102
  layers: pageState && pageState.layers.length > 0 ? pageState.layers : base.layers,
86
103
  strokes: pageState?.strokes ?? [],
87
104
  shapes: pageState?.shapes ?? [],
@@ -90,18 +107,60 @@ export const pageStateToCanvasState = (data, pageState, pageIndex) => {
90
107
  };
91
108
  };
92
109
  // Inverse of pageStateToCanvasState: the elements to persist for one page.
93
- // Viewport and pdfDocument are derived and never stored on the page doc.
94
- export const canvasStateToPageState = (canvas, pageIndex, stamp) => ({
95
- schemaVersion: 1,
96
- pageIndex,
97
- layers: canvas.layers,
98
- strokes: canvas.strokes,
99
- shapes: canvas.shapes,
100
- placedMeasurements: canvas.placedMeasurements,
101
- ...(canvas.planes !== undefined ? { planes: canvas.planes } : {}),
102
- updatedAt: stamp.updatedAt,
103
- ...(stamp.rev ? { rev: stamp.rev } : {}),
104
- });
110
+ // The viewport's size and pdfDocument are derived from the file record and
111
+ // never stored on the page doc; its image layers are page CONTENT (an
112
+ // expanded image tile lives there) and are.
113
+ export const canvasStateToPageState = (canvas, pageIndex, stamp) => {
114
+ const backgroundImages = backgroundLayersOf(canvas.viewport);
115
+ return {
116
+ schemaVersion: 1,
117
+ pageIndex,
118
+ layers: canvas.layers,
119
+ strokes: canvas.strokes,
120
+ shapes: canvas.shapes,
121
+ placedMeasurements: canvas.placedMeasurements,
122
+ ...(canvas.planes !== undefined ? { planes: canvas.planes } : {}),
123
+ ...(backgroundImages.length > 0 ? { backgroundImages } : {}),
124
+ updatedAt: stamp.updatedAt,
125
+ ...(stamp.rev ? { rev: stamp.rev } : {}),
126
+ };
127
+ };
128
+ // The measurement bindings of one page's tiles, for
129
+ // `PdfCanvasData.placementsByPage`. Group-header chips, object tiles and
130
+ // unbound tiles (a plane dimension, an unbound "+") bind no measurement and
131
+ // are left out, so an empty list means "nothing placed on this page".
132
+ export const pdfCanvasPagePlacements = (placed) => {
133
+ const out = [];
134
+ const seen = new Set();
135
+ for (const p of placed) {
136
+ if (!p || p.isGroupHeader || p.fileId)
137
+ continue;
138
+ const bound = !!p.measurementId || !!p.measurementPath;
139
+ if (!bound && !(p.groupId && p.columnId))
140
+ continue;
141
+ const entry = {
142
+ ...(p.measurementId ? { measurementId: p.measurementId } : {}),
143
+ ...(p.measurementPath ? { measurementPath: p.measurementPath } : {}),
144
+ ...(p.groupId ? { groupId: p.groupId } : {}),
145
+ ...(p.columnId ? { columnId: p.columnId } : {}),
146
+ };
147
+ const key = JSON.stringify(entry);
148
+ if (seen.has(key))
149
+ continue;
150
+ seen.add(key);
151
+ out.push(entry);
152
+ }
153
+ return out;
154
+ };
155
+ // Every page's measurement bindings on a PdfCanvas file, flattened — what the
156
+ // placed-in-file readers treat exactly like an inline canvas's
157
+ // placedMeasurements.
158
+ export const pdfCanvasPlacementsOf = (pdfCanvas) => {
159
+ const byPage = pdfCanvas?.placementsByPage;
160
+ if (!byPage || typeof byPage !== 'object')
161
+ return [];
162
+ return Object.values(byPage).flatMap((list) => Array.isArray(list) ? list : []);
163
+ };
105
164
  export const rotatePointInPage = (p, size, delta) => {
106
165
  switch (normalizePdfRotation(delta)) {
107
166
  case 90:
@@ -237,10 +296,30 @@ const rotatePlane = (plane, size, delta) => {
237
296
  }),
238
297
  };
239
298
  };
299
+ // Image layers carry no rotation (a transform is translate + scale), so like
300
+ // text they stay upright: the layer keeps its size and is re-anchored so the
301
+ // centre of its full image turns with the page.
302
+ const rotateBackgroundLayer = (layer, size, delta) => {
303
+ const full = backgroundLayerFullDocRect(layer);
304
+ const centre = rotatePointInPage({ x: full.x + full.width / 2, y: full.y + full.height / 2 }, size, delta);
305
+ return {
306
+ ...layer,
307
+ transform: {
308
+ ...layer.transform,
309
+ x: centre.x - full.width / 2,
310
+ y: centre.y - full.height / 2,
311
+ },
312
+ };
313
+ };
240
314
  // Rewrite every coordinate of a page's state from its current displayed size
241
315
  // into the size it will have after turning by `delta`.
242
316
  export const rotatePageState = (state, currentSize, delta) => ({
243
317
  ...state,
318
+ ...(state.backgroundImages !== undefined
319
+ ? {
320
+ backgroundImages: state.backgroundImages.map((l) => rotateBackgroundLayer(l, currentSize, delta)),
321
+ }
322
+ : {}),
244
323
  strokes: state.strokes.map((s) => rotateStroke(s, currentSize, delta)),
245
324
  shapes: state.shapes.map((s) => rotateShape(s, currentSize, delta)),
246
325
  placedMeasurements: state.placedMeasurements.map((p) => rotatePlaced(p, currentSize, delta)),
@@ -0,0 +1,16 @@
1
+ import type { AnnotationCanvasState, TileScalePlatform } from '../../types/annotation.js';
2
+ import type { PdfCanvasData, PdfCanvasPageState } from '../../types/pdfCanvas.js';
3
+ export declare const computeBaseFitZoom: (canvas: AnnotationCanvasState, width: number, height: number) => number;
4
+ export interface ResolvedTileFactors {
5
+ tileScaleFactor: number;
6
+ headerTileScaleFactor: number;
7
+ fileTileScaleFactor: number;
8
+ }
9
+ export declare const resolveTileFactors: (canvas: AnnotationCanvasState, platform: TileScalePlatform, baseFitZoom: number, hasViewport: boolean) => ResolvedTileFactors;
10
+ export interface TileDocSizes {
11
+ tileDocSize: number;
12
+ headerTileDocSize: number;
13
+ fileTileDocSize: number;
14
+ }
15
+ export declare const tileDocSizesFor: (canvas: AnnotationCanvasState, platform: TileScalePlatform, width: number, height: number) => TileDocSizes;
16
+ export declare const pdfCanvasPageTileDocSizes: (data: PdfCanvasData, pageStates: Partial<Record<number, PdfCanvasPageState>>, platform: TileScalePlatform, width: number, height: number) => Record<number, TileDocSizes>;
@@ -0,0 +1,91 @@
1
+ // How large tiles are on a canvas, as pure functions — the editor
2
+ // (useAnnotationCanvasState) and the PDF export resolve them the same way.
3
+ //
4
+ // A tile's footprint is base × per-tile `scale` × the platform's document-wide
5
+ // factor, at the canvas's CONTENT-FIT zoom (tiles are doc-constant from there).
6
+ // So how much of the drawing a tile covers depends on the screen the canvas is
7
+ // fitted into: only a client knows its own, and an exporter that wants "the
8
+ // same as on my screen" needs the screen size, not just the stored factors.
9
+ import { backgroundLayerDocRect, backgroundLayersOf, } from './backgroundLayers.js';
10
+ import { pageStateToCanvasState } from './pdfCanvasPages.js';
11
+ import { pdfFirstPageDocRect } from './pdfDocument.js';
12
+ import { resolveFileTileScaleFactor, resolveHeaderTileScaleFactor, resolveTileScaleFactor, tileDocSizeForFactor, tileScaleFromDocSize, } from './stampLayout.js';
13
+ import { fitRectToScreen } from './viewport.js';
14
+ // The content-fit zoom tiles are sized against. A PDF-backed canvas
15
+ // references its FIRST PAGE (not the whole filmstrip — see
16
+ // pdfFirstPageDocRect), whatever image layers sit on it: the PDF is the
17
+ // drawing, and images are placed ON it. Referencing an image instead made
18
+ // every tile on a PDF page jump in size the moment an image object tile was
19
+ // expanded (there was no image before it), and jump back on collapse or
20
+ // delete (Asana 1218877420681573). Otherwise the first image layer — on a
21
+ // photo canvas the photo IS the drawing — else the virtual doc rect.
22
+ export const computeBaseFitZoom = (canvas, width, height) => {
23
+ if (!(width > 0) || !(height > 0))
24
+ return 1;
25
+ const { viewport } = canvas;
26
+ const pdfFirstPage = viewport.pdfDocument
27
+ ? pdfFirstPageDocRect(viewport.pdfDocument)
28
+ : null;
29
+ const first = pdfFirstPage ? undefined : backgroundLayersOf(viewport)[0];
30
+ const baseRect = pdfFirstPage ??
31
+ (first ? backgroundLayerDocRect(first) : null) ?? {
32
+ x: 0,
33
+ y: 0,
34
+ width: viewport.width,
35
+ height: viewport.height,
36
+ };
37
+ return fitRectToScreen(baseRect, width, height).zoom;
38
+ };
39
+ // The platform's three tile-kind factors. A mobile canvas whose own knob is
40
+ // still unset derives its factor from the web-stamped doc-space calibration
41
+ // (tileDocSize) instead of web's raw screen-space value: at the content-fit
42
+ // view its tiles then cover the same area of the drawing the web user saw.
43
+ // The header / object factors ride the same calibration ratio so their
44
+ // relation to the measurement factor survives the override. The first mobile
45
+ // slider commit writes tileScaleFactorMobile and takes over for good.
46
+ export const resolveTileFactors = (canvas, platform, baseFitZoom, hasViewport) => {
47
+ let tileScaleFactor = resolveTileScaleFactor(canvas, platform);
48
+ let headerTileScaleFactor = resolveHeaderTileScaleFactor(canvas, platform);
49
+ let fileTileScaleFactor = resolveFileTileScaleFactor(canvas, platform);
50
+ if (platform === 'mobile' &&
51
+ canvas.tileScaleFactorMobile == null &&
52
+ canvas.tileDocSize != null &&
53
+ canvas.tileDocSize > 0 &&
54
+ hasViewport) {
55
+ const calibrated = tileScaleFromDocSize(canvas.tileDocSize, baseFitZoom);
56
+ headerTileScaleFactor =
57
+ tileScaleFactor > 0
58
+ ? headerTileScaleFactor * (calibrated / tileScaleFactor)
59
+ : calibrated;
60
+ fileTileScaleFactor =
61
+ tileScaleFactor > 0
62
+ ? fileTileScaleFactor * (calibrated / tileScaleFactor)
63
+ : calibrated;
64
+ tileScaleFactor = calibrated;
65
+ }
66
+ return { tileScaleFactor, headerTileScaleFactor, fileTileScaleFactor };
67
+ };
68
+ // The doc-space edge a scale-1 tile of each kind covers on `canvas` when it is
69
+ // fitted into a `width` × `height` view on `platform` — what an export needs
70
+ // to draw tiles exactly as that screen shows them.
71
+ export const tileDocSizesFor = (canvas, platform, width, height) => {
72
+ const zoom = computeBaseFitZoom(canvas, width, height);
73
+ const f = resolveTileFactors(canvas, platform, zoom, width > 0 && height > 0);
74
+ return {
75
+ tileDocSize: tileDocSizeForFactor(f.tileScaleFactor, zoom),
76
+ headerTileDocSize: tileDocSizeForFactor(f.headerTileScaleFactor, zoom),
77
+ fileTileDocSize: tileDocSizeForFactor(f.fileTileScaleFactor, zoom),
78
+ };
79
+ };
80
+ // Every page of a PdfCanvas file, calibrated as the requesting screen shows
81
+ // it: each page mounts as its own canvas with its own knobs (tile scale is per
82
+ // page — Asana 1218877420681571) and its own content-fit zoom, so one
83
+ // calibration taken from the page on screen would mis-size every other page.
84
+ export const pdfCanvasPageTileDocSizes = (data, pageStates, platform, width, height) => {
85
+ const out = {};
86
+ for (let i = 0; i < data.document.pages.length; i++) {
87
+ const canvas = pageStateToCanvasState(data, pageStates[i] ?? null, i);
88
+ out[i] = tileDocSizesFor(canvas, platform, width, height);
89
+ }
90
+ return out;
91
+ };
@@ -1,10 +1,11 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { applyPatch, invertPatch, DEFAULT_LAYER_ID, } from '../../types/annotation.js';
3
- import { resolveHeaderTileScaleFactor, resolveFileTileScaleFactor, resolveTileScaleFactor, stampTileDims, STAMP_TILE_SIZE, tileDocSizeForFactor, tileScaleFromDocSize, } from './stampLayout.js';
3
+ import { stampTileDims, STAMP_TILE_SIZE, tileDocSizeForFactor, } from './stampLayout.js';
4
4
  import { SNAP_RADIUS_PX } from './snapGeometry.js';
5
+ import { computeBaseFitZoom, resolveTileFactors } from './tileCalibration.js';
5
6
  import { createViewportApi, fitDocRectViewport, fitRectToScreen, panBy, zoomAt, DEFAULT_VIEWPORT, DEFAULT_ZOOM_LIMITS, } from './viewport.js';
6
- import { computeZoomFloor, pdfFirstPageDocRect } from './pdfDocument.js';
7
- import { backgroundLayerDocRect, backgroundLayersOf, backgroundLayersUnionRect, } from './backgroundLayers.js';
7
+ import { computeZoomFloor } from './pdfDocument.js';
8
+ import { backgroundLayersOf, backgroundLayersUnionRect, } from './backgroundLayers.js';
8
9
  import { buildRemoveMeasurementOps, recomputeAnchor, rectCenter, DEFAULT_LINE_POS, } from './measurementGeometry.js';
9
10
  // The viewport that frames the document's content inside a screen rect of the
10
11
  // canvas box. When background images are present we fit the union of their
@@ -70,38 +71,6 @@ const computeContentFitRect = (canvas, rect) => {
70
71
  };
71
72
  };
72
73
  const computeContentFit = (canvas, width, height) => computeContentFitRect(canvas, { x: 0, y: 0, width, height });
73
- // Content-fit zoom of the DOCUMENT alone — the FIRST background layer's
74
- // rendered rect when present, the document rect otherwise, deliberately
75
- // ignoring placed tiles — into the full canvas box. This is the stable
76
- // reference zoom for tile sizing (see tileViewportScale below): at this zoom
77
- // a tile renders at exactly its slider size. computeContentFitRect's
78
- // tile-extended union would make the reference move whenever a tile is
79
- // dragged outside the image (every tile on the canvas would resize); the
80
- // document rect can't. The reference is pinned to the first layer — never
81
- // the layer union — for the same reason: adding, dragging, or scaling a
82
- // SECOND image must not silently resize (and re-hit-test) every placed tile
83
- // on the canvas. For a single-image doc the first layer's rect is exactly
84
- // the legacy fit rect, so existing canvases keep their tile sizes.
85
- const computeBaseFitZoom = (canvas, width, height) => {
86
- if (!(width > 0) || !(height > 0))
87
- return 1;
88
- const { viewport } = canvas;
89
- const first = backgroundLayersOf(viewport)[0];
90
- // A PDF-backed canvas with no image layers references its FIRST PAGE, not
91
- // the whole filmstrip — see pdfFirstPageDocRect for why.
92
- const pdfFirstPage = viewport.pdfDocument
93
- ? pdfFirstPageDocRect(viewport.pdfDocument)
94
- : null;
95
- const baseRect = first
96
- ? backgroundLayerDocRect(first)
97
- : (pdfFirstPage ?? {
98
- x: 0,
99
- y: 0,
100
- width: viewport.width,
101
- height: viewport.height,
102
- });
103
- return fitRectToScreen(baseRect, width, height).zoom;
104
- };
105
74
  // Default leader length (doc units) for a placed measurement when no explicit
106
75
  // length is given. A generous fraction of the document width, clamped, so the
107
76
  // leader — and its draggable endcaps — stay clear of the value tile at
@@ -191,38 +160,9 @@ export const useAnnotationCanvasState = (props) => {
191
160
  // the same area of the image the web user saw. The first mobile slider
192
161
  // commit writes tileScaleFactorMobile and takes over for good.
193
162
  const tileScalePlatformResolved = tileScalePlatform ?? 'web';
194
- let tileScaleFactor = resolveTileScaleFactor(effectiveCanvas, tileScalePlatformResolved);
195
- // Group/form header chips carry their own document-wide knob (Asana
196
- // 1217570500895543), resolved the same way. With no header knob set it
197
- // equals the measurement factor (resolveHeaderTileScaleFactor's fallback) —
198
- // the pre-split one-knob behavior.
199
- let headerTileScaleFactor = resolveHeaderTileScaleFactor(effectiveCanvas, tileScalePlatformResolved);
200
- // Object tiles share the measurement factor — their own knob was retired
201
- // (see resolveFileTileScaleFactor), so this always equals tileScaleFactor
202
- // and rides the same calibration below.
203
- let fileTileScaleFactor = resolveFileTileScaleFactor(effectiveCanvas, tileScalePlatformResolved);
204
- if (tileScalePlatformResolved === 'mobile' &&
205
- effectiveCanvas.tileScaleFactorMobile == null &&
206
- effectiveCanvas.tileDocSize != null &&
207
- effectiveCanvas.tileDocSize > 0 &&
208
- width > 0 &&
209
- height > 0) {
210
- const calibrated = tileScaleFromDocSize(effectiveCanvas.tileDocSize, baseFitZoom);
211
- // Scale the header factor by the same calibration ratio so its relation
212
- // to the measurement factor survives the override: a doc with no header
213
- // knob keeps headers exactly at the calibrated measurement factor, and a
214
- // doc whose web user set a header knob keeps the same header-to-tile
215
- // proportion the web user saw.
216
- headerTileScaleFactor =
217
- tileScaleFactor > 0
218
- ? headerTileScaleFactor * (calibrated / tileScaleFactor)
219
- : calibrated;
220
- fileTileScaleFactor =
221
- tileScaleFactor > 0
222
- ? fileTileScaleFactor * (calibrated / tileScaleFactor)
223
- : calibrated;
224
- tileScaleFactor = calibrated;
225
- }
163
+ // Shared with the PDF export (tileCalibration.ts) so an exported page draws
164
+ // its tiles exactly as this screen does.
165
+ const { tileScaleFactor, headerTileScaleFactor, fileTileScaleFactor } = resolveTileFactors(effectiveCanvas, tileScalePlatformResolved, baseFitZoom, width > 0 && height > 0);
226
166
  const ctx = useMemo(() => ({
227
167
  document: canvas,
228
168
  selection,
@@ -1,15 +1,19 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { applyPatch, createEmptyCanvasState, } from '../../../types/annotation.js';
3
- import { canvasStateToPageState, pageStateToCanvasState, pdfCanvasKnobsEqual, pdfCanvasKnobsOf, pdfPageDisplaySize, rotatePageState, rotatePdfCanvasPage, } from '../../canvas/pdfCanvasPages.js';
3
+ import { canvasStateToPageState, pageStateToCanvasState, pdfCanvasKnobsEqual, pdfCanvasPagePlacements, pdfCanvasKnobsOf, pdfCanvasPageKnobs, pdfPageDisplaySize, rotatePageState, rotatePdfCanvasPage, } from '../../canvas/pdfCanvasPages.js';
4
4
  import { useAnnotationData } from '../AnnotationDataContext.js';
5
5
  import { annotationScopeKey, } from '../AnnotationDataProvider.js';
6
+ import { backgroundLayersOf } from '../../canvas/backgroundLayers.js';
6
7
  import { createCoalescedRunner } from '../coalescedRunner.js';
7
8
  import { useAnnotationDoc } from './useAnnotationDoc.js';
8
9
  const makeClientId = () => `${Date.now().toString(36)}-${Math.floor(Math.random() * 0x100000000).toString(36)}`;
9
10
  const hasContent = (canvas) => canvas.strokes.length > 0 ||
10
11
  canvas.shapes.length > 0 ||
11
12
  canvas.placedMeasurements.length > 0 ||
12
- (canvas.planes?.length ?? 0) > 0;
13
+ (canvas.planes?.length ?? 0) > 0 ||
14
+ // An expanded image tile is page content too (PdfCanvasPageState
15
+ // .backgroundImages) — a page holding only one must still turn with it.
16
+ backgroundLayersOf(canvas.viewport).length > 0;
13
17
  // Drop undefined keys (Firestore — RN in particular — rejects them) and detach
14
18
  // the persisted payload from the working state's object identities.
15
19
  const jsonClone = (value) => JSON.parse(JSON.stringify(value));
@@ -120,11 +124,21 @@ export const usePdfCanvasDoc = (options) => {
120
124
  values: [target.pageIndex],
121
125
  };
122
126
  }
127
+ // The page's measurement bindings, for the job grid's placed-in-file
128
+ // link (Asana 1218877420681569). Only this page's entry is written,
129
+ // and only when it changed.
130
+ const placements = pdfCanvasPagePlacements(state.placedMeasurements);
131
+ const pageKey = String(target.pageIndex);
132
+ const previous = target.pdfCanvas.placementsByPage?.[pageKey] ?? [];
133
+ if (JSON.stringify(placements) !== JSON.stringify(previous)) {
134
+ patch[`fileData.pdfCanvas.placementsByPage.${pageKey}`] = placements;
135
+ }
136
+ // Tile scale is per page (Asana 1218877420681571): write this page's
137
+ // complete knob set, only when it moved. The legacy file-wide knobs
138
+ // are left alone — they stay the fallback for pages never re-scaled.
123
139
  const knobs = pdfCanvasKnobsOf(target.canvas);
124
- if (!pdfCanvasKnobsEqual(knobs, pdfCanvasKnobsOf(target.pdfCanvas))) {
125
- for (const [key, value] of Object.entries(knobs)) {
126
- patch[`fileData.pdfCanvas.${key}`] = value;
127
- }
140
+ if (!pdfCanvasKnobsEqual(knobs, pdfCanvasPageKnobs(target.pdfCanvas, target.pageIndex))) {
141
+ patch[`fileData.pdfCanvas.pageTileScales.${pageKey}`] = knobs;
128
142
  }
129
143
  if (Object.keys(patch).length > 0) {
130
144
  await provider.update(currentScope, currentFileId, patch);
@@ -292,7 +306,10 @@ export const usePdfCanvasDoc = (options) => {
292
306
  // Knobs the flush just wrote may not have echoed back yet; carry them.
293
307
  const nextData = {
294
308
  ...data,
295
- ...pdfCanvasKnobsOf(canvas),
309
+ pageTileScales: {
310
+ ...data.pageTileScales,
311
+ [String(index)]: pdfCanvasKnobsOf(canvas),
312
+ },
296
313
  document: { ...data.document, pages },
297
314
  };
298
315
  setStatus('saving');
@@ -12,7 +12,7 @@
12
12
  // /Rotate and the page matrix.
13
13
  import { EncryptedPDFError, PDFDocument, degrees, } from 'pdf-lib';
14
14
  import { backgroundLayersOf } from '../../annotation/canvas/backgroundLayers.js';
15
- import { effectivePageRotation, pdfPageDisplaySize, } from '../../annotation/canvas/pdfCanvasPages.js';
15
+ import { effectivePageRotation, pdfCanvasPageKnobs, pdfPageDisplaySize, } from '../../annotation/canvas/pdfCanvasPages.js';
16
16
  import { fitRectToScreen } from '../../annotation/canvas/viewport.js';
17
17
  import { STAMP_TILE_SIZE } from '../../annotation/canvas/stampLayout.js';
18
18
  import { clipRectOps, withMatrix } from './emit.js';
@@ -174,7 +174,7 @@ const renderJobs = async (doc, jobs, options, skipped) => {
174
174
  const onPage = (b) => b !== null && rectsIntersect(b, rect);
175
175
  const hiddenLayerIds = new Set(job.layerDefs.filter((l) => l.visible === false).map((l) => l.id));
176
176
  const visible = (layerId) => !hiddenLayerIds.has(layerId);
177
- const footprintFor = (placed, content) => tileFootprint(placed, { tileDocSize: job.tileDocSize }, options.calibration, fallbackDocEdge, designBoxFor(placed, content));
177
+ const footprintFor = (placed, content) => tileFootprint(placed, { tileDocSize: job.tileDocSize }, job.calibration ?? options.calibration, fallbackDocEdge, designBoxFor(placed, content));
178
178
  const contentOf = (placed) => tileContent.get(placed.id) ?? { kind: 'empty' };
179
179
  if (options.mode === 'flatten') {
180
180
  // Everything into the page's content stream, under one matrix + clip.
@@ -363,18 +363,20 @@ export const renderPdfCanvasToPdf = async (input) => {
363
363
  box,
364
364
  rotation: effective,
365
365
  rect: { x: 0, y: 0, width: display.width, height: display.height },
366
- layers: [],
366
+ // The page's image layers (expanded image tiles), in page doc space.
367
+ layers: state?.backgroundImages ?? [],
367
368
  layerDefs: state?.layers ?? [],
368
369
  strokes: state?.strokes ?? [],
369
370
  shapes: state?.shapes ?? [],
370
371
  placed: state?.placedMeasurements ?? [],
371
- tileDocSize: pdfCanvas.tileDocSize,
372
+ tileDocSize: pdfCanvasPageKnobs(pdfCanvas, i).tileDocSize,
373
+ calibration: input.pageCalibrations?.[i],
372
374
  });
373
375
  });
374
376
  const skipped = [];
375
377
  const annotationCount = await renderJobs(doc, jobs, {
376
378
  resolveTile: input.resolveTile,
377
- layerImages: {},
379
+ layerImages: input.layerImages ?? {},
378
380
  calibration: input.calibration,
379
381
  primaryFont: input.primaryFont,
380
382
  mode: input.mode ?? 'annotations',
@@ -106,6 +106,17 @@ export interface RenderPdfCanvasToPdfInput {
106
106
  /** Page docs by page index; a page without one is exported un-annotated. */
107
107
  pageStates: Partial<Record<number, PdfCanvasPageState>>;
108
108
  resolveTile: (ref: PlacedMeasurementRef) => ResolvedTileContent | undefined;
109
+ /**
110
+ * Decoded bytes per image layer id, across every page's
111
+ * `backgroundImages`. Missing → that layer is skipped.
112
+ */
113
+ layerImages?: Record<string, EmbeddedImage>;
114
+ /**
115
+ * Per-page calibration (pdfCanvasPageTileDocSizes). Tile scale is per page,
116
+ * so a page's entry wins over `calibration`, which then only covers pages
117
+ * without one (and requests from clients that predate the split).
118
+ */
119
+ pageCalibrations?: Partial<Record<number, PdfExportCalibration>>;
109
120
  calibration?: PdfExportCalibration;
110
121
  primaryFont: Uint8Array;
111
122
  mode?: 'annotations' | 'flatten';
package/dist/exports.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './types/firestore.js';
7
7
  export * from './types/layout.js';
8
8
  export * from './types/annotation.js';
9
9
  export * from './types/pdfCanvas.js';
10
+ export { computeBaseFitZoom, resolveTileFactors, tileDocSizesFor, pdfCanvasPageTileDocSizes, type ResolvedTileFactors, type TileDocSizes, } from './annotation/canvas/tileCalibration.js';
10
11
  export { getToleranceColor, calculateDeviationPercentage, isWithinTolerance, generateToleranceGradient, createDefaultToleranceThresholds, DEFAULT_TOLERANCE_COLORS, type ToleranceThreshold, type ToleranceConfig, } from './utils/tolerance.js';
11
12
  export { GeneralColors, SecondaryColors, FormColors, TileColors, FormulaColors, YellowColors, RedColors, GreenColors, tailwindColors, } from './theme/colors.js';
12
13
  export { DEFAULT_GROUP_INDEX, isDefaultGroup, findDefaultGroup, isFormGroupCompleted, } from './utils/groups.js';
@@ -39,7 +40,7 @@ export { STAMP_TILE_SIZE, GROUP_HEADER_TILE_WIDTH, GROUP_HEADER_TILE_HEIGHT, GRO
39
40
  export { GROUP_HEADER_STYLE, describeGroupHeader, groupHeaderSignature, type GroupHeaderBadge, type GroupHeaderDescriptor, } from './annotation/canvas/groupHeaderLayout.js';
40
41
  export { createViewportApi, fitToScreen, fitRectToScreen, fitDocRectViewport, imageDocRect, panBy, zoomAt, DEFAULT_VIEWPORT, DEFAULT_ZOOM_LIMITS, type ViewportApi, type ViewportState, type ZoomLimits, } from './annotation/canvas/viewport.js';
41
42
  export { DEFAULT_PDF_PAGE_GAP, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, makePdfDocumentId, createPdfDocument, pdfPageDocRects, pdfDocumentExtent, pdfFirstPageDocRect, computeZoomFloor, type PdfDocRect, } from './annotation/canvas/pdfDocument.js';
42
- export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, emptyPdfCanvasPageState, pdfCanvasPageDocument, pdfCanvasKnobsOf, pdfCanvasKnobsEqual, pageStateToCanvasState, canvasStateToPageState, rotatePointInPage, rotatePageState, rotatePdfCanvasPage, type PdfPageDisplaySize, type PdfRotationDelta, } from './annotation/canvas/pdfCanvasPages.js';
43
+ export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, emptyPdfCanvasPageState, pdfCanvasPageDocument, pdfCanvasKnobsOf, pdfCanvasKnobsEqual, pageStateToCanvasState, canvasStateToPageState, rotatePointInPage, rotatePageState, rotatePdfCanvasPage, pdfCanvasPagePlacements, pdfCanvasPlacementsOf, pdfCanvasPageKnobs, type PdfPageDisplaySize, type PdfRotationDelta, } from './annotation/canvas/pdfCanvasPages.js';
43
44
  export { LEGACY_BACKGROUND_LAYER_ID, BACKGROUND_PLACEMENT_GAP_RATIO, backgroundLayersOf, backgroundLayerIsLocked, isBackgroundLayerLocked, defaultBackgroundLayerLocked, backgroundLayerDocRect, backgroundLayersUnionRect, placeNewBackgroundLayer, } from './annotation/canvas/backgroundLayers.js';
44
45
  export { fileTileImageSource, expandFileTileToBackground, collapseBackgroundToFileTile, type FileTileImageSource, } from './annotation/canvas/fileTileSwap.js';
45
46
  export { createPenTool, type PenToolOptions, } from './annotation/canvas/tools/penTool.js';
package/dist/exports.js CHANGED
@@ -11,6 +11,7 @@ export * from './types/firestore.js';
11
11
  export * from './types/layout.js';
12
12
  export * from './types/annotation.js';
13
13
  export * from './types/pdfCanvas.js';
14
+ export { computeBaseFitZoom, resolveTileFactors, tileDocSizesFor, pdfCanvasPageTileDocSizes, } from './annotation/canvas/tileCalibration.js';
14
15
  export { getToleranceColor, calculateDeviationPercentage, isWithinTolerance, generateToleranceGradient, createDefaultToleranceThresholds, DEFAULT_TOLERANCE_COLORS, } from './utils/tolerance.js';
15
16
  // Shared color palette (canonical source for both apps' brand colors).
16
17
  export { GeneralColors, SecondaryColors, FormColors, TileColors, FormulaColors, YellowColors, RedColors, GreenColors, tailwindColors, } from './theme/colors.js';
@@ -45,7 +46,7 @@ export { STAMP_TILE_SIZE, GROUP_HEADER_TILE_WIDTH, GROUP_HEADER_TILE_HEIGHT, GRO
45
46
  export { GROUP_HEADER_STYLE, describeGroupHeader, groupHeaderSignature, } from './annotation/canvas/groupHeaderLayout.js';
46
47
  export { createViewportApi, fitToScreen, fitRectToScreen, fitDocRectViewport, imageDocRect, panBy, zoomAt, DEFAULT_VIEWPORT, DEFAULT_ZOOM_LIMITS, } from './annotation/canvas/viewport.js';
47
48
  export { DEFAULT_PDF_PAGE_GAP, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, makePdfDocumentId, createPdfDocument, pdfPageDocRects, pdfDocumentExtent, pdfFirstPageDocRect, computeZoomFloor, } from './annotation/canvas/pdfDocument.js';
48
- export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, emptyPdfCanvasPageState, pdfCanvasPageDocument, pdfCanvasKnobsOf, pdfCanvasKnobsEqual, pageStateToCanvasState, canvasStateToPageState, rotatePointInPage, rotatePageState, rotatePdfCanvasPage, } from './annotation/canvas/pdfCanvasPages.js';
49
+ export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, emptyPdfCanvasPageState, pdfCanvasPageDocument, pdfCanvasKnobsOf, pdfCanvasKnobsEqual, pageStateToCanvasState, canvasStateToPageState, rotatePointInPage, rotatePageState, rotatePdfCanvasPage, pdfCanvasPagePlacements, pdfCanvasPlacementsOf, pdfCanvasPageKnobs, } from './annotation/canvas/pdfCanvasPages.js';
49
50
  export { LEGACY_BACKGROUND_LAYER_ID, BACKGROUND_PLACEMENT_GAP_RATIO, backgroundLayersOf, backgroundLayerIsLocked, isBackgroundLayerLocked, defaultBackgroundLayerLocked, backgroundLayerDocRect, backgroundLayersUnionRect, placeNewBackgroundLayer, } from './annotation/canvas/backgroundLayers.js';
50
51
  export { fileTileImageSource, expandFileTileToBackground, collapseBackgroundToFileTile, } from './annotation/canvas/fileTileSwap.js';
51
52
  export { createPenTool, } from './annotation/canvas/tools/penTool.js';
package/dist/server.d.ts CHANGED
@@ -6,7 +6,8 @@ export * from './types/firestore.js';
6
6
  export * from './types/layout.js';
7
7
  export * from './types/annotation.js';
8
8
  export * from './types/pdfCanvas.js';
9
- export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, pageStateToCanvasState, rotatePageState, type PdfPageDisplaySize, type PdfRotationDelta, } from './annotation/canvas/pdfCanvasPages.js';
9
+ export { computeBaseFitZoom, resolveTileFactors, tileDocSizesFor, pdfCanvasPageTileDocSizes, type ResolvedTileFactors, type TileDocSizes, } from './annotation/canvas/tileCalibration.js';
10
+ export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, pageStateToCanvasState, rotatePageState, pdfCanvasPagePlacements, pdfCanvasPlacementsOf, pdfCanvasPageKnobs, type PdfPageDisplaySize, type PdfRotationDelta, } from './annotation/canvas/pdfCanvasPages.js';
10
11
  export { getToleranceColor, getToleranceSecondaryColor, calculateDeviationPercentage, isWithinTolerance, generateToleranceGradient, createDefaultToleranceThresholds, DEFAULT_TOLERANCE_COLORS, DEFAULT_TOLERANCE_SECONDARY_COLORS, type ToleranceThreshold, type ToleranceConfig, } from './utils/tolerance.js';
11
12
  export { DEFAULT_GROUP_INDEX, isDefaultGroup, findDefaultGroup, isFormGroupCompleted, } from './utils/groups.js';
12
13
  export { ROLE_RANK, EMPTY_GRANTS, normalizeRole, roleAtLeast, effectiveRole, canView, canEdit, canManage, canManageOrg, isGuest, groupGrants, resolveAccess, type MaybeRole, type GroupedGrants, type ResolvedAccess, } from './utils/permissions.js';
package/dist/server.js CHANGED
@@ -11,7 +11,8 @@ export * from './types/firestore.js';
11
11
  export * from './types/layout.js';
12
12
  export * from './types/annotation.js';
13
13
  export * from './types/pdfCanvas.js';
14
- export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, pageStateToCanvasState, rotatePageState, } from './annotation/canvas/pdfCanvasPages.js';
14
+ export { computeBaseFitZoom, resolveTileFactors, tileDocSizesFor, pdfCanvasPageTileDocSizes, } from './annotation/canvas/tileCalibration.js';
15
+ export { normalizePdfRotation, effectivePageRotation, pdfPageDisplaySize, pdfCanvasPageDocId, pageStateToCanvasState, rotatePageState, pdfCanvasPagePlacements, pdfCanvasPlacementsOf, pdfCanvasPageKnobs, } from './annotation/canvas/pdfCanvasPages.js';
15
16
  export { getToleranceColor, getToleranceSecondaryColor, calculateDeviationPercentage, isWithinTolerance, generateToleranceGradient, createDefaultToleranceThresholds, DEFAULT_TOLERANCE_COLORS, DEFAULT_TOLERANCE_SECONDARY_COLORS, } from './utils/tolerance.js';
16
17
  export { DEFAULT_GROUP_INDEX, isDefaultGroup, findDefaultGroup, isFormGroupCompleted, } from './utils/groups.js';
17
18
  export { ROLE_RANK, EMPTY_GRANTS, normalizeRole, roleAtLeast, effectiveRole, canView, canEdit, canManage, canManageOrg, isGuest, groupGrants, resolveAccess, } from './utils/permissions.js';
@@ -1,4 +1,4 @@
1
- import type { AnnotationLayer, AnnotationShape, AnnotationStroke, PlacedMeasurementRef, PlaneCalibration } from './annotation.js';
1
+ import type { AnnotationBackgroundLayer, AnnotationLayer, AnnotationShape, AnnotationStroke, PlacedMeasurementRef, PlaneCalibration } from './annotation.js';
2
2
  export type PdfPageRotation = 0 | 90 | 180 | 270;
3
3
  export interface PdfCanvasPage {
4
4
  width: number;
@@ -23,8 +23,18 @@ export interface PdfCanvasData {
23
23
  headerTileScaleFactor?: number;
24
24
  headerTileScaleFactorMobile?: number;
25
25
  tileDocSize?: number;
26
+ pageTileScales?: Record<string, PdfCanvasTileScales>;
26
27
  annotatedPages: number[];
28
+ placementsByPage?: Record<string, PdfCanvasPlacement[]>;
27
29
  }
30
+ export interface PdfCanvasTileScales {
31
+ tileScaleFactor?: number;
32
+ tileScaleFactorMobile?: number;
33
+ headerTileScaleFactor?: number;
34
+ headerTileScaleFactorMobile?: number;
35
+ tileDocSize?: number;
36
+ }
37
+ export type PdfCanvasPlacement = Pick<PlacedMeasurementRef, 'measurementId' | 'measurementPath' | 'groupId' | 'columnId'>;
28
38
  export interface PdfCanvasPageState {
29
39
  schemaVersion: 1;
30
40
  pageIndex: number;
@@ -33,6 +43,7 @@ export interface PdfCanvasPageState {
33
43
  shapes: AnnotationShape[];
34
44
  placedMeasurements: PlacedMeasurementRef[];
35
45
  planes?: PlaneCalibration[];
46
+ backgroundImages?: AnnotationBackgroundLayer[];
36
47
  updatedAt: number;
37
48
  rev?: PdfCanvasRev;
38
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reekon-tools/boldr-utils",
3
- "version": "1.26.1",
3
+ "version": "1.26.2",
4
4
  "description": "Shared utilities for formulas and measurement conversion used in Reekon apps",
5
5
  "author": "REEKON Tools",
6
6
  "license": "MIT",
@@ -1,11 +0,0 @@
1
- import { type ReactNode } from 'react';
2
- import type { AnnotationCanvasInnerProps } from './AnnotationCanvasInner.js';
3
- export type { AnnotationCanvasHandle, GestureConfig, PanTrigger, } from './AnnotationCanvasInner.js';
4
- export interface CanvasKitOpts {
5
- locateFile?: (file: string) => string;
6
- }
7
- export type AnnotationCanvasProps = AnnotationCanvasInnerProps & {
8
- fallback?: ReactNode;
9
- canvasKitOpts?: CanvasKitOpts;
10
- };
11
- export declare const AnnotationCanvas: ({ fallback, canvasKitOpts, ...rest }: AnnotationCanvasProps) => import("react/jsx-runtime").JSX.Element;