@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.
- package/dist/AnnotationList-CkijRHet.d.cts +27163 -0
- package/dist/AnnotationList-CkijRHet.d.ts +27163 -0
- package/dist/Change-CKoxfTNY.js +889 -0
- package/dist/Change-CKoxfTNY.js.map +1 -0
- package/dist/Change-CjImsrN0.cjs +1020 -0
- package/dist/Change-CjImsrN0.cjs.map +1 -0
- package/dist/DocumentRenderService-BfFdFSwI.cjs +120 -0
- package/dist/DocumentRenderService-BfFdFSwI.cjs.map +1 -0
- package/dist/DocumentRenderService-DV3J9lMz.js +85 -0
- package/dist/DocumentRenderService-DV3J9lMz.js.map +1 -0
- package/dist/{DocumentSecurityService-_JRjqvoI.d.ts → DocumentSecurityService-CU1M6Q7m.d.ts} +100 -112
- package/dist/{DocumentSecurityService-ClTZc0zO.d.cts → DocumentSecurityService-e6vp2de8.d.cts} +100 -112
- package/dist/LocalDocumentHandle-D02FMm-Z.cjs +53 -0
- package/dist/LocalDocumentHandle-D02FMm-Z.cjs.map +1 -0
- package/dist/LocalDocumentHandle-DAPdaOt_.js +30 -0
- package/dist/LocalDocumentHandle-DAPdaOt_.js.map +1 -0
- package/dist/LocalEngine-DFoBm4B3.d.cts +1565 -0
- package/dist/LocalEngine-DPriFujS.d.ts +1565 -0
- package/dist/PageInsertBlankInput-CXHAM7l6.cjs +17 -0
- package/dist/PageInsertBlankInput-CXHAM7l6.cjs.map +1 -0
- package/dist/PageInsertBlankInput-DhVYVJ_S.js +12 -0
- package/dist/PageInsertBlankInput-DhVYVJ_S.js.map +1 -0
- package/dist/authority-B3BklsB4.js +2239 -0
- package/dist/authority-B3BklsB4.js.map +1 -0
- package/dist/authority-DLMNR7ff.cjs +2676 -0
- package/dist/authority-DLMNR7ff.cjs.map +1 -0
- package/dist/builders-CNj0POeY.js +161 -0
- package/dist/builders-CNj0POeY.js.map +1 -0
- package/dist/builders-Cgdp_YCn.cjs +184 -0
- package/dist/builders-Cgdp_YCn.cjs.map +1 -0
- package/dist/calibrate-7T3fnP8-.js +2404 -0
- package/dist/calibrate-7T3fnP8-.js.map +1 -0
- package/dist/calibrate-DJBXnhLE.cjs +2919 -0
- package/dist/calibrate-DJBXnhLE.cjs.map +1 -0
- package/dist/charmap-Cprti1ta.js +239 -0
- package/dist/charmap-Cprti1ta.js.map +1 -0
- package/dist/charmap-aYlefQP9.cjs +304 -0
- package/dist/charmap-aYlefQP9.cjs.map +1 -0
- package/dist/conformance.cjs +16753 -5247
- package/dist/conformance.cjs.map +1 -1
- package/dist/conformance.d.cts +928 -165
- package/dist/conformance.d.ts +928 -165
- package/dist/conformance.js +16731 -5268
- package/dist/conformance.js.map +1 -1
- package/dist/format-Bp3QFSH1.cjs +574 -0
- package/dist/format-Bp3QFSH1.cjs.map +1 -0
- package/dist/format-Dgg428Kb.js +461 -0
- package/dist/format-Dgg428Kb.js.map +1 -0
- package/dist/index.cjs +330 -103
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -5
- package/dist/pageRefs-B3ur7Ckm.cjs +51 -0
- package/dist/pageRefs-B3ur7Ckm.cjs.map +1 -0
- package/dist/pageRefs-DkbuxSCV.js +40 -0
- package/dist/pageRefs-DkbuxSCV.js.map +1 -0
- package/dist/public.cjs +57 -0
- package/dist/public.d.cts +5 -0
- package/dist/public.d.ts +5 -0
- package/dist/public.js +9 -0
- package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs +3646 -0
- package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs.map +1 -0
- package/dist/resolveAnnotationDraft-CUpf0SMh.js +3167 -0
- package/dist/resolveAnnotationDraft-CUpf0SMh.js.map +1 -0
- package/dist/{resolver-inAVzsWt.js → resolver-CzxmD9Ck.js} +294 -144
- package/dist/resolver-CzxmD9Ck.js.map +1 -0
- package/dist/{resolver-CYVNV4hn.cjs → resolver-D2OCtE8F.cjs} +347 -143
- package/dist/resolver-D2OCtE8F.cjs.map +1 -0
- package/dist/runtime-Cl0v56mm.d.ts +2032 -0
- package/dist/runtime-p1WDzDo4.d.cts +2032 -0
- package/dist/runtime.cjs +733 -284
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +5 -1348
- package/dist/runtime.d.ts +5 -1348
- package/dist/runtime.js +396 -179
- package/dist/runtime.js.map +1 -1
- package/dist/schema-C-gU4vj7.cjs +3034 -0
- package/dist/schema-C-gU4vj7.cjs.map +1 -0
- package/dist/schema-DzlUNk38.js +2081 -0
- package/dist/schema-DzlUNk38.js.map +1 -0
- package/dist/schemas-Berf1swF.cjs +2481 -0
- package/dist/schemas-Berf1swF.cjs.map +1 -0
- package/dist/schemas-D6xOGjqg.js +1654 -0
- package/dist/schemas-D6xOGjqg.js.map +1 -0
- package/dist/subtype-CG4WpIRj.js +129 -0
- package/dist/subtype-CG4WpIRj.js.map +1 -0
- package/dist/subtype-aDt5TFmK.cjs +158 -0
- package/dist/subtype-aDt5TFmK.cjs.map +1 -0
- package/dist/validation.cjs +578 -0
- package/dist/validation.cjs.map +1 -0
- package/dist/validation.d.cts +174 -0
- package/dist/validation.d.ts +174 -0
- package/dist/validation.js +571 -0
- package/dist/validation.js.map +1 -0
- package/dist/value-BFS1R8ek.cjs +435 -0
- package/dist/value-BFS1R8ek.cjs.map +1 -0
- package/dist/value-Dhc8eYcx.js +340 -0
- package/dist/value-Dhc8eYcx.js.map +1 -0
- package/dist/viewport-BJW13EGZ.js +540 -0
- package/dist/viewport-BJW13EGZ.js.map +1 -0
- package/dist/viewport-DlL5Bxbg.cjs +671 -0
- package/dist/viewport-DlL5Bxbg.cjs.map +1 -0
- package/dist/wire.cjs +1056 -379
- package/dist/wire.cjs.map +1 -1
- package/dist/wire.d.cts +15069 -725
- package/dist/wire.d.ts +15069 -725
- package/dist/wire.js +825 -239
- package/dist/wire.js.map +1 -1
- package/package.json +10 -1
- package/dist/AbortError-Cq2igpZ6.cjs +0 -38
- package/dist/AbortError-Cq2igpZ6.cjs.map +0 -1
- package/dist/AbortError-DHqPBnYc.js +0 -27
- package/dist/AbortError-DHqPBnYc.js.map +0 -1
- package/dist/AnnotationListSnapshot-D3mFut1e.d.cts +0 -6871
- package/dist/AnnotationListSnapshot-D3mFut1e.d.ts +0 -6871
- package/dist/Engine-Bx0NOfmd.d.cts +0 -1264
- package/dist/Engine-DxtpCYcP.d.ts +0 -1264
- package/dist/PageInsertBlankInput-CqXBmFhN.cjs +0 -449
- package/dist/PageInsertBlankInput-CqXBmFhN.cjs.map +0 -1
- package/dist/PageInsertBlankInput-cHq9-i-z.js +0 -378
- package/dist/PageInsertBlankInput-cHq9-i-z.js.map +0 -1
- package/dist/builders-Bqm8yTpi.cjs +0 -3564
- package/dist/builders-Bqm8yTpi.cjs.map +0 -1
- package/dist/builders-Dqo2X-dt.js +0 -3121
- package/dist/builders-Dqo2X-dt.js.map +0 -1
- package/dist/fold-2V29cxCV.cjs +0 -447
- package/dist/fold-2V29cxCV.cjs.map +0 -1
- package/dist/fold-CdEJTBTn.js +0 -334
- package/dist/fold-CdEJTBTn.js.map +0 -1
- package/dist/resolver-CYVNV4hn.cjs.map +0 -1
- package/dist/resolver-inAVzsWt.js.map +0 -1
- package/dist/schemas-BLARNCgx.js +0 -3113
- package/dist/schemas-BLARNCgx.js.map +0 -1
- package/dist/schemas-DIqFMmrC.cjs +0 -4468
- 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
|