@embedpdf/engine-core 3.0.0-next.13 → 3.0.0-next.15

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 (135) hide show
  1. package/dist/AnnotationList-CkijRHet.d.cts +27163 -0
  2. package/dist/AnnotationList-CkijRHet.d.ts +27163 -0
  3. package/dist/Change-CKoxfTNY.js +889 -0
  4. package/dist/Change-CKoxfTNY.js.map +1 -0
  5. package/dist/Change-CjImsrN0.cjs +1020 -0
  6. package/dist/Change-CjImsrN0.cjs.map +1 -0
  7. package/dist/DocumentRenderService-BfFdFSwI.cjs +120 -0
  8. package/dist/DocumentRenderService-BfFdFSwI.cjs.map +1 -0
  9. package/dist/DocumentRenderService-DV3J9lMz.js +85 -0
  10. package/dist/DocumentRenderService-DV3J9lMz.js.map +1 -0
  11. package/dist/{DocumentSecurityService-_JRjqvoI.d.ts → DocumentSecurityService-CU1M6Q7m.d.ts} +100 -112
  12. package/dist/{DocumentSecurityService-ClTZc0zO.d.cts → DocumentSecurityService-e6vp2de8.d.cts} +100 -112
  13. package/dist/LocalDocumentHandle-D02FMm-Z.cjs +53 -0
  14. package/dist/LocalDocumentHandle-D02FMm-Z.cjs.map +1 -0
  15. package/dist/LocalDocumentHandle-DAPdaOt_.js +30 -0
  16. package/dist/LocalDocumentHandle-DAPdaOt_.js.map +1 -0
  17. package/dist/LocalEngine-DFoBm4B3.d.cts +1565 -0
  18. package/dist/LocalEngine-DPriFujS.d.ts +1565 -0
  19. package/dist/PageInsertBlankInput-CXHAM7l6.cjs +17 -0
  20. package/dist/PageInsertBlankInput-CXHAM7l6.cjs.map +1 -0
  21. package/dist/PageInsertBlankInput-DhVYVJ_S.js +12 -0
  22. package/dist/PageInsertBlankInput-DhVYVJ_S.js.map +1 -0
  23. package/dist/authority-B3BklsB4.js +2239 -0
  24. package/dist/authority-B3BklsB4.js.map +1 -0
  25. package/dist/authority-DLMNR7ff.cjs +2676 -0
  26. package/dist/authority-DLMNR7ff.cjs.map +1 -0
  27. package/dist/builders-CNj0POeY.js +161 -0
  28. package/dist/builders-CNj0POeY.js.map +1 -0
  29. package/dist/builders-Cgdp_YCn.cjs +184 -0
  30. package/dist/builders-Cgdp_YCn.cjs.map +1 -0
  31. package/dist/calibrate-7T3fnP8-.js +2404 -0
  32. package/dist/calibrate-7T3fnP8-.js.map +1 -0
  33. package/dist/calibrate-DJBXnhLE.cjs +2919 -0
  34. package/dist/calibrate-DJBXnhLE.cjs.map +1 -0
  35. package/dist/charmap-Cprti1ta.js +239 -0
  36. package/dist/charmap-Cprti1ta.js.map +1 -0
  37. package/dist/charmap-aYlefQP9.cjs +304 -0
  38. package/dist/charmap-aYlefQP9.cjs.map +1 -0
  39. package/dist/conformance.cjs +16753 -5247
  40. package/dist/conformance.cjs.map +1 -1
  41. package/dist/conformance.d.cts +928 -165
  42. package/dist/conformance.d.ts +928 -165
  43. package/dist/conformance.js +16731 -5268
  44. package/dist/conformance.js.map +1 -1
  45. package/dist/format-Bp3QFSH1.cjs +574 -0
  46. package/dist/format-Bp3QFSH1.cjs.map +1 -0
  47. package/dist/format-Dgg428Kb.js +461 -0
  48. package/dist/format-Dgg428Kb.js.map +1 -0
  49. package/dist/index.cjs +330 -103
  50. package/dist/index.d.cts +2 -2
  51. package/dist/index.d.ts +2 -2
  52. package/dist/index.js +14 -5
  53. package/dist/pageRefs-B3ur7Ckm.cjs +51 -0
  54. package/dist/pageRefs-B3ur7Ckm.cjs.map +1 -0
  55. package/dist/pageRefs-DkbuxSCV.js +40 -0
  56. package/dist/pageRefs-DkbuxSCV.js.map +1 -0
  57. package/dist/public.cjs +57 -0
  58. package/dist/public.d.cts +5 -0
  59. package/dist/public.d.ts +5 -0
  60. package/dist/public.js +9 -0
  61. package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs +3646 -0
  62. package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs.map +1 -0
  63. package/dist/resolveAnnotationDraft-CUpf0SMh.js +3167 -0
  64. package/dist/resolveAnnotationDraft-CUpf0SMh.js.map +1 -0
  65. package/dist/{resolver-inAVzsWt.js → resolver-CzxmD9Ck.js} +294 -144
  66. package/dist/resolver-CzxmD9Ck.js.map +1 -0
  67. package/dist/{resolver-CYVNV4hn.cjs → resolver-D2OCtE8F.cjs} +347 -143
  68. package/dist/resolver-D2OCtE8F.cjs.map +1 -0
  69. package/dist/runtime-Cl0v56mm.d.ts +2032 -0
  70. package/dist/runtime-p1WDzDo4.d.cts +2032 -0
  71. package/dist/runtime.cjs +733 -284
  72. package/dist/runtime.cjs.map +1 -1
  73. package/dist/runtime.d.cts +5 -1348
  74. package/dist/runtime.d.ts +5 -1348
  75. package/dist/runtime.js +396 -179
  76. package/dist/runtime.js.map +1 -1
  77. package/dist/schema-C-gU4vj7.cjs +3034 -0
  78. package/dist/schema-C-gU4vj7.cjs.map +1 -0
  79. package/dist/schema-DzlUNk38.js +2081 -0
  80. package/dist/schema-DzlUNk38.js.map +1 -0
  81. package/dist/schemas-Berf1swF.cjs +2481 -0
  82. package/dist/schemas-Berf1swF.cjs.map +1 -0
  83. package/dist/schemas-D6xOGjqg.js +1654 -0
  84. package/dist/schemas-D6xOGjqg.js.map +1 -0
  85. package/dist/subtype-CG4WpIRj.js +129 -0
  86. package/dist/subtype-CG4WpIRj.js.map +1 -0
  87. package/dist/subtype-aDt5TFmK.cjs +158 -0
  88. package/dist/subtype-aDt5TFmK.cjs.map +1 -0
  89. package/dist/validation.cjs +578 -0
  90. package/dist/validation.cjs.map +1 -0
  91. package/dist/validation.d.cts +174 -0
  92. package/dist/validation.d.ts +174 -0
  93. package/dist/validation.js +571 -0
  94. package/dist/validation.js.map +1 -0
  95. package/dist/value-BFS1R8ek.cjs +435 -0
  96. package/dist/value-BFS1R8ek.cjs.map +1 -0
  97. package/dist/value-Dhc8eYcx.js +340 -0
  98. package/dist/value-Dhc8eYcx.js.map +1 -0
  99. package/dist/viewport-BJW13EGZ.js +540 -0
  100. package/dist/viewport-BJW13EGZ.js.map +1 -0
  101. package/dist/viewport-DlL5Bxbg.cjs +671 -0
  102. package/dist/viewport-DlL5Bxbg.cjs.map +1 -0
  103. package/dist/wire.cjs +1056 -379
  104. package/dist/wire.cjs.map +1 -1
  105. package/dist/wire.d.cts +15069 -725
  106. package/dist/wire.d.ts +15069 -725
  107. package/dist/wire.js +825 -239
  108. package/dist/wire.js.map +1 -1
  109. package/package.json +10 -1
  110. package/dist/AbortError-Cq2igpZ6.cjs +0 -38
  111. package/dist/AbortError-Cq2igpZ6.cjs.map +0 -1
  112. package/dist/AbortError-DHqPBnYc.js +0 -27
  113. package/dist/AbortError-DHqPBnYc.js.map +0 -1
  114. package/dist/AnnotationListSnapshot-D3mFut1e.d.cts +0 -6871
  115. package/dist/AnnotationListSnapshot-D3mFut1e.d.ts +0 -6871
  116. package/dist/Engine-Bx0NOfmd.d.cts +0 -1264
  117. package/dist/Engine-DxtpCYcP.d.ts +0 -1264
  118. package/dist/PageInsertBlankInput-CqXBmFhN.cjs +0 -449
  119. package/dist/PageInsertBlankInput-CqXBmFhN.cjs.map +0 -1
  120. package/dist/PageInsertBlankInput-cHq9-i-z.js +0 -378
  121. package/dist/PageInsertBlankInput-cHq9-i-z.js.map +0 -1
  122. package/dist/builders-Bqm8yTpi.cjs +0 -3564
  123. package/dist/builders-Bqm8yTpi.cjs.map +0 -1
  124. package/dist/builders-Dqo2X-dt.js +0 -3121
  125. package/dist/builders-Dqo2X-dt.js.map +0 -1
  126. package/dist/fold-2V29cxCV.cjs +0 -447
  127. package/dist/fold-2V29cxCV.cjs.map +0 -1
  128. package/dist/fold-CdEJTBTn.js +0 -334
  129. package/dist/fold-CdEJTBTn.js.map +0 -1
  130. package/dist/resolver-CYVNV4hn.cjs.map +0 -1
  131. package/dist/resolver-inAVzsWt.js.map +0 -1
  132. package/dist/schemas-BLARNCgx.js +0 -3113
  133. package/dist/schemas-BLARNCgx.js.map +0 -1
  134. package/dist/schemas-DIqFMmrC.cjs +0 -4468
  135. package/dist/schemas-DIqFMmrC.cjs.map +0 -1
@@ -0,0 +1,3646 @@
1
+ const require_format = require("./format-Bp3QFSH1.cjs");
2
+ //#region src/geometry/convert.ts
3
+ /**
4
+ * Whether `value` is a rect: `left`, `bottom`, `right` and `top`, each a finite number. A rect
5
+ * with `Infinity` or `NaN` in it places nothing, so every write refuses one: here on the page,
6
+ * and by `PdfRectSchema` and `PageBoxSchema` in the engine and on the wire.
7
+ */
8
+ function isFinitePdfRect(value) {
9
+ if (typeof value !== "object" || value === null) return false;
10
+ const { left, bottom, right, top } = value;
11
+ return [
12
+ left,
13
+ bottom,
14
+ right,
15
+ top
16
+ ].every((edge) => typeof edge === "number" && Number.isFinite(edge));
17
+ }
18
+ /**
19
+ * Normalize a `PdfRect` to the y-up invariant (`left <= right`, `bottom <= top`),
20
+ * regardless of how the PDF ordered the corners (a `/Rect` array or `FS_RECTF`
21
+ * may carry them in any order). The enforcement point for the "PdfRect is always
22
+ * normalized" rule: every producer that reads a raw box from PDFium passes it
23
+ * through here, so the invariant holds once, centrally — downstream `width =
24
+ * right - left` is never negative.
25
+ */
26
+ function normalizePdfRect(r) {
27
+ return {
28
+ left: Math.min(r.left, r.right),
29
+ right: Math.max(r.left, r.right),
30
+ bottom: Math.min(r.top, r.bottom),
31
+ top: Math.max(r.top, r.bottom)
32
+ };
33
+ }
34
+ function pdfRectWidth(r) {
35
+ return r.right - r.left;
36
+ }
37
+ function pdfRectHeight(r) {
38
+ return r.top - r.bottom;
39
+ }
40
+ function pdfRectSize(r) {
41
+ return {
42
+ width: r.right - r.left,
43
+ height: r.top - r.bottom
44
+ };
45
+ }
46
+ /** Convert edges -> origin+size, staying y-up (origin at bottom-left). */
47
+ function pdfRectToOriginSize(r) {
48
+ return {
49
+ x: r.left,
50
+ y: r.bottom,
51
+ width: r.right - r.left,
52
+ height: r.top - r.bottom
53
+ };
54
+ }
55
+ /** Convert y-up origin+size back to edges. */
56
+ function pdfRectFromOriginSize(o) {
57
+ return {
58
+ left: o.x,
59
+ bottom: o.y,
60
+ right: o.x + o.width,
61
+ top: o.y + o.height
62
+ };
63
+ }
64
+ /**
65
+ * Enclosing axis-aligned box of a quad, in PDF user space. Orientation
66
+ * agnostic (correct for rotated/skewed quads) — this is what highlight
67
+ * rendering and hit-testing want.
68
+ */
69
+ function pdfQuadBounds(q) {
70
+ const xs = [
71
+ q.upperLeft.x,
72
+ q.upperRight.x,
73
+ q.lowerLeft.x,
74
+ q.lowerRight.x
75
+ ];
76
+ const ys = [
77
+ q.upperLeft.y,
78
+ q.upperRight.y,
79
+ q.lowerLeft.y,
80
+ q.lowerRight.y
81
+ ];
82
+ return {
83
+ left: Math.min(...xs),
84
+ right: Math.max(...xs),
85
+ bottom: Math.min(...ys),
86
+ top: Math.max(...ys)
87
+ };
88
+ }
89
+ const QUAD_EPSILON = 1e-6;
90
+ const isFinitePoint = (point) => Number.isFinite(point.x) && Number.isFinite(point.y);
91
+ /** Whether the four points, read as upper-left, upper-right, lower-left, lower-right, make a quad. */
92
+ function isZigzagQuad([ul, ur, ll, lr]) {
93
+ if (![
94
+ ul,
95
+ ur,
96
+ ll,
97
+ lr
98
+ ].every(isFinitePoint)) return false;
99
+ const upper = {
100
+ x: ur.x - ul.x,
101
+ y: ur.y - ul.y
102
+ };
103
+ const lower = {
104
+ x: lr.x - ll.x,
105
+ y: lr.y - ll.y
106
+ };
107
+ const left = {
108
+ x: ll.x - ul.x,
109
+ y: ll.y - ul.y
110
+ };
111
+ const right = {
112
+ x: lr.x - ur.x,
113
+ y: lr.y - ur.y
114
+ };
115
+ const lengths = [
116
+ upper,
117
+ lower,
118
+ left,
119
+ right
120
+ ].map((edge) => Math.hypot(edge.x, edge.y));
121
+ if (lengths.some((length) => length <= QUAD_EPSILON)) return false;
122
+ if (upper.x * lower.x + upper.y * lower.y <= 0) return false;
123
+ if (left.x * right.x + left.y * right.y <= 0) return false;
124
+ const leftArea = upper.x * left.y - upper.y * left.x;
125
+ const rightArea = lower.x * right.y - lower.y * right.x;
126
+ if (Math.abs(leftArea) <= QUAD_EPSILON * lengths[0] * lengths[2]) return false;
127
+ if (Math.abs(rightArea) <= QUAD_EPSILON * lengths[1] * lengths[3]) return false;
128
+ return Math.sign(leftArea) === Math.sign(rightArea);
129
+ }
130
+ /**
131
+ * Name the corners of a `/QuadPoints` entry. Writers disagree on the order,
132
+ * so every quad read from a file passes through here, and the corner names
133
+ * hold once, centrally:
134
+ *
135
+ * 1. the order Acrobat writes and reads (upper-left, upper-right,
136
+ * lower-left, lower-right) is kept, turned quads included;
137
+ * 2. the ring order some writers use (upper-left, upper-right, lower-right,
138
+ * lower-left) is repaired;
139
+ * 3. anything else is named from its shape: the corners go round their
140
+ * middle, the edge highest on the page (largest y) is the upper one, and
141
+ * its end with the smaller x is the left. Which side of a quad is up
142
+ * cannot be told from four points alone; this rule decides it.
143
+ *
144
+ * A misnamed quad can move an underline to the wrong edge, never cross it.
145
+ */
146
+ function normalizePdfQuad(points) {
147
+ const [first, second, third, fourth] = points;
148
+ if (isZigzagQuad(points)) return {
149
+ upperLeft: first,
150
+ upperRight: second,
151
+ lowerLeft: third,
152
+ lowerRight: fourth
153
+ };
154
+ if (isZigzagQuad([
155
+ first,
156
+ second,
157
+ fourth,
158
+ third
159
+ ])) return {
160
+ upperLeft: first,
161
+ upperRight: second,
162
+ lowerLeft: fourth,
163
+ lowerRight: third
164
+ };
165
+ const finite = points.map((point) => isFinitePoint(point) ? point : {
166
+ x: 0,
167
+ y: 0
168
+ });
169
+ const cx = finite.reduce((sum, point) => sum + point.x, 0) / 4;
170
+ const cy = finite.reduce((sum, point) => sum + point.y, 0) / 4;
171
+ const ring = [...finite].sort((a, b) => Math.atan2(a.y - cy, a.x - cx) - Math.atan2(b.y - cy, b.x - cx));
172
+ let upper = 0;
173
+ for (let i = 1; i < 4; i++) if (ring[i].y + ring[(i + 1) % 4].y > ring[upper].y + ring[(upper + 1) % 4].y) upper = i;
174
+ const upperEnds = [ring[upper], ring[(upper + 1) % 4]];
175
+ const lowerEnds = [ring[(upper + 3) % 4], ring[(upper + 2) % 4]];
176
+ return upperEnds[0].x <= upperEnds[1].x ? {
177
+ upperLeft: upperEnds[0],
178
+ upperRight: upperEnds[1],
179
+ lowerLeft: lowerEnds[0],
180
+ lowerRight: lowerEnds[1]
181
+ } : {
182
+ upperLeft: upperEnds[1],
183
+ upperRight: upperEnds[0],
184
+ lowerLeft: lowerEnds[1],
185
+ lowerRight: lowerEnds[0]
186
+ };
187
+ }
188
+ /**
189
+ * The upright box around `rect` turned by `degrees` about its middle, either
190
+ * way: the `/Rect` of a turned box. A quarter turn is exact: the sides swap.
191
+ */
192
+ function pdfRectTurnedBounds(rect, degrees) {
193
+ const quarterTurn = quarterTurnOf(degrees);
194
+ if (quarterTurn !== null) return pdfQuarterTurnBox(rect, quarterTurn);
195
+ const radians = degrees * Math.PI / 180;
196
+ const cos = Math.abs(Math.cos(radians));
197
+ const sin = Math.abs(Math.sin(radians));
198
+ const width = rect.right - rect.left;
199
+ const height = rect.top - rect.bottom;
200
+ const halfAcross = (width * cos + height * sin) / 2;
201
+ const halfUp = (width * sin + height * cos) / 2;
202
+ const x = (rect.left + rect.right) / 2;
203
+ const y = (rect.bottom + rect.top) / 2;
204
+ return {
205
+ left: x - halfAcross,
206
+ bottom: y - halfUp,
207
+ right: x + halfAcross,
208
+ top: y + halfUp
209
+ };
210
+ }
211
+ /**
212
+ * `degrees` as a quarter turn (clockwise, 0 to 270), or `null` when it isn't
213
+ * one. A negative turn, or one past a whole turn, is the same turn.
214
+ */
215
+ function quarterTurnOf(degrees) {
216
+ const turn = (degrees % 360 + 360) % 360;
217
+ return turn % 90 === 0 ? turn : null;
218
+ }
219
+ /**
220
+ * The box that, turned `rotation` about its middle, stands upright in
221
+ * `rect`: `rect` itself, its sides swapped under 90 and 270. The inverse of
222
+ * {@link pdfRectTurnedBounds} for a quarter turn, which is the one turn a
223
+ * rect pins a box down for.
224
+ */
225
+ function pdfQuarterTurnBox(rect, rotation) {
226
+ if (rotation === 0 || rotation === 180) return rect;
227
+ const x = (rect.left + rect.right) / 2;
228
+ const y = (rect.bottom + rect.top) / 2;
229
+ const halfWidth = (rect.top - rect.bottom) / 2;
230
+ const halfHeight = (rect.right - rect.left) / 2;
231
+ return {
232
+ left: x - halfWidth,
233
+ bottom: y - halfHeight,
234
+ right: x + halfWidth,
235
+ top: y + halfHeight
236
+ };
237
+ }
238
+ /**
239
+ * The area two normalized rects share, or `null` when they share none. Keys
240
+ * in `normalizePdfRect`'s order, so a rect reads the same whichever made it.
241
+ */
242
+ function pdfRectIntersection(rect, other) {
243
+ const left = Math.max(rect.left, other.left);
244
+ const right = Math.min(rect.right, other.right);
245
+ const bottom = Math.max(rect.bottom, other.bottom);
246
+ const top = Math.min(rect.top, other.top);
247
+ return left < right && bottom < top ? {
248
+ left,
249
+ right,
250
+ bottom,
251
+ top
252
+ } : null;
253
+ }
254
+ /**
255
+ * Whether two rects are the same within what a file's numbers hold: Acrobat
256
+ * writes three decimals, and a rect the engine writes is floats.
257
+ */
258
+ function isSamePdfRect(rect, other) {
259
+ const tolerance = .01 + 1e-4 * Math.max(rect.right - rect.left, rect.top - rect.bottom, other.right - other.left, other.top - other.bottom);
260
+ return Math.abs(rect.left - other.left) <= tolerance && Math.abs(rect.bottom - other.bottom) <= tolerance && Math.abs(rect.right - other.right) <= tolerance && Math.abs(rect.top - other.top) <= tolerance;
261
+ }
262
+ //#endregion
263
+ //#region src/geometry/pointTurn.ts
264
+ /** The box around `points`; a zero box at the origin when there are none. */
265
+ function pdfPointsBounds(points) {
266
+ if (points.length === 0) return {
267
+ left: 0,
268
+ bottom: 0,
269
+ right: 0,
270
+ top: 0
271
+ };
272
+ let left = Infinity;
273
+ let bottom = Infinity;
274
+ let right = -Infinity;
275
+ let top = -Infinity;
276
+ for (const { x, y } of points) {
277
+ left = Math.min(left, x);
278
+ bottom = Math.min(bottom, y);
279
+ right = Math.max(right, x);
280
+ top = Math.max(top, y);
281
+ }
282
+ return {
283
+ left,
284
+ bottom,
285
+ right,
286
+ top
287
+ };
288
+ }
289
+ /** `point` turned by `turn`; a negative angle turns it back. */
290
+ function pdfPointTurned(point, turn) {
291
+ const radians = turn.degrees * Math.PI / 180;
292
+ const cos = Math.cos(radians);
293
+ const sin = Math.sin(radians);
294
+ const dx = point.x - turn.center.x;
295
+ const dy = point.y - turn.center.y;
296
+ return {
297
+ x: turn.center.x + dx * cos + dy * sin,
298
+ y: turn.center.y - dx * sin + dy * cos
299
+ };
300
+ }
301
+ /** `point` turned back by `turn`. */
302
+ function pdfPointUnturned(point, turn) {
303
+ return pdfPointTurned(point, {
304
+ degrees: -turn.degrees,
305
+ center: turn.center
306
+ });
307
+ }
308
+ const middleOf = (rect) => ({
309
+ x: (rect.left + rect.right) / 2,
310
+ y: (rect.bottom + rect.top) / 2
311
+ });
312
+ /** The turn that draws upright `points`: `degrees` about the middle of their box. */
313
+ function pdfTurnOfUpright(points, degrees) {
314
+ return {
315
+ degrees,
316
+ center: middleOf(pdfPointsBounds(points))
317
+ };
318
+ }
319
+ /**
320
+ * The turn drawn `points` were made with: `degrees` about the middle of the
321
+ * box around the upright points they came from. That middle is where the turn
322
+ * leaves it, so it follows from the drawing: turned back about the origin, the
323
+ * points are the upright ones moved, and turning their middle forward again
324
+ * about the origin gives it.
325
+ */
326
+ function pdfTurnOfDrawn(points, degrees) {
327
+ const origin = {
328
+ x: 0,
329
+ y: 0
330
+ };
331
+ const back = points.map((point) => pdfPointTurned(point, {
332
+ degrees: -degrees,
333
+ center: origin
334
+ }));
335
+ return {
336
+ degrees,
337
+ center: pdfPointTurned(middleOf(pdfPointsBounds(back)), {
338
+ degrees,
339
+ center: origin
340
+ })
341
+ };
342
+ }
343
+ //#endregion
344
+ //#region src/dto/RichText.ts
345
+ /**
346
+ * The body a partial input body completes itself from: Helvetica 12 pt
347
+ * black, left-aligned, ltr, nothing else set.
348
+ */
349
+ const DEFAULT_RICH_TEXT_BODY = {
350
+ family: "Helvetica",
351
+ weight: 400,
352
+ italic: false,
353
+ size: 12,
354
+ color: "#000000",
355
+ decoration: [],
356
+ script: "normal",
357
+ letterSpacing: 0,
358
+ horizontalScale: 1,
359
+ align: "left",
360
+ dir: "ltr"
361
+ };
362
+ /** The plain-text projection of a rich document: paragraphs joined by `\r`,
363
+ * runs concatenated — what the engine writes to `/Contents`. */
364
+ function richTextPlainText(doc) {
365
+ return doc.paragraphs.map((p) => p.runs.map((r) => r.text).join("")).join("\r");
366
+ }
367
+ /** Plain text as rich paragraphs: one per line break, one run each, no
368
+ * overrides (the engine's own `/Contents` fallback shape). */
369
+ function richTextParagraphsFromPlainText(text) {
370
+ return text.split(/\r\n|\r|\n/).map((line) => ({ runs: [{ text: line }] }));
371
+ }
372
+ //#endregion
373
+ //#region src/annotation/color.ts
374
+ /** One 0..255 component as two lowercase hex digits. */
375
+ const hex2 = (component) => Math.max(0, Math.min(255, Math.round(component))).toString(16).padStart(2, "0");
376
+ /** A color from its 0..255 sRGB components, as the engine gives colors. */
377
+ function colorOf(r, g, b) {
378
+ return `#${hex2(r)}${hex2(g)}${hex2(b)}`;
379
+ }
380
+ /** The 0..255 sRGB components of a `'#rrggbb'` color, in either case. */
381
+ function rgbOf(color) {
382
+ const value = Number.parseInt(color.slice(1), 16);
383
+ return {
384
+ r: value >> 16 & 255,
385
+ g: value >> 8 & 255,
386
+ b: value & 255
387
+ };
388
+ }
389
+ /** Whether two colors are the same, whatever the case of their digits. */
390
+ function sameColor(a, b) {
391
+ return a.toLowerCase() === b.toLowerCase();
392
+ }
393
+ //#endregion
394
+ //#region src/annotation/kind-facts.generated.ts
395
+ /** Each annotation kind's {@link KindFacts}, in the engine's catalog order. */
396
+ const ANNOTATION_KIND_FACTS = {
397
+ highlight: {
398
+ owners: {
399
+ ref: "engine",
400
+ page: "engine",
401
+ hasAppearance: "engine",
402
+ appearanceState: "engine",
403
+ nm: "data",
404
+ rect: "engine",
405
+ contents: "data",
406
+ subject: "data",
407
+ blendMode: "data",
408
+ invisible: "data",
409
+ hidden: "data",
410
+ print: "data",
411
+ noZoom: "data",
412
+ noRotate: "data",
413
+ noView: "data",
414
+ readOnly: "data",
415
+ locked: "data",
416
+ toggleNoView: "data",
417
+ lockedContents: "data",
418
+ reply: "data",
419
+ popup: "engine",
420
+ groupId: "data",
421
+ author: "attribution",
422
+ createdAt: "attribution",
423
+ modifiedAt: "attribution",
424
+ userId: "attribution",
425
+ createdBy: "attribution",
426
+ modifiedBy: "attribution",
427
+ importedBy: "engine",
428
+ actions: "preserved",
429
+ color: "data",
430
+ opacity: "data",
431
+ quadPoints: "data"
432
+ },
433
+ readBack: ["rect"],
434
+ colors: ["color"],
435
+ spaces: {
436
+ rect: "box",
437
+ actions: "actions",
438
+ quadPoints: "quads"
439
+ },
440
+ resources: {}
441
+ },
442
+ underline: {
443
+ owners: {
444
+ ref: "engine",
445
+ page: "engine",
446
+ hasAppearance: "engine",
447
+ appearanceState: "engine",
448
+ nm: "data",
449
+ rect: "engine",
450
+ contents: "data",
451
+ subject: "data",
452
+ blendMode: "data",
453
+ invisible: "data",
454
+ hidden: "data",
455
+ print: "data",
456
+ noZoom: "data",
457
+ noRotate: "data",
458
+ noView: "data",
459
+ readOnly: "data",
460
+ locked: "data",
461
+ toggleNoView: "data",
462
+ lockedContents: "data",
463
+ reply: "data",
464
+ popup: "engine",
465
+ groupId: "data",
466
+ author: "attribution",
467
+ createdAt: "attribution",
468
+ modifiedAt: "attribution",
469
+ userId: "attribution",
470
+ createdBy: "attribution",
471
+ modifiedBy: "attribution",
472
+ importedBy: "engine",
473
+ actions: "preserved",
474
+ color: "data",
475
+ opacity: "data",
476
+ quadPoints: "data"
477
+ },
478
+ readBack: ["rect"],
479
+ colors: ["color"],
480
+ spaces: {
481
+ rect: "box",
482
+ actions: "actions",
483
+ quadPoints: "quads"
484
+ },
485
+ resources: {}
486
+ },
487
+ squiggly: {
488
+ owners: {
489
+ ref: "engine",
490
+ page: "engine",
491
+ hasAppearance: "engine",
492
+ appearanceState: "engine",
493
+ nm: "data",
494
+ rect: "engine",
495
+ contents: "data",
496
+ subject: "data",
497
+ blendMode: "data",
498
+ invisible: "data",
499
+ hidden: "data",
500
+ print: "data",
501
+ noZoom: "data",
502
+ noRotate: "data",
503
+ noView: "data",
504
+ readOnly: "data",
505
+ locked: "data",
506
+ toggleNoView: "data",
507
+ lockedContents: "data",
508
+ reply: "data",
509
+ popup: "engine",
510
+ groupId: "data",
511
+ author: "attribution",
512
+ createdAt: "attribution",
513
+ modifiedAt: "attribution",
514
+ userId: "attribution",
515
+ createdBy: "attribution",
516
+ modifiedBy: "attribution",
517
+ importedBy: "engine",
518
+ actions: "preserved",
519
+ color: "data",
520
+ opacity: "data",
521
+ quadPoints: "data"
522
+ },
523
+ readBack: ["rect"],
524
+ colors: ["color"],
525
+ spaces: {
526
+ rect: "box",
527
+ actions: "actions",
528
+ quadPoints: "quads"
529
+ },
530
+ resources: {}
531
+ },
532
+ strikeout: {
533
+ owners: {
534
+ ref: "engine",
535
+ page: "engine",
536
+ hasAppearance: "engine",
537
+ appearanceState: "engine",
538
+ nm: "data",
539
+ rect: "engine",
540
+ contents: "data",
541
+ subject: "data",
542
+ blendMode: "data",
543
+ invisible: "data",
544
+ hidden: "data",
545
+ print: "data",
546
+ noZoom: "data",
547
+ noRotate: "data",
548
+ noView: "data",
549
+ readOnly: "data",
550
+ locked: "data",
551
+ toggleNoView: "data",
552
+ lockedContents: "data",
553
+ reply: "data",
554
+ popup: "engine",
555
+ groupId: "data",
556
+ author: "attribution",
557
+ createdAt: "attribution",
558
+ modifiedAt: "attribution",
559
+ userId: "attribution",
560
+ createdBy: "attribution",
561
+ modifiedBy: "attribution",
562
+ importedBy: "engine",
563
+ actions: "preserved",
564
+ color: "data",
565
+ opacity: "data",
566
+ quadPoints: "data",
567
+ intent: "data"
568
+ },
569
+ readBack: ["rect"],
570
+ colors: ["color"],
571
+ spaces: {
572
+ rect: "box",
573
+ actions: "actions",
574
+ quadPoints: "quads"
575
+ },
576
+ resources: {}
577
+ },
578
+ circle: {
579
+ owners: {
580
+ ref: "engine",
581
+ page: "engine",
582
+ hasAppearance: "engine",
583
+ appearanceState: "engine",
584
+ nm: "data",
585
+ rect: "engine",
586
+ contents: "data",
587
+ subject: "data",
588
+ blendMode: "data",
589
+ invisible: "data",
590
+ hidden: "data",
591
+ print: "data",
592
+ noZoom: "data",
593
+ noRotate: "data",
594
+ noView: "data",
595
+ readOnly: "data",
596
+ locked: "data",
597
+ toggleNoView: "data",
598
+ lockedContents: "data",
599
+ reply: "data",
600
+ popup: "engine",
601
+ groupId: "data",
602
+ author: "attribution",
603
+ createdAt: "attribution",
604
+ modifiedAt: "attribution",
605
+ userId: "attribution",
606
+ createdBy: "attribution",
607
+ modifiedBy: "attribution",
608
+ importedBy: "engine",
609
+ actions: "preserved",
610
+ color: "data",
611
+ opacity: "data",
612
+ strokeWidth: "data",
613
+ borderStyle: "data",
614
+ dashArray: "data",
615
+ interiorColor: "data",
616
+ box: "data",
617
+ rotation: "data",
618
+ cloudyIntensity: "data"
619
+ },
620
+ readBack: ["rect", "borderStyle"],
621
+ colors: ["color", "interiorColor"],
622
+ spaces: {
623
+ rect: "box",
624
+ actions: "actions",
625
+ box: "box"
626
+ },
627
+ resources: {}
628
+ },
629
+ square: {
630
+ owners: {
631
+ ref: "engine",
632
+ page: "engine",
633
+ hasAppearance: "engine",
634
+ appearanceState: "engine",
635
+ nm: "data",
636
+ rect: "engine",
637
+ contents: "data",
638
+ subject: "data",
639
+ blendMode: "data",
640
+ invisible: "data",
641
+ hidden: "data",
642
+ print: "data",
643
+ noZoom: "data",
644
+ noRotate: "data",
645
+ noView: "data",
646
+ readOnly: "data",
647
+ locked: "data",
648
+ toggleNoView: "data",
649
+ lockedContents: "data",
650
+ reply: "data",
651
+ popup: "engine",
652
+ groupId: "data",
653
+ author: "attribution",
654
+ createdAt: "attribution",
655
+ modifiedAt: "attribution",
656
+ userId: "attribution",
657
+ createdBy: "attribution",
658
+ modifiedBy: "attribution",
659
+ importedBy: "engine",
660
+ actions: "preserved",
661
+ color: "data",
662
+ opacity: "data",
663
+ strokeWidth: "data",
664
+ borderStyle: "data",
665
+ dashArray: "data",
666
+ interiorColor: "data",
667
+ box: "data",
668
+ rotation: "data",
669
+ cloudyIntensity: "data"
670
+ },
671
+ readBack: ["rect", "borderStyle"],
672
+ colors: ["color", "interiorColor"],
673
+ spaces: {
674
+ rect: "box",
675
+ actions: "actions",
676
+ box: "box"
677
+ },
678
+ resources: {}
679
+ },
680
+ polygon: {
681
+ owners: {
682
+ ref: "engine",
683
+ page: "engine",
684
+ hasAppearance: "engine",
685
+ appearanceState: "engine",
686
+ nm: "data",
687
+ rect: "engine",
688
+ contents: "data",
689
+ subject: "data",
690
+ blendMode: "data",
691
+ invisible: "data",
692
+ hidden: "data",
693
+ print: "data",
694
+ noZoom: "data",
695
+ noRotate: "data",
696
+ noView: "data",
697
+ readOnly: "data",
698
+ locked: "data",
699
+ toggleNoView: "data",
700
+ lockedContents: "data",
701
+ reply: "data",
702
+ popup: "engine",
703
+ groupId: "data",
704
+ author: "attribution",
705
+ createdAt: "attribution",
706
+ modifiedAt: "attribution",
707
+ userId: "attribution",
708
+ createdBy: "attribution",
709
+ modifiedBy: "attribution",
710
+ importedBy: "engine",
711
+ actions: "preserved",
712
+ color: "data",
713
+ opacity: "data",
714
+ strokeWidth: "data",
715
+ borderStyle: "data",
716
+ dashArray: "data",
717
+ interiorColor: "data",
718
+ vertices: "data",
719
+ rotation: "data",
720
+ captionEnabled: "data",
721
+ captionCenter: "data",
722
+ intent: "data",
723
+ measure: "data",
724
+ cloudyIntensity: "data"
725
+ },
726
+ readBack: [
727
+ "rect",
728
+ "borderStyle",
729
+ "vertices"
730
+ ],
731
+ colors: ["color", "interiorColor"],
732
+ spaces: {
733
+ rect: "box",
734
+ actions: "actions",
735
+ vertices: "points",
736
+ captionCenter: "point",
737
+ measure: "measure"
738
+ },
739
+ resources: {}
740
+ },
741
+ polyline: {
742
+ owners: {
743
+ ref: "engine",
744
+ page: "engine",
745
+ hasAppearance: "engine",
746
+ appearanceState: "engine",
747
+ nm: "data",
748
+ rect: "engine",
749
+ contents: "data",
750
+ subject: "data",
751
+ blendMode: "data",
752
+ invisible: "data",
753
+ hidden: "data",
754
+ print: "data",
755
+ noZoom: "data",
756
+ noRotate: "data",
757
+ noView: "data",
758
+ readOnly: "data",
759
+ locked: "data",
760
+ toggleNoView: "data",
761
+ lockedContents: "data",
762
+ reply: "data",
763
+ popup: "engine",
764
+ groupId: "data",
765
+ author: "attribution",
766
+ createdAt: "attribution",
767
+ modifiedAt: "attribution",
768
+ userId: "attribution",
769
+ createdBy: "attribution",
770
+ modifiedBy: "attribution",
771
+ importedBy: "engine",
772
+ actions: "preserved",
773
+ color: "data",
774
+ opacity: "data",
775
+ strokeWidth: "data",
776
+ borderStyle: "data",
777
+ dashArray: "data",
778
+ interiorColor: "data",
779
+ vertices: "data",
780
+ rotation: "data",
781
+ captionEnabled: "data",
782
+ captionCenter: "data",
783
+ intent: "data",
784
+ measure: "data",
785
+ lineEndings: "data"
786
+ },
787
+ readBack: [
788
+ "rect",
789
+ "borderStyle",
790
+ "vertices"
791
+ ],
792
+ colors: ["color", "interiorColor"],
793
+ spaces: {
794
+ rect: "box",
795
+ actions: "actions",
796
+ vertices: "points",
797
+ captionCenter: "point",
798
+ measure: "measure"
799
+ },
800
+ resources: {}
801
+ },
802
+ line: {
803
+ owners: {
804
+ ref: "engine",
805
+ page: "engine",
806
+ hasAppearance: "engine",
807
+ appearanceState: "engine",
808
+ nm: "data",
809
+ rect: "engine",
810
+ contents: "data",
811
+ subject: "data",
812
+ blendMode: "data",
813
+ invisible: "data",
814
+ hidden: "data",
815
+ print: "data",
816
+ noZoom: "data",
817
+ noRotate: "data",
818
+ noView: "data",
819
+ readOnly: "data",
820
+ locked: "data",
821
+ toggleNoView: "data",
822
+ lockedContents: "data",
823
+ reply: "data",
824
+ popup: "engine",
825
+ groupId: "data",
826
+ author: "attribution",
827
+ createdAt: "attribution",
828
+ modifiedAt: "attribution",
829
+ userId: "attribution",
830
+ createdBy: "attribution",
831
+ modifiedBy: "attribution",
832
+ importedBy: "engine",
833
+ actions: "preserved",
834
+ color: "data",
835
+ opacity: "data",
836
+ strokeWidth: "data",
837
+ borderStyle: "data",
838
+ dashArray: "data",
839
+ interiorColor: "data",
840
+ linePoints: "data",
841
+ lineEndings: "data",
842
+ rotation: "data",
843
+ intent: "data",
844
+ measure: "data",
845
+ captionEnabled: "data",
846
+ captionPosition: "data",
847
+ captionOffset: "data",
848
+ leader: "data"
849
+ },
850
+ readBack: ["rect", "borderStyle"],
851
+ colors: ["color", "interiorColor"],
852
+ spaces: {
853
+ rect: "box",
854
+ actions: "actions",
855
+ linePoints: "linePoints",
856
+ measure: "measure"
857
+ },
858
+ resources: {}
859
+ },
860
+ link: {
861
+ owners: {
862
+ ref: "engine",
863
+ page: "engine",
864
+ hasAppearance: "engine",
865
+ appearanceState: "engine",
866
+ nm: "data",
867
+ rect: "data",
868
+ contents: "data",
869
+ subject: "data",
870
+ blendMode: "data",
871
+ invisible: "data",
872
+ hidden: "data",
873
+ print: "data",
874
+ noZoom: "data",
875
+ noRotate: "data",
876
+ noView: "data",
877
+ readOnly: "data",
878
+ locked: "data",
879
+ toggleNoView: "data",
880
+ lockedContents: "data",
881
+ reply: "data",
882
+ popup: "engine",
883
+ groupId: "data",
884
+ author: "attribution",
885
+ createdAt: "attribution",
886
+ modifiedAt: "attribution",
887
+ userId: "attribution",
888
+ createdBy: "attribution",
889
+ modifiedBy: "attribution",
890
+ importedBy: "engine",
891
+ actions: "preserved",
892
+ target: "data"
893
+ },
894
+ readBack: ["target"],
895
+ colors: [],
896
+ spaces: {
897
+ rect: "box",
898
+ actions: "actions",
899
+ target: "linkTarget"
900
+ },
901
+ resources: {}
902
+ },
903
+ ink: {
904
+ owners: {
905
+ ref: "engine",
906
+ page: "engine",
907
+ hasAppearance: "engine",
908
+ appearanceState: "engine",
909
+ nm: "data",
910
+ rect: "engine",
911
+ contents: "data",
912
+ subject: "data",
913
+ blendMode: "data",
914
+ invisible: "data",
915
+ hidden: "data",
916
+ print: "data",
917
+ noZoom: "data",
918
+ noRotate: "data",
919
+ noView: "data",
920
+ readOnly: "data",
921
+ locked: "data",
922
+ toggleNoView: "data",
923
+ lockedContents: "data",
924
+ reply: "data",
925
+ popup: "engine",
926
+ groupId: "data",
927
+ author: "attribution",
928
+ createdAt: "attribution",
929
+ modifiedAt: "attribution",
930
+ userId: "attribution",
931
+ createdBy: "attribution",
932
+ modifiedBy: "attribution",
933
+ importedBy: "engine",
934
+ actions: "preserved",
935
+ color: "data",
936
+ opacity: "data",
937
+ strokeWidth: "data",
938
+ borderStyle: "data",
939
+ dashArray: "data",
940
+ inkList: "data",
941
+ intent: "data",
942
+ rotation: "data"
943
+ },
944
+ readBack: [
945
+ "rect",
946
+ "strokeWidth",
947
+ "borderStyle",
948
+ "inkList"
949
+ ],
950
+ colors: ["color"],
951
+ spaces: {
952
+ rect: "box",
953
+ actions: "actions",
954
+ inkList: "strokes"
955
+ },
956
+ resources: {}
957
+ },
958
+ "free-text": {
959
+ owners: {
960
+ ref: "engine",
961
+ page: "engine",
962
+ hasAppearance: "engine",
963
+ appearanceState: "engine",
964
+ nm: "data",
965
+ rect: "engine",
966
+ contents: "data",
967
+ subject: "data",
968
+ blendMode: "data",
969
+ invisible: "data",
970
+ hidden: "data",
971
+ print: "data",
972
+ noZoom: "data",
973
+ noRotate: "data",
974
+ noView: "data",
975
+ readOnly: "data",
976
+ locked: "data",
977
+ toggleNoView: "data",
978
+ lockedContents: "data",
979
+ reply: "data",
980
+ popup: "engine",
981
+ groupId: "data",
982
+ author: "attribution",
983
+ createdAt: "attribution",
984
+ modifiedAt: "attribution",
985
+ userId: "attribution",
986
+ createdBy: "attribution",
987
+ modifiedBy: "attribution",
988
+ importedBy: "engine",
989
+ actions: "preserved",
990
+ box: "data",
991
+ rotation: "data",
992
+ intent: "data",
993
+ fontFamily: "data",
994
+ fontSize: "data",
995
+ textAlign: "data",
996
+ verticalAlign: "data",
997
+ richText: "data",
998
+ color: "data",
999
+ fontColor: "data",
1000
+ interiorColor: "data",
1001
+ opacity: "data",
1002
+ strokeWidth: "data",
1003
+ borderStyle: "data",
1004
+ dashArray: "data",
1005
+ cloudyIntensity: "data",
1006
+ calloutLine: "data",
1007
+ lineEnding: "data"
1008
+ },
1009
+ readBack: [
1010
+ "rect",
1011
+ "intent",
1012
+ "borderStyle"
1013
+ ],
1014
+ colors: [
1015
+ "color",
1016
+ "fontColor",
1017
+ "interiorColor"
1018
+ ],
1019
+ spaces: {
1020
+ rect: "box",
1021
+ actions: "actions",
1022
+ box: "box",
1023
+ richText: "length",
1024
+ calloutLine: "calloutLine"
1025
+ },
1026
+ resources: {}
1027
+ },
1028
+ caret: {
1029
+ owners: {
1030
+ ref: "engine",
1031
+ page: "engine",
1032
+ hasAppearance: "engine",
1033
+ appearanceState: "engine",
1034
+ nm: "data",
1035
+ rect: "engine",
1036
+ contents: "data",
1037
+ subject: "data",
1038
+ blendMode: "data",
1039
+ invisible: "data",
1040
+ hidden: "data",
1041
+ print: "data",
1042
+ noZoom: "data",
1043
+ noRotate: "data",
1044
+ noView: "data",
1045
+ readOnly: "data",
1046
+ locked: "data",
1047
+ toggleNoView: "data",
1048
+ lockedContents: "data",
1049
+ reply: "data",
1050
+ popup: "engine",
1051
+ groupId: "data",
1052
+ author: "attribution",
1053
+ createdAt: "attribution",
1054
+ modifiedAt: "attribution",
1055
+ userId: "attribution",
1056
+ createdBy: "attribution",
1057
+ modifiedBy: "attribution",
1058
+ importedBy: "engine",
1059
+ actions: "preserved",
1060
+ color: "data",
1061
+ opacity: "data",
1062
+ box: "data",
1063
+ rotation: "data",
1064
+ intent: "data"
1065
+ },
1066
+ readBack: ["rect"],
1067
+ colors: ["color"],
1068
+ spaces: {
1069
+ rect: "box",
1070
+ actions: "actions",
1071
+ box: "box"
1072
+ },
1073
+ resources: {}
1074
+ },
1075
+ text: {
1076
+ owners: {
1077
+ ref: "engine",
1078
+ page: "engine",
1079
+ hasAppearance: "engine",
1080
+ appearanceState: "engine",
1081
+ nm: "data",
1082
+ rect: "data",
1083
+ contents: "data",
1084
+ subject: "data",
1085
+ blendMode: "data",
1086
+ invisible: "data",
1087
+ hidden: "data",
1088
+ print: "data",
1089
+ noZoom: "data",
1090
+ noRotate: "data",
1091
+ noView: "data",
1092
+ readOnly: "data",
1093
+ locked: "data",
1094
+ toggleNoView: "data",
1095
+ lockedContents: "data",
1096
+ reply: "data",
1097
+ popup: "engine",
1098
+ groupId: "data",
1099
+ author: "attribution",
1100
+ createdAt: "attribution",
1101
+ modifiedAt: "attribution",
1102
+ userId: "attribution",
1103
+ createdBy: "attribution",
1104
+ modifiedBy: "attribution",
1105
+ importedBy: "engine",
1106
+ actions: "preserved",
1107
+ color: "data",
1108
+ opacity: "data",
1109
+ icon: "data",
1110
+ open: "data",
1111
+ state: "data",
1112
+ stateModel: "data"
1113
+ },
1114
+ readBack: [],
1115
+ colors: ["color"],
1116
+ spaces: {
1117
+ rect: "box",
1118
+ actions: "actions"
1119
+ },
1120
+ resources: {}
1121
+ },
1122
+ stamp: {
1123
+ owners: {
1124
+ ref: "engine",
1125
+ page: "engine",
1126
+ hasAppearance: "engine",
1127
+ appearanceState: "engine",
1128
+ nm: "data",
1129
+ rect: "engine",
1130
+ contents: "data",
1131
+ subject: "data",
1132
+ blendMode: "data",
1133
+ invisible: "data",
1134
+ hidden: "data",
1135
+ print: "data",
1136
+ noZoom: "data",
1137
+ noRotate: "data",
1138
+ noView: "data",
1139
+ readOnly: "data",
1140
+ locked: "data",
1141
+ toggleNoView: "data",
1142
+ lockedContents: "data",
1143
+ reply: "data",
1144
+ popup: "engine",
1145
+ groupId: "data",
1146
+ author: "attribution",
1147
+ createdAt: "attribution",
1148
+ modifiedAt: "attribution",
1149
+ userId: "attribution",
1150
+ createdBy: "attribution",
1151
+ modifiedBy: "attribution",
1152
+ importedBy: "engine",
1153
+ actions: "preserved",
1154
+ box: "data",
1155
+ rotation: "data",
1156
+ name: "data",
1157
+ fit: "data",
1158
+ opacity: "data"
1159
+ },
1160
+ readBack: ["rect"],
1161
+ colors: [],
1162
+ spaces: {
1163
+ rect: "box",
1164
+ actions: "actions",
1165
+ box: "box"
1166
+ },
1167
+ resources: { appearance: "required" }
1168
+ },
1169
+ "file-attachment": {
1170
+ owners: {
1171
+ ref: "engine",
1172
+ page: "engine",
1173
+ hasAppearance: "engine",
1174
+ appearanceState: "engine",
1175
+ nm: "data",
1176
+ rect: "data",
1177
+ contents: "data",
1178
+ subject: "data",
1179
+ blendMode: "data",
1180
+ invisible: "data",
1181
+ hidden: "data",
1182
+ print: "data",
1183
+ noZoom: "data",
1184
+ noRotate: "data",
1185
+ noView: "data",
1186
+ readOnly: "data",
1187
+ locked: "data",
1188
+ toggleNoView: "data",
1189
+ lockedContents: "data",
1190
+ reply: "data",
1191
+ popup: "engine",
1192
+ groupId: "data",
1193
+ author: "attribution",
1194
+ createdAt: "attribution",
1195
+ modifiedAt: "attribution",
1196
+ userId: "attribution",
1197
+ createdBy: "attribution",
1198
+ modifiedBy: "attribution",
1199
+ importedBy: "engine",
1200
+ actions: "preserved",
1201
+ color: "data",
1202
+ opacity: "data",
1203
+ icon: "data",
1204
+ file: "data"
1205
+ },
1206
+ readBack: [],
1207
+ colors: ["color"],
1208
+ spaces: {
1209
+ rect: "box",
1210
+ actions: "actions"
1211
+ },
1212
+ resources: { file: "required" }
1213
+ },
1214
+ widget: {
1215
+ owners: {
1216
+ ref: "engine",
1217
+ page: "engine",
1218
+ hasAppearance: "engine",
1219
+ appearanceState: "engine",
1220
+ nm: "data",
1221
+ rect: "engine",
1222
+ contents: "data",
1223
+ subject: "data",
1224
+ blendMode: "data",
1225
+ invisible: "data",
1226
+ hidden: "data",
1227
+ print: "data",
1228
+ noZoom: "data",
1229
+ noRotate: "data",
1230
+ noView: "data",
1231
+ readOnly: "data",
1232
+ locked: "data",
1233
+ toggleNoView: "data",
1234
+ lockedContents: "data",
1235
+ reply: "data",
1236
+ popup: "engine",
1237
+ groupId: "data",
1238
+ author: "attribution",
1239
+ createdAt: "attribution",
1240
+ modifiedAt: "attribution",
1241
+ userId: "attribution",
1242
+ createdBy: "attribution",
1243
+ modifiedBy: "attribution",
1244
+ importedBy: "engine",
1245
+ actions: "data",
1246
+ box: "data",
1247
+ rotation: "data",
1248
+ color: "data",
1249
+ interiorColor: "data",
1250
+ strokeWidth: "data",
1251
+ borderStyle: "data",
1252
+ dashArray: "data",
1253
+ fontFamily: "data",
1254
+ fontSize: "data",
1255
+ fontColor: "data",
1256
+ textAlign: "data",
1257
+ caption: "data",
1258
+ checkStyle: "data",
1259
+ field: "engine",
1260
+ fieldFamily: "engine"
1261
+ },
1262
+ readBack: ["rect", "actions"],
1263
+ colors: [
1264
+ "color",
1265
+ "interiorColor",
1266
+ "fontColor"
1267
+ ],
1268
+ spaces: {
1269
+ rect: "box",
1270
+ actions: "actions",
1271
+ box: "box"
1272
+ },
1273
+ resources: {}
1274
+ },
1275
+ redact: {
1276
+ owners: {
1277
+ ref: "engine",
1278
+ page: "engine",
1279
+ hasAppearance: "engine",
1280
+ appearanceState: "engine",
1281
+ nm: "data",
1282
+ rect: "data",
1283
+ contents: "data",
1284
+ subject: "data",
1285
+ blendMode: "data",
1286
+ invisible: "data",
1287
+ hidden: "data",
1288
+ print: "data",
1289
+ noZoom: "data",
1290
+ noRotate: "data",
1291
+ noView: "data",
1292
+ readOnly: "data",
1293
+ locked: "data",
1294
+ toggleNoView: "data",
1295
+ lockedContents: "data",
1296
+ reply: "data",
1297
+ popup: "engine",
1298
+ groupId: "data",
1299
+ author: "attribution",
1300
+ createdAt: "attribution",
1301
+ modifiedAt: "attribution",
1302
+ userId: "attribution",
1303
+ createdBy: "attribution",
1304
+ modifiedBy: "attribution",
1305
+ importedBy: "engine",
1306
+ actions: "preserved",
1307
+ color: "data",
1308
+ opacity: "data",
1309
+ quadPoints: "data",
1310
+ interiorColor: "data",
1311
+ overlayText: "data",
1312
+ repeat: "data",
1313
+ fontFamily: "data",
1314
+ fontSize: "data",
1315
+ fontColor: "data",
1316
+ textAlign: "data"
1317
+ },
1318
+ readBack: [],
1319
+ colors: [
1320
+ "color",
1321
+ "interiorColor",
1322
+ "fontColor"
1323
+ ],
1324
+ spaces: {
1325
+ rect: "box",
1326
+ actions: "actions",
1327
+ quadPoints: "quads"
1328
+ },
1329
+ resources: {}
1330
+ },
1331
+ popup: {
1332
+ owners: {
1333
+ ref: "engine",
1334
+ page: "engine",
1335
+ hasAppearance: "engine",
1336
+ appearanceState: "engine",
1337
+ nm: "data",
1338
+ rect: "data",
1339
+ contents: "data",
1340
+ subject: "data",
1341
+ blendMode: "data",
1342
+ invisible: "data",
1343
+ hidden: "data",
1344
+ print: "data",
1345
+ noZoom: "data",
1346
+ noRotate: "data",
1347
+ noView: "data",
1348
+ readOnly: "data",
1349
+ locked: "data",
1350
+ toggleNoView: "data",
1351
+ lockedContents: "data",
1352
+ reply: "data",
1353
+ popup: "engine",
1354
+ groupId: "data",
1355
+ author: "attribution",
1356
+ createdAt: "attribution",
1357
+ modifiedAt: "attribution",
1358
+ userId: "attribution",
1359
+ createdBy: "attribution",
1360
+ modifiedBy: "attribution",
1361
+ importedBy: "engine",
1362
+ actions: "preserved",
1363
+ parent: "data",
1364
+ open: "data"
1365
+ },
1366
+ readBack: ["parent"],
1367
+ colors: [],
1368
+ spaces: {
1369
+ rect: "box",
1370
+ actions: "actions"
1371
+ },
1372
+ resources: {}
1373
+ },
1374
+ unsupported: {
1375
+ owners: {
1376
+ ref: "engine",
1377
+ page: "engine",
1378
+ hasAppearance: "engine",
1379
+ appearanceState: "engine",
1380
+ nm: "data",
1381
+ rect: "data",
1382
+ contents: "data",
1383
+ subject: "data",
1384
+ blendMode: "data",
1385
+ invisible: "data",
1386
+ hidden: "data",
1387
+ print: "data",
1388
+ noZoom: "data",
1389
+ noRotate: "data",
1390
+ noView: "data",
1391
+ readOnly: "data",
1392
+ locked: "data",
1393
+ toggleNoView: "data",
1394
+ lockedContents: "data",
1395
+ reply: "data",
1396
+ popup: "engine",
1397
+ groupId: "data",
1398
+ author: "attribution",
1399
+ createdAt: "attribution",
1400
+ modifiedAt: "attribution",
1401
+ userId: "attribution",
1402
+ createdBy: "attribution",
1403
+ modifiedBy: "attribution",
1404
+ importedBy: "engine",
1405
+ actions: "preserved",
1406
+ rawSubtypeCode: "engine",
1407
+ rawSubtypeName: "engine"
1408
+ },
1409
+ readBack: [],
1410
+ colors: [],
1411
+ spaces: {
1412
+ rect: "box",
1413
+ actions: "actions"
1414
+ },
1415
+ resources: {}
1416
+ }
1417
+ };
1418
+ //#endregion
1419
+ //#region src/annotation/kind-facts.ts
1420
+ /** A table with an entry for every kind, worked out from the kind's facts. */
1421
+ function mapKindFacts(of) {
1422
+ const table = {};
1423
+ for (const subtype of Object.keys(ANNOTATION_KIND_FACTS)) table[subtype] = of(ANNOTATION_KIND_FACTS[subtype]);
1424
+ return table;
1425
+ }
1426
+ /** The facts of the kind `subtype` names, or `null` when no kind has that subtype. */
1427
+ function kindFactsOf(subtype) {
1428
+ return Object.prototype.hasOwnProperty.call(ANNOTATION_KIND_FACTS, subtype) ? ANNOTATION_KIND_FACTS[subtype] : null;
1429
+ }
1430
+ //#endregion
1431
+ //#region src/annotation/field-names.ts
1432
+ /**
1433
+ * Every field each annotation kind declares, in declaration order, without zod, for code that
1434
+ * checks the keys of a write but can't load the schemas (the engines, their workers, the
1435
+ * viewer). From `kind-facts.generated.ts`.
1436
+ */
1437
+ const ANNOTATION_FIELD_NAMES = mapKindFacts((facts) => Object.keys(facts.owners));
1438
+ /** The resources each kind takes (`create(data, resources)`), without zod. */
1439
+ const ANNOTATION_RESOURCE_ROLES = mapKindFacts((facts) => facts.resources);
1440
+ //#endregion
1441
+ //#region src/annotation/field-spaces.ts
1442
+ /**
1443
+ * The fields of each annotation kind that hold something measured on the page, and what they
1444
+ * hold (`FieldSpace`), without zod, for the code that converts values between the file's
1445
+ * coordinates and page space in the engines and their workers. From
1446
+ * `kind-facts.generated.ts`; `test/annotation/field-spaces.test.ts` checks that no declared
1447
+ * field holds a place on the page without saying so.
1448
+ */
1449
+ const ANNOTATION_FIELD_SPACES = mapKindFacts((facts) => facts.spaces);
1450
+ /**
1451
+ * Every measured field by its name, whatever its kind: a name holds the same
1452
+ * thing in every kind (`test/annotation/field-spaces.test.ts` keeps it so).
1453
+ * An update that leaves out its kind converts by this.
1454
+ */
1455
+ const ANNOTATION_FIELD_SPACES_BY_NAME = Object.assign({}, ...Object.values(ANNOTATION_FIELD_SPACES));
1456
+ //#endregion
1457
+ //#region src/dto/IsoDateTime.ts
1458
+ /**
1459
+ * Order two moments by the instant they name. Their text doesn't order them:
1460
+ * `2017-07-12T21:44:38-07:00` is later than `2017-07-13T01:00:00Z`.
1461
+ */
1462
+ function compareIsoDateTime(a, b) {
1463
+ const difference = Date.parse(a) - Date.parse(b);
1464
+ return Number.isNaN(difference) ? 0 : difference;
1465
+ }
1466
+ //#endregion
1467
+ //#region src/transfer/bundle.ts
1468
+ /**
1469
+ * What every bundle shares, whichever family it carries: resources named by
1470
+ * the SHA-256 of their bytes, the table of pages its rows are on, the checks
1471
+ * of that shape, and where an import puts each page.
1472
+ */
1473
+ const RESOURCE_ID_PATTERN = /^sha256-[0-9a-f]{64}$/;
1474
+ const PAGE_FIELDS = [
1475
+ "page",
1476
+ "position",
1477
+ "size"
1478
+ ];
1479
+ /** The id of a resource: the SHA-256 of its bytes. */
1480
+ async function resourceIdOf(bytes) {
1481
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
1482
+ let hex = "";
1483
+ for (const byte of digest) hex += byte.toString(16).padStart(2, "0");
1484
+ return `sha256-${hex}`;
1485
+ }
1486
+ /** Every resource's bytes hash to its id. */
1487
+ async function assertResourceIds(kind, resources) {
1488
+ for (const [id, bytes] of Object.entries(resources)) if (await resourceIdOf(bytes) !== id) invalidBundle(kind, `resource ${id} doesn't match its id`);
1489
+ }
1490
+ /**
1491
+ * Check a bundle's page table: each entry a page ref, a position and a
1492
+ * size, no page or position listed twice. Returns the listed pages' keys,
1493
+ * for the rows to be checked against.
1494
+ */
1495
+ function assertBundlePages(kind, pages) {
1496
+ const pageKeys = /* @__PURE__ */ new Set();
1497
+ const positions = /* @__PURE__ */ new Set();
1498
+ for (const entry of pages) {
1499
+ if (!isRecord(entry) || !isBundlePageRef(entry.page) || !isSize(entry.size)) invalidBundle(kind, "a page needs a page ref, a position and a size");
1500
+ assertKnownFields(kind, "a page", entry, PAGE_FIELDS);
1501
+ const { position } = entry;
1502
+ if (!Number.isInteger(position) || position < 0) invalidBundle(kind, `page position ${JSON.stringify(position)} is not a position`);
1503
+ const key = require_format.encodePageKey(entry.page);
1504
+ if (pageKeys.has(key)) invalidBundle(kind, `page ${key} is listed twice`);
1505
+ if (positions.has(position)) invalidBundle(kind, `position ${position} is listed twice`);
1506
+ pageKeys.add(key);
1507
+ positions.add(position);
1508
+ }
1509
+ return pageKeys;
1510
+ }
1511
+ /**
1512
+ * Check the resources a row names, by role: each role one of `roles`, each
1513
+ * id a resource id the bundle holds. Each id is added to `named`.
1514
+ */
1515
+ function assertRowResources(kind, row, resources, roles, resourceSizes, named) {
1516
+ if (!isRecord(resources)) invalidBundle(kind, `${row} has no resources`);
1517
+ for (const [role, id] of Object.entries(resources)) {
1518
+ if (!roles.includes(role)) invalidBundle(kind, `${row} names an unknown resource role '${role}'`);
1519
+ if (typeof id !== "string" || !RESOURCE_ID_PATTERN.test(id)) invalidBundle(kind, `${row} names ${JSON.stringify(id)}, which is not a resource id`);
1520
+ if (!resourceSizes.has(id)) invalidBundle(kind, `${row} names ${id}, which the bundle doesn't hold`);
1521
+ named.add(id);
1522
+ }
1523
+ }
1524
+ /**
1525
+ * Every resource the bundle holds has a resource id and is named by one of
1526
+ * its `rows` (`'item'`, `'widget'`).
1527
+ */
1528
+ function assertResourcesNamed(kind, rows, resourceSizes, named) {
1529
+ for (const id of resourceSizes.keys()) {
1530
+ if (!RESOURCE_ID_PATTERN.test(id)) invalidBundle(kind, `${JSON.stringify(id)} is not a resource id`);
1531
+ if (!named.has(id)) invalidBundle(kind, `resource ${id} is named by no ${rows}`);
1532
+ }
1533
+ }
1534
+ /** `value` has no field outside `fields`. */
1535
+ function assertKnownFields(kind, what, value, fields) {
1536
+ for (const field of Object.keys(value)) if (!fields.includes(field)) invalidBundle(kind, `${what} has an unknown field '${field}'`);
1537
+ }
1538
+ /** Refuse a bundle that isn't one: `InvalidArg`, naming its kind. */
1539
+ function invalidBundle(kind, message) {
1540
+ throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${kind} bundle: ${message}`);
1541
+ }
1542
+ function isRecord(value) {
1543
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1544
+ }
1545
+ /** A page ref as a bundle holds one: by object number. */
1546
+ function isBundlePageRef(value) {
1547
+ return isRecord(value) && value.kind === "objectNumber" && Number.isInteger(value.objectNumber) && value.objectNumber > 0;
1548
+ }
1549
+ function isSize(value) {
1550
+ return isRecord(value) && [value.width, value.height].every((length) => typeof length === "number" && Number.isFinite(length) && length >= 0);
1551
+ }
1552
+ /**
1553
+ * Where an import puts each page of a bundle, as `pages` says, among the
1554
+ * `target` document's pages. Every page in `named` (the pages the rows are
1555
+ * on or point at) must map somewhere: `InvalidArg` names those that don't,
1556
+ * and a list that maps to a page the target doesn't have is `NotFound`.
1557
+ * Nothing here touches a document, so an import refuses before its first
1558
+ * write.
1559
+ */
1560
+ function bundlePageMapping(input) {
1561
+ const table = pageTable(input.bundlePages, input.pages, input.target);
1562
+ const unmapped = /* @__PURE__ */ new Set();
1563
+ for (const page of input.named) if (!table.has(require_format.encodePageKey(page))) unmapped.add(require_format.encodePageKey(page));
1564
+ if (unmapped.size > 0) {
1565
+ const pages = [...unmapped];
1566
+ throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `import: ${pages.length === 1 ? "page" : "pages"} ${pages.join(", ")} map to no page of this document`, { details: { pages } });
1567
+ }
1568
+ return (page) => table.get(require_format.encodePageKey(page));
1569
+ }
1570
+ /** Bundle page key → target page. */
1571
+ function pageTable(bundlePages, pages, target) {
1572
+ const targetByKey = new Map(target.map((entry) => [require_format.encodePageKey(entry.page), entry.page]));
1573
+ const table = /* @__PURE__ */ new Map();
1574
+ if (pages === "same") for (const entry of bundlePages) {
1575
+ const to = targetByKey.get(require_format.encodePageKey(entry.page));
1576
+ if (to) table.set(require_format.encodePageKey(entry.page), to);
1577
+ }
1578
+ else if (pages === "by-position") {
1579
+ const byPosition = new Map(target.map((entry) => [entry.position, entry.page]));
1580
+ for (const entry of bundlePages) {
1581
+ const to = byPosition.get(entry.position);
1582
+ if (to) table.set(require_format.encodePageKey(entry.page), to);
1583
+ }
1584
+ } else for (const { from, to } of pages) {
1585
+ const key = require_format.encodePageKey(from);
1586
+ if (table.has(key)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `import: page ${key} is mapped twice`);
1587
+ const found = targetByKey.get(require_format.encodePageKey(to));
1588
+ if (!found) throw new require_format.EngineError(require_format.EngineErrorCode.NotFound, `import: the document has no page ${require_format.encodePageKey(to)}`);
1589
+ table.set(key, found);
1590
+ }
1591
+ return table;
1592
+ }
1593
+ //#endregion
1594
+ //#region src/annotation/relationships.ts
1595
+ /**
1596
+ * Classify one annotation by its relationship edge alone. Mirrors the
1597
+ * spec's "practical parser rule": no `/IRT` is top-level; `/RT /Group` is
1598
+ * a grouped subordinate; anything else with an `/IRT` is a reply (the
1599
+ * engine has already normalized a missing `/RT` to `'reply'`).
1600
+ */
1601
+ function classifyRelation(a) {
1602
+ if (!a.reply) return "top-level";
1603
+ return a.reply.type === "group" ? "grouped-subordinate" : "reply";
1604
+ }
1605
+ /**
1606
+ * Compose a flat annotation list (one page, or a whole document from
1607
+ * `annotations.list()`) into {@link AnnotationThread}s in primary
1608
+ * order.
1609
+ *
1610
+ * Rules (ISO 32000 §12.5.6.2 + the spec's UI rule):
1611
+ * - Annotations with no `/IRT` become thread primaries, in input order.
1612
+ * - A `/RT /Group` child is attached to its primary's `groupedParts`.
1613
+ * - A `/RT /R` (or default) child is attached to its primary's `replies`.
1614
+ * - A child whose parent is not in the input set is treated as an
1615
+ * orphan primary (defensive — e.g. a partial/filtered page slice).
1616
+ *
1617
+ * Limitations (v1, matches the legacy stack): threading is one level deep
1618
+ * — a reply-to-a-reply is keyed to its direct parent, so it only appears
1619
+ * nested if that direct parent is itself a primary. Callers that want a
1620
+ * sidebar of "most annotations, not widgets/popups" should pre-filter the
1621
+ * input; this helper is intentionally unopinionated about eligibility.
1622
+ */
1623
+ function buildThreads(annotations) {
1624
+ const byKey = new Map(annotations.map((a) => [require_format.annotationKey(a.ref), a]));
1625
+ const threads = [];
1626
+ const threadByPrimaryKey = /* @__PURE__ */ new Map();
1627
+ const primaryThread = (primary) => {
1628
+ const key = require_format.annotationKey(primary.ref);
1629
+ let thread = threadByPrimaryKey.get(key);
1630
+ if (!thread) {
1631
+ thread = {
1632
+ primary,
1633
+ groupedParts: [],
1634
+ replies: []
1635
+ };
1636
+ threadByPrimaryKey.set(key, thread);
1637
+ threads.push(thread);
1638
+ }
1639
+ return thread;
1640
+ };
1641
+ for (const a of annotations) if (!a.reply) primaryThread(a);
1642
+ for (const a of annotations) {
1643
+ if (!a.reply) continue;
1644
+ const parent = byKey.get(require_format.annotationKey(a.reply.to));
1645
+ if (!parent || parent.reply) {
1646
+ primaryThread(a);
1647
+ continue;
1648
+ }
1649
+ const thread = primaryThread(parent);
1650
+ if (a.reply.type === "group") thread.groupedParts.push(a);
1651
+ else thread.replies.push(a);
1652
+ }
1653
+ return threads;
1654
+ }
1655
+ /**
1656
+ * An annotation and everything deleted with it: every annotation whose
1657
+ * `reply.to` leads to it (replies, their replies, grouped parts, review
1658
+ * states) and every popup of these. A reply left behind would point at
1659
+ * nothing and show as a note of its own in other viewers; a popup left
1660
+ * behind would show nobody's text. Children come before their parent and a
1661
+ * popup before the annotation it shows, so the annotation itself is last.
1662
+ * Empty when `ref` names nothing in `annotations`.
1663
+ */
1664
+ function deletedWith(annotations, ref) {
1665
+ const byKey = new Map(annotations.map((annotation) => [require_format.annotationKey(annotation.ref), annotation]));
1666
+ const target = byKey.get(require_format.annotationKey(ref));
1667
+ if (!target) return [];
1668
+ const children = /* @__PURE__ */ new Map();
1669
+ const popups = /* @__PURE__ */ new Map();
1670
+ const add = (map, key, value) => map.set(key, [...map.get(key) ?? [], value]);
1671
+ for (const annotation of annotations) {
1672
+ const parent = annotation.reply ? byKey.get(require_format.annotationKey(annotation.reply.to)) : void 0;
1673
+ if (parent && parent !== annotation) add(children, parent, annotation);
1674
+ const shows = annotation.subtype === "popup" && annotation.parent ? byKey.get(require_format.annotationKey(annotation.parent)) : void 0;
1675
+ if (shows && shows !== annotation) add(popups, shows, annotation);
1676
+ const own = annotation.popup ? byKey.get(require_format.annotationKey(annotation.popup)) : void 0;
1677
+ if (own && own !== annotation && own.subtype === "popup") add(popups, annotation, own);
1678
+ }
1679
+ const members = [];
1680
+ const seen = /* @__PURE__ */ new Set();
1681
+ const visit = (annotation) => {
1682
+ if (seen.has(annotation)) return;
1683
+ seen.add(annotation);
1684
+ for (const child of children.get(annotation) ?? []) visit(child);
1685
+ for (const popup of popups.get(annotation) ?? []) {
1686
+ if (seen.has(popup)) continue;
1687
+ seen.add(popup);
1688
+ members.push(popup);
1689
+ }
1690
+ members.push(annotation);
1691
+ };
1692
+ visit(target);
1693
+ return members;
1694
+ }
1695
+ //#endregion
1696
+ //#region src/annotation/comments.ts
1697
+ /** Subtypes that never participate in comment threads: a popup is its
1698
+ * parent's window and an unsupported blob is unreadable, so neither makes
1699
+ * a comment card. */
1700
+ const EXCLUDED_SUBTYPES = /* @__PURE__ */ new Set([
1701
+ "widget",
1702
+ "link",
1703
+ "popup",
1704
+ "unsupported"
1705
+ ]);
1706
+ const REVIEW_STATES = /* @__PURE__ */ new Set([
1707
+ "accepted",
1708
+ "rejected",
1709
+ "cancelled",
1710
+ "completed",
1711
+ "none"
1712
+ ]);
1713
+ const MARKED_STATES = /* @__PURE__ */ new Set(["marked", "unmarked"]);
1714
+ /**
1715
+ * The model a standard state belongs to (ISO 32000 §12.5.6.3, Table 174):
1716
+ * `'review'` for accepted, rejected, cancelled, completed and none,
1717
+ * `'marked'` for marked and unmarked; `null` for a custom state.
1718
+ */
1719
+ function standardStateModelOf(state) {
1720
+ if (REVIEW_STATES.has(state)) return "review";
1721
+ if (MARKED_STATES.has(state)) return "marked";
1722
+ return null;
1723
+ }
1724
+ const nonEmpty = (v) => typeof v === "string" && v !== "";
1725
+ /**
1726
+ * An ISO 32000 §12.5.6.3 state annotation: a text annotation carrying a
1727
+ * non-empty `/State` or `/StateModel`. Classification never relies on
1728
+ * flags — producers disagree on how state annotations are flagged.
1729
+ */
1730
+ function isStateAnnotation(a) {
1731
+ return a.subtype === "text" && (nonEmpty(a.state) || nonEmpty(a.stateModel));
1732
+ }
1733
+ function buildCommentThreads(annotations, opts = {}) {
1734
+ const eligible = annotations.filter((a) => !EXCLUDED_SUBTYPES.has(a.subtype));
1735
+ const byKey = new Map(eligible.map((a) => [require_format.annotationKey(a.ref), a]));
1736
+ const chronological = chronologicalIn(new Map(eligible.map((a, at) => [require_format.annotationKey(a.ref), at])));
1737
+ const children = /* @__PURE__ */ new Map();
1738
+ for (const a of eligible) {
1739
+ if (!a.reply) continue;
1740
+ const parent = byKey.get(require_format.annotationKey(a.reply.to));
1741
+ if (!parent) continue;
1742
+ const parentKey = require_format.annotationKey(parent.ref);
1743
+ const list = children.get(parentKey);
1744
+ if (list) list.push(a);
1745
+ else children.set(parentKey, [a]);
1746
+ }
1747
+ const visited = /* @__PURE__ */ new Set();
1748
+ const threads = [];
1749
+ const walk = (root) => {
1750
+ const replies = [];
1751
+ const groupedParts = [];
1752
+ const states = [];
1753
+ visited.add(require_format.annotationKey(root.ref));
1754
+ const stack = [...children.get(require_format.annotationKey(root.ref)) ?? []];
1755
+ while (stack.length > 0) {
1756
+ const a = stack.pop();
1757
+ const key = require_format.annotationKey(a.ref);
1758
+ if (visited.has(key)) continue;
1759
+ visited.add(key);
1760
+ if (isStateAnnotation(a)) states.push(a);
1761
+ else if (classifyRelation(a) === "grouped-subordinate") groupedParts.push(a);
1762
+ else replies.push(a);
1763
+ stack.push(...children.get(key) ?? []);
1764
+ }
1765
+ replies.sort(chronological);
1766
+ states.sort(chronological);
1767
+ threads.push({
1768
+ root,
1769
+ page: root.page,
1770
+ replies,
1771
+ groupedParts,
1772
+ review: computeReview(states, opts)
1773
+ });
1774
+ };
1775
+ for (const a of eligible) if (!a.reply && !isStateAnnotation(a)) walk(a);
1776
+ for (const a of eligible) {
1777
+ if (visited.has(require_format.annotationKey(a.ref))) continue;
1778
+ if (isStateAnnotation(a)) continue;
1779
+ walk(a);
1780
+ }
1781
+ return threads;
1782
+ }
1783
+ /** `createdAt ?? modifiedAt` ascending; undated last; z-order tiebreak. */
1784
+ function chronologicalIn(zOrder) {
1785
+ return (a, b) => {
1786
+ const at = a.createdAt ?? a.modifiedAt;
1787
+ const bt = b.createdAt ?? b.modifiedAt;
1788
+ const order = at !== null && bt !== null ? compareIsoDateTime(at, bt) : 0;
1789
+ if (order !== 0) return order;
1790
+ if (at !== null && bt === null) return -1;
1791
+ if (at === null && bt !== null) return 1;
1792
+ return zOrder.get(require_format.annotationKey(a.ref)) - zOrder.get(require_format.annotationKey(b.ref));
1793
+ };
1794
+ }
1795
+ function computeReview(states, opts) {
1796
+ const byReviewer = {};
1797
+ const markedLatest = {};
1798
+ let lastChange = null;
1799
+ for (const a of states) {
1800
+ const status = toReviewStatus(a);
1801
+ if (!status) continue;
1802
+ if (status.stateModel === "marked") {
1803
+ if (status.by !== null && isNewer(status, markedLatest[status.by])) markedLatest[status.by] = status;
1804
+ continue;
1805
+ }
1806
+ if (status.by !== null && isNewer(status, byReviewer[status.by])) byReviewer[status.by] = status;
1807
+ if (isNewer(status, lastChange)) lastChange = status;
1808
+ }
1809
+ const markedBy = Object.keys(markedLatest).filter((key) => markedLatest[key].state === "marked").sort();
1810
+ const review = {
1811
+ byReviewer,
1812
+ lastChange,
1813
+ markedBy,
1814
+ statusRefs: states.map((a) => a.ref)
1815
+ };
1816
+ if (opts.currentUserId !== void 0) review.mine = byReviewer[opts.currentUserId] ?? null;
1817
+ return review;
1818
+ }
1819
+ /**
1820
+ * Derive a `ReviewStatus` from one state annotation, or null when nothing
1821
+ * classifiable can be derived. ISO defaulting happens here, not in the
1822
+ * engine reader: a known model with an absent state means `none` /
1823
+ * `unmarked`; an absent model infers from a known state; a custom model
1824
+ * with no state has no derivable status.
1825
+ */
1826
+ function toReviewStatus(a) {
1827
+ if (a.subtype !== "text") return null;
1828
+ const rawState = nonEmpty(a.state) ? a.state : null;
1829
+ let stateModel = nonEmpty(a.stateModel) ? a.stateModel : null;
1830
+ if (stateModel === null && rawState !== null) {
1831
+ stateModel = standardStateModelOf(rawState);
1832
+ if (stateModel === null) return null;
1833
+ }
1834
+ let state = rawState;
1835
+ if (state === null) if (stateModel === "review") state = "none";
1836
+ else if (stateModel === "marked") state = "unmarked";
1837
+ else return null;
1838
+ if (stateModel === null) return null;
1839
+ return {
1840
+ state,
1841
+ stateModel,
1842
+ by: a.userId ?? (nonEmpty(a.author) ? a.author : null),
1843
+ at: a.modifiedAt ?? a.createdAt ?? null,
1844
+ ref: a.ref
1845
+ };
1846
+ }
1847
+ /** `a` newer than (or supersedes) `b`. `at` decides; dated beats undated.
1848
+ * States arrive pre-sorted by `chronological` (date, then z-order), so on
1849
+ * equal or missing dates the later entry wins via `>=` / `true`. */
1850
+ function isNewer(a, b) {
1851
+ if (!b) return true;
1852
+ if (a.at !== null && b.at !== null) return compareIsoDateTime(a.at, b.at) >= 0;
1853
+ if (a.at !== null) return true;
1854
+ if (b.at !== null) return false;
1855
+ return true;
1856
+ }
1857
+ //#endregion
1858
+ //#region src/annotation/appearance.ts
1859
+ /**
1860
+ * The kinds a write gives no appearance of the engine's: a link, which the
1861
+ * engine writes with no border to draw, and a form widget, which its form
1862
+ * field draws (one on its own has nothing to draw). A write leaves whether
1863
+ * the file holds one as it was, and a new one has none. Every other kind's
1864
+ * create bakes one, and so does every change to it that is visible and not a
1865
+ * pure move.
1866
+ */
1867
+ const UNBAKED_KINDS = /* @__PURE__ */ new Set(["link", "widget"]);
1868
+ /**
1869
+ * Coordinate tolerance in PDF user-space points. PDF numbers round-trip
1870
+ * through f32 (~7 significant digits) and client-side space conversions, so
1871
+ * exact float equality would misclassify no-op writes; 1e-3 pt is far below
1872
+ * anything visible while far above accumulated f32 drift at page magnitudes.
1873
+ */
1874
+ const EPSILON = .001;
1875
+ /**
1876
+ * Keys that never affect `/AP` on any kind: the discriminator, the `/F`
1877
+ * flags, the name, relationships and grouping, the conversation-plane
1878
+ * entries (`/Subj` subject line, `/State` + `/StateModel` review status —
1879
+ * dictionary-only per ISO 32000 §12.5.6.3, never painted), and the fields a
1880
+ * write accepts but never applies (addresses, attribution, `/A` and `/AA`).
1881
+ */
1882
+ const INERT_KEYS = /* @__PURE__ */ new Set([
1883
+ "subtype",
1884
+ "invisible",
1885
+ "hidden",
1886
+ "print",
1887
+ "noZoom",
1888
+ "noRotate",
1889
+ "noView",
1890
+ "readOnly",
1891
+ "locked",
1892
+ "toggleNoView",
1893
+ "lockedContents",
1894
+ "nm",
1895
+ "reply",
1896
+ "popup",
1897
+ "parent",
1898
+ "open",
1899
+ "groupId",
1900
+ "subject",
1901
+ "state",
1902
+ "stateModel",
1903
+ "ref",
1904
+ "page",
1905
+ "hasAppearance",
1906
+ "appearanceState",
1907
+ "author",
1908
+ "createdAt",
1909
+ "modifiedAt",
1910
+ "userId",
1911
+ "createdBy",
1912
+ "modifiedBy",
1913
+ "importedBy",
1914
+ "actions",
1915
+ "file",
1916
+ "field",
1917
+ "fieldFamily"
1918
+ ]);
1919
+ /**
1920
+ * Kinds whose `/AP` paints `/Contents`. Everywhere else `contents` is popup
1921
+ * note text — editing a comment must never re-bake (or destroy) the shape's
1922
+ * appearance.
1923
+ */
1924
+ const CONTENTS_PAINTED = /* @__PURE__ */ new Set(["free-text", "redact"]);
1925
+ /**
1926
+ * The kinds that turn by `rotation`: a box kind about the middle of its
1927
+ * `box`, a point kind about the middle of its points' box. Tri-state on
1928
+ * writes: an omitted rotation is kept, `null` (or `0`) clears it. Translation
1929
+ * verification therefore compares the after-state —
1930
+ * `patch.rotation ?? current.rotation` — not the patch keys.
1931
+ */
1932
+ const TURNING_KINDS = /* @__PURE__ */ new Set([
1933
+ "square",
1934
+ "circle",
1935
+ "free-text",
1936
+ "stamp",
1937
+ "caret",
1938
+ "widget",
1939
+ "line",
1940
+ "polyline",
1941
+ "polygon",
1942
+ "ink"
1943
+ ]);
1944
+ /**
1945
+ * Per-kind absolute-geometry fields (PDF user space) that a rigid translation
1946
+ * shifts together. A kind absent from this table never takes the translation
1947
+ * route. The first is where a move shows: the field the caller gives the
1948
+ * shape in (a `box`, the points, the quads), or `rect` where that is the
1949
+ * shape. A `rect` the engine works out moves with it.
1950
+ */
1951
+ const TRANSLATABLE_GEOMETRY = {
1952
+ square: ["box"],
1953
+ circle: ["box"],
1954
+ "free-text": ["box", "calloutLine"],
1955
+ line: ["linePoints"],
1956
+ polygon: ["vertices"],
1957
+ polyline: ["vertices"],
1958
+ ink: ["inkList"],
1959
+ highlight: ["quadPoints"],
1960
+ underline: ["quadPoints"],
1961
+ squiggly: ["quadPoints"],
1962
+ strikeout: ["quadPoints"],
1963
+ redact: ["rect", "quadPoints"],
1964
+ caret: ["box"],
1965
+ text: ["rect"],
1966
+ stamp: ["box"],
1967
+ "file-attachment": ["rect"],
1968
+ link: ["rect"],
1969
+ widget: ["box"]
1970
+ };
1971
+ const numEq = (a, b) => Math.abs(a - b) <= EPSILON;
1972
+ /** Degrees normalized to [0,360); absent reads as 0 (no rotation). */
1973
+ const normDeg = (v) => {
1974
+ return typeof v === "number" ? (v % 360 + 360) % 360 : 0;
1975
+ };
1976
+ /**
1977
+ * Semantic equality between a patch value and the current DTO value.
1978
+ * Numbers compare within {@link epsilon}; `null` and `undefined` both mean
1979
+ * "entry absent" (the tri-state clear of an absent entry is a no-op); arrays
1980
+ * and objects compare structurally.
1981
+ */
1982
+ function semanticEqual(a, b) {
1983
+ if (a == null || b == null) return a == null && b == null;
1984
+ if (typeof a === "number" && typeof b === "number") return numEq(a, b);
1985
+ if (Array.isArray(a) || Array.isArray(b)) {
1986
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
1987
+ return a.every((v, i) => semanticEqual(v, b[i]));
1988
+ }
1989
+ if (typeof a === "object" && typeof b === "object") {
1990
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
1991
+ for (const k of keys) if (!semanticEqual(a[k], b[k])) return false;
1992
+ return true;
1993
+ }
1994
+ return a === b;
1995
+ }
1996
+ /**
1997
+ * Is `next` exactly `cur` shifted by `(dx, dy)`? Walks the value structurally:
1998
+ * numeric leaves under x-axis keys (`left`/`right`/`x`) must shift by `dx`,
1999
+ * y-axis keys (`bottom`/`top`/`y`) by `dy`, and any other numeric leaf must be
2000
+ * unchanged. Arrays walk elementwise (same length required).
2001
+ */
2002
+ function shiftedBy(cur, next, dx, dy) {
2003
+ if (cur == null || next == null) return cur == null && next == null;
2004
+ if (Array.isArray(cur) || Array.isArray(next)) {
2005
+ if (!Array.isArray(cur) || !Array.isArray(next) || cur.length !== next.length) return false;
2006
+ return cur.every((v, i) => shiftedBy(v, next[i], dx, dy));
2007
+ }
2008
+ if (typeof cur === "object" && typeof next === "object") {
2009
+ const keys = /* @__PURE__ */ new Set([...Object.keys(cur), ...Object.keys(next)]);
2010
+ for (const k of keys) {
2011
+ const c = cur[k];
2012
+ const n = next[k];
2013
+ if (typeof c === "number" && typeof n === "number") {
2014
+ if (!numEq(c + (k === "left" || k === "right" || k === "x" ? dx : k === "bottom" || k === "top" || k === "y" ? dy : 0), n)) return false;
2015
+ } else if (!shiftedBy(c, n, dx, dy)) return false;
2016
+ }
2017
+ return true;
2018
+ }
2019
+ return cur === next;
2020
+ }
2021
+ /**
2022
+ * The first point a geometry value holds: a rect's bottom-left, a point, a
2023
+ * line's start, a quad's first corner, the first of a list.
2024
+ */
2025
+ function firstPoint(value) {
2026
+ if (value == null || typeof value !== "object") return void 0;
2027
+ if (Array.isArray(value)) return value.length > 0 ? firstPoint(value[0]) : void 0;
2028
+ const record = value;
2029
+ if (typeof record.x === "number" && typeof record.y === "number") return {
2030
+ x: record.x,
2031
+ y: record.y
2032
+ };
2033
+ if (typeof record.left === "number" && typeof record.bottom === "number") return {
2034
+ x: record.left,
2035
+ y: record.bottom
2036
+ };
2037
+ return firstPoint(record.start ?? record.upperLeft);
2038
+ }
2039
+ /**
2040
+ * The distance the touched geometry moves by, when it is one rigid
2041
+ * translation of the current state; `null` when it isn't. Requires the kind's
2042
+ * first geometry field in the patch; it and every other geometry field
2043
+ * present on the annotation must ride along shifted by the same delta — a box
2044
+ * resized, or a leader left behind by a moved text box, is not a translation
2045
+ * (the dictionary would desync from the pixels). For the kinds that turn, the
2046
+ * turn is checked as an after-state: an omitted rotation keeps the current
2047
+ * one.
2048
+ */
2049
+ function rigidTranslationOf(cur, pat, subtype, geometryKeys) {
2050
+ const [anchor] = geometryKeys;
2051
+ const from = firstPoint(cur[anchor]);
2052
+ const to = firstPoint(pat[anchor]);
2053
+ if (!from || !to) return null;
2054
+ const dx = to.x - from.x;
2055
+ const dy = to.y - from.y;
2056
+ if (TURNING_KINDS.has(subtype)) {
2057
+ const rotAfter = pat.rotation === void 0 ? cur.rotation : pat.rotation;
2058
+ if (!numEq(normDeg(cur.rotation), normDeg(rotAfter))) return null;
2059
+ }
2060
+ for (const key of geometryKeys) {
2061
+ const c = cur[key];
2062
+ const p = pat[key];
2063
+ if (c == null && p == null) continue;
2064
+ if (c == null || p == null) return null;
2065
+ if (!shiftedBy(c, p, dx, dy)) return null;
2066
+ }
2067
+ return {
2068
+ x: dx,
2069
+ y: dy
2070
+ };
2071
+ }
2072
+ const INERT = { impact: "inert" };
2073
+ const REGENERATE = { impact: "regenerate" };
2074
+ /**
2075
+ * Classify an update patch against the annotation's current read, in either
2076
+ * space, with the distance a translation moves the drawing by.
2077
+ *
2078
+ * 1. Value-diff: drop keys whose value semantically equals the current one,
2079
+ * plus the always-inert metadata keys (and per-kind inert keys: `contents`
2080
+ * where it isn't painted).
2081
+ * Nothing left → `'inert'`.
2082
+ * 2. If every remaining key is translatable geometry for this kind and the
2083
+ * values are one rigid translation → `'translation'`, by how far.
2084
+ * 3. Anything else — style, text, unknown keys, unknown kinds → `'regenerate'`.
2085
+ */
2086
+ function appearanceChangeOf(current, patch) {
2087
+ if (patch.subtype !== void 0 && patch.subtype !== current.subtype) return REGENERATE;
2088
+ const cur = current;
2089
+ const pat = patch;
2090
+ const subtype = current.subtype;
2091
+ const captionEnabled = pat.captionEnabled === void 0 ? cur.captionEnabled : pat.captionEnabled;
2092
+ const contentsPainted = CONTENTS_PAINTED.has(subtype) || [
2093
+ "line",
2094
+ "polygon",
2095
+ "polyline"
2096
+ ].includes(subtype) && captionEnabled === true;
2097
+ const touched = [];
2098
+ for (const [key, value] of Object.entries(pat)) {
2099
+ if (value === void 0 || INERT_KEYS.has(key)) continue;
2100
+ if (key === "contents" && !contentsPainted) continue;
2101
+ if (!semanticEqual(value, cur[key])) touched.push(key);
2102
+ }
2103
+ if (touched.length === 0) return INERT;
2104
+ const baseGeometryKeys = TRANSLATABLE_GEOMETRY[subtype];
2105
+ const geometryKeys = baseGeometryKeys && (subtype === "polygon" || subtype === "polyline") && cur.captionCenter ? [...baseGeometryKeys, "captionCenter"] : baseGeometryKeys;
2106
+ if (!geometryKeys) return REGENERATE;
2107
+ if (!touched.every((k) => geometryKeys.includes(k))) return REGENERATE;
2108
+ const by = rigidTranslationOf(cur, pat, subtype, geometryKeys);
2109
+ return by ? {
2110
+ impact: "translation",
2111
+ by
2112
+ } : REGENERATE;
2113
+ }
2114
+ /** {@link appearanceChangeOf}'s verdict alone, on the file's values: what the engine's update decides. */
2115
+ function appearanceImpactOf(current, patch) {
2116
+ return appearanceChangeOf(current, patch).impact;
2117
+ }
2118
+ //#endregion
2119
+ //#region src/annotation/shapeForRect.ts
2120
+ /**
2121
+ * The kinds whose `rect` the engine works out from their shape: a box, points
2122
+ * or quads (`drawnRectFields`). `test/annotation/shapeForRect.test.ts` keeps
2123
+ * it equal to the declarations.
2124
+ */
2125
+ const DRAWN_RECT_KINDS = /* @__PURE__ */ new Set([
2126
+ "highlight",
2127
+ "underline",
2128
+ "squiggly",
2129
+ "strikeout",
2130
+ "square",
2131
+ "circle",
2132
+ "free-text",
2133
+ "stamp",
2134
+ "caret",
2135
+ "widget",
2136
+ "line",
2137
+ "polyline",
2138
+ "polygon",
2139
+ "ink"
2140
+ ]);
2141
+ /**
2142
+ * The places that make up a drawing. A measurement's origin isn't one: it's
2143
+ * where the scale's coordinates start, so it stays when the drawing moves.
2144
+ */
2145
+ const DRAWING_SPACES = /* @__PURE__ */ new Set([
2146
+ "box",
2147
+ "point",
2148
+ "points",
2149
+ "strokes",
2150
+ "quads",
2151
+ "linePoints",
2152
+ "calloutLine"
2153
+ ]);
2154
+ /** The fields that hold a drawn kind's shape: its places, and its turn. */
2155
+ function shapeFieldsOf(subtype) {
2156
+ return [...Object.entries(ANNOTATION_FIELD_SPACES[subtype]).filter(([name, space]) => name !== "rect" && DRAWING_SPACES.has(space)).map(([name]) => name), "rotation"];
2157
+ }
2158
+ /**
2159
+ * The shape fields that put `annotation` at `rect`, in the file's
2160
+ * coordinates: the rule an update's `rect` follows. The public
2161
+ * `shapeForRect` runs it on page-space values.
2162
+ *
2163
+ * - A kind whose shape is its rect (note, file attachment, link, popup,
2164
+ * redaction) takes `rect` as it is.
2165
+ * - A drawn kind has every place it holds mapped from its current `rect` onto
2166
+ * `rect`, each axis on its own, as Acrobat does when a script sets
2167
+ * `annot.rect`: a rect of the same size moves the shape, a bigger one
2168
+ * stretches it. The engine then works out the rect from the shape, so it
2169
+ * can differ a little from `rect` (a stroke keeps its width). A box at a
2170
+ * quarter turn stretches along its own sides, as it stands on the page.
2171
+ *
2172
+ * A drawn kind refuses a rect that would squash it to no width or height, a
2173
+ * rect of another size when it's turned (it can only move) unless it is a
2174
+ * box at a quarter turn, and one when its own rect has no width or height to
2175
+ * stretch. Each refusal is `InvalidArg` on `rect`.
2176
+ */
2177
+ function pdfShapeForRect(annotation, rect) {
2178
+ const subtype = annotation.subtype;
2179
+ if (!DRAWN_RECT_KINDS.has(subtype)) return { rect };
2180
+ const to = normalizePdfRect(rect);
2181
+ const from = normalizePdfRect(annotation.rect);
2182
+ const toWidth = to.right - to.left;
2183
+ const toHeight = to.top - to.bottom;
2184
+ const fromWidth = from.right - from.left;
2185
+ const fromHeight = from.top - from.bottom;
2186
+ const sameWidth = semanticEqual(toWidth, fromWidth);
2187
+ const sameHeight = semanticEqual(toHeight, fromHeight);
2188
+ if (!sameWidth && toWidth <= 0 || !sameHeight && toHeight <= 0) throw refused(subtype, "a rect needs a width and a height");
2189
+ const rotation = annotation.rotation ?? 0;
2190
+ const spaces = ANNOTATION_FIELD_SPACES[subtype];
2191
+ const quarterTurn = spaces.box === "box" ? quarterTurnOf(rotation) : null;
2192
+ if (!semanticEqual((rotation % 360 + 360) % 360, 0) && quarterTurn === null) {
2193
+ if (!sameWidth || !sameHeight) throw refused(subtype, "a turned drawing's rect can only move; resize its shape instead");
2194
+ }
2195
+ if (!sameWidth && fromWidth <= 0 || !sameHeight && fromHeight <= 0) throw refused(subtype, "its rect has no width or height to stretch; it can only move");
2196
+ const scaleX = sameWidth ? 1 : toWidth / fromWidth;
2197
+ const scaleY = sameHeight ? 1 : toHeight / fromHeight;
2198
+ const point = (p) => ({
2199
+ x: to.left + (p.x - from.left) * scaleX,
2200
+ y: to.bottom + (p.y - from.bottom) * scaleY
2201
+ });
2202
+ const placeRect = (rect) => {
2203
+ const corner = point({
2204
+ x: rect.left,
2205
+ y: rect.bottom
2206
+ });
2207
+ const opposite = point({
2208
+ x: rect.right,
2209
+ y: rect.top
2210
+ });
2211
+ return normalizePdfRect({
2212
+ left: corner.x,
2213
+ bottom: corner.y,
2214
+ right: opposite.x,
2215
+ top: opposite.y
2216
+ });
2217
+ };
2218
+ const place = {
2219
+ box: (box) => quarterTurn === null || quarterTurn === 0 || quarterTurn === 180 ? placeRect(box) : pdfQuarterTurnBox(placeRect(pdfRectTurnedBounds(box, quarterTurn)), quarterTurn),
2220
+ point,
2221
+ points: (points) => points.map(point),
2222
+ strokes: (strokes) => strokes.map((stroke) => stroke.map(point)),
2223
+ quads: (quads) => quads.map((quad) => ({
2224
+ upperLeft: point(quad.upperLeft),
2225
+ upperRight: point(quad.upperRight),
2226
+ lowerLeft: point(quad.lowerLeft),
2227
+ lowerRight: point(quad.lowerRight)
2228
+ })),
2229
+ linePoints: (line) => ({
2230
+ start: point(line.start),
2231
+ end: point(line.end)
2232
+ }),
2233
+ calloutLine: (points) => points.map(point)
2234
+ };
2235
+ const shape = {};
2236
+ for (const name of shapeFieldsOf(subtype)) {
2237
+ const value = annotation[name];
2238
+ const space = spaces[name];
2239
+ if (value != null && space) shape[name] = place[space](value);
2240
+ }
2241
+ return shape;
2242
+ }
2243
+ function refused(subtype, reason) {
2244
+ return new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${subtype} field 'rect': ${reason}`, { details: { field: "rect" } });
2245
+ }
2246
+ //#endregion
2247
+ //#region src/measure/compute.ts
2248
+ const positive = (n) => Number.isFinite(n) && n > 0;
2249
+ function scaleOf(measure) {
2250
+ const sx = Math.fround(measure.x[0]?.conversion ?? NaN);
2251
+ if (!positive(sx)) return null;
2252
+ if (!measure.y) return {
2253
+ sx,
2254
+ sy: sx
2255
+ };
2256
+ const cy = Math.fround(measure.y[0]?.conversion ?? NaN);
2257
+ const cyx = Math.fround(measure.cyx ?? NaN);
2258
+ const sy = cy * cyx;
2259
+ return positive(cy) && positive(cyx) && positive(sy) ? {
2260
+ sx,
2261
+ sy
2262
+ } : null;
2263
+ }
2264
+ const measurementPoint = (p) => ({
2265
+ x: Math.fround(p.x),
2266
+ y: Math.fround(p.y)
2267
+ });
2268
+ function pathLength(points, closed, scale) {
2269
+ let total = 0;
2270
+ const normalized = points.map(measurementPoint);
2271
+ for (let i = 0; i < (closed ? normalized.length : normalized.length - 1); i++) {
2272
+ const a = normalized[i], b = normalized[(i + 1) % normalized.length];
2273
+ total += Math.hypot((b.x - a.x) * scale.sx, (b.y - a.y) * scale.sy);
2274
+ }
2275
+ return total;
2276
+ }
2277
+ function polygonArea(points, scale) {
2278
+ if (points.length < 3) return 0;
2279
+ const normalized = points.map(measurementPoint);
2280
+ const origin = normalized[0];
2281
+ let twice = 0;
2282
+ for (let i = 1; i < normalized.length - 1; i++) {
2283
+ const a = normalized[i], b = normalized[i + 1];
2284
+ twice += (a.x - origin.x) * (b.y - origin.y) - (b.x - origin.x) * (a.y - origin.y);
2285
+ }
2286
+ return Math.abs(twice * scale.sx * scale.sy) / 2;
2287
+ }
2288
+ /** A measured area must have one simple, nonzero boundary. Repeated closing
2289
+ * vertices are allowed in imported PDFs; crossings and overlapping edges are not. */
2290
+ function validAreaBoundary(points) {
2291
+ const boundary = points.map(measurementPoint);
2292
+ const same = (a, b) => a.x === b.x && a.y === b.y;
2293
+ if (boundary.length > 1 && same(boundary[0], boundary[boundary.length - 1])) boundary.pop();
2294
+ if (boundary.length < 3 || polygonArea(boundary, {
2295
+ sx: 1,
2296
+ sy: 1
2297
+ }) === 0) return false;
2298
+ const cross = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
2299
+ const onSegment = (a, b, p) => cross(a, b, p) === 0 && p.x >= Math.min(a.x, b.x) && p.x <= Math.max(a.x, b.x) && p.y >= Math.min(a.y, b.y) && p.y <= Math.max(a.y, b.y);
2300
+ for (let i = 0; i < boundary.length; i++) {
2301
+ const a = boundary[i];
2302
+ const b = boundary[(i + 1) % boundary.length];
2303
+ if (same(a, b)) return false;
2304
+ const next = boundary[(i + 2) % boundary.length];
2305
+ const doublesBack = (b.x - a.x) * (next.x - b.x) + (b.y - a.y) * (next.y - b.y) < 0;
2306
+ if (cross(a, b, next) === 0 && doublesBack) return false;
2307
+ for (let j = i + 1; j < boundary.length; j++) {
2308
+ if (j === i + 1 || i === 0 && j === boundary.length - 1) continue;
2309
+ const c = boundary[j];
2310
+ const d = boundary[(j + 1) % boundary.length];
2311
+ if (onSegment(a, b, c) || onSegment(a, b, d) || onSegment(c, d, a) || onSegment(c, d, b)) return false;
2312
+ if (Math.sign(cross(a, b, c)) !== Math.sign(cross(a, b, d)) && Math.sign(cross(c, d, a)) !== Math.sign(cross(c, d, b))) return false;
2313
+ }
2314
+ }
2315
+ return true;
2316
+ }
2317
+ //#endregion
2318
+ //#region src/measure/derive.ts
2319
+ function isDimension(dto) {
2320
+ return dto.subtype === "line" && dto.intent === "line-dimension" || dto.subtype === "polyline" && dto.intent === "polyline-dimension" || dto.subtype === "polygon" && dto.intent === "polygon-dimension";
2321
+ }
2322
+ const isReadout = (r) => !("unavailable" in r);
2323
+ function measurementReadout(dto) {
2324
+ if (!isDimension(dto)) return { unavailable: "not-dimension" };
2325
+ if (!dto.measure) return { unavailable: "no-measure" };
2326
+ if (dto.measure.subtype !== "rectilinear") return { unavailable: "foreign-measure" };
2327
+ const m = dto.measure, scale = scaleOf(m);
2328
+ if (!scale) return { unavailable: "no-scale" };
2329
+ const points = dto.subtype === "line" ? dto.linePoints ? [dto.linePoints.start, dto.linePoints.end] : [] : dto.vertices ?? [];
2330
+ if (points.length < (dto.subtype === "polygon" ? 3 : 2) || points.some((p) => !Number.isFinite(Math.fround(p.x)) || !Number.isFinite(Math.fround(p.y)))) return { unavailable: "invalid-geometry" };
2331
+ const area = dto.subtype === "polygon";
2332
+ if (area && !validAreaBoundary(points)) return { unavailable: "invalid-geometry" };
2333
+ const formats = area ? m.area : m.distance;
2334
+ if (!formats.length) return { unavailable: area ? "no-area-format" : "no-distance-format" };
2335
+ if (!formats.every(require_format.validNumberFormat)) return { unavailable: "invalid-format" };
2336
+ const value = area ? polygonArea(points, scale) : pathLength(points, false, scale);
2337
+ try {
2338
+ return {
2339
+ kind: area ? "area" : dto.subtype === "line" ? "distance" : "perimeter",
2340
+ value: value * Math.fround(formats[0].conversion),
2341
+ label: require_format.formatMeasurement(value, formats),
2342
+ ...area && m.distance.length && m.distance.every(require_format.validNumberFormat) ? { perimeter: require_format.formatMeasurement(pathLength(points, true, scale), m.distance) } : {}
2343
+ };
2344
+ } catch {
2345
+ return { unavailable: "invalid-format" };
2346
+ }
2347
+ }
2348
+ function deriveMeasurementLabel(dto) {
2349
+ const readout = measurementReadout(dto);
2350
+ return isReadout(readout) && dto.contents !== readout.label ? {
2351
+ ...dto,
2352
+ contents: readout.label
2353
+ } : dto;
2354
+ }
2355
+ //#endregion
2356
+ //#region src/measure/validate.ts
2357
+ /** Shared preflight for HTTP, workers and calibration helpers. Does not write. */
2358
+ function assertWritableMeasure(value) {
2359
+ if (value.subtype !== "rectilinear") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Only rectilinear measures can be authored");
2360
+ if (value.ratio !== void 0 && (typeof value.ratio !== "string" || value.ratio.includes("\0"))) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid scale ratio");
2361
+ for (const key of [
2362
+ "x",
2363
+ "y",
2364
+ "distance",
2365
+ "area",
2366
+ "angle",
2367
+ "slope"
2368
+ ]) {
2369
+ const list = value[key];
2370
+ if (list === void 0 && (key === "y" || key === "angle" || key === "slope")) continue;
2371
+ if (!Array.isArray(list)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `Missing ${key} formats`);
2372
+ for (const nf of list) {
2373
+ if (!nf || typeof nf.unit !== "string" || !require_format.validNumberFormat(nf)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid number format");
2374
+ if (nf.fraction !== void 0 && ![
2375
+ "decimal",
2376
+ "fraction",
2377
+ "round",
2378
+ "truncate"
2379
+ ].includes(nf.fraction)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid fraction mode");
2380
+ if (nf.labelPosition !== void 0 && !["prefix", "suffix"].includes(nf.labelPosition)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid label position");
2381
+ if (nf.fixed !== void 0 && typeof nf.fixed !== "boolean") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid fixed precision");
2382
+ for (const key of [
2383
+ "unit",
2384
+ "thousands",
2385
+ "decimal",
2386
+ "prefixSpacing",
2387
+ "suffixSpacing"
2388
+ ]) {
2389
+ const text = nf[key];
2390
+ if (text !== void 0 && (typeof text !== "string" || text.includes("\0"))) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid measurement text");
2391
+ }
2392
+ }
2393
+ }
2394
+ if (value.origin !== void 0) for (const n of [value.origin.x, value.origin.y]) assertPdfFloat(n);
2395
+ if (value.cyx !== void 0) {
2396
+ assertPdfFloat(value.cyx);
2397
+ if (Math.fround(value.cyx) <= 0) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid CYX");
2398
+ }
2399
+ }
2400
+ function assertPdfFloat(n) {
2401
+ if (typeof n !== "number" || !Number.isFinite(Math.fround(n))) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Value is outside PDF float range");
2402
+ }
2403
+ //#endregion
2404
+ //#region src/annotation/resolve/measurement.ts
2405
+ /** The fields a measurement's label is worked out from. */
2406
+ const LABEL_INPUTS = [
2407
+ "linePoints",
2408
+ "vertices",
2409
+ "measure",
2410
+ "intent",
2411
+ "contents"
2412
+ ];
2413
+ const CAPTION_FIELDS = [
2414
+ "captionEnabled",
2415
+ "captionPosition",
2416
+ "captionOffset",
2417
+ "captionCenter"
2418
+ ];
2419
+ const isDimensionKind = (subtype) => subtype === "line" || subtype === "polygon" || subtype === "polyline";
2420
+ /** Whether a write names any of the caption's fields. */
2421
+ const touchesCaption = (value) => CAPTION_FIELDS.some((name) => value[name] !== void 0);
2422
+ function validate(subtype, write) {
2423
+ try {
2424
+ if (write.measure != null && write.measure.subtype === "rectilinear") assertWritableMeasure(write.measure);
2425
+ if (subtype === "line" && "leader" in write && write.leader != null && (typeof write.leader !== "object" || Array.isArray(write.leader))) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid line leader");
2426
+ const intents = subtype === "line" ? ["line-dimension", "line-arrow"] : subtype === "polygon" ? ["polygon-dimension", "polygon-cloud"] : ["polyline-dimension"];
2427
+ if (write.intent != null && !intents.includes(write.intent)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid measurement intent");
2428
+ if (write.captionEnabled != null && typeof write.captionEnabled !== "boolean") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid caption visibility");
2429
+ if ("captionPosition" in write && write.captionPosition != null) {
2430
+ if (!["inline", "top"].includes(write.captionPosition)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Invalid caption position");
2431
+ }
2432
+ if ("captionOffset" in write && write.captionOffset) {
2433
+ assertPdfFloat(write.captionOffset.along);
2434
+ assertPdfFloat(write.captionOffset.perpendicular);
2435
+ }
2436
+ if ("captionCenter" in write && write.captionCenter) {
2437
+ assertPdfFloat(write.captionCenter.x);
2438
+ assertPdfFloat(write.captionCenter.y);
2439
+ }
2440
+ if ("leader" in write && write.leader) {
2441
+ for (const length of [
2442
+ write.leader.length,
2443
+ write.leader.extension ?? 0,
2444
+ write.leader.offset ?? 0
2445
+ ]) assertPdfFloat(length);
2446
+ if ((write.leader.extension ?? 0) < 0 || (write.leader.offset ?? 0) < 0) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Leader extension and offset must be nonnegative");
2447
+ }
2448
+ const points = "linePoints" in write ? write.linePoints ? [write.linePoints.start, write.linePoints.end] : [] : "vertices" in write ? write.vertices ?? [] : [];
2449
+ for (const point of points) {
2450
+ assertPdfFloat(point.x);
2451
+ assertPdfFloat(point.y);
2452
+ }
2453
+ } catch (error) {
2454
+ throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, error.message);
2455
+ }
2456
+ }
2457
+ /** Infer a rigid transform only when all corresponding vertices agree. Vertex edits leave a manual center fixed. */
2458
+ function moveCenter(before, after, center) {
2459
+ if (before.length !== after.length || before.length < 2) return void 0;
2460
+ const origin = before[0];
2461
+ const moved = after[0];
2462
+ const index = before.findIndex((point) => Math.hypot(point.x - origin.x, point.y - origin.y) > 1e-6);
2463
+ if (index < 0) return void 0;
2464
+ const from = {
2465
+ x: before[index].x - origin.x,
2466
+ y: before[index].y - origin.y
2467
+ };
2468
+ const to = {
2469
+ x: after[index].x - moved.x,
2470
+ y: after[index].y - moved.y
2471
+ };
2472
+ const length = from.x * from.x + from.y * from.y;
2473
+ const cos = (from.x * to.x + from.y * to.y) / length;
2474
+ const sin = (from.x * to.y - from.y * to.x) / length;
2475
+ if (Math.abs(cos * cos + sin * sin - 1) > 1e-5) return void 0;
2476
+ const transform = (point) => ({
2477
+ x: moved.x + cos * (point.x - origin.x) - sin * (point.y - origin.y),
2478
+ y: moved.y + sin * (point.x - origin.x) + cos * (point.y - origin.y)
2479
+ });
2480
+ if (!before.every((point, i) => {
2481
+ const placed = transform(point);
2482
+ return Math.hypot(placed.x - after[i].x, placed.y - after[i].y) <= .001;
2483
+ })) return void 0;
2484
+ return transform(center);
2485
+ }
2486
+ /**
2487
+ * A dimension draft ready for the writer: validated, its label worked out,
2488
+ * and its caption fields complete, since the native caption setters write
2489
+ * the whole caption at once.
2490
+ */
2491
+ function resolveMeasurementDraft(draft) {
2492
+ if (!isDimensionKind(draft.subtype)) return draft;
2493
+ const dimension = draft;
2494
+ validate(draft.subtype, dimension);
2495
+ if (dimension.measure != null && dimension.measure.subtype !== "rectilinear") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `A ${dimension.measure.subtype === "geospatial" ? "geospatial" : "foreign"} measure can't be written; leave measure out`);
2496
+ let resolved = draft;
2497
+ if (touchesCaption(draft)) {
2498
+ if (draft.subtype === "line") resolved = {
2499
+ ...draft,
2500
+ captionEnabled: draft.captionEnabled === void 0 ? false : draft.captionEnabled,
2501
+ captionPosition: draft.captionPosition ?? "inline",
2502
+ captionOffset: draft.captionOffset ?? null
2503
+ };
2504
+ else if (draft.subtype === "polygon" || draft.subtype === "polyline") resolved = {
2505
+ ...draft,
2506
+ captionEnabled: draft.captionEnabled ?? (draft.captionCenter != null ? false : null),
2507
+ captionCenter: draft.captionCenter ?? null
2508
+ };
2509
+ }
2510
+ return deriveMeasurementLabel(resolved);
2511
+ }
2512
+ /**
2513
+ * A dimension patch with what follows from it. The patch's subtype is the
2514
+ * target's. Caption fields merge with the current caption into a complete
2515
+ * caption; a whole-shape move carries a manual caption center along; a
2516
+ * foreign measure marker sent back keeps the measure as it is; and the label
2517
+ * is worked out from the resulting geometry and scale.
2518
+ */
2519
+ function measurementFollows(current, patch) {
2520
+ if (current.subtype !== "line" && current.subtype !== "polygon" && current.subtype !== "polyline") return patch;
2521
+ const subtype = current.subtype;
2522
+ let next = {
2523
+ ...patch,
2524
+ subtype
2525
+ };
2526
+ validate(subtype, next);
2527
+ if (next.measure != null && next.measure.subtype !== "rectilinear") {
2528
+ if (current.measure?.subtype !== next.measure.subtype) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `A ${next.measure.subtype === "geospatial" ? "geospatial" : "foreign"} measure can't be written; leave measure out`);
2529
+ next = {
2530
+ ...next,
2531
+ measure: void 0
2532
+ };
2533
+ }
2534
+ if (current.subtype === "line" && next.subtype === "line") {
2535
+ if (touchesCaption(next)) next = {
2536
+ ...next,
2537
+ captionEnabled: next.captionEnabled === void 0 ? current.captionEnabled : next.captionEnabled,
2538
+ captionPosition: next.captionPosition ?? current.captionPosition,
2539
+ captionOffset: next.captionOffset === void 0 ? current.captionOffset : next.captionOffset
2540
+ };
2541
+ } else if (current.subtype !== "line" && next.subtype !== "line") {
2542
+ if (next.vertices && next.captionEnabled !== null && next.captionCenter === void 0 && current.captionCenter) {
2543
+ const center = moveCenter(current.vertices, next.vertices, current.captionCenter);
2544
+ if (center) next = {
2545
+ ...next,
2546
+ captionCenter: center
2547
+ };
2548
+ }
2549
+ if (touchesCaption(next)) next = next.captionEnabled === null ? {
2550
+ ...next,
2551
+ captionEnabled: null,
2552
+ captionCenter: null
2553
+ } : {
2554
+ ...next,
2555
+ captionEnabled: next.captionEnabled ?? current.captionEnabled ?? false,
2556
+ captionCenter: next.captionCenter === void 0 ? current.captionCenter : next.captionCenter
2557
+ };
2558
+ }
2559
+ if (LABEL_INPUTS.some((key) => next[key] !== void 0)) {
2560
+ const readout = measurementReadout({
2561
+ ...current,
2562
+ ...next
2563
+ });
2564
+ if (isReadout(readout)) next = {
2565
+ ...next,
2566
+ contents: readout.label
2567
+ };
2568
+ }
2569
+ validate(subtype, next);
2570
+ return next;
2571
+ }
2572
+ //#endregion
2573
+ //#region src/annotation/calloutEnd.ts
2574
+ /**
2575
+ * Where a callout's line meets its text box: the middle of the side `toward`
2576
+ * (the knee, else the tip) lies beyond, chosen by which way it is farther
2577
+ * from the box's middle, across or up and down. A turned box decides in its
2578
+ * own frame, and the point lands on the turned side, the one the page shows.
2579
+ * In the file's coordinates.
2580
+ */
2581
+ function pdfCalloutEnd(box, toward, rotation) {
2582
+ const center = {
2583
+ x: (box.left + box.right) / 2,
2584
+ y: (box.bottom + box.top) / 2
2585
+ };
2586
+ const turn = {
2587
+ degrees: rotation,
2588
+ center
2589
+ };
2590
+ const local = rotation ? pdfPointUnturned(toward, turn) : toward;
2591
+ const dx = local.x - center.x;
2592
+ const dy = local.y - center.y;
2593
+ const end = Math.abs(dx) >= Math.abs(dy) ? {
2594
+ x: dx >= 0 ? box.right : box.left,
2595
+ y: center.y
2596
+ } : {
2597
+ x: center.x,
2598
+ y: dy > 0 ? box.top : box.bottom
2599
+ };
2600
+ return rotation ? pdfPointTurned(end, turn) : end;
2601
+ }
2602
+ //#endregion
2603
+ //#region src/annotation/fontFaces.ts
2604
+ const STANDARD_FACES = {
2605
+ courier: {
2606
+ family: "Courier",
2607
+ weight: 400,
2608
+ italic: false
2609
+ },
2610
+ "courier-bold": {
2611
+ family: "Courier",
2612
+ weight: 700,
2613
+ italic: false
2614
+ },
2615
+ "courier-bold-oblique": {
2616
+ family: "Courier",
2617
+ weight: 700,
2618
+ italic: true
2619
+ },
2620
+ "courier-oblique": {
2621
+ family: "Courier",
2622
+ weight: 400,
2623
+ italic: true
2624
+ },
2625
+ helvetica: {
2626
+ family: "Helvetica",
2627
+ weight: 400,
2628
+ italic: false
2629
+ },
2630
+ "helvetica-bold": {
2631
+ family: "Helvetica",
2632
+ weight: 700,
2633
+ italic: false
2634
+ },
2635
+ "helvetica-bold-oblique": {
2636
+ family: "Helvetica",
2637
+ weight: 700,
2638
+ italic: true
2639
+ },
2640
+ "helvetica-oblique": {
2641
+ family: "Helvetica",
2642
+ weight: 400,
2643
+ italic: true
2644
+ },
2645
+ "times-roman": {
2646
+ family: "Times",
2647
+ weight: 400,
2648
+ italic: false
2649
+ },
2650
+ "times-bold": {
2651
+ family: "Times",
2652
+ weight: 700,
2653
+ italic: false
2654
+ },
2655
+ "times-bold-italic": {
2656
+ family: "Times",
2657
+ weight: 700,
2658
+ italic: true
2659
+ },
2660
+ "times-italic": {
2661
+ family: "Times",
2662
+ weight: 400,
2663
+ italic: true
2664
+ },
2665
+ symbol: {
2666
+ family: "Symbol",
2667
+ weight: 400,
2668
+ italic: false
2669
+ },
2670
+ "zapf-dingbats": {
2671
+ family: "ZapfDingbats",
2672
+ weight: 400,
2673
+ italic: false
2674
+ }
2675
+ };
2676
+ /** One of the 14 standard names. The names are reserved: a registered font keyed `'helvetica'` resolves as the standard one. */
2677
+ const isStandardFontName = (font) => require_format.STANDARD_FONTS.includes(font);
2678
+ /**
2679
+ * The face a free text's `fontFamily` (or a rich run's `family`) asks for: a
2680
+ * standard font's family, weight and italic; a registered key's identity; or,
2681
+ * for anything else, the string itself as a family name (a face the document
2682
+ * embeds, or one the engine will substitute).
2683
+ */
2684
+ function faceForFreeTextFont(font, describe) {
2685
+ if (isStandardFontName(font)) {
2686
+ const face = STANDARD_FACES[font];
2687
+ return {
2688
+ family: face.family,
2689
+ weight: face.weight,
2690
+ italic: face.italic
2691
+ };
2692
+ }
2693
+ const registered = describe?.(font);
2694
+ if (registered) return {
2695
+ family: registered.familyName,
2696
+ weight: registered.weight,
2697
+ italic: registered.italic
2698
+ };
2699
+ return { family: font };
2700
+ }
2701
+ /** Family names compare without case, spaces, hyphens and underscores
2702
+ * (the engine's own rule): "Noto Sans" == "NotoSans" == "noto-sans". */
2703
+ function familyKey(family) {
2704
+ return family.toLowerCase().replace(/[\s\-_'"]/g, "");
2705
+ }
2706
+ /** Families that are a standard font's, by `familyKey`. */
2707
+ const STANDARD_FAMILY_KEYS = {
2708
+ helvetica: "Helvetica",
2709
+ arial: "Helvetica",
2710
+ arialmt: "Helvetica",
2711
+ sansserif: "Helvetica",
2712
+ times: "Times",
2713
+ timesroman: "Times",
2714
+ timesnewroman: "Times",
2715
+ timesnewromanpsmt: "Times",
2716
+ serif: "Times",
2717
+ courier: "Courier",
2718
+ couriernew: "Courier",
2719
+ couriernewpsmt: "Courier",
2720
+ monospace: "Courier",
2721
+ symbol: "Symbol",
2722
+ zapfdingbats: "ZapfDingbats",
2723
+ dingbats: "ZapfDingbats"
2724
+ };
2725
+ /**
2726
+ * The `fontFamily` that names a face (the other direction of
2727
+ * {@link faceForFreeTextFont}): the registered `key` whose identity matches,
2728
+ * else the standard font's kebab name, else the family itself.
2729
+ */
2730
+ function freeTextFontForFace(face, keyForFace) {
2731
+ const key = keyForFace?.(face.family, face.weight, face.italic);
2732
+ if (key !== void 0) return key;
2733
+ const standard = STANDARD_FAMILY_KEYS[familyKey(face.family)];
2734
+ if (standard) {
2735
+ const faces = Object.entries(STANDARD_FACES);
2736
+ const bold = face.weight >= 600;
2737
+ const any = faces.find(([, spec]) => spec.family === standard && spec.weight >= 600 === bold && spec.italic === face.italic) ?? faces.find(([, spec]) => spec.family === standard);
2738
+ if (any) return any[0];
2739
+ }
2740
+ return face.family;
2741
+ }
2742
+ //#endregion
2743
+ //#region src/annotation/resolve/freeText.ts
2744
+ /**
2745
+ * `base` with a free text's text style over it. The font, size, text color
2746
+ * and alignment are the rich body's (runs are deltas over it, so every run
2747
+ * that didn't set one follows), and a style given by itself wins over the
2748
+ * body's.
2749
+ */
2750
+ function bodyWithStyle(base, style, describe) {
2751
+ const body = { ...base };
2752
+ if (style.fontFamily !== void 0) {
2753
+ const face = faceForFreeTextFont(style.fontFamily, describe);
2754
+ body.family = face.family;
2755
+ if (face.weight !== void 0) body.weight = face.weight;
2756
+ if (face.italic !== void 0) body.italic = face.italic;
2757
+ }
2758
+ if (style.fontSize !== void 0) body.size = style.fontSize;
2759
+ if (style.fontColor != null) body.color = style.fontColor;
2760
+ if (style.textAlign !== void 0) body.align = style.textAlign;
2761
+ return body;
2762
+ }
2763
+ /** A callout line goes only with the callout intent: another box would draw without it. */
2764
+ function assertCalloutIntent(intent, calloutLine) {
2765
+ if (calloutLine != null && intent !== "free-text-callout") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `free-text: a callout line needs intent 'free-text-callout', not '${intent}'`, { details: { field: "intent" } });
2766
+ }
2767
+ /**
2768
+ * The text style fields a new body brings, beside it: a free text reads its
2769
+ * size, text color and alignment off its body, so they change with it. Only
2770
+ * the ones that differ from `current`, and never over one the patch states.
2771
+ */
2772
+ function styleOfBody(body, current, text) {
2773
+ const style = {};
2774
+ if (text.fontSize === void 0 && body.size !== void 0 && body.size !== current.fontSize) style.fontSize = body.size;
2775
+ if (text.fontColor === void 0 && body.color !== void 0 && body.color.toLowerCase() !== current.fontColor?.toLowerCase()) style.fontColor = body.color.toLowerCase();
2776
+ if (text.textAlign === void 0 && body.align !== void 0 && body.align !== current.textAlign) style.textAlign = body.align;
2777
+ return style;
2778
+ }
2779
+ /**
2780
+ * A free text's text lives in its rich text: `contents` is its plain
2781
+ * projection, and the font, size, text color and alignment are its body.
2782
+ *
2783
+ * - New `richText` brings its `contents`; a partial body merges over the
2784
+ * current one.
2785
+ * - New `contents` becomes body-style paragraphs, one per line break (run
2786
+ * formatting goes: a plain-text writer can't keep what it can't see).
2787
+ * - A new font, size, text color or alignment moves the body.
2788
+ * - Whatever moves the body states the complete rich text to write, and the
2789
+ * size, text color and alignment it reads back with.
2790
+ * - A callout line needs the callout intent.
2791
+ */
2792
+ function freeTextFollows(current, patch, describe) {
2793
+ if (current.subtype !== "free-text" || patch.subtype !== "free-text") return patch;
2794
+ const text = patch;
2795
+ if (text.intent !== void 0 || text.calloutLine !== void 0) assertCalloutIntent(text.intent ?? current.intent, text.calloutLine !== void 0 ? text.calloutLine : current.calloutLine);
2796
+ const changes = bodyWithStyle({}, text, describe);
2797
+ const textChanged = text.richText !== void 0 || text.contents !== void 0;
2798
+ if (!textChanged && Object.keys(changes).length === 0) return text;
2799
+ const body = {
2800
+ ...current.richText.body,
2801
+ ...text.richText?.body,
2802
+ ...changes
2803
+ };
2804
+ const richText = {
2805
+ body,
2806
+ paragraphs: text.richText?.paragraphs ?? (text.contents !== void 0 ? richTextParagraphsFromPlainText(text.contents ?? "") : current.richText.paragraphs)
2807
+ };
2808
+ return {
2809
+ ...text,
2810
+ ...styleOfBody(body, current, text),
2811
+ richText,
2812
+ ...textChanged ? { contents: richTextPlainText(richText) } : {}
2813
+ };
2814
+ }
2815
+ /**
2816
+ * A callout's line stays attached: when its box moves or turns and the
2817
+ * patch doesn't state its own `calloutLine`, the line's end moves to where
2818
+ * the line now meets the box (the side its knee, else its tip, lies beyond).
2819
+ * A stated `calloutLine` is kept as given; a patch that moves neither the box
2820
+ * nor its turn keeps the stored end.
2821
+ */
2822
+ function calloutEndFollows(current, patch) {
2823
+ if (current.subtype !== "free-text" || patch.subtype !== "free-text") return patch;
2824
+ const text = patch;
2825
+ const line = current.calloutLine;
2826
+ if (!line || text.calloutLine !== void 0) return text;
2827
+ if (text.box === void 0 && text.rotation === void 0) return text;
2828
+ const box = text.box ?? current.box;
2829
+ const rotation = (text.rotation === void 0 ? current.rotation : text.rotation) ?? 0;
2830
+ const knee = line.length === 3 ? line[1] : void 0;
2831
+ const end = pdfCalloutEnd(box, knee ?? line[0], rotation);
2832
+ return {
2833
+ ...text,
2834
+ calloutLine: knee ? [
2835
+ line[0],
2836
+ knee,
2837
+ end
2838
+ ] : [line[0], end]
2839
+ };
2840
+ }
2841
+ /**
2842
+ * A free text draft, stated whole:
2843
+ *
2844
+ * - its intent: the callout intent when it has a callout line, else a plain
2845
+ * box (a callout line needs the callout intent);
2846
+ * - its text as rich text: the draft's rich text, else body-style paragraphs
2847
+ * from its `contents` (one per line break, an empty box without any), with
2848
+ * the text style over the body (a style given by itself wins over the
2849
+ * body's; what neither gives is the engine's default);
2850
+ * - `contents` as the rich text's plain projection.
2851
+ */
2852
+ function freeTextDraftFollows(draft, describe) {
2853
+ if (draft.subtype !== "free-text") return draft;
2854
+ const intent = draft.intent ?? (draft.calloutLine != null ? "free-text-callout" : "free-text");
2855
+ assertCalloutIntent(intent, draft.calloutLine);
2856
+ const richText = {
2857
+ body: bodyWithStyle(draft.richText?.body ?? {}, draft, describe),
2858
+ paragraphs: draft.richText?.paragraphs ?? richTextParagraphsFromPlainText(draft.contents ?? "")
2859
+ };
2860
+ return {
2861
+ ...draft,
2862
+ intent,
2863
+ richText,
2864
+ contents: richTextPlainText(richText)
2865
+ };
2866
+ }
2867
+ //#endregion
2868
+ //#region src/annotation/resolve/rectCommand.ts
2869
+ /**
2870
+ * An update's `rect` on a drawn kind, resolved as a command: the rect it
2871
+ * read, sent back, is dropped (nothing moves); another rect becomes the shape
2872
+ * fields that put the drawing there. It can't come with a change to the shape
2873
+ * itself: which of the two to follow would be a guess, so it is refused
2874
+ * (`InvalidArg` on `rect`), as is a rect that isn't four finite numbers. A
2875
+ * kind whose shape is its rect keeps it as a plain field. The page-space
2876
+ * `resolveRectCommand` runs it on page-space values.
2877
+ */
2878
+ function pdfResolveRectCommand(current, patch) {
2879
+ const { rect, ...rest } = patch;
2880
+ if (rect === void 0 || !DRAWN_RECT_KINDS.has(current.subtype)) return patch;
2881
+ if (!isFinitePdfRect(rect)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${current.subtype} update field 'rect': not a rect of finite numbers`, { details: { field: "rect" } });
2882
+ if (semanticEqual(normalizePdfRect(rect), current.rect)) return rest;
2883
+ const read = current;
2884
+ const changed = shapeFieldsOf(current.subtype).find((name) => {
2885
+ const value = patch[name];
2886
+ return value !== void 0 && !semanticEqual(value, read[name]);
2887
+ });
2888
+ if (changed) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${current.subtype} update field 'rect': a new rect moves the shape, so it can't come with a new '${changed}'`, { details: { field: "rect" } });
2889
+ const { left, bottom, right, top } = rect;
2890
+ return {
2891
+ ...rest,
2892
+ ...pdfShapeForRect(current, {
2893
+ left,
2894
+ bottom,
2895
+ right,
2896
+ top
2897
+ })
2898
+ };
2899
+ }
2900
+ //#endregion
2901
+ //#region src/annotation/resolve/sentBack.ts
2902
+ /** `patch` without the fields `drop` names. `subtype` always stays. */
2903
+ function without(patch, drop) {
2904
+ const kept = Object.entries(patch).filter(([name, value]) => name === "subtype" || !drop(name, value));
2905
+ return Object.fromEntries(kept);
2906
+ }
2907
+ /**
2908
+ * The patch without the colors it sends back as they read. A gray or CMYK
2909
+ * color in the file reads as its sRGB equivalent; writing that back would
2910
+ * store it as sRGB, so leaving it out keeps the file as it is.
2911
+ */
2912
+ function withoutUnchangedColors(current, patch) {
2913
+ const colors = kindFactsOf(current.subtype)?.colors ?? [];
2914
+ const read = current;
2915
+ return without(patch, (name, value) => {
2916
+ const was = read[name];
2917
+ return colors.includes(name) && typeof value === "string" && typeof was === "string" && sameColor(value, was);
2918
+ });
2919
+ }
2920
+ /**
2921
+ * The patch without the fields it sends back as they read: a field equal to
2922
+ * the read is not a change, so the annotation keeps it and nothing follows
2923
+ * from it. Equal as `semanticEqual` judges it: `null` and a field left out
2924
+ * are the same, and numbers within the precision a round trip through page
2925
+ * space keeps, so a read sent back from page space is the read. A changed
2926
+ * value stays, whatever it is; the engine's check refuses one its kind can't
2927
+ * write.
2928
+ */
2929
+ function withoutUnchangedFields(current, patch) {
2930
+ const read = current;
2931
+ return without(patch, (name, value) => value !== void 0 && semanticEqual(value, read[name]));
2932
+ }
2933
+ //#endregion
2934
+ //#region src/annotation/resolve/text.ts
2935
+ /**
2936
+ * A review state needs its model (ISO 32000 §12.5.6.3): a standard state
2937
+ * brings its own, so only a custom state with none is refused.
2938
+ */
2939
+ function stateNeedsModel(state) {
2940
+ return new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `text: the custom state '${state}' needs a stateModel`, { details: { field: "stateModel" } });
2941
+ }
2942
+ /**
2943
+ * A note draft's state with its model filled in: a standard state brings its
2944
+ * own; a custom state without one is refused, before the first write.
2945
+ */
2946
+ function noteDraftStateFollows(draft) {
2947
+ if (draft.subtype !== "text" || draft.state == null || draft.stateModel != null) return draft;
2948
+ const model = standardStateModelOf(draft.state);
2949
+ if (!model) throw stateNeedsModel(draft.state);
2950
+ return {
2951
+ ...draft,
2952
+ stateModel: model
2953
+ };
2954
+ }
2955
+ /**
2956
+ * A note's new state with its model filled in: a standard state sets its
2957
+ * own; a custom one keeps the annotation's, and needs one.
2958
+ */
2959
+ function noteStateFollows(current, patch) {
2960
+ if (current.subtype !== "text" || patch.subtype !== "text") return patch;
2961
+ if (patch.state == null || patch.stateModel !== void 0) return patch;
2962
+ const model = standardStateModelOf(patch.state);
2963
+ if (model) return {
2964
+ ...patch,
2965
+ stateModel: model
2966
+ };
2967
+ if (!current.stateModel) throw stateNeedsModel(patch.state);
2968
+ return patch;
2969
+ }
2970
+ /**
2971
+ * A draft or patch that carries both `contents` and `richText` must agree:
2972
+ * `contents` is the plain projection of the rich text. A stale read sent back
2973
+ * fails loud here instead of quietly restoring old text.
2974
+ */
2975
+ function assertRichTextAgreement(input) {
2976
+ if (input.subtype !== "free-text" || input.richText === void 0) return;
2977
+ if (input.contents === void 0 || input.contents === null) return;
2978
+ if (input.contents !== richTextPlainText(input.richText)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "free-text: `contents` must equal the plain projection of `richText` when both are given");
2979
+ }
2980
+ //#endregion
2981
+ //#region src/annotation/resolve/resolveAnnotationPatch.ts
2982
+ /**
2983
+ * What a patch means: the one place the engine decides which fields an
2984
+ * update changes. An update is prepared (`pdfPrepareAnnotationPatch`), then
2985
+ * what follows from it is derived (`pdfDeriveAnnotationPatch`). The engine
2986
+ * checks the prepared patch against its kind's schema between the two
2987
+ * (`pdfResolveAnnotationPatch`, from `@embedpdf/engine-core/validation`), a
2988
+ * check that only accepts or refuses; a viewer shows a pending change by
2989
+ * running the same two steps without schema validation (`applyAnnotationPatch`),
2990
+ * so the two can't disagree. The writers write what this resolves and nothing
2991
+ * more.
2992
+ */
2993
+ /** A write names only fields its kind declares: a misspelled or foreign field is refused, never ignored. */
2994
+ function assertDeclaredFields(subtype, write) {
2995
+ const known = ANNOTATION_FIELD_NAMES[subtype];
2996
+ const unknown = Object.keys(write).filter((name) => name !== "subtype" && !known.includes(name));
2997
+ if (unknown.length > 0) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${subtype} has no field ${unknown.map((name) => `'${name}'`).join(", ")}`);
2998
+ }
2999
+ /**
3000
+ * The patch as the target's kind: its subtype filled in from the target, which
3001
+ * a caller may leave out. A different subtype, a changed name, a field the
3002
+ * kind doesn't declare, or any change to an annotation of a type the engine
3003
+ * doesn't model is refused.
3004
+ */
3005
+ function annotationPatchForTarget(current, patch) {
3006
+ if (patch.subtype !== void 0 && patch.subtype !== current.subtype) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "Annotation subtype cannot change");
3007
+ if (current.subtype === "unsupported") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "an annotation of a type the engine doesn't model can't be updated");
3008
+ if (patch.nm !== void 0 && patch.nm !== current.nm) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "an annotation's nm can't change after create");
3009
+ assertDeclaredFields(current.subtype, patch);
3010
+ return {
3011
+ ...patch,
3012
+ subtype: current.subtype
3013
+ };
3014
+ }
3015
+ /**
3016
+ * The patch an update writes before anything follows from it, in the file's
3017
+ * coordinates, the same on the page and in the engine:
3018
+ *
3019
+ * - its subtype filled in from `current` (a different one is refused, as is
3020
+ * a field the kind doesn't declare and a changed `nm`);
3021
+ * - a drawn kind's `rect`: the rect it reads, sent back, dropped; another one
3022
+ * the shape fields that put the drawing there (`pdfResolveRectCommand`);
3023
+ * - a color sent back as it reads dropped, so the file keeps it as it has it;
3024
+ * - every other field sent back as it reads dropped: it is not a change
3025
+ * (`withoutUnchangedFields`).
3026
+ *
3027
+ * A changed value stays whatever it is: whether the kind can write it is the
3028
+ * engine's check, which never adds, drops or changes a field.
3029
+ */
3030
+ function pdfPrepareAnnotationPatch(current, patch) {
3031
+ return withoutUnchangedFields(current, withoutUnchangedColors(current, pdfResolveRectCommand(current, annotationPatchForTarget(current, patch))));
3032
+ }
3033
+ /**
3034
+ * A prepared patch with every field that follows from it stated, in the
3035
+ * file's coordinates:
3036
+ *
3037
+ * - a note's standard review state brings its state model;
3038
+ * - a free text's `contents` and rich text follow each other, and its font,
3039
+ * size, text color and alignment move its rich body;
3040
+ * - a callout's line stays attached to its box when the box moves or turns;
3041
+ * - a measurement's label follows its points and scale, and a polygon's
3042
+ * caption follows its vertices.
3043
+ *
3044
+ * Throws `InvalidArg` where nothing can follow. Pure: it reads nothing but its
3045
+ * arguments.
3046
+ */
3047
+ function pdfDeriveAnnotationPatch(current, prepared, options = {}) {
3048
+ const resolved = noteStateFollows(current, prepared);
3049
+ assertRichTextAgreement(resolved);
3050
+ return measurementFollows(current, calloutEndFollows(current, freeTextFollows(current, resolved, options.describeFont)));
3051
+ }
3052
+ /**
3053
+ * The patch the engine writes for `patch` on `current`, as a viewer predicts
3054
+ * it, in the file's coordinates: prepared and derived as the engine does
3055
+ * (`pdfPrepareAnnotationPatch`, `pdfDeriveAnnotationPatch`), without schema
3056
+ * validation. A patch the engine's schema check refuses
3057
+ * (`pdfResolveAnnotationPatch`, from `@embedpdf/engine-core/validation`) is
3058
+ * refused when the update reaches it.
3059
+ *
3060
+ * Throws `InvalidArg` for what preparation and derivation refuse: a field the
3061
+ * kind doesn't declare, a changed subtype or name, a rect with a new shape or
3062
+ * that isn't finite, and where nothing can follow.
3063
+ */
3064
+ function pdfPredictAnnotationPatch(current, patch, options = {}) {
3065
+ return pdfDeriveAnnotationPatch(current, pdfPrepareAnnotationPatch(current, patch), options);
3066
+ }
3067
+ //#endregion
3068
+ //#region src/annotation/resolve/box.ts
3069
+ /**
3070
+ * A box kind's create placed by where it stands: a `rect` with a quarter
3071
+ * turn pins the box down, `rect` itself or its sides swapped under 90 and
3072
+ * 270 (`pdfQuarterTurnBox`). A create that gives `box` keeps it, and its
3073
+ * `rect` is worked out from it as ever, so a read sent back as a create
3074
+ * places the copy by its box.
3075
+ *
3076
+ * Refused with `InvalidArg`: a create with neither, a `rect` that isn't four
3077
+ * finite numbers, and a `rect` at another angle, where many boxes stand in
3078
+ * the same rect.
3079
+ */
3080
+ function boxDraftFollows(draft) {
3081
+ if (ANNOTATION_FIELD_SPACES[draft.subtype]?.box !== "box") return draft;
3082
+ const given = draft;
3083
+ if (given.box !== void 0) return draft;
3084
+ if (given.rect === void 0) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${draft.subtype} create: give its \`box\`, or its \`rect\` with a quarter turn`, { details: { field: "box" } });
3085
+ if (!isFinitePdfRect(given.rect)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${draft.subtype} create field 'rect': not a rect of finite numbers`, { details: { field: "rect" } });
3086
+ const rotation = given.rotation ?? 0;
3087
+ const turn = quarterTurnOf(rotation);
3088
+ if (turn === null) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `${draft.subtype} create field 'rect': a rect places a box at a quarter turn; at ${rotation}° give its \`box\``, { details: { field: "rect" } });
3089
+ const { rect: _placed, ...rest } = draft;
3090
+ return {
3091
+ ...rest,
3092
+ box: pdfQuarterTurnBox(normalizePdfRect(given.rect), turn)
3093
+ };
3094
+ }
3095
+ //#endregion
3096
+ //#region src/annotation/resolve/redact.ts
3097
+ /**
3098
+ * A redaction over text that names no `rect` covers its quads: the box
3099
+ * around them is its region.
3100
+ */
3101
+ function redactDraftFollows(draft) {
3102
+ if (draft.subtype !== "redact" || draft.rect != null) return draft;
3103
+ const quads = draft.quadPoints ?? [];
3104
+ if (quads.length === 0) return draft;
3105
+ const rect = pdfPointsBounds(quads.flatMap((quad) => [
3106
+ quad.upperLeft,
3107
+ quad.upperRight,
3108
+ quad.lowerLeft,
3109
+ quad.lowerRight
3110
+ ]));
3111
+ return {
3112
+ ...draft,
3113
+ rect
3114
+ };
3115
+ }
3116
+ //#endregion
3117
+ //#region src/annotation/resolve/resolveAnnotationDraft.ts
3118
+ /**
3119
+ * The draft a create writes before anything follows from it, in the file's
3120
+ * coordinates, the same on the page and in the engine: its fields named by
3121
+ * its kind, and a box kind placed by its `rect` at a quarter turn stating its
3122
+ * `box` (`boxDraftFollows`). The engine checks the prepared draft against its
3123
+ * kind's schema before deriving the rest (`pdfResolveAnnotationDraft`), a
3124
+ * check that only accepts or refuses.
3125
+ *
3126
+ * Throws `InvalidArg` for a field the kind doesn't declare, and for a box
3127
+ * kind it can't place.
3128
+ */
3129
+ function pdfPrepareAnnotationDraft(draft) {
3130
+ assertDeclaredFields(draft.subtype, draft);
3131
+ return boxDraftFollows(draft);
3132
+ }
3133
+ /**
3134
+ * A prepared draft with every field that follows from it stated, in the
3135
+ * file's coordinates:
3136
+ *
3137
+ * - a note's standard review state brings its state model;
3138
+ * - a free text states its intent, and its text as rich text carrying its
3139
+ * text style, with `contents` its plain projection;
3140
+ * - a redaction over text without a `rect` covers its quads;
3141
+ * - a measurement's label follows its points and scale, and its caption is
3142
+ * stated whole.
3143
+ *
3144
+ * Throws `InvalidArg` where nothing can follow: a custom review state with no
3145
+ * model, contents that disagree with the rich text, a callout line without
3146
+ * the callout intent, a measurement it can't label. Pure: it reads nothing
3147
+ * but its arguments. Defaults stay out of the draft: a field left out reads
3148
+ * back as `annotation/defaults.ts` says.
3149
+ */
3150
+ function pdfDeriveAnnotationDraft(prepared, options = {}) {
3151
+ assertRichTextAgreement(prepared);
3152
+ return resolveMeasurementDraft(redactDraftFollows(freeTextDraftFollows(noteDraftStateFollows(prepared), options.describeFont)));
3153
+ }
3154
+ /**
3155
+ * The draft the engine writes for `draft`, as a viewer predicts it: prepared
3156
+ * and derived as the engine does (`pdfPrepareAnnotationDraft`,
3157
+ * `pdfDeriveAnnotationDraft`), without schema validation. A draft the
3158
+ * engine's schema check refuses (`pdfResolveAnnotationDraft`, from
3159
+ * `@embedpdf/engine-core/validation`) is refused when the create reaches it.
3160
+ */
3161
+ function pdfPredictAnnotationDraft(draft, options = {}) {
3162
+ return pdfDeriveAnnotationDraft(pdfPrepareAnnotationDraft(draft), options);
3163
+ }
3164
+ //#endregion
3165
+ Object.defineProperty(exports, "ANNOTATION_FIELD_NAMES", {
3166
+ enumerable: true,
3167
+ get: function() {
3168
+ return ANNOTATION_FIELD_NAMES;
3169
+ }
3170
+ });
3171
+ Object.defineProperty(exports, "ANNOTATION_FIELD_SPACES", {
3172
+ enumerable: true,
3173
+ get: function() {
3174
+ return ANNOTATION_FIELD_SPACES;
3175
+ }
3176
+ });
3177
+ Object.defineProperty(exports, "ANNOTATION_FIELD_SPACES_BY_NAME", {
3178
+ enumerable: true,
3179
+ get: function() {
3180
+ return ANNOTATION_FIELD_SPACES_BY_NAME;
3181
+ }
3182
+ });
3183
+ Object.defineProperty(exports, "ANNOTATION_RESOURCE_ROLES", {
3184
+ enumerable: true,
3185
+ get: function() {
3186
+ return ANNOTATION_RESOURCE_ROLES;
3187
+ }
3188
+ });
3189
+ Object.defineProperty(exports, "DEFAULT_RICH_TEXT_BODY", {
3190
+ enumerable: true,
3191
+ get: function() {
3192
+ return DEFAULT_RICH_TEXT_BODY;
3193
+ }
3194
+ });
3195
+ Object.defineProperty(exports, "DRAWN_RECT_KINDS", {
3196
+ enumerable: true,
3197
+ get: function() {
3198
+ return DRAWN_RECT_KINDS;
3199
+ }
3200
+ });
3201
+ Object.defineProperty(exports, "STANDARD_FACES", {
3202
+ enumerable: true,
3203
+ get: function() {
3204
+ return STANDARD_FACES;
3205
+ }
3206
+ });
3207
+ Object.defineProperty(exports, "UNBAKED_KINDS", {
3208
+ enumerable: true,
3209
+ get: function() {
3210
+ return UNBAKED_KINDS;
3211
+ }
3212
+ });
3213
+ Object.defineProperty(exports, "appearanceChangeOf", {
3214
+ enumerable: true,
3215
+ get: function() {
3216
+ return appearanceChangeOf;
3217
+ }
3218
+ });
3219
+ Object.defineProperty(exports, "appearanceImpactOf", {
3220
+ enumerable: true,
3221
+ get: function() {
3222
+ return appearanceImpactOf;
3223
+ }
3224
+ });
3225
+ Object.defineProperty(exports, "assertBundlePages", {
3226
+ enumerable: true,
3227
+ get: function() {
3228
+ return assertBundlePages;
3229
+ }
3230
+ });
3231
+ Object.defineProperty(exports, "assertDeclaredFields", {
3232
+ enumerable: true,
3233
+ get: function() {
3234
+ return assertDeclaredFields;
3235
+ }
3236
+ });
3237
+ Object.defineProperty(exports, "assertKnownFields", {
3238
+ enumerable: true,
3239
+ get: function() {
3240
+ return assertKnownFields;
3241
+ }
3242
+ });
3243
+ Object.defineProperty(exports, "assertPdfFloat", {
3244
+ enumerable: true,
3245
+ get: function() {
3246
+ return assertPdfFloat;
3247
+ }
3248
+ });
3249
+ Object.defineProperty(exports, "assertResourceIds", {
3250
+ enumerable: true,
3251
+ get: function() {
3252
+ return assertResourceIds;
3253
+ }
3254
+ });
3255
+ Object.defineProperty(exports, "assertResourcesNamed", {
3256
+ enumerable: true,
3257
+ get: function() {
3258
+ return assertResourcesNamed;
3259
+ }
3260
+ });
3261
+ Object.defineProperty(exports, "assertRichTextAgreement", {
3262
+ enumerable: true,
3263
+ get: function() {
3264
+ return assertRichTextAgreement;
3265
+ }
3266
+ });
3267
+ Object.defineProperty(exports, "assertRowResources", {
3268
+ enumerable: true,
3269
+ get: function() {
3270
+ return assertRowResources;
3271
+ }
3272
+ });
3273
+ Object.defineProperty(exports, "assertWritableMeasure", {
3274
+ enumerable: true,
3275
+ get: function() {
3276
+ return assertWritableMeasure;
3277
+ }
3278
+ });
3279
+ Object.defineProperty(exports, "buildCommentThreads", {
3280
+ enumerable: true,
3281
+ get: function() {
3282
+ return buildCommentThreads;
3283
+ }
3284
+ });
3285
+ Object.defineProperty(exports, "buildThreads", {
3286
+ enumerable: true,
3287
+ get: function() {
3288
+ return buildThreads;
3289
+ }
3290
+ });
3291
+ Object.defineProperty(exports, "bundlePageMapping", {
3292
+ enumerable: true,
3293
+ get: function() {
3294
+ return bundlePageMapping;
3295
+ }
3296
+ });
3297
+ Object.defineProperty(exports, "classifyRelation", {
3298
+ enumerable: true,
3299
+ get: function() {
3300
+ return classifyRelation;
3301
+ }
3302
+ });
3303
+ Object.defineProperty(exports, "colorOf", {
3304
+ enumerable: true,
3305
+ get: function() {
3306
+ return colorOf;
3307
+ }
3308
+ });
3309
+ Object.defineProperty(exports, "compareIsoDateTime", {
3310
+ enumerable: true,
3311
+ get: function() {
3312
+ return compareIsoDateTime;
3313
+ }
3314
+ });
3315
+ Object.defineProperty(exports, "deletedWith", {
3316
+ enumerable: true,
3317
+ get: function() {
3318
+ return deletedWith;
3319
+ }
3320
+ });
3321
+ Object.defineProperty(exports, "deriveMeasurementLabel", {
3322
+ enumerable: true,
3323
+ get: function() {
3324
+ return deriveMeasurementLabel;
3325
+ }
3326
+ });
3327
+ Object.defineProperty(exports, "faceForFreeTextFont", {
3328
+ enumerable: true,
3329
+ get: function() {
3330
+ return faceForFreeTextFont;
3331
+ }
3332
+ });
3333
+ Object.defineProperty(exports, "familyKey", {
3334
+ enumerable: true,
3335
+ get: function() {
3336
+ return familyKey;
3337
+ }
3338
+ });
3339
+ Object.defineProperty(exports, "freeTextFontForFace", {
3340
+ enumerable: true,
3341
+ get: function() {
3342
+ return freeTextFontForFace;
3343
+ }
3344
+ });
3345
+ Object.defineProperty(exports, "invalidBundle", {
3346
+ enumerable: true,
3347
+ get: function() {
3348
+ return invalidBundle;
3349
+ }
3350
+ });
3351
+ Object.defineProperty(exports, "isBundlePageRef", {
3352
+ enumerable: true,
3353
+ get: function() {
3354
+ return isBundlePageRef;
3355
+ }
3356
+ });
3357
+ Object.defineProperty(exports, "isDimension", {
3358
+ enumerable: true,
3359
+ get: function() {
3360
+ return isDimension;
3361
+ }
3362
+ });
3363
+ Object.defineProperty(exports, "isReadout", {
3364
+ enumerable: true,
3365
+ get: function() {
3366
+ return isReadout;
3367
+ }
3368
+ });
3369
+ Object.defineProperty(exports, "isRecord", {
3370
+ enumerable: true,
3371
+ get: function() {
3372
+ return isRecord;
3373
+ }
3374
+ });
3375
+ Object.defineProperty(exports, "isSamePdfRect", {
3376
+ enumerable: true,
3377
+ get: function() {
3378
+ return isSamePdfRect;
3379
+ }
3380
+ });
3381
+ Object.defineProperty(exports, "isStandardFontName", {
3382
+ enumerable: true,
3383
+ get: function() {
3384
+ return isStandardFontName;
3385
+ }
3386
+ });
3387
+ Object.defineProperty(exports, "isStateAnnotation", {
3388
+ enumerable: true,
3389
+ get: function() {
3390
+ return isStateAnnotation;
3391
+ }
3392
+ });
3393
+ Object.defineProperty(exports, "kindFactsOf", {
3394
+ enumerable: true,
3395
+ get: function() {
3396
+ return kindFactsOf;
3397
+ }
3398
+ });
3399
+ Object.defineProperty(exports, "measurementPoint", {
3400
+ enumerable: true,
3401
+ get: function() {
3402
+ return measurementPoint;
3403
+ }
3404
+ });
3405
+ Object.defineProperty(exports, "measurementReadout", {
3406
+ enumerable: true,
3407
+ get: function() {
3408
+ return measurementReadout;
3409
+ }
3410
+ });
3411
+ Object.defineProperty(exports, "normalizePdfQuad", {
3412
+ enumerable: true,
3413
+ get: function() {
3414
+ return normalizePdfQuad;
3415
+ }
3416
+ });
3417
+ Object.defineProperty(exports, "normalizePdfRect", {
3418
+ enumerable: true,
3419
+ get: function() {
3420
+ return normalizePdfRect;
3421
+ }
3422
+ });
3423
+ Object.defineProperty(exports, "pathLength", {
3424
+ enumerable: true,
3425
+ get: function() {
3426
+ return pathLength;
3427
+ }
3428
+ });
3429
+ Object.defineProperty(exports, "pdfDeriveAnnotationDraft", {
3430
+ enumerable: true,
3431
+ get: function() {
3432
+ return pdfDeriveAnnotationDraft;
3433
+ }
3434
+ });
3435
+ Object.defineProperty(exports, "pdfDeriveAnnotationPatch", {
3436
+ enumerable: true,
3437
+ get: function() {
3438
+ return pdfDeriveAnnotationPatch;
3439
+ }
3440
+ });
3441
+ Object.defineProperty(exports, "pdfPointTurned", {
3442
+ enumerable: true,
3443
+ get: function() {
3444
+ return pdfPointTurned;
3445
+ }
3446
+ });
3447
+ Object.defineProperty(exports, "pdfPointUnturned", {
3448
+ enumerable: true,
3449
+ get: function() {
3450
+ return pdfPointUnturned;
3451
+ }
3452
+ });
3453
+ Object.defineProperty(exports, "pdfPointsBounds", {
3454
+ enumerable: true,
3455
+ get: function() {
3456
+ return pdfPointsBounds;
3457
+ }
3458
+ });
3459
+ Object.defineProperty(exports, "pdfPredictAnnotationDraft", {
3460
+ enumerable: true,
3461
+ get: function() {
3462
+ return pdfPredictAnnotationDraft;
3463
+ }
3464
+ });
3465
+ Object.defineProperty(exports, "pdfPredictAnnotationPatch", {
3466
+ enumerable: true,
3467
+ get: function() {
3468
+ return pdfPredictAnnotationPatch;
3469
+ }
3470
+ });
3471
+ Object.defineProperty(exports, "pdfPrepareAnnotationDraft", {
3472
+ enumerable: true,
3473
+ get: function() {
3474
+ return pdfPrepareAnnotationDraft;
3475
+ }
3476
+ });
3477
+ Object.defineProperty(exports, "pdfPrepareAnnotationPatch", {
3478
+ enumerable: true,
3479
+ get: function() {
3480
+ return pdfPrepareAnnotationPatch;
3481
+ }
3482
+ });
3483
+ Object.defineProperty(exports, "pdfQuadBounds", {
3484
+ enumerable: true,
3485
+ get: function() {
3486
+ return pdfQuadBounds;
3487
+ }
3488
+ });
3489
+ Object.defineProperty(exports, "pdfQuarterTurnBox", {
3490
+ enumerable: true,
3491
+ get: function() {
3492
+ return pdfQuarterTurnBox;
3493
+ }
3494
+ });
3495
+ Object.defineProperty(exports, "pdfRectFromOriginSize", {
3496
+ enumerable: true,
3497
+ get: function() {
3498
+ return pdfRectFromOriginSize;
3499
+ }
3500
+ });
3501
+ Object.defineProperty(exports, "pdfRectHeight", {
3502
+ enumerable: true,
3503
+ get: function() {
3504
+ return pdfRectHeight;
3505
+ }
3506
+ });
3507
+ Object.defineProperty(exports, "pdfRectIntersection", {
3508
+ enumerable: true,
3509
+ get: function() {
3510
+ return pdfRectIntersection;
3511
+ }
3512
+ });
3513
+ Object.defineProperty(exports, "pdfRectSize", {
3514
+ enumerable: true,
3515
+ get: function() {
3516
+ return pdfRectSize;
3517
+ }
3518
+ });
3519
+ Object.defineProperty(exports, "pdfRectToOriginSize", {
3520
+ enumerable: true,
3521
+ get: function() {
3522
+ return pdfRectToOriginSize;
3523
+ }
3524
+ });
3525
+ Object.defineProperty(exports, "pdfRectTurnedBounds", {
3526
+ enumerable: true,
3527
+ get: function() {
3528
+ return pdfRectTurnedBounds;
3529
+ }
3530
+ });
3531
+ Object.defineProperty(exports, "pdfRectWidth", {
3532
+ enumerable: true,
3533
+ get: function() {
3534
+ return pdfRectWidth;
3535
+ }
3536
+ });
3537
+ Object.defineProperty(exports, "pdfResolveRectCommand", {
3538
+ enumerable: true,
3539
+ get: function() {
3540
+ return pdfResolveRectCommand;
3541
+ }
3542
+ });
3543
+ Object.defineProperty(exports, "pdfShapeForRect", {
3544
+ enumerable: true,
3545
+ get: function() {
3546
+ return pdfShapeForRect;
3547
+ }
3548
+ });
3549
+ Object.defineProperty(exports, "pdfTurnOfDrawn", {
3550
+ enumerable: true,
3551
+ get: function() {
3552
+ return pdfTurnOfDrawn;
3553
+ }
3554
+ });
3555
+ Object.defineProperty(exports, "pdfTurnOfUpright", {
3556
+ enumerable: true,
3557
+ get: function() {
3558
+ return pdfTurnOfUpright;
3559
+ }
3560
+ });
3561
+ Object.defineProperty(exports, "polygonArea", {
3562
+ enumerable: true,
3563
+ get: function() {
3564
+ return polygonArea;
3565
+ }
3566
+ });
3567
+ Object.defineProperty(exports, "quarterTurnOf", {
3568
+ enumerable: true,
3569
+ get: function() {
3570
+ return quarterTurnOf;
3571
+ }
3572
+ });
3573
+ Object.defineProperty(exports, "resolveMeasurementDraft", {
3574
+ enumerable: true,
3575
+ get: function() {
3576
+ return resolveMeasurementDraft;
3577
+ }
3578
+ });
3579
+ Object.defineProperty(exports, "resourceIdOf", {
3580
+ enumerable: true,
3581
+ get: function() {
3582
+ return resourceIdOf;
3583
+ }
3584
+ });
3585
+ Object.defineProperty(exports, "rgbOf", {
3586
+ enumerable: true,
3587
+ get: function() {
3588
+ return rgbOf;
3589
+ }
3590
+ });
3591
+ Object.defineProperty(exports, "richTextParagraphsFromPlainText", {
3592
+ enumerable: true,
3593
+ get: function() {
3594
+ return richTextParagraphsFromPlainText;
3595
+ }
3596
+ });
3597
+ Object.defineProperty(exports, "richTextPlainText", {
3598
+ enumerable: true,
3599
+ get: function() {
3600
+ return richTextPlainText;
3601
+ }
3602
+ });
3603
+ Object.defineProperty(exports, "sameColor", {
3604
+ enumerable: true,
3605
+ get: function() {
3606
+ return sameColor;
3607
+ }
3608
+ });
3609
+ Object.defineProperty(exports, "scaleOf", {
3610
+ enumerable: true,
3611
+ get: function() {
3612
+ return scaleOf;
3613
+ }
3614
+ });
3615
+ Object.defineProperty(exports, "semanticEqual", {
3616
+ enumerable: true,
3617
+ get: function() {
3618
+ return semanticEqual;
3619
+ }
3620
+ });
3621
+ Object.defineProperty(exports, "shapeFieldsOf", {
3622
+ enumerable: true,
3623
+ get: function() {
3624
+ return shapeFieldsOf;
3625
+ }
3626
+ });
3627
+ Object.defineProperty(exports, "standardStateModelOf", {
3628
+ enumerable: true,
3629
+ get: function() {
3630
+ return standardStateModelOf;
3631
+ }
3632
+ });
3633
+ Object.defineProperty(exports, "touchesCaption", {
3634
+ enumerable: true,
3635
+ get: function() {
3636
+ return touchesCaption;
3637
+ }
3638
+ });
3639
+ Object.defineProperty(exports, "validAreaBoundary", {
3640
+ enumerable: true,
3641
+ get: function() {
3642
+ return validAreaBoundary;
3643
+ }
3644
+ });
3645
+
3646
+ //# sourceMappingURL=resolveAnnotationDraft-7Ke4nV7F.cjs.map