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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/AnnotationList-CkijRHet.d.cts +27163 -0
  2. package/dist/AnnotationList-CkijRHet.d.ts +27163 -0
  3. package/dist/Change-CKoxfTNY.js +889 -0
  4. package/dist/Change-CKoxfTNY.js.map +1 -0
  5. package/dist/Change-CjImsrN0.cjs +1020 -0
  6. package/dist/Change-CjImsrN0.cjs.map +1 -0
  7. package/dist/DocumentRenderService-BfFdFSwI.cjs +120 -0
  8. package/dist/DocumentRenderService-BfFdFSwI.cjs.map +1 -0
  9. package/dist/DocumentRenderService-DV3J9lMz.js +85 -0
  10. package/dist/DocumentRenderService-DV3J9lMz.js.map +1 -0
  11. package/dist/{DocumentSecurityService-Ca6EK3-3.d.ts → DocumentSecurityService-CU1M6Q7m.d.ts} +100 -112
  12. package/dist/{DocumentSecurityService-CIx1MT0T.d.cts → DocumentSecurityService-e6vp2de8.d.cts} +100 -112
  13. package/dist/LocalDocumentHandle-D02FMm-Z.cjs +53 -0
  14. package/dist/LocalDocumentHandle-D02FMm-Z.cjs.map +1 -0
  15. package/dist/LocalDocumentHandle-DAPdaOt_.js +30 -0
  16. package/dist/LocalDocumentHandle-DAPdaOt_.js.map +1 -0
  17. package/dist/LocalEngine-DFoBm4B3.d.cts +1565 -0
  18. package/dist/LocalEngine-DPriFujS.d.ts +1565 -0
  19. package/dist/PageInsertBlankInput-CXHAM7l6.cjs +17 -0
  20. package/dist/PageInsertBlankInput-CXHAM7l6.cjs.map +1 -0
  21. package/dist/PageInsertBlankInput-DhVYVJ_S.js +12 -0
  22. package/dist/PageInsertBlankInput-DhVYVJ_S.js.map +1 -0
  23. package/dist/authority-B3BklsB4.js +2239 -0
  24. package/dist/authority-B3BklsB4.js.map +1 -0
  25. package/dist/{viewport-BWeNHZmb.cjs → authority-DLMNR7ff.cjs} +820 -1647
  26. package/dist/authority-DLMNR7ff.cjs.map +1 -0
  27. package/dist/builders-CNj0POeY.js +161 -0
  28. package/dist/builders-CNj0POeY.js.map +1 -0
  29. package/dist/builders-Cgdp_YCn.cjs +184 -0
  30. package/dist/builders-Cgdp_YCn.cjs.map +1 -0
  31. package/dist/calibrate-7T3fnP8-.js +2404 -0
  32. package/dist/calibrate-7T3fnP8-.js.map +1 -0
  33. package/dist/calibrate-DJBXnhLE.cjs +2919 -0
  34. package/dist/calibrate-DJBXnhLE.cjs.map +1 -0
  35. package/dist/charmap-Cprti1ta.js +239 -0
  36. package/dist/charmap-Cprti1ta.js.map +1 -0
  37. package/dist/charmap-aYlefQP9.cjs +304 -0
  38. package/dist/charmap-aYlefQP9.cjs.map +1 -0
  39. package/dist/conformance.cjs +16466 -5247
  40. package/dist/conformance.cjs.map +1 -1
  41. package/dist/conformance.d.cts +924 -165
  42. package/dist/conformance.d.ts +924 -165
  43. package/dist/conformance.js +16429 -5252
  44. package/dist/conformance.js.map +1 -1
  45. package/dist/format-Bp3QFSH1.cjs +574 -0
  46. package/dist/format-Bp3QFSH1.cjs.map +1 -0
  47. package/dist/format-Dgg428Kb.js +461 -0
  48. package/dist/format-Dgg428Kb.js.map +1 -0
  49. package/dist/index.cjs +323 -116
  50. package/dist/index.d.cts +2 -2
  51. package/dist/index.d.ts +2 -2
  52. package/dist/index.js +14 -5
  53. package/dist/pageRefs-B3ur7Ckm.cjs +51 -0
  54. package/dist/pageRefs-B3ur7Ckm.cjs.map +1 -0
  55. package/dist/pageRefs-DkbuxSCV.js +40 -0
  56. package/dist/pageRefs-DkbuxSCV.js.map +1 -0
  57. package/dist/public.cjs +57 -0
  58. package/dist/public.d.cts +5 -0
  59. package/dist/public.d.ts +5 -0
  60. package/dist/public.js +9 -0
  61. package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs +3646 -0
  62. package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs.map +1 -0
  63. package/dist/resolveAnnotationDraft-CUpf0SMh.js +3167 -0
  64. package/dist/resolveAnnotationDraft-CUpf0SMh.js.map +1 -0
  65. package/dist/{resolver-inAVzsWt.js → resolver-CzxmD9Ck.js} +294 -144
  66. package/dist/resolver-CzxmD9Ck.js.map +1 -0
  67. package/dist/{resolver-CYVNV4hn.cjs → resolver-D2OCtE8F.cjs} +347 -143
  68. package/dist/resolver-D2OCtE8F.cjs.map +1 -0
  69. package/dist/runtime-Cl0v56mm.d.ts +2032 -0
  70. package/dist/runtime-p1WDzDo4.d.cts +2032 -0
  71. package/dist/runtime.cjs +726 -297
  72. package/dist/runtime.cjs.map +1 -1
  73. package/dist/runtime.d.cts +5 -1372
  74. package/dist/runtime.d.ts +5 -1372
  75. package/dist/runtime.js +396 -179
  76. package/dist/runtime.js.map +1 -1
  77. package/dist/schema-C-gU4vj7.cjs +3034 -0
  78. package/dist/schema-C-gU4vj7.cjs.map +1 -0
  79. package/dist/schema-DzlUNk38.js +2081 -0
  80. package/dist/schema-DzlUNk38.js.map +1 -0
  81. package/dist/schemas-Berf1swF.cjs +2481 -0
  82. package/dist/schemas-Berf1swF.cjs.map +1 -0
  83. package/dist/schemas-D6xOGjqg.js +1654 -0
  84. package/dist/schemas-D6xOGjqg.js.map +1 -0
  85. package/dist/subtype-CG4WpIRj.js +129 -0
  86. package/dist/subtype-CG4WpIRj.js.map +1 -0
  87. package/dist/subtype-aDt5TFmK.cjs +158 -0
  88. package/dist/subtype-aDt5TFmK.cjs.map +1 -0
  89. package/dist/validation.cjs +578 -0
  90. package/dist/validation.cjs.map +1 -0
  91. package/dist/validation.d.cts +174 -0
  92. package/dist/validation.d.ts +174 -0
  93. package/dist/validation.js +571 -0
  94. package/dist/validation.js.map +1 -0
  95. package/dist/value-BFS1R8ek.cjs +435 -0
  96. package/dist/value-BFS1R8ek.cjs.map +1 -0
  97. package/dist/value-Dhc8eYcx.js +340 -0
  98. package/dist/value-Dhc8eYcx.js.map +1 -0
  99. package/dist/viewport-BJW13EGZ.js +540 -0
  100. package/dist/viewport-BJW13EGZ.js.map +1 -0
  101. package/dist/viewport-DlL5Bxbg.cjs +671 -0
  102. package/dist/viewport-DlL5Bxbg.cjs.map +1 -0
  103. package/dist/wire.cjs +1051 -397
  104. package/dist/wire.cjs.map +1 -1
  105. package/dist/wire.d.cts +12403 -790
  106. package/dist/wire.d.ts +12403 -790
  107. package/dist/wire.js +821 -242
  108. package/dist/wire.js.map +1 -1
  109. package/package.json +10 -1
  110. package/dist/AbortError-DwuHPK_I.js +0 -27
  111. package/dist/AbortError-DwuHPK_I.js.map +0 -1
  112. package/dist/AbortError-P0RU0Mgg.cjs +0 -38
  113. package/dist/AbortError-P0RU0Mgg.cjs.map +0 -1
  114. package/dist/AnnotationListSnapshot-D9R5n2Kf.d.cts +0 -7101
  115. package/dist/AnnotationListSnapshot-D9R5n2Kf.d.ts +0 -7101
  116. package/dist/Engine-EWWHhEIi.d.ts +0 -1277
  117. package/dist/Engine-FNaAEjfb.d.cts +0 -1277
  118. package/dist/derive-BbsDk_Kz.cjs +0 -880
  119. package/dist/derive-BbsDk_Kz.cjs.map +0 -1
  120. package/dist/derive-DAFJolWu.js +0 -647
  121. package/dist/derive-DAFJolWu.js.map +0 -1
  122. package/dist/format-Bbphf4zR.js +0 -435
  123. package/dist/format-Bbphf4zR.js.map +0 -1
  124. package/dist/format-D_epWlob.cjs +0 -518
  125. package/dist/format-D_epWlob.cjs.map +0 -1
  126. package/dist/resolver-CYVNV4hn.cjs.map +0 -1
  127. package/dist/resolver-inAVzsWt.js.map +0 -1
  128. package/dist/schemas-CFQGvWiu.js +0 -3243
  129. package/dist/schemas-CFQGvWiu.js.map +0 -1
  130. package/dist/schemas-D0g6tKa9.cjs +0 -4694
  131. package/dist/schemas-D0g6tKa9.cjs.map +0 -1
  132. package/dist/viewport-BWeNHZmb.cjs.map +0 -1
  133. package/dist/viewport-CvMcadfQ.js +0 -3072
  134. package/dist/viewport-CvMcadfQ.js.map +0 -1
@@ -1,282 +1,189 @@
1
- const require_derive = require("./derive-BbsDk_Kz.cjs");
2
- const require_format = require("./format-D_epWlob.cjs");
3
- const require_resolver = require("./resolver-CYVNV4hn.cjs");
4
- //#region src/dto/PdfAction.ts
1
+ const require_value = require("./value-BFS1R8ek.cjs");
2
+ const require_resolveAnnotationDraft = require("./resolveAnnotationDraft-7Ke4nV7F.cjs");
3
+ const require_format = require("./format-Bp3QFSH1.cjs");
4
+ const require_calibrate = require("./calibrate-DJBXnhLE.cjs");
5
+ const require_resolver = require("./resolver-D2OCtE8F.cjs");
6
+ const require_viewport = require("./viewport-DlL5Bxbg.cjs");
7
+ //#region src/geometry/pageBoxes.ts
8
+ /** The media box of a page that has none, which the file needs: US Letter. */
9
+ const DEFAULT_MEDIA_BOX = {
10
+ left: 0,
11
+ bottom: 0,
12
+ right: 612,
13
+ top: 792
14
+ };
15
+ /** A box that shares nothing with the media box: no size, at the origin. */
16
+ const EMPTY_BOX = {
17
+ left: 0,
18
+ right: 0,
19
+ bottom: 0,
20
+ top: 0
21
+ };
5
22
  /**
6
- * The one Table-240 decode, shared by the reader, the schema's producers,
7
- * and tests — so bit semantics cannot fork.
23
+ * The page's five boxes as ISO 32000-1 §14.11.2 defines them: the crop box
24
+ * defaults to the media box and the other three to the crop box, and each is
25
+ * reduced to the part it shares with the media box. The crop box is then the
26
+ * visible page, what the page shows.
27
+ *
28
+ * For broken files, which the standard doesn't cover, we do what Acrobat
29
+ * does: a page without a media box is US Letter, and a box that shares
30
+ * nothing with the media box is empty, so a crop box off the page leaves a
31
+ * page with no size.
8
32
  */
9
- const decodeSubmitFormFlags = (raw) => {
10
- const bit = (n) => (raw & 1 << n - 1) !== 0;
11
- const format = bit(9) ? "pdf" : bit(6) ? "xfdf" : bit(3) ? "html" : "fdf";
33
+ function pageBoxesOf(written) {
34
+ const media = written.media ?? DEFAULT_MEDIA_BOX;
35
+ const within = (box) => require_resolveAnnotationDraft.pdfRectIntersection(box, media) ?? EMPTY_BOX;
36
+ const crop = within(written.crop ?? media);
12
37
  return {
13
- raw,
14
- exclude: bit(1),
15
- includeNoValueFields: bit(2),
16
- format,
17
- method: bit(4) && (format === "html" || format === "pdf") ? "get" : "post",
18
- submitCoordinates: bit(5),
19
- includeAppendSaves: bit(7),
20
- includeAnnotations: bit(8),
21
- canonicalFormat: bit(10),
22
- exclNonUserAnnots: bit(11),
23
- exclFKey: bit(12),
24
- embedForm: bit(14)
38
+ media,
39
+ crop,
40
+ bleed: within(written.bleed ?? crop),
41
+ trim: within(written.trim ?? crop),
42
+ art: within(written.art ?? crop)
25
43
  };
26
- };
27
- //#endregion
28
- //#region src/dto/PageRender.ts
29
- function createPageImageHandle(result, blobSource) {
30
- return {
31
- ...result,
32
- async objectUrl(signal) {
33
- if (typeof Blob === "undefined" || typeof URL === "undefined" || !URL.createObjectURL) throw new require_derive.EngineError(require_format.EngineErrorCode.RuntimeUnavailable, "Object URLs are not available in this environment");
34
- const blob = await blobSource.blob(signal);
35
- const url = URL.createObjectURL(blob);
36
- return {
37
- url,
38
- revoke: () => URL.revokeObjectURL(url)
39
- };
40
- }
41
- };
42
- }
43
- //#endregion
44
- //#region src/dto/PdfSaveMode.ts
45
- const DEFAULT_PDF_SAVE_MODE = "incremental";
46
- //#endregion
47
- //#region src/dto/RichText.ts
48
- /** The plain-text projection of a rich document: paragraphs joined by `\r`,
49
- * runs concatenated — what the engine writes to `/Contents`. */
50
- function richTextPlainText(doc) {
51
- return doc.paragraphs.map((p) => p.runs.map((r) => r.text).join("")).join("\r");
52
44
  }
53
- /** Plain text as rich paragraphs: one per line break, one run each, no
54
- * overrides (the engine's own `/Contents` fallback shape). */
55
- function richTextParagraphsFromPlainText(text) {
56
- return text.split(/\r\n|\r|\n/).map((line) => ({ runs: [{ text: line }] }));
57
- }
58
- //#endregion
59
- //#region src/identity/PageObjectNumber.ts
60
45
  /**
61
- * Type guard for the rare malformed-PDF case where a page is a direct
62
- * object. The engine treats `0` as malformed and throws `EngineError(NotFound)`
63
- * before any page-level operation.
46
+ * A page's turn from its `/Rotate`, degrees clockwise. ISO 32000-1 Table 30
47
+ * allows multiples of 90; a negative one or one past 360 is the same turn.
48
+ * For a value that isn't a multiple of 90, which the file shouldn't hold, we
49
+ * do what Acrobat does: no turn.
64
50
  */
65
- function isValidPageObjectNumber(value) {
66
- return typeof value === "number" && Number.isInteger(value) && value > 0;
51
+ function pageRotationOf(written) {
52
+ if (!Number.isInteger(written) || written % 90 !== 0) return 0;
53
+ return (written % 360 + 360) % 360;
67
54
  }
68
55
  //#endregion
69
- //#region src/identity/AnnotationStableId.ts
56
+ //#region src/dto/PageRender.ts
70
57
  /**
71
- * URL-safe encoding of a stable id, used by the cloud HTTP surface as the
72
- * `:annotKey` route parameter. Decoded by the server back into an
73
- * `AnnotationStableId` via `decodeStableIdKey`.
74
- *
75
- * Format:
76
- * `{ kind: 'objectNumber', value: 42 }` -> `'obj:42'`
77
- * `{ kind: 'nm', value: 'foo bar' }` -> `'nm:foo bar'`
78
- *
79
- * The caller is responsible for `encodeURIComponent`-ing the result before
80
- * splicing it into a URL path; `wirePaths.layerAnnotationByKey` already does
81
- * that. /NM values are opaque strings and may contain anything; the
82
- * `nm:` prefix lets the decoder distinguish them from numeric ids
83
- * unambiguously.
58
+ * What a page picture draws: the options as passed, and for each one left
59
+ * out, what the caller may read. A left-out option never asks for more than
60
+ * the caller may see; `true` for something they may not read is refused
61
+ * (`PermissionDenied`, naming the option).
84
62
  */
85
- function encodeStableIdKey(id) {
86
- if (id.kind === "objectNumber") {
87
- if (!Number.isInteger(id.value) || id.value <= 0) throw new RangeError(`encodeStableIdKey: objectNumber must be a positive integer, got ${id.value}`);
88
- return `obj:${id.value}`;
89
- }
90
- return `nm:${id.value}`;
63
+ function resolvePageLayers(options, may) {
64
+ const { includeAnnotations, includeFormFields } = options ?? {};
65
+ if (includeAnnotations === true && !may.annotations) throw new require_resolver.PermissionDenied("doc.annotate.read", "includeAnnotations");
66
+ if (includeFormFields === true && !may.formFields) throw new require_resolver.PermissionDenied("doc.forms.read", "includeFormFields");
67
+ return {
68
+ includeAnnotations: includeAnnotations ?? may.annotations,
69
+ includeFormFields: includeFormFields ?? (includeAnnotations === false ? false : may.formFields)
70
+ };
71
+ }
72
+ /** `InvalidArg` unless `quality` is absent or between 0 and 1. */
73
+ function checkImageQuality(quality) {
74
+ if (quality === void 0 || quality >= 0 && quality <= 1) return;
75
+ throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "quality must be between 0 and 1", { details: { field: "quality" } });
76
+ }
77
+ function createPageImageHandle(result, blobSource) {
78
+ return {
79
+ ...result,
80
+ ...imageSourceOf(blobSource)
81
+ };
91
82
  }
92
83
  /**
93
- * Inverse of `encodeStableIdKey`. Returns `null` for malformed input so
94
- * the server can answer 400 InvalidArg with a useful message instead of
95
- * throwing.
96
- *
97
- * The input is the already-`decodeURIComponent`-ed segment from the route
98
- * path. Empty `/NM` values are rejected: `nm:` with no suffix is not a
99
- * valid identity.
84
+ * An image whose encoded bytes this device holds (a picture it made, such as
85
+ * a stamp's preview), to paint like one an engine rendered.
100
86
  */
101
- function decodeStableIdKey(key) {
102
- if (key.startsWith("obj:")) {
103
- const rest = key.slice(4);
104
- const n = Number.parseInt(rest, 10);
105
- if (!Number.isInteger(n) || n <= 0 || String(n) !== rest) return null;
106
- return {
107
- kind: "objectNumber",
108
- value: n
109
- };
110
- }
111
- if (key.startsWith("nm:")) {
112
- const value = key.slice(3);
113
- if (value.length === 0) return null;
114
- return {
115
- kind: "nm",
116
- value
117
- };
118
- }
119
- return null;
120
- }
121
- //#endregion
122
- //#region src/revision/RevisionToken.ts
123
- function revisionTokensEqual(a, b) {
124
- return a.docSessionId === b.docSessionId && a.pageObjectNumber === b.pageObjectNumber && a.generation === b.generation;
87
+ function imageSourceOfBytes(bytes, contentType) {
88
+ return imageSourceOf({ blob: async () => new Blob([bytes.slice()], { type: contentType }) });
125
89
  }
126
- //#endregion
127
- //#region src/revision/WeakAnnotationState.ts
128
- const UNKNOWN_WEAK_ANNOTATION_STATE = { kind: "unknown" };
129
- function knownWeakAnnotationState(hasAnyWeakAnnotations) {
90
+ /** The image `blobSource` gives, as a blob and as an object URL. */
91
+ function imageSourceOf(blobSource) {
130
92
  return {
131
- kind: "known",
132
- hasAnyWeakAnnotations
93
+ blob() {
94
+ return require_viewport.AbortablePromise.run(async (signal) => {
95
+ if (typeof Blob === "undefined") throw new require_format.EngineError(require_format.EngineErrorCode.RuntimeUnavailable, "Blob is not available in this environment");
96
+ const blob = await blobSource.blob(signal);
97
+ if (signal.aborted) throw signal.reason;
98
+ return blob;
99
+ });
100
+ },
101
+ objectUrl() {
102
+ return require_viewport.AbortablePromise.run(async (signal) => {
103
+ if (typeof Blob === "undefined" || typeof URL === "undefined" || !URL.createObjectURL) throw new require_format.EngineError(require_format.EngineErrorCode.RuntimeUnavailable, "Object URLs are not available in this environment");
104
+ const blob = await blobSource.blob(signal);
105
+ const url = URL.createObjectURL(blob);
106
+ if (signal.aborted) {
107
+ URL.revokeObjectURL(url);
108
+ throw signal.reason;
109
+ }
110
+ return {
111
+ url,
112
+ revoke: () => URL.revokeObjectURL(url)
113
+ };
114
+ });
115
+ }
133
116
  };
134
117
  }
135
118
  //#endregion
136
- //#region src/annotation/primitives.ts
137
- const NO_ANNOTATION_FLAGS = {
138
- invisible: false,
139
- hidden: false,
140
- print: false,
141
- noZoom: false,
142
- noRotate: false,
143
- noView: false,
144
- readOnly: false,
145
- locked: false,
146
- toggleNoView: false,
147
- lockedContents: false
148
- };
119
+ //#region src/dto/PdfSaveMode.ts
120
+ const DEFAULT_PDF_SAVE_MODE = "incremental";
149
121
  //#endregion
150
- //#region src/resource/BinarySource.ts
151
- function isBlob(value) {
152
- return typeof Blob !== "undefined" && value instanceof Blob;
153
- }
122
+ //#region src/identity/uuid.ts
154
123
  /**
155
- * Copy a Uint8Array view into a fresh, exactly-sized ArrayBuffer the call
156
- * owns. Always copies — even when the view spans its whole buffer — because
157
- * the result may be transferred to a worker (detaching it), and the view is
158
- * the caller's. Returning `view.buffer` here would let one transfer zero
159
- * the caller's bytes (a stamp library's Uint8Array became single-use).
160
- * Callers wanting a zero-copy hand-off need an explicit "give" form, as
161
- * `pages.insert` has for bare ArrayBuffers; `BinarySource` has none.
124
+ * Generate an RFC 4122 v4 UUID using the Web Crypto API surface available in
125
+ * every realm this code runs in: Node 19+, Web Workers, browsers on https,
126
+ * http, intranet and file: pages, and edge runtimes.
127
+ *
128
+ * Why this implementation:
129
+ * - `crypto.randomUUID()` is limited to secure contexts in some browsers;
130
+ * `getRandomValues()` has no such restriction.
131
+ * - `import { randomUUID } from 'crypto'` makes Vite externalize the
132
+ * `crypto` module for browser builds and fail at runtime.
133
+ *
134
+ * Output is the canonical hex form: `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`,
135
+ * where `y` is one of `8/9/a/b` (variant nibble = 10xx).
162
136
  */
163
- function toOwnedArrayBuffer(view) {
164
- const copy = new ArrayBuffer(view.byteLength);
165
- new Uint8Array(copy).set(view);
166
- return copy;
137
+ function generateUuid() {
138
+ const bytes = globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
139
+ bytes[6] = bytes[6] & 15 | 64;
140
+ bytes[8] = bytes[8] & 63 | 128;
141
+ return formatUuid(bytes);
167
142
  }
168
143
  /**
169
- * Resolve any accepted `BinarySource` form into a `WireResource`.
170
- * Async because Blob bytes can only be read asynchronously.
144
+ * Generate an RFC 9562 v7 UUID: the Unix time in milliseconds in the first
145
+ * 48 bits, then random bits. Names made later sort later, which is what an
146
+ * annotation's `/NM` gets when the data names none.
171
147
  */
172
- async function resolveBinarySource(source) {
173
- if (source instanceof Uint8Array) return { bytes: toOwnedArrayBuffer(source) };
174
- if (isBlob(source)) {
175
- const blobName = blobFileName(source);
176
- return {
177
- bytes: await source.arrayBuffer(),
178
- mimeType: source.type || void 0,
179
- ...blobName !== void 0 ? { name: blobName } : {}
180
- };
148
+ function generateUuidV7(now = Date.now()) {
149
+ const bytes = globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
150
+ let time = Math.max(0, Math.floor(now));
151
+ for (let i = 5; i >= 0; i--) {
152
+ bytes[i] = time % 256;
153
+ time = Math.floor(time / 256);
181
154
  }
182
- const { data, mimeType, name } = source;
183
- const inner = data instanceof Uint8Array ? {
184
- bytes: toOwnedArrayBuffer(data),
185
- mimeType: void 0,
186
- name: void 0
187
- } : {
188
- bytes: await data.arrayBuffer(),
189
- mimeType: data.type || void 0,
190
- name: blobFileName(data)
191
- };
192
- const resolvedName = name ?? inner.name;
193
- return {
194
- bytes: inner.bytes,
195
- ...mimeType ?? inner.mimeType ? { mimeType: mimeType ?? inner.mimeType } : {},
196
- ...resolvedName !== void 0 ? { name: resolvedName } : {}
197
- };
155
+ bytes[6] = bytes[6] & 15 | 112;
156
+ bytes[8] = bytes[8] & 63 | 128;
157
+ return formatUuid(bytes);
198
158
  }
199
- /** A browser `File` is a Blob with a `name` — pick it up so attachment
200
- * drafts can pass a `File` directly without repeating the file name. */
201
- function blobFileName(blob) {
202
- const name = blob.name;
203
- return typeof name === "string" && name.length > 0 ? name : void 0;
159
+ function formatUuid(bytes) {
160
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
161
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
204
162
  }
205
163
  //#endregion
206
- //#region src/resource/binaryMetadata.ts
207
- /**
208
- * Detect format (and intrinsic dimensions for rasters) of a binary buffer.
209
- *
210
- * @returns metadata, or `null` for unsupported/corrupt data
211
- */
212
- function sniffBinaryMetadata(buffer) {
213
- if (buffer.byteLength < 4) return null;
214
- const bytes = new Uint8Array(buffer);
215
- if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) return parsePng(buffer);
216
- if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return parseJpeg(buffer);
217
- if (bytes[0] === 37 && bytes[1] === 80 && bytes[2] === 68 && bytes[3] === 70) return { mimeType: "application/pdf" };
218
- return null;
219
- }
220
- function parsePng(buffer) {
221
- if (buffer.byteLength < 24) return null;
222
- const view = new DataView(buffer);
223
- const width = view.getUint32(16);
224
- const height = view.getUint32(20);
225
- if (width === 0 || height === 0) return null;
226
- return {
227
- mimeType: "image/png",
228
- width,
229
- height
230
- };
231
- }
232
- function parseJpeg(buffer) {
233
- const bytes = new Uint8Array(buffer);
234
- let offset = 2;
235
- while (offset + 4 < bytes.byteLength) {
236
- if (bytes[offset] !== 255) return null;
237
- const marker = bytes[offset + 1];
238
- if (marker >= 192 && marker <= 195) {
239
- if (offset + 9 > bytes.byteLength) return null;
240
- const view = new DataView(buffer, offset + 5);
241
- const height = view.getUint16(0);
242
- const width = view.getUint16(2);
243
- if (width === 0 || height === 0) return null;
244
- return {
245
- mimeType: "image/jpeg",
246
- width,
247
- height
248
- };
249
- }
250
- if (marker === 216 || marker === 217) offset += 2;
251
- else if (marker === 255) offset += 1;
252
- else {
253
- const segLen = bytes[offset + 2] << 8 | bytes[offset + 3];
254
- offset += 2 + segLen;
255
- }
256
- }
257
- return null;
164
+ //#region src/annotation/family.ts
165
+ /** The family an annotation of `subtype` belongs to. */
166
+ function familyOfSubtype(subtype) {
167
+ return subtype === "widget" ? "widgets" : "annotations";
258
168
  }
259
169
  //#endregion
260
- //#region src/annotation/kinds/file-attachment/normalize.ts
170
+ //#region src/dto/normalizeAttachmentFileSource.ts
261
171
  /**
262
172
  * Resolve an attachment `file` into its wire halves: metadata into the
263
- * JSON body, bytes into the resource map. Unlike stamps there is NO
173
+ * JSON body, bytes into the resource map. Unlike stamps there is no
264
174
  * format allowlist — attaching arbitrary files is the point — so the
265
- * declared mime type wins (attachment formats cannot be reliably sniffed;
266
- * the writer falls back to `application/octet-stream` when absent).
267
- *
268
- * Shared by the file-attachment annotation draft (below) and the
269
- * document-level `attachments.create` mutation — one vocabulary, one
270
- * normalizer for both homes a file can enter a PDF through.
175
+ * declared mime type wins, then the Blob's; attachment formats cannot be
176
+ * reliably sniffed, so without either the file has no type.
177
+ * Used by the document-level `attachments.create`.
271
178
  */
272
179
  async function normalizeAttachmentFileSource(file, key) {
273
- const resolved = await resolveBinarySource(file.data);
180
+ const resolved = await require_calibrate.resolveBinarySource(file.data instanceof ArrayBuffer ? new Uint8Array(file.data) : file.data);
274
181
  const name = file.name ?? resolved.name;
275
- if (!name) throw new require_derive.EngineError(require_format.EngineErrorCode.InvalidArg, "attachment file requires a name — pass { data, name } or a File whose name is set");
182
+ if (!name) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "attachment file requires a name — pass { data, name } or a File whose name is set");
276
183
  const mimeType = file.mimeType ?? resolved.mimeType;
277
184
  return {
278
185
  wireFile: {
279
- resource: key,
186
+ resources: { file: key },
280
187
  name,
281
188
  ...mimeType !== void 0 ? { mimeType } : {},
282
189
  ...file.description !== void 0 ? { description: file.description } : {}
@@ -288,512 +195,309 @@ async function normalizeAttachmentFileSource(file, key) {
288
195
  }
289
196
  };
290
197
  }
291
- async function normalizeFileAttachmentDraft(draft, allocateKey) {
292
- const { file, ...rest } = draft;
293
- const key = allocateKey();
294
- const { wireFile, resource } = await normalizeAttachmentFileSource(file, key);
295
- return {
296
- wire: {
297
- ...rest,
298
- file: wireFile
299
- },
300
- resources: { [key]: resource }
301
- };
302
- }
303
198
  //#endregion
304
- //#region src/annotation/kinds/stamp/normalize.ts
199
+ //#region src/transfer/annotationImport.ts
305
200
  /**
306
- * Resolve + validate a stamp `source`. Sniffing happens here — before any
307
- * transport — so callers get a fast `InvalidArg` instead of a worker/HTTP
308
- * round-trip. The sniffed mime type overwrites whatever was declared.
201
+ * The facts an import committed: one `annotations.created` per annotation, in
202
+ * bundle order, each with the envelope of its own page and its own id. The
203
+ * cache pins move once, with the last.
309
204
  */
310
- async function resolveStampSource(source) {
311
- const resolved = await resolveBinarySource(source);
312
- const meta = sniffBinaryMetadata(resolved.bytes);
313
- if (!meta) throw new require_derive.EngineError(require_format.EngineErrorCode.InvalidArg, "stamp source must be PNG, JPEG, or single-page PDF bytes (format is detected from the data, not the declared mime type)");
314
- return {
315
- ...resolved,
316
- mimeType: meta.mimeType
317
- };
318
- }
319
- async function normalizeStampDraft(draft, allocateKey) {
320
- const { source, ...rest } = draft;
321
- const resolved = await resolveStampSource(source);
322
- const key = allocateKey();
323
- return {
324
- wire: {
325
- ...rest,
326
- source: { resource: key }
327
- },
328
- resources: { [key]: resolved }
329
- };
330
- }
331
- async function normalizeStampPatch(patch, allocateKey) {
332
- if (patch.source === void 0) {
333
- const { source: _source, ...rest } = patch;
205
+ function annotationImportFacts(result) {
206
+ const last = result.annotations.length - 1;
207
+ return result.annotations.map((annotation, index) => {
208
+ const { page } = annotation.ref;
334
209
  return {
335
- wire: rest,
336
- resources: {}
337
- };
338
- }
339
- const { source, ...rest } = patch;
340
- const resolved = await resolveStampSource(source);
341
- const key = allocateKey();
342
- return {
343
- wire: {
344
- ...rest,
345
- source: { resource: key }
346
- },
347
- resources: { [key]: resolved }
348
- };
349
- }
350
- //#endregion
351
- //#region src/annotation/normalize.ts
352
- function createResourceKeyAllocator() {
353
- let next = 0;
354
- return () => `r${next++}`;
355
- }
356
- async function normalizeAnnotationDraft(draft) {
357
- switch (draft.subtype) {
358
- case "stamp": return normalizeStampDraft(draft, createResourceKeyAllocator());
359
- case "file-attachment": return normalizeFileAttachmentDraft(draft, createResourceKeyAllocator());
360
- default: return {
361
- wire: draft,
362
- resources: {}
363
- };
364
- }
365
- }
366
- async function normalizeAnnotationPatch(patch) {
367
- switch (patch.subtype) {
368
- case "stamp": return normalizeStampPatch(patch, createResourceKeyAllocator());
369
- default: return {
370
- wire: patch,
371
- resources: {}
210
+ page,
211
+ annotation,
212
+ meta: {
213
+ affectedPages: result.meta.affectedPages.filter((affected) => affected.objectNumber === page.objectNumber),
214
+ cacheDelta: index === last ? result.meta.cacheDelta : null,
215
+ opId: result.meta.opId,
216
+ undoable: result.meta.undoable,
217
+ changed: [annotation.ref]
218
+ }
372
219
  };
373
- }
220
+ });
374
221
  }
375
222
  //#endregion
376
- //#region src/annotation/comments.ts
377
- /** Subtypes that never participate in comment threads. `unsupported`
378
- * covers `/Popup` dictionaries (raw code 16) and unreadable foreign
379
- * blobs — neither makes a meaningful comment card. */
380
- const EXCLUDED_SUBTYPES = /* @__PURE__ */ new Set([
381
- "widget",
382
- "link",
383
- "unsupported"
384
- ]);
385
- const REVIEW_STATES = /* @__PURE__ */ new Set([
386
- "accepted",
387
- "rejected",
388
- "cancelled",
389
- "completed",
390
- "none"
391
- ]);
392
- const MARKED_STATES = /* @__PURE__ */ new Set(["marked", "unmarked"]);
393
- const nonEmpty = (v) => typeof v === "string" && v !== "";
223
+ //#region src/transfer/exportSelection.ts
394
224
  /**
395
- * An ISO 32000 §12.5.6.3 state annotation: a text annotation carrying a
396
- * non-empty `/State` or `/StateModel`. Classification never relies on
397
- * flags — producers disagree on how state annotations are flagged.
225
+ * The annotations an export takes, in document order: page order, then each
226
+ * page's annotation order. Besides the selection:
227
+ *
228
+ * - a reply brings its parent, and the parent's, up to the root;
229
+ * - an annotation brings its popup, and a popup brings its parent;
230
+ * - with `include: 'threads'`, the default, every reply below a selected
231
+ * annotation.
232
+ *
233
+ * A reply is on its parent's page (ISO 32000-2 §12.5.6.2, `/IRT`) and a popup
234
+ * on its parent's, so none of this reaches another page. `readPage` returns a
235
+ * page's annotations in `/Annots` order and is called once for each page
236
+ * the selection is on. `pages` is the document's pages in order. A selected
237
+ * page or annotation the document doesn't have is refused with `NotFound`.
398
238
  */
399
- function isStateAnnotation(a) {
400
- return a.subtype === "text" && (nonEmpty(a.state) || nonEmpty(a.stateModel));
401
- }
402
- function buildCommentThreads(annotations, opts = {}) {
403
- const eligible = annotations.filter((a) => !EXCLUDED_SUBTYPES.has(a.subtype));
239
+ function closeExportSelection(selection, pages, readPage) {
240
+ const inDocument = new Set(pages.map(require_format.encodePageKey));
241
+ const read = /* @__PURE__ */ new Map();
404
242
  const byKey = /* @__PURE__ */ new Map();
405
- for (const a of eligible) {
406
- const key = require_derive.refKey(a.ref);
407
- if (!byKey.has(key)) byKey.set(key, a);
408
- if (a.nm) {
409
- const aliasKey = require_derive.refKey({
410
- kind: "nm",
411
- pageObjectNumber: a.pageObjectNumber,
412
- nm: a.nm
413
- });
414
- if (!byKey.has(aliasKey)) byKey.set(aliasKey, a);
415
- }
416
- }
417
- const children = /* @__PURE__ */ new Map();
418
- for (const a of eligible) {
419
- if (!a.inReplyTo) continue;
420
- const parent = byKey.get(require_derive.refKey(a.inReplyTo));
421
- if (!parent) continue;
422
- const parentKey = require_derive.refKey(parent.ref);
423
- const list = children.get(parentKey);
424
- if (list) list.push(a);
425
- else children.set(parentKey, [a]);
426
- }
427
- const visited = /* @__PURE__ */ new Set();
428
- const threads = [];
429
- const walk = (root) => {
430
- const replies = [];
431
- const groupedParts = [];
432
- const states = [];
433
- visited.add(require_derive.refKey(root.ref));
434
- const stack = [...children.get(require_derive.refKey(root.ref)) ?? []];
435
- while (stack.length > 0) {
436
- const a = stack.pop();
437
- const key = require_derive.refKey(a.ref);
438
- if (visited.has(key)) continue;
439
- visited.add(key);
440
- if (isStateAnnotation(a)) states.push(a);
441
- else if (require_derive.classifyRelation(a) === "grouped-subordinate") groupedParts.push(a);
442
- else replies.push(a);
443
- stack.push(...children.get(key) ?? []);
444
- }
445
- replies.sort(chronological);
446
- states.sort(chronological);
447
- threads.push({
448
- root,
449
- pageObjectNumber: root.pageObjectNumber,
450
- replies,
451
- groupedParts,
452
- review: computeReview(states, opts)
453
- });
243
+ const readOnce = (page) => {
244
+ const pageKey = require_format.encodePageKey(page);
245
+ if (read.has(pageKey) || !inDocument.has(pageKey)) return;
246
+ const annotations = readPage(page);
247
+ read.set(pageKey, annotations);
248
+ for (const annotation of annotations) byKey.set(require_format.annotationKey(annotation.ref), annotation);
454
249
  };
455
- for (const a of eligible) if (!a.inReplyTo && !isStateAnnotation(a)) walk(a);
456
- for (const a of eligible) {
457
- if (visited.has(require_derive.refKey(a.ref))) continue;
458
- if (isStateAnnotation(a)) continue;
459
- walk(a);
460
- }
461
- return threads;
462
- }
463
- /** `created ?? modified` ascending; undated last; z-order tiebreak. */
464
- function chronological(a, b) {
465
- const at = a.created ?? a.modified;
466
- const bt = b.created ?? b.modified;
467
- if (at !== null && bt !== null && at !== bt) return at < bt ? -1 : 1;
468
- if (at !== null && bt === null) return -1;
469
- if (at === null && bt !== null) return 1;
470
- return a.index - b.index;
471
- }
472
- function computeReview(states, opts) {
473
- const byReviewer = {};
474
- const markedLatest = {};
475
- let lastChange = null;
476
- for (const a of states) {
477
- const status = toReviewStatus(a);
478
- if (!status) continue;
479
- if (status.stateModel === "marked") {
480
- if (status.by !== null && isNewer(status, markedLatest[status.by])) markedLatest[status.by] = status;
481
- continue;
482
- }
483
- if (status.by !== null && isNewer(status, byReviewer[status.by])) byReviewer[status.by] = status;
484
- if (isNewer(status, lastChange)) lastChange = status;
485
- }
486
- const markedBy = Object.keys(markedLatest).filter((key) => markedLatest[key].state === "marked").sort();
487
- const review = {
488
- byReviewer,
489
- lastChange,
490
- markedBy,
491
- statusRefs: states.map((a) => a.ref)
250
+ const find = (ref) => {
251
+ readOnce(ref.page);
252
+ return byKey.get(require_format.annotationKey(ref));
492
253
  };
493
- if (opts.currentUserId !== void 0) review.mine = byReviewer[opts.currentUserId] ?? null;
494
- return review;
495
- }
496
- /**
497
- * Derive a `ReviewStatus` from one state annotation, or null when nothing
498
- * classifiable can be derived. ISO defaulting happens HERE, not in the
499
- * engine reader: a known model with an absent state means `none` /
500
- * `unmarked`; an absent model infers from a known state; a custom model
501
- * with no state has no derivable status.
502
- */
503
- function toReviewStatus(a) {
504
- if (a.subtype !== "text") return null;
505
- const rawState = nonEmpty(a.state) ? a.state : null;
506
- let stateModel = nonEmpty(a.stateModel) ? a.stateModel : null;
507
- if (stateModel === null && rawState !== null) if (REVIEW_STATES.has(rawState)) stateModel = "review";
508
- else if (MARKED_STATES.has(rawState)) stateModel = "marked";
509
- else return null;
510
- let state = rawState;
511
- if (state === null) if (stateModel === "review") state = "none";
512
- else if (stateModel === "marked") state = "unmarked";
513
- else return null;
514
- if (stateModel === null) return null;
515
- return {
516
- state,
517
- stateModel,
518
- by: a.userId ?? (nonEmpty(a.author) ? a.author : null),
519
- at: a.modified ?? a.created ?? null,
520
- ref: a.ref
254
+ for (const page of selection.pages ?? []) if (!inDocument.has(require_format.encodePageKey(page))) throw new require_format.EngineError(require_format.EngineErrorCode.NotFound, `export: the document has no page ${require_format.encodePageKey(page)}`);
255
+ const everything = !selection.refs && !selection.pages;
256
+ const threads = (selection.include ?? "threads") === "threads";
257
+ for (const page of everything ? pages : selection.pages ?? []) readOnce(page);
258
+ const taken = /* @__PURE__ */ new Set();
259
+ const pending = [];
260
+ const take = (annotation) => {
261
+ if (!annotation) return;
262
+ const key = require_format.annotationKey(annotation.ref);
263
+ if (taken.has(key)) return;
264
+ taken.add(key);
265
+ pending.push(annotation);
521
266
  };
522
- }
523
- /** `a` newer than (or supersedes) `b`. `at` decides; dated beats undated.
524
- * States arrive pre-sorted by `chronological` (date, then z-order), so on
525
- * equal or missing dates the later entry wins via `>=` / `true`. */
526
- function isNewer(a, b) {
527
- if (!b) return true;
528
- if (a.at !== null && b.at !== null) return a.at >= b.at;
529
- if (a.at !== null) return true;
530
- if (b.at !== null) return false;
531
- return true;
267
+ if (everything) for (const annotations of read.values()) annotations.forEach(take);
268
+ else {
269
+ for (const page of selection.pages ?? []) read.get(require_format.encodePageKey(page))?.forEach(take);
270
+ for (const ref of selection.refs ?? []) {
271
+ const annotation = find(ref);
272
+ if (!annotation) throw new require_format.EngineError(require_format.EngineErrorCode.NotFound, `export: the document has no annotation ${require_format.annotationKey(ref)}`);
273
+ take(annotation);
274
+ }
275
+ }
276
+ if (threads) {
277
+ for (const annotation of [...pending]) if (annotation.subtype === "popup" && annotation.parent) take(find(annotation.parent));
278
+ const replies = /* @__PURE__ */ new Map();
279
+ for (const annotations of read.values()) for (const annotation of annotations) {
280
+ const parent = annotation.reply ? find(annotation.reply.to) : void 0;
281
+ if (!parent) continue;
282
+ const key = require_format.annotationKey(parent.ref);
283
+ replies.set(key, [...replies.get(key) ?? [], annotation]);
284
+ }
285
+ for (let i = 0; i < pending.length; i++) replies.get(require_format.annotationKey(pending[i].ref))?.forEach(take);
286
+ }
287
+ for (let i = 0; i < pending.length; i++) {
288
+ const annotation = pending[i];
289
+ if (annotation.reply) take(find(annotation.reply.to));
290
+ if (annotation.popup) take(find(annotation.popup));
291
+ if (annotation.subtype === "popup" && annotation.parent) take(find(annotation.parent));
292
+ }
293
+ const ordered = [];
294
+ for (const page of pages) for (const annotation of read.get(require_format.encodePageKey(page)) ?? []) if (taken.has(require_format.annotationKey(annotation.ref))) ordered.push(annotation);
295
+ return ordered;
532
296
  }
533
297
  //#endregion
534
- //#region src/mutation/AnnotationMutationImpactPolicy.ts
535
- function changesAnnotationList(_kind) {
536
- return true;
537
- }
538
- function shiftsExistingAnnotationIndices(kind) {
539
- return kind === "delete" || kind === "move";
540
- }
541
- function invalidatesWeakIndexRefs(kind, weakStateBefore) {
542
- if (!shiftsExistingAnnotationIndices(kind)) return false;
543
- if (weakStateBefore.kind !== "known") throw new Error(`invalidatesWeakIndexRefs requires a known WeakAnnotationState for ${kind}; got 'unknown'. Caller must scan the page before computing mutation impact.`);
544
- return weakStateBefore.hasAnyWeakAnnotations;
298
+ //#region src/annotation/AnnotationList.ts
299
+ /** Lists of different pages as one, in their order. */
300
+ function concatAnnotationLists(lists) {
301
+ return {
302
+ annotations: lists.flatMap((list) => list.annotations),
303
+ pages: lists.flatMap((list) => list.pages)
304
+ };
545
305
  }
546
306
  //#endregion
547
- //#region src/annotation/appearance.ts
307
+ //#region src/mutation/AnnotationMutationResults.ts
548
308
  /**
549
- * Coordinate tolerance in PDF user-space points. PDF numbers round-trip
550
- * through f32 (~7 significant digits) and client-side space conversions, so
551
- * exact float equality would misclassify no-op writes; 1e-3 pt is far below
552
- * anything visible while far above accumulated f32 drift at page magnitudes.
309
+ * What a delete removed, as its `annotations.deleted` event names it for
310
+ * listeners that didn't make the call: the refs in `meta.changed`, the
311
+ * annotation first.
553
312
  */
554
- const EPSILON = .001;
555
- /**
556
- * Keys that never affect `/AP` on any kind: the discriminator, behavioral
557
- * flags, reply relationships, grouping, and the conversation-plane
558
- * entries (`/Subj` subject line, `/State` + `/StateModel` review status —
559
- * dictionary-only per ISO 32000 §12.5.6.3, never painted).
560
- */
561
- const INERT_KEYS = /* @__PURE__ */ new Set([
562
- "subtype",
563
- "flags",
564
- "inReplyTo",
565
- "replyType",
566
- "groupId",
567
- "subject",
568
- "state",
569
- "stateModel"
570
- ]);
571
- /**
572
- * Kinds whose `/AP` paints `/Contents`. Everywhere else `contents` is popup
573
- * note text — editing a comment must never re-bake (or destroy) the shape's
574
- * appearance.
575
- */
576
- const CONTENTS_PAINTED = /* @__PURE__ */ new Set(["free-text", "redact"]);
577
- /**
578
- * The vertex family's `/EMBD_Metadata/Rotation` is an advisory scalar (the
579
- * vertices are already rotated); it never drives the AP generator, so patches
580
- * touching it are appearance-inert for these kinds.
581
- */
582
- const ADVISORY_ROTATION = /* @__PURE__ */ new Set([
583
- "line",
584
- "polyline",
585
- "polygon",
586
- "ink"
587
- ]);
588
- /**
589
- * The box kinds carry the `/EMBD_Metadata` transform pair (`rotation` +
590
- * `unrotatedRect`), tri-state on writes: omitted fields are PRESERVED, `null`
591
- * (or `0` for rotation) clears. Translation verification therefore compares
592
- * the after-state — `patch.rotation ?? current.rotation` — not the patch keys.
593
- */
594
- const BOX_TRANSFORM = /* @__PURE__ */ new Set([
595
- "square",
596
- "circle",
597
- "free-text",
598
- "stamp"
599
- ]);
313
+ function deletedAnnotationsOf(result) {
314
+ return result.meta.changed;
315
+ }
316
+ //#endregion
317
+ //#region src/annotation/resolve/patchBetween.ts
318
+ /** Exact deep equality: the same value, or arrays and objects whose members are. */
319
+ function sameValue(left, right) {
320
+ if (Object.is(left, right)) return true;
321
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return false;
322
+ if (Array.isArray(left) !== Array.isArray(right)) return false;
323
+ const leftRecord = left;
324
+ const rightRecord = right;
325
+ const names = Object.keys(leftRecord).filter((name) => leftRecord[name] !== void 0);
326
+ const others = Object.keys(rightRecord).filter((name) => rightRecord[name] !== void 0);
327
+ return names.length === others.length && names.every((name) => sameValue(leftRecord[name], rightRecord[name]));
328
+ }
600
329
  /**
601
- * Per-kind absolute-geometry fields (PDF user space) that a rigid translation
602
- * shifts together. A kind absent from this table never takes the translation
603
- * route. `rect` is required in every entry: a translation always moves `/Rect`.
330
+ * What `after` changed from `before`, as `update` takes it: each data field
331
+ * whose value differs, whole. Fields the engine works out, stamps or keeps as
332
+ * found (a drawn kind's `rect`, the dates and authors, the refs it keeps) are
333
+ * left out. Values are compared exactly, so a value rebuilt with the same
334
+ * content is no change. Works in either space: it only compares values by
335
+ * field name.
604
336
  */
605
- const TRANSLATABLE_GEOMETRY = {
606
- square: ["rect", "unrotatedRect"],
607
- circle: ["rect", "unrotatedRect"],
608
- "free-text": [
609
- "rect",
610
- "unrotatedRect",
611
- "calloutLine"
612
- ],
613
- line: ["rect", "linePoints"],
614
- polygon: ["rect", "vertices"],
615
- polyline: ["rect", "vertices"],
616
- ink: ["rect", "inkList"],
617
- highlight: ["rect", "quadPoints"],
618
- underline: ["rect", "quadPoints"],
619
- squiggly: ["rect", "quadPoints"],
620
- strikeout: ["rect", "quadPoints"],
621
- redact: ["rect", "quadPoints"],
622
- caret: ["rect"],
623
- text: ["rect"],
624
- stamp: ["rect", "unrotatedRect"],
625
- "file-attachment": ["rect"],
626
- link: ["rect"]
627
- };
628
- const numEq = (a, b) => Math.abs(a - b) <= EPSILON;
629
- /** Degrees normalized to [0,360); absent reads as 0 (no rotation). */
630
- const normDeg = (v) => {
631
- return typeof v === "number" ? (v % 360 + 360) % 360 : 0;
632
- };
337
+ function annotationPatchBetween(before, after) {
338
+ const patch = {};
339
+ if (before === after) return patch;
340
+ const owners = require_resolveAnnotationDraft.kindFactsOf(after.subtype)?.owners ?? {};
341
+ const was = before;
342
+ const now = after;
343
+ for (const [name, owner] of Object.entries(owners)) {
344
+ if (owner !== "data" || sameValue(was[name], now[name])) continue;
345
+ patch[name] = now[name] === void 0 ? null : now[name];
346
+ }
347
+ return patch;
348
+ }
633
349
  /**
634
- * Semantic equality between a patch value and the current DTO value.
635
- * Numbers compare within {@link EPSILON}; `null` and `undefined` both mean
636
- * "entry absent" (the tri-state clear of an absent entry is a no-op); arrays
637
- * and objects compare structurally.
350
+ * `current` with `patch`'s data fields set, each as a read spells it: the
351
+ * patch as given. No field follows from another here; `applyAnnotationPatch`
352
+ * adds what the engine's rules make follow. Works in either space.
638
353
  */
639
- function semanticEqual(a, b) {
640
- if (a == null || b == null) return a == null && b == null;
641
- if (typeof a === "number" && typeof b === "number") return numEq(a, b);
642
- if (Array.isArray(a) || Array.isArray(b)) {
643
- if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
644
- return a.every((v, i) => semanticEqual(v, b[i]));
645
- }
646
- if (typeof a === "object" && typeof b === "object") {
647
- const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
648
- for (const k of keys) if (!semanticEqual(a[k], b[k])) return false;
649
- return true;
650
- }
651
- return a === b;
354
+ function mergeAnnotationPatch(current, patch) {
355
+ return require_calibrate.applyResolvedPatch(current, patch);
652
356
  }
357
+ //#endregion
358
+ //#region src/pageSpace/pages.ts
653
359
  /**
654
- * Is `next` exactly `cur` shifted by `(dx, dy)`? Walks the value structurally:
655
- * numeric leaves under x-axis keys (`left`/`right`/`x`) must shift by `dx`,
656
- * y-axis keys (`bottom`/`top`/`y`) by `dy`, and any other numeric leaf must be
657
- * unchanged. Arrays walk elementwise (same length required).
360
+ * A page's five boxes in page space: measured from the top-left of the crop
361
+ * box, the visible page, which is therefore `{ x: 0, y: 0, width, height }`.
658
362
  */
659
- function shiftedBy(cur, next, dx, dy) {
660
- if (cur == null || next == null) return cur == null && next == null;
661
- if (Array.isArray(cur) || Array.isArray(next)) {
662
- if (!Array.isArray(cur) || !Array.isArray(next) || cur.length !== next.length) return false;
663
- return cur.every((v, i) => shiftedBy(v, next[i], dx, dy));
664
- }
665
- if (typeof cur === "object" && typeof next === "object") {
666
- const keys = /* @__PURE__ */ new Set([...Object.keys(cur), ...Object.keys(next)]);
667
- for (const k of keys) {
668
- const c = cur[k];
669
- const n = next[k];
670
- if (typeof c === "number" && typeof n === "number") {
671
- if (!numEq(c + (k === "left" || k === "right" || k === "x" ? dx : k === "bottom" || k === "top" || k === "y" ? dy : 0), n)) return false;
672
- } else if (!shiftedBy(c, n, dx, dy)) return false;
673
- }
674
- return true;
675
- }
676
- return cur === next;
363
+ function pageSpaceBoxesOf(boxes) {
364
+ const visible = boxes.crop;
365
+ return {
366
+ media: require_calibrate.pageBoxOf(boxes.media, visible),
367
+ crop: require_calibrate.pageBoxOf(boxes.crop, visible),
368
+ bleed: require_calibrate.pageBoxOf(boxes.bleed, visible),
369
+ trim: require_calibrate.pageBoxOf(boxes.trim, visible),
370
+ art: require_calibrate.pageBoxOf(boxes.art, visible)
371
+ };
677
372
  }
678
373
  /**
679
- * Verify the touched geometry is one rigid translation of the current state.
680
- * Requires `patch.rect` (a translation always moves `/Rect`) with width and
681
- * height preserved; every other geometry field present on the annotation must
682
- * ride along shifted by the same delta — a rect move that leaves `vertices`
683
- * behind is NOT a translation (the dictionary would desync from the pixels).
684
- * For box kinds the `/EMBD_Metadata` transform group is checked as an
685
- * after-state: writes are tri-state (omitted preserves, null/0 clears), so an
686
- * omitted rotation keeps the current one — but a preserved-yet-unshifted
687
- * `unrotatedRect` still fails the congruence check via the field loop.
374
+ * A page's layout in page space. Its actions may go to other pages, so each
375
+ * destination is measured with `boxOf`, the box of the page it goes to.
688
376
  */
689
- function isRigidTranslation(cur, pat, subtype, geometryKeys) {
690
- const curRect = cur.rect;
691
- const patRect = pat.rect;
692
- if (!curRect || !patRect) return false;
693
- const dx = patRect.left - curRect.left;
694
- const dy = patRect.bottom - curRect.bottom;
695
- if (!numEq(patRect.right - patRect.left, curRect.right - curRect.left)) return false;
696
- if (!numEq(patRect.top - patRect.bottom, curRect.top - curRect.bottom)) return false;
697
- if (BOX_TRANSFORM.has(subtype)) {
698
- const rotAfter = pat.rotation === void 0 ? cur.rotation : pat.rotation;
699
- if (!numEq(normDeg(cur.rotation), normDeg(rotAfter))) return false;
700
- }
701
- for (const key of geometryKeys) {
702
- if (key === "rect") continue;
703
- const c = cur[key];
704
- const p = pat[key];
705
- if (c == null && p == null) continue;
706
- if (c == null || p == null) return false;
707
- if (!shiftedBy(c, p, dx, dy)) return false;
708
- }
709
- return true;
377
+ function pageLayoutOf(layout, boxOf) {
378
+ const { actions, ...rest } = layout;
379
+ return {
380
+ ...rest,
381
+ boxes: pageSpaceBoxesOf(layout.boxes),
382
+ ...actions ? { actions: require_calibrate.mapPageActions(actions, (destination) => require_calibrate.pageDestinationOf(destination, boxOf)) } : {}
383
+ };
384
+ }
385
+ /** Every page's visible box, by page, from the pages' layouts. */
386
+ function visibleBoxesOf(pages) {
387
+ const boxes = new Map(pages.map((page) => [page.ref.objectNumber, page.boxes.crop]));
388
+ return (page) => {
389
+ const box = boxes.get(page.objectNumber);
390
+ if (!box) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `No page ${page.objectNumber} in this document`);
391
+ return box;
392
+ };
393
+ }
394
+ /** The document's pages in page space. */
395
+ function pageListOf(snapshot) {
396
+ const boxOf = visibleBoxesOf(snapshot.pages);
397
+ return {
398
+ ...snapshot,
399
+ pages: snapshot.pages.map((page) => pageLayoutOf(page, boxOf))
400
+ };
401
+ }
402
+ //#endregion
403
+ //#region src/pageSpace/rendering.ts
404
+ /** A page-space render target in the file's coordinates. `visible` is the page's visible box. */
405
+ function pdfRenderTargetOf(target, visible) {
406
+ return target.kind === "rect" ? {
407
+ ...target,
408
+ rect: require_calibrate.pdfRectOf(target.rect, visible)
409
+ } : target;
710
410
  }
711
411
  /**
712
- * Classify an update patch against the annotation's current DTO.
713
- *
714
- * 1. Value-diff: drop keys whose value semantically equals the current one,
715
- * plus the always-inert metadata keys (and per-kind inert keys: `contents`
716
- * where it isn't painted, advisory `rotation` on the vertex family).
717
- * Nothing left → `'inert'`.
718
- * 2. If every remaining key is translatable geometry for this kind AND the
719
- * values are one rigid translation → `'translation'`.
720
- * 3. Anything else — style, text, unknown keys, unknown kinds → `'regenerate'`.
412
+ * Where each rendered appearance goes, in page space: raster or encoded, each
413
+ * placed by its `rect`. `visible` is the page's visible box.
721
414
  */
722
- function appearanceImpactOf(current, patch) {
723
- if (patch.subtype !== current.subtype) return "regenerate";
724
- const cur = current;
725
- const pat = patch;
726
- const subtype = patch.subtype;
727
- const caption = pat.caption === void 0 ? cur.caption : pat.caption;
728
- const contentsPainted = CONTENTS_PAINTED.has(subtype) || [
729
- "line",
730
- "polygon",
731
- "polyline"
732
- ].includes(subtype) && !!caption?.enabled;
733
- const touched = [];
734
- for (const [key, value] of Object.entries(pat)) {
735
- if (value === void 0 || INERT_KEYS.has(key)) continue;
736
- if (key === "contents" && !contentsPainted) continue;
737
- if (key === "rotation" && ADVISORY_ROTATION.has(subtype)) continue;
738
- if (!semanticEqual(value, cur[key])) touched.push(key);
739
- }
740
- if (touched.length === 0) return "inert";
741
- const baseGeometryKeys = TRANSLATABLE_GEOMETRY[subtype];
742
- const geometryKeys = baseGeometryKeys && (subtype === "polygon" || subtype === "polyline") && cur.caption?.center ? [...baseGeometryKeys, "caption"] : baseGeometryKeys;
743
- if (!geometryKeys) return "regenerate";
744
- if (!touched.every((k) => geometryKeys.includes(k))) return "regenerate";
745
- return isRigidTranslation(cur, pat, subtype, geometryKeys) ? "translation" : "regenerate";
415
+ function pageAppearancesOf(appearances, visible) {
416
+ return appearances.map((appearance) => ({
417
+ ...appearance,
418
+ rect: require_calibrate.pageBoxOf(appearance.rect, visible)
419
+ }));
746
420
  }
747
421
  //#endregion
748
- //#region src/identity/FormFieldRef.ts
422
+ //#region src/pageSpace/forms.ts
423
+ /** A page-space widget placement in the file's coordinates, measured on its page. */
424
+ function pdfWidgetPlacementOf(placement, boxOf) {
425
+ const { actions, ...rest } = placement;
426
+ const visible = boxOf(placement.page);
427
+ return {
428
+ ...rest,
429
+ ...placement.rect ? { rect: require_calibrate.pdfRectOf(placement.rect, visible) } : {},
430
+ ...placement.box ? { box: require_calibrate.pdfRectOf(placement.box, visible) } : {},
431
+ ...actions ? { actions: require_calibrate.mapTriggerActions(actions, (destination) => require_calibrate.pdfDestinationOf(destination, boxOf)) } : {}
432
+ };
433
+ }
434
+ /** A field draft in the file's coordinates: each widget measured on its own page. */
435
+ function pdfFormFieldDraftOf(draft, boxOf) {
436
+ if (!draft.widgets) return draft;
437
+ return {
438
+ ...draft,
439
+ widgets: draft.widgets.map((widget) => pdfWidgetPlacementOf(widget, boxOf))
440
+ };
441
+ }
442
+ /** A field's actions in page space, each destination measured on its page. */
443
+ function pageFieldActionsOf(actions, boxOf) {
444
+ return require_calibrate.mapFieldActions(actions, (destination) => require_calibrate.pageDestinationOf(destination, boxOf));
445
+ }
446
+ /** A form field in page space: each destination of its actions on the page it goes to. */
447
+ function pageFormFieldOf(field, boxOf) {
448
+ if (!field.actions) return field;
449
+ return {
450
+ ...field,
451
+ actions: pageFieldActionsOf(field.actions, boxOf)
452
+ };
453
+ }
454
+ /** A widget row in page space, measured on its page. */
455
+ function pageWidgetOf(widget, boxOf) {
456
+ return require_calibrate.pageAnnotationOf(widget, boxOf(widget.page), boxOf);
457
+ }
458
+ /** A form in page space: its fields, and each widget row on its page. */
459
+ function pageFormSnapshotOf(snapshot, boxOf) {
460
+ return {
461
+ ...snapshot,
462
+ fields: snapshot.fields.map((field) => pageFormFieldOf(field, boxOf)),
463
+ widgets: snapshot.widgets.map((widget) => pageWidgetOf(widget, boxOf))
464
+ };
465
+ }
466
+ //#endregion
467
+ //#region src/forms/draft.ts
749
468
  /**
750
- * URL-safe encoding of a `FormFieldRef`, used by the cloud HTTP surface as
751
- * the `:fieldKey` route parameter. Decoded by the server back into a
752
- * `FormFieldRef` via `decodeFieldRefKey`. Mirrors the annotation plane's
753
- * `encodeStableIdKey` (`obj:42` / `nm:…`) so the two member-key syntaxes
754
- * read the same on the wire:
755
- *
756
- * `{ kind: 'objectNumber', fieldObjectNumber: 12 }` -> `'obj:12'`
757
- * `{ kind: 'fqn', name: 'billing.name' }` -> `'fqn:billing.name'`
758
- *
759
- * The caller is responsible for `encodeURIComponent`-ing the result before
760
- * splicing it into a URL path; the `wirePaths.layerFormField*` builders
761
- * already do that. FQNs are opaque strings and may contain anything; the
762
- * `fqn:` prefix keeps them unambiguous from numeric ids.
469
+ * Whether creating a field from `draft` takes `doc.forms.script`: a script
470
+ * among its actions, or a widget action that holds JavaScript, a submit or a
471
+ * link.
763
472
  */
764
- function encodeFieldRefKey(ref) {
765
- if (ref.kind === "objectNumber") {
766
- if (!Number.isInteger(ref.fieldObjectNumber) || ref.fieldObjectNumber <= 0) throw new RangeError(`encodeFieldRefKey: fieldObjectNumber must be a positive integer, got ${ref.fieldObjectNumber}`);
767
- return `obj:${ref.fieldObjectNumber}`;
768
- }
769
- return `fqn:${ref.name}`;
473
+ function draftWritesScripts(draft) {
474
+ return require_value.writesScripts(draft.actions) || (draft.widgets ?? []).some((placement) => require_value.writesScripts(placement.actions));
770
475
  }
476
+ //#endregion
477
+ //#region src/forms/placement.ts
771
478
  /**
772
- * Inverse of `encodeFieldRefKey`. Returns `null` for malformed input so the
773
- * server can answer 400 InvalidArg with a useful message instead of
774
- * throwing. The input is the already-`decodeURIComponent`-ed segment from
775
- * the route path. Empty FQNs are rejected: `fqn:` with no suffix is not a
776
- * valid identity.
479
+ * A placement as the file holds it: `/Rect`, where the widget stands, and
480
+ * its turn. A `box` given is turned into the rect it stands in, as a box
481
+ * kind's create turns it; without one, `rect` is that place.
482
+ *
483
+ * Refused with `InvalidArg`: a placement with neither, and a turn that
484
+ * isn't a quarter turn (a widget's contents turn only so, `/MK /R`).
777
485
  */
778
- function decodeFieldRefKey(key) {
779
- if (key.startsWith("obj:")) {
780
- const rest = key.slice(4);
781
- const n = Number.parseInt(rest, 10);
782
- if (!Number.isInteger(n) || n <= 0 || String(n) !== rest) return null;
783
- return {
784
- kind: "objectNumber",
785
- fieldObjectNumber: n
786
- };
787
- }
788
- if (key.startsWith("fqn:")) {
789
- const name = key.slice(4);
790
- if (name.length === 0) return null;
791
- return {
792
- kind: "fqn",
793
- name
794
- };
795
- }
796
- return null;
486
+ function placedWidgetOf(placement) {
487
+ const { box, rect, rotation, ...rest } = placement;
488
+ if (rotation !== void 0 && ![
489
+ 0,
490
+ 90,
491
+ 180,
492
+ 270
493
+ ].includes(rotation)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, `a widget turns by a quarter turn (0, 90, 180 or 270), not ${rotation}`, { details: { field: "rotation" } });
494
+ const place = box ? require_resolveAnnotationDraft.pdfRectTurnedBounds(require_resolveAnnotationDraft.normalizePdfRect(box), rotation ?? 0) : rect;
495
+ if (!place) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "a widget's placement needs its `rect` or its `box`", { details: { field: "rect" } });
496
+ return {
497
+ ...rest,
498
+ rect: place,
499
+ ...rotation !== void 0 ? { rotation } : {}
500
+ };
797
501
  }
798
502
  //#endregion
799
503
  //#region src/signature/analysis/pdf-value.ts
@@ -817,7 +521,7 @@ function parsePdfValue(text) {
817
521
  var Parser = class {
818
522
  constructor(text) {
819
523
  this.text = text;
820
- require_derive._defineProperty(this, "pos", 0);
524
+ require_format._defineProperty(this, "pos", 0);
821
525
  }
822
526
  atEnd() {
823
527
  return this.pos >= this.text.length;
@@ -1161,7 +865,7 @@ const ACROFORM_KEYS = /* @__PURE__ */ new Set([
1161
865
  * are allowed"): everything but the field's identity and its place in
1162
866
  * the tree. v1/08 /V+/AP, 09 /Ff, 10 /Rect, 11 /F, 18 /TU, 21 /DA; 13 /T
1163
867
  * forbidden. /AA (v1/12) and /A are accepted by Acrobat and deliberately
1164
- * NOT listed: an action on a signed form is executable content.
868
+ * not listed: an action on a signed form is executable content.
1165
869
  *
1166
870
  * fill (a P=2 certification): value and appearance. v1/26 /V+/AP, v3/64
1167
871
  * /Ff (ReadOnly added), 65 /DA, 68 /AP stream; v3/66 /TU, 67 /F, v2/50
@@ -1216,7 +920,7 @@ const WIDGET_KEYS_BY_LEVEL = {
1216
920
  ])
1217
921
  };
1218
922
  /**
1219
- * Signing an EXISTING field: its value, appearance and ReadOnly at any
923
+ * Signing an existing field: its value, appearance and ReadOnly at any
1220
924
  * level; after an approval signature also its tooltip and a /Lock installed
1221
925
  * with the signature (v1/18, 19: both valid; under P=2 the combination is
1222
926
  * rejected, v3/69, so neither is allowed at `fill`).
@@ -1249,7 +953,7 @@ function signedFieldKeysFor(level) {
1249
953
  return level === "annotate" ? SIGNED_FIELD_KEYS_BY_LEVEL.annotate : SIGNED_FIELD_KEYS_BY_LEVEL.fill;
1250
954
  }
1251
955
  /**
1252
- * Rules whose permitted findings do not make a revision a CHANGE of the
956
+ * Rules whose permitted findings do not make a revision a change of the
1253
957
  * document: an object written again with the sealed value, an object nobody
1254
958
  * references, a cross-reference container, the trailer's own bookkeeping.
1255
959
  * A window with nothing else is `unchanged` (Acrobat: "not modified").
@@ -1260,7 +964,7 @@ const NON_EFFECTIVE_RULES = /* @__PURE__ */ new Set([
1260
964
  "xref-container",
1261
965
  "trailer"
1262
966
  ]);
1263
- /** The level a step runs at, and the locks it enforces: what the OLDER revision's signatures established. */
967
+ /** The level a step runs at, and the locks it enforces: what the older revision's signatures established. */
1264
968
  function restrictionsOf(before) {
1265
969
  const protection = require_resolver.deriveProtection(before.signatures);
1266
970
  return {
@@ -1270,7 +974,7 @@ function restrictionsOf(before) {
1270
974
  }
1271
975
  /**
1272
976
  * Judge one pairwise step. Every reference to a changed object, in the
1273
- * older AND the newer revision, must be claimed by a rule that inspected
977
+ * older and the newer revision, must be claimed by a rule that inspected
1274
978
  * that use; an unclaimed reference is `unexplained` and forbidden. A
1275
979
  * truncated value is `incomplete`, never permitted. Pure: JSON in, JSON
1276
980
  * out, the same code in the browser worker, on the server and in tests.
@@ -1377,7 +1081,7 @@ function indexRoles(s) {
1377
1081
  for (const w of f.widgets) widgetToField.set(w, f.objectNumber);
1378
1082
  }
1379
1083
  const signedFields = /* @__PURE__ */ new Set();
1380
- for (const sig of s.signatures) if (sig.signed && sig.field.kind === "objectNumber") signedFields.add(sig.field.fieldObjectNumber);
1084
+ for (const sig of s.signatures) if (sig.signed && sig.field.kind === "objectNumber") signedFields.add(sig.field.objectNumber);
1381
1085
  return {
1382
1086
  fieldByObj,
1383
1087
  widgetToField,
@@ -1394,12 +1098,12 @@ var StepContext = class {
1394
1098
  this.input = input;
1395
1099
  this.level = level;
1396
1100
  this.locks = locks;
1397
- require_derive._defineProperty(this, "findings", []);
1398
- require_derive._defineProperty(this, "byNum", /* @__PURE__ */ new Map());
1399
- require_derive._defineProperty(this, "before", void 0);
1400
- require_derive._defineProperty(this, "after", void 0);
1401
- require_derive._defineProperty(this, "claims", /* @__PURE__ */ new Map());
1402
- require_derive._defineProperty(this, "locked", /* @__PURE__ */ new Set());
1101
+ require_format._defineProperty(this, "findings", []);
1102
+ require_format._defineProperty(this, "byNum", /* @__PURE__ */ new Map());
1103
+ require_format._defineProperty(this, "before", void 0);
1104
+ require_format._defineProperty(this, "after", void 0);
1105
+ require_format._defineProperty(this, "claims", /* @__PURE__ */ new Map());
1106
+ require_format._defineProperty(this, "locked", /* @__PURE__ */ new Set());
1403
1107
  for (const c of input.changes) this.byNum.set(c.objectNumber, c);
1404
1108
  this.before = indexRoles(input.before);
1405
1109
  this.after = indexRoles(input.after);
@@ -1498,9 +1202,9 @@ var StepContext = class {
1498
1202
  const out = [];
1499
1203
  for (const sig of this.input.after.signatures) {
1500
1204
  if (!sig.signed || sig.field.kind !== "objectNumber") continue;
1501
- if (this.before.signedFields.has(sig.field.fieldObjectNumber)) continue;
1205
+ if (this.before.signedFields.has(sig.field.objectNumber)) continue;
1502
1206
  out.push({
1503
- fieldObjectNumber: sig.field.fieldObjectNumber,
1207
+ fieldObjectNumber: sig.field.objectNumber,
1504
1208
  sig
1505
1209
  });
1506
1210
  }
@@ -1977,7 +1681,7 @@ function ruleUnexplained(ctx) {
1977
1681
  //#endregion
1978
1682
  //#region src/signature/analysis/restrictions.ts
1979
1683
  /**
1980
- * The restrictions a window is judged under, read from the SEALED
1684
+ * The restrictions a window is judged under, read from the sealed
1981
1685
  * revision's structure (what that revision declared, not what the final
1982
1686
  * file says).
1983
1687
  *
@@ -1987,7 +1691,7 @@ function ruleUnexplained(ctx) {
1987
1691
  * Every certification's level, own or earlier, applies (inherited:
1988
1692
  * conservative until observed otherwise), as does a /Lock's /P.
1989
1693
  * - no `judged` (a revision-anchored analysis): every signed signature's
1990
- * locks and levels, as before.
1694
+ * locks and levels apply.
1991
1695
  */
1992
1696
  function restrictionsFor(before, judged) {
1993
1697
  const anchors = [];
@@ -2073,9 +1777,9 @@ var EdgeResolver = class {
2073
1777
  this.read = read;
2074
1778
  this.anchors = anchors;
2075
1779
  this.budget = budget;
2076
- require_derive._defineProperty(this, "memo", /* @__PURE__ */ new Map());
2077
- require_derive._defineProperty(this, "reads", 0);
2078
- require_derive._defineProperty(this, "exhausted", false);
1780
+ require_format._defineProperty(this, "memo", /* @__PURE__ */ new Map());
1781
+ require_format._defineProperty(this, "reads", 0);
1782
+ require_format._defineProperty(this, "exhausted", false);
2079
1783
  }
2080
1784
  /** Anchored edges of `objectNumber`, or `incomplete` when a budget was exceeded. */
2081
1785
  resolve(objectNumber) {
@@ -2126,199 +1830,18 @@ var EdgeResolver = class {
2126
1830
  }
2127
1831
  };
2128
1832
  //#endregion
2129
- //#region src/search/literal.ts
2130
- /**
2131
- * The fold flavor a literal query needs. Haystack and needle MUST be
2132
- * folded with the same options; corpus caches key their pre-folded text
2133
- * on this (the default `{}` fold is the persistable one — non-default
2134
- * queries fold the original page text at query time).
2135
- */
2136
- function foldOptionsFor(query) {
1833
+ //#region src/search/types.ts
1834
+ /** The query part of a request (or of any object that carries one): its text and flags, set flags only. */
1835
+ function searchQueryOf(query) {
2137
1836
  return {
2138
- keepCase: !!query.matchCase,
2139
- keepMarks: !!query.matchDiacritics,
2140
- dropWhitespace: !!query.ignoreWhitespace
1837
+ text: query.text,
1838
+ ...query.regex ? { regex: true } : {},
1839
+ ...query.matchCase ? { matchCase: true } : {},
1840
+ ...query.wholeWord ? { wholeWord: true } : {},
1841
+ ...query.matchDiacritics ? { matchDiacritics: true } : {},
1842
+ ...query.ignoreWhitespace ? { ignoreWhitespace: true } : {}
2141
1843
  };
2142
1844
  }
2143
- const WORD_UNIT = /[\p{L}\p{N}_]/u;
2144
- const MARK = /\p{M}/u;
2145
- /** Whether the code point ENDING at `index` (exclusive) is a word char. */
2146
- function wordBefore(text, index) {
2147
- if (index <= 0) return false;
2148
- const unit = text.charCodeAt(index - 1);
2149
- const start = unit >= 56320 && unit <= 57343 && index >= 2 ? index - 2 : index - 1;
2150
- return WORD_UNIT.test(String.fromCodePoint(text.codePointAt(start)));
2151
- }
2152
- /** Whether the code point starting at `index` is a word char. */
2153
- function wordAt(text, index) {
2154
- if (index >= text.length) return false;
2155
- return WORD_UNIT.test(String.fromCodePoint(text.codePointAt(index)));
2156
- }
2157
- /**
2158
- * Whether the folded hit at `at` sits on word boundaries. Checked on the
2159
- * folded plane, except under `ignoreWhitespace`: dropping whitespace glues
2160
- * neighbouring words together ("i n v o i c e 42" folds to "invoice42"), so there the
2161
- * boundaries are read off the ORIGINAL text around the mapped range.
2162
- */
2163
- function isWholeWordHit(haystack, at, needleLength, query) {
2164
- if (!query.ignoreWhitespace) return !wordBefore(haystack.folded, at) && !wordAt(haystack.folded, at + needleLength);
2165
- const { start, length } = require_derive.toOriginalRange(haystack, at, needleLength);
2166
- return !wordBefore(haystack.original, start) && !wordAt(haystack.original, start + length);
2167
- }
2168
- /**
2169
- * All non-overlapping literal matches, in original code-unit space.
2170
- * `haystack` must have been folded with `foldOptionsFor(query)`.
2171
- */
2172
- function matchLiteral(haystack, query) {
2173
- const needle = require_derive.foldText(query.text, foldOptionsFor(query)).folded;
2174
- if (needle.trim().length === 0) return [];
2175
- const out = [];
2176
- let from = 0;
2177
- while (from <= haystack.folded.length - needle.length) {
2178
- const at = haystack.folded.indexOf(needle, from);
2179
- if (at < 0) break;
2180
- if (query.wholeWord && !isWholeWordHit(haystack, at, needle.length, query)) {
2181
- from = at + 1;
2182
- continue;
2183
- }
2184
- if (query.matchDiacritics && at + needle.length < haystack.folded.length) {
2185
- const after = haystack.folded.codePointAt(at + needle.length);
2186
- if (MARK.test(String.fromCodePoint(after))) {
2187
- from = at + 1;
2188
- continue;
2189
- }
2190
- }
2191
- out.push(require_derive.toOriginalRange(haystack, at, needle.length));
2192
- from = at + needle.length;
2193
- }
2194
- return out;
2195
- }
2196
- //#endregion
2197
- //#region src/search/regex.ts
2198
- /**
2199
- * The portable search-regex dialect: JavaScript `u`-mode syntax MINUS the
2200
- * features RE2 refuses (backreferences, lookaround). The local engine
2201
- * executes patterns with JS `RegExp`; the server executes the SAME
2202
- * pattern with RE2, whose linear-time guarantee is the ReDoS defense.
2203
- * Restricting both sides to the common subset keeps one query string
2204
- * valid — and equally powerful — everywhere.
2205
- */
2206
- const SEARCH_REGEX_MAX_LENGTH = 512;
2207
- /**
2208
- * Validate a pattern against the dialect. Cheap and dependency-free —
2209
- * viewers call it on keystroke for early feedback; engines re-validate
2210
- * and reject bad patterns with `InvalidArg`.
2211
- */
2212
- function validateSearchRegex(pattern) {
2213
- if (pattern.length === 0) return {
2214
- ok: false,
2215
- issue: "empty",
2216
- message: "Pattern is empty."
2217
- };
2218
- if (pattern.length > 512) return {
2219
- ok: false,
2220
- issue: "too-long",
2221
- message: `Pattern exceeds 512 characters.`
2222
- };
2223
- let inClass = false;
2224
- for (let i = 0; i < pattern.length; i++) {
2225
- const ch = pattern[i];
2226
- if (ch === "\\") {
2227
- const next = pattern[i + 1] ?? "";
2228
- if (!inClass && next >= "1" && next <= "9") return {
2229
- ok: false,
2230
- issue: "backreference",
2231
- message: `Backreference \\${next} is not supported.`
2232
- };
2233
- if (!inClass && next === "k" && pattern[i + 2] === "<") return {
2234
- ok: false,
2235
- issue: "backreference",
2236
- message: "Named backreferences (\\k<…>) are not supported."
2237
- };
2238
- i++;
2239
- continue;
2240
- }
2241
- if (inClass) {
2242
- if (ch === "]") inClass = false;
2243
- continue;
2244
- }
2245
- if (ch === "[") {
2246
- inClass = true;
2247
- continue;
2248
- }
2249
- if (ch === "(" && pattern[i + 1] === "?") {
2250
- const tag = pattern[i + 2] ?? "";
2251
- if (tag === "=" || tag === "!") return {
2252
- ok: false,
2253
- issue: "lookaround",
2254
- message: "Lookahead ((?=…), (?!…)) is not supported."
2255
- };
2256
- if (tag === "<" && (pattern[i + 3] === "=" || pattern[i + 3] === "!")) return {
2257
- ok: false,
2258
- issue: "lookaround",
2259
- message: "Lookbehind ((?<=…), (?<!…)) is not supported."
2260
- };
2261
- }
2262
- }
2263
- try {
2264
- new RegExp(pattern, "mu");
2265
- } catch (error) {
2266
- return {
2267
- ok: false,
2268
- issue: "syntax",
2269
- message: error.message
2270
- };
2271
- }
2272
- return { ok: true };
2273
- }
2274
- function validateSearchQuery(query) {
2275
- if (!query.regex) return { ok: true };
2276
- if (query.matchDiacritics) return {
2277
- ok: false,
2278
- issue: "diacritics-with-regex",
2279
- message: "Diacritic-sensitive matching is not available for regex patterns (regex runs on the raw text plane)."
2280
- };
2281
- if (query.ignoreWhitespace) return {
2282
- ok: false,
2283
- issue: "ignore-whitespace-with-regex",
2284
- message: "Whitespace-insensitive matching is not available for regex patterns (regex runs on the raw text plane; use \\s* in the pattern)."
2285
- };
2286
- return validateSearchRegex(query.text);
2287
- }
2288
- /**
2289
- * All matches of a dialect-valid pattern over the RAW page text (regex
2290
- * does not run against folded text — case handling is the `i` flag, and
2291
- * diacritic folding never applies). `wholeWord` is a boundary POST-FILTER
2292
- * using the same Unicode word test as the literal path — never `\b`,
2293
- * whose ASCII word set would give the toggle different semantics in regex
2294
- * mode ("caf" whole-word matching inside "café"). Filtering after the
2295
- * match also survives the move to RE2 server-side. Zero-length matches
2296
- * are skipped: search UI cannot highlight nothing, and skipping them is
2297
- * also the infinite-loop guard for patterns like `a*`.
2298
- */
2299
- function matchRegex(text, query) {
2300
- const valid = validateSearchQuery({
2301
- ...query,
2302
- regex: true
2303
- });
2304
- if (!valid.ok) throw new Error(`Invalid search query (${valid.issue}): ${valid.message}`);
2305
- const re = new RegExp(query.text, query.matchCase ? "gmu" : "gimu");
2306
- const out = [];
2307
- let m;
2308
- while ((m = re.exec(text)) !== null) {
2309
- if (m[0].length === 0) {
2310
- const unit = text.charCodeAt(re.lastIndex);
2311
- re.lastIndex += unit >= 55296 && unit <= 56319 ? 2 : 1;
2312
- continue;
2313
- }
2314
- if (query.wholeWord && (wordBefore(text, m.index) || wordAt(text, m.index + m[0].length))) continue;
2315
- out.push({
2316
- start: m.index,
2317
- length: m[0].length
2318
- });
2319
- }
2320
- return out;
2321
- }
2322
1845
  //#endregion
2323
1846
  //#region src/search/matcher.ts
2324
1847
  /**
@@ -2329,8 +1852,8 @@ function matchRegex(text, query) {
2329
1852
  * here.
2330
1853
  */
2331
1854
  function matchPageText(text, query) {
2332
- if (query.regex) return matchRegex(text, query);
2333
- return matchLiteral(require_derive.foldText(text, foldOptionsFor(query)), query);
1855
+ if (query.regex) return require_viewport.matchRegex(text, query);
1856
+ return require_viewport.matchLiteral(require_calibrate.foldText(text, require_viewport.foldOptionsFor(query)), query);
2334
1857
  }
2335
1858
  //#endregion
2336
1859
  //#region src/search/snippet.ts
@@ -2343,13 +1866,13 @@ const isLowSurrogate = (text, index) => {
2343
1866
  return unit >= 56320 && unit <= 57343;
2344
1867
  };
2345
1868
  /**
2346
- * The `'full'`-mode excerpt around one match: up to `context` code units
2347
- * of page text on each side, trimmed to a whitespace boundary when one
1869
+ * The excerpt around one match (`snippets: true`): up to `context` code
1870
+ * units of page text on each side, trimmed to a whitespace boundary when one
2348
1871
  * exists inside the window (so snippets start and end on whole words),
2349
- * never splitting a surrogate pair, whitespace flattened 1:1.
1872
+ * never splitting a surrogate pair, whitespace flattened to spaces.
2350
1873
  */
2351
1874
  function buildSnippet(text, range, context = 48) {
2352
- const matchEnd = range.start + range.length;
1875
+ const matchEnd = range.start + range.count;
2353
1876
  let start = Math.max(0, range.start - context);
2354
1877
  if (start > 0) {
2355
1878
  for (let i = start; i < range.start; i++) if (WHITESPACE.test(text[i])) {
@@ -2366,484 +1889,31 @@ function buildSnippet(text, range, context = 48) {
2366
1889
  }
2367
1890
  if (isLowSurrogate(text, end)) end--;
2368
1891
  }
1892
+ const flat = (from, to) => text.slice(from, to).replace(WHITESPACE_ALL, " ");
2369
1893
  return {
2370
- text: text.slice(start, end).replace(WHITESPACE_ALL, " "),
2371
- matchStart: range.start - start,
2372
- matchLength: range.length
1894
+ before: flat(start, range.start),
1895
+ match: flat(range.start, matchEnd),
1896
+ after: flat(matchEnd, end)
2373
1897
  };
2374
1898
  }
2375
1899
  //#endregion
2376
- //#region src/text/layout.ts
2377
- const FLAG_EMPTY = 2;
2378
- const isBoundary = (flags) => (flags & 3) !== 0;
2379
- const isEmpty = (flags) => (flags & FLAG_EMPTY) !== 0;
2380
- const CHAR_DISTANCE_FACTOR = 2.5;
2381
- const FONT_SIZE_RATIO_THRESHOLD = 1.5;
2382
- const VERTICAL_OVERLAP_THRESHOLD = .8;
2383
- const LINE_OVERLAP_THRESHOLD = .5;
2384
- /** Baseline directions within ~0.5° share a frame. */
2385
- const FRAME_DOT_TOLERANCE = Math.cos(.0087);
2386
- const EDGE_EPSILON = 1e-6;
2387
- const IDENTITY_FRAME = {
2388
- baseline: {
2389
- x: 1,
2390
- y: 0
2391
- },
2392
- ascent: {
2393
- x: 0,
2394
- y: 1
2395
- }
2396
- };
2397
- const toFrame = (f, p) => ({
2398
- x: p.x * f.baseline.x + p.y * f.baseline.y,
2399
- y: p.x * f.ascent.x + p.y * f.ascent.y
2400
- });
2401
- const fromFrame = (f, p) => ({
2402
- x: f.baseline.x * p.x + f.ascent.x * p.y,
2403
- y: f.baseline.y * p.x + f.ascent.y * p.y
2404
- });
2405
- const ZERO_RECT = {
2406
- left: 0,
2407
- bottom: 0,
2408
- right: 0,
2409
- top: 0
2410
- };
2411
- /** Frame-local AABB of a page-space cell (exact under the frame's rotation;
2412
- * shear residue is the documented in-frame envelope). */
2413
- function frameBoxOfQuad(f, q) {
2414
- const pts = [
2415
- q.p1,
2416
- q.p2,
2417
- q.p3,
2418
- q.p4
2419
- ].map((p) => toFrame(f, p));
2420
- const xs = pts.map((p) => p.x);
2421
- const ys = pts.map((p) => p.y);
2422
- return {
2423
- left: Math.min(...xs),
2424
- bottom: Math.min(...ys),
2425
- right: Math.max(...xs),
2426
- top: Math.max(...ys)
2427
- };
2428
- }
2429
- /** Find-or-create the frame for a rotated run, keyed by the baseline
2430
- * direction + ascent handedness of its first classifiable glyph's edges. */
2431
- function frameForRun(frames, run) {
2432
- const seed = run.glyphs.find((g) => !isEmpty(g.flags));
2433
- if (!seed) return 0;
2434
- const q = seed.looseQuad;
2435
- const bx = q.p2.x - q.p1.x;
2436
- const by = q.p2.y - q.p1.y;
2437
- const len = Math.hypot(bx, by);
2438
- if (len <= EDGE_EPSILON) return 0;
2439
- const baseline = {
2440
- x: bx / len,
2441
- y: by / len
2442
- };
2443
- const ax = q.p1.x - q.p3.x;
2444
- const ay = q.p1.y - q.p3.y;
2445
- const sign = baseline.x * ay - baseline.y * ax >= 0 ? 1 : -1;
2446
- const ascent = {
2447
- x: -baseline.y * sign,
2448
- y: baseline.x * sign
2449
- };
2450
- for (let i = 0; i < frames.length; i++) {
2451
- const f = frames[i];
2452
- const dot = f.baseline.x * baseline.x + f.baseline.y * baseline.y;
2453
- const sameHandedness = f.ascent.x * ascent.x + f.ascent.y * ascent.y > 0;
2454
- if (dot >= FRAME_DOT_TOLERANCE && sameHandedness) return i;
2455
- }
2456
- frames.push({
2457
- baseline,
2458
- ascent
2459
- });
2460
- return frames.length - 1;
2461
- }
2462
- /**
2463
- * Flatten a page's geometry snapshot into the canonical layout. Upright runs
2464
- * copy their wire boxes verbatim (frame 0 — byte-identical to the
2465
- * pre-orientation behavior, wire run rects included); rotated runs project
2466
- * their glyph cells into their cluster's frame.
2467
- */
2468
- function buildPageTextLayout(snapshot) {
2469
- const glyphs = [];
2470
- const runs = [];
2471
- const frames = [IDENTITY_FRAME];
2472
- for (const run of snapshot.runs) {
2473
- if (!require_derive.isRotatedGeometryRun(run)) {
2474
- for (const g of run.glyphs) glyphs.push({
2475
- loose: g.looseBox,
2476
- tight: g.tightBox,
2477
- flags: g.flags
2478
- });
2479
- runs.push({
2480
- charStart: run.charStart,
2481
- count: run.glyphs.length,
2482
- rect: run.rect,
2483
- fontSize: run.fontSize,
2484
- frame: 0
2485
- });
2486
- continue;
2487
- }
2488
- const frame = frameForRun(frames, run);
2489
- const f = frames[frame];
2490
- let runBox = null;
2491
- for (const g of run.glyphs) {
2492
- if (isEmpty(g.flags)) {
2493
- glyphs.push({
2494
- loose: ZERO_RECT,
2495
- flags: g.flags
2496
- });
2497
- continue;
2498
- }
2499
- const loose = frameBoxOfQuad(f, g.looseQuad);
2500
- const tight = g.tightQuad ? frameBoxOfQuad(f, g.tightQuad) : void 0;
2501
- glyphs.push({
2502
- loose,
2503
- tight,
2504
- flags: g.flags
2505
- });
2506
- runBox = runBox ? {
2507
- left: Math.min(runBox.left, loose.left),
2508
- bottom: Math.min(runBox.bottom, loose.bottom),
2509
- right: Math.max(runBox.right, loose.right),
2510
- top: Math.max(runBox.top, loose.top)
2511
- } : loose;
2512
- }
2513
- runs.push({
2514
- charStart: run.charStart,
2515
- count: run.glyphs.length,
2516
- rect: runBox ?? ZERO_RECT,
2517
- fontSize: run.fontSize,
2518
- frame
2519
- });
2520
- }
2521
- return {
2522
- glyphs,
2523
- runs,
2524
- frames
2525
- };
2526
- }
2527
- const inRect = (r, p) => p.x >= r.left && p.x <= r.right && p.y >= r.bottom && p.y <= r.top;
2528
- function avgGlyphHeight(layout) {
2529
- let total = 0;
2530
- let count = 0;
2531
- for (const g of layout.glyphs) {
2532
- if (isEmpty(g.flags)) continue;
2533
- total += g.loose.top - g.loose.bottom;
2534
- count++;
2535
- }
2536
- return count === 0 ? 0 : total / count;
2537
- }
2538
- /** The point expressed in every frame (index-aligned with `layout.frames`).
2539
- * Frame 0 returns the point itself — no float round-trip on the fast path. */
2540
- function pointPerFrame(layout, p) {
2541
- return layout.frames.map((f, i) => i === 0 ? p : toFrame(f, p));
2542
- }
2543
- /**
2544
- * The glyph (char) index at a page-space point, or null when nothing is near
2545
- * (so callers can show the pointer cursor off-text). PDFium `GetIndexAtPos`:
2546
- * exact tight-box containment first, then a tolerance pass (closest by
2547
- * Manhattan distance within `toleranceFactor × average glyph height`). Each
2548
- * run tests the point in ITS OWN frame, so rotated text hit-tests exactly.
2549
- */
2550
- function textGlyphAt(layout, point, toleranceFactor = 1.5) {
2551
- const pts = pointPerFrame(layout, point);
2552
- for (const run of layout.runs) {
2553
- const q = pts[run.frame];
2554
- if (!inRect(run.rect, q)) continue;
2555
- for (let i = 0; i < run.count; i++) {
2556
- const g = layout.glyphs[run.charStart + i];
2557
- if (inRect(g.tight ?? g.loose, q)) return run.charStart + i;
2558
- }
2559
- }
2560
- if (toleranceFactor <= 0) return null;
2561
- const half = avgGlyphHeight(layout) * toleranceFactor / 2;
2562
- let best = -1;
2563
- let bestDist = Infinity;
2564
- for (const run of layout.runs) {
2565
- const q = pts[run.frame];
2566
- const r = run.rect;
2567
- if (q.x < r.left - half || q.x > r.right + half || q.y < r.bottom - half || q.y > r.top + half) continue;
2568
- for (let i = 0; i < run.count; i++) {
2569
- const g = layout.glyphs[run.charStart + i];
2570
- if (isEmpty(g.flags)) continue;
2571
- const b = g.tight ?? g.loose;
2572
- if (q.x < b.left - half || q.x > b.right + half || q.y < b.bottom - half || q.y > b.top + half) continue;
2573
- const dx = Math.min(Math.abs(q.x - b.left), Math.abs(q.x - b.right));
2574
- const dy = Math.min(Math.abs(q.y - b.bottom), Math.abs(q.y - b.top));
2575
- if (dx + dy < bestDist) {
2576
- bestDist = dx + dy;
2577
- best = run.charStart + i;
2578
- }
2579
- }
2580
- }
2581
- return best >= 0 ? best : null;
2582
- }
2583
- /** Double-click: the word around `glyph` (walk to space/empty glyphs both ways). */
2584
- function expandTextRangeToWord(layout, glyph) {
2585
- const n = layout.glyphs.length;
2586
- if (glyph < 0 || glyph >= n) return [glyph, glyph];
2587
- let from = glyph;
2588
- while (from > 0 && !isBoundary(layout.glyphs[from - 1].flags)) from--;
2589
- let to = glyph;
2590
- while (to < n - 1 && !isBoundary(layout.glyphs[to + 1].flags)) to++;
2591
- return [from, to];
2592
- }
2593
- /** Triple-click: the full visual line — SAME-FRAME runs whose vertical extent
2594
- * overlaps the anchor run's. A differently-oriented run is a line boundary. */
2595
- function expandTextRangeToLine(layout, glyph) {
2596
- const ri = layout.runs.findIndex((r) => glyph >= r.charStart && glyph < r.charStart + r.count);
2597
- if (ri < 0) return [glyph, glyph];
2598
- const anchor = layout.runs[ri];
2599
- const bottom = anchor.rect.bottom;
2600
- const top = anchor.rect.top;
2601
- let from = anchor.charStart;
2602
- let to = anchor.charStart + anchor.count - 1;
2603
- for (let r = ri - 1; r >= 0; r--) {
2604
- const run = layout.runs[r];
2605
- if (isZero(run.rect)) continue;
2606
- if (run.frame !== anchor.frame) break;
2607
- if (!overlapV(run.rect.bottom, run.rect.top, bottom, top)) break;
2608
- from = run.charStart;
2609
- }
2610
- for (let r = ri + 1; r < layout.runs.length; r++) {
2611
- const run = layout.runs[r];
2612
- if (isZero(run.rect)) continue;
2613
- if (run.frame !== anchor.frame) break;
2614
- if (!overlapV(run.rect.bottom, run.rect.top, bottom, top)) break;
2615
- to = run.charStart + run.count - 1;
2616
- }
2617
- return [from, to];
2618
- }
2619
- /**
2620
- * A single glyph's oriented cell in page space, or null for degenerate
2621
- * glyphs — the anchor for caret placement at a selection boundary.
2622
- */
2623
- function textGlyphQuad(layout, glyph) {
2624
- const g = layout.glyphs[glyph];
2625
- if (!g || isEmpty(g.flags)) return null;
2626
- const b = g.loose;
2627
- if (b.right <= b.left || b.top <= b.bottom) return null;
2628
- const run = layout.runs.find((r) => glyph >= r.charStart && glyph < r.charStart + r.count);
2629
- const f = layout.frames[run?.frame ?? 0];
2630
- return quadOfFrameRect(f, run?.frame ?? 0, b);
2631
- }
2632
- /**
2633
- * Merged visual-line segments for the half-open char range
2634
- * `[charStart, charStart + charCount)` — THE canonical segmentation. Chars
2635
- * outside the layout are ignored.
2636
- */
2637
- function textSegmentsForRange(layout, charStart, charCount) {
2638
- if (charCount <= 0 || layout.glyphs.length === 0) return [];
2639
- const lo = Math.max(0, charStart);
2640
- const hi = Math.min(layout.glyphs.length - 1, charStart + charCount - 1);
2641
- if (hi < lo) return [];
2642
- const subRuns = [];
2643
- for (const run of layout.runs) {
2644
- const runEnd = run.charStart + run.count - 1;
2645
- if (runEnd < lo || run.charStart > hi) continue;
2646
- const s = Math.max(lo, run.charStart);
2647
- const e = Math.min(hi, runEnd);
2648
- let left = Infinity;
2649
- let right = -Infinity;
2650
- let bottom = Infinity;
2651
- let top = -Infinity;
2652
- let charCountAcc = 0;
2653
- let widthSum = 0;
2654
- let prevRight = -Infinity;
2655
- let firstX = 0;
2656
- let lastX = 0;
2657
- const flush = () => {
2658
- if (charCountAcc > 0 && left !== Infinity) subRuns.push({
2659
- rect: {
2660
- left,
2661
- bottom,
2662
- right,
2663
- top
2664
- },
2665
- charCount: charCountAcc,
2666
- fontSize: run.fontSize,
2667
- frame: run.frame,
2668
- firstX,
2669
- lastX
2670
- });
2671
- left = Infinity;
2672
- right = -Infinity;
2673
- bottom = Infinity;
2674
- top = -Infinity;
2675
- charCountAcc = 0;
2676
- widthSum = 0;
2677
- prevRight = -Infinity;
2678
- };
2679
- for (let ci = s; ci <= e; ci++) {
2680
- const g = layout.glyphs[ci];
2681
- if (isEmpty(g.flags)) continue;
2682
- const b = g.loose;
2683
- if (charCountAcc > 0 && prevRight > -Infinity) {
2684
- const avg = widthSum / charCountAcc;
2685
- if (avg > 0 && Math.abs(b.left - prevRight) > CHAR_DISTANCE_FACTOR * avg) flush();
2686
- }
2687
- const centerX = (b.left + b.right) / 2;
2688
- if (charCountAcc === 0) firstX = centerX;
2689
- lastX = centerX;
2690
- left = Math.min(left, b.left);
2691
- right = Math.max(right, b.right);
2692
- bottom = Math.min(bottom, b.bottom);
2693
- top = Math.max(top, b.top);
2694
- charCountAcc++;
2695
- widthSum += b.right - b.left;
2696
- prevRight = b.right;
2697
- }
2698
- flush();
2699
- }
2700
- return mergeAdjacentSubRuns(subRuns).map((m) => materializeSegment(layout, m));
2701
- }
2702
- function mergeAdjacentSubRuns(runs) {
2703
- const out = [];
2704
- let prev = null;
2705
- let cur = null;
2706
- for (const run of runs) {
2707
- if (prev && cur && prev.frame === run.frame && shouldMerge(prev, run)) cur = {
2708
- rect: {
2709
- left: Math.min(cur.rect.left, run.rect.left),
2710
- bottom: Math.min(cur.rect.bottom, run.rect.bottom),
2711
- right: Math.max(cur.rect.right, run.rect.right),
2712
- top: Math.max(cur.rect.top, run.rect.top)
2713
- },
2714
- frame: cur.frame,
2715
- firstX: cur.firstX,
2716
- lastX: run.lastX
2717
- };
2718
- else {
2719
- if (cur) out.push(cur);
2720
- cur = {
2721
- rect: run.rect,
2722
- frame: run.frame,
2723
- firstX: run.firstX,
2724
- lastX: run.lastX
2725
- };
2726
- }
2727
- prev = run;
2728
- }
2729
- if (cur && cur.rect.right > cur.rect.left && cur.rect.top > cur.rect.bottom) out.push(cur);
2730
- return out;
2731
- }
2732
- /** Segment quad for a frame-local rect (frame 0: the rect's own corners,
2733
- * no float round-trip). Slot order US, UE, LS, LE; frame is y-up, so the
2734
- * upper corners sit at the frame TOP. */
2735
- function quadOfFrameRect(f, frameIndex, r) {
2736
- if (frameIndex === 0) return {
2737
- p1: {
2738
- x: r.left,
2739
- y: r.top
2740
- },
2741
- p2: {
2742
- x: r.right,
2743
- y: r.top
2744
- },
2745
- p3: {
2746
- x: r.left,
2747
- y: r.bottom
2748
- },
2749
- p4: {
2750
- x: r.right,
2751
- y: r.bottom
2752
- }
2753
- };
2754
- return {
2755
- p1: fromFrame(f, {
2756
- x: r.left,
2757
- y: r.top
2758
- }),
2759
- p2: fromFrame(f, {
2760
- x: r.right,
2761
- y: r.top
2762
- }),
2763
- p3: fromFrame(f, {
2764
- x: r.left,
2765
- y: r.bottom
2766
- }),
2767
- p4: fromFrame(f, {
2768
- x: r.right,
2769
- y: r.bottom
2770
- })
2771
- };
2772
- }
2773
- function materializeSegment(layout, m) {
2774
- const frame = layout.frames[m.frame];
2775
- const quad = quadOfFrameRect(frame, m.frame, m.rect);
2776
- return {
2777
- quad,
2778
- rect: m.frame === 0 ? m.rect : {
2779
- left: Math.min(quad.p1.x, quad.p2.x, quad.p3.x, quad.p4.x),
2780
- bottom: Math.min(quad.p1.y, quad.p2.y, quad.p3.y, quad.p4.y),
2781
- right: Math.max(quad.p1.x, quad.p2.x, quad.p3.x, quad.p4.x),
2782
- top: Math.max(quad.p1.y, quad.p2.y, quad.p3.y, quad.p4.y)
2783
- },
2784
- advance: m.lastX >= m.firstX ? 1 : -1
2785
- };
2786
- }
2787
- function shouldMerge(a, b) {
2788
- if (a.fontSize != null && b.fontSize != null && a.fontSize > 0 && b.fontSize > 0) {
2789
- if (Math.max(a.fontSize, b.fontSize) / Math.min(a.fontSize, b.fontSize) > FONT_SIZE_RATIO_THRESHOLD) return false;
2790
- }
2791
- if (verticalOverlap(a.rect, b.rect) < VERTICAL_OVERLAP_THRESHOLD) return false;
2792
- const aw = (a.rect.right - a.rect.left) / a.charCount;
2793
- const bw = (b.rect.right - b.rect.left) / b.charCount;
2794
- const aL = a.rect.left - aw;
2795
- const aR = a.rect.right + aw;
2796
- const bL = b.rect.left - bw;
2797
- return aL < b.rect.right + bw && aR > bL;
2798
- }
2799
- function verticalOverlap(a, b) {
2800
- const ah = a.top - a.bottom;
2801
- const bh = b.top - b.bottom;
2802
- if (ah <= 0 || bh <= 0) return 0;
2803
- const u = Math.max(a.top, b.top) - Math.min(a.bottom, b.bottom);
2804
- if (u === ah || u === bh) return 1;
2805
- return Math.max(0, Math.min(a.top, b.top) - Math.max(a.bottom, b.bottom)) / u;
2806
- }
2807
- const isZero = (r) => r.right === r.left && r.top === r.bottom;
2808
- function overlapV(bottom1, top1, bottom2, top2) {
2809
- const u = Math.max(top1, top2) - Math.min(bottom1, bottom2);
2810
- if (u === 0) return false;
2811
- return Math.max(0, Math.min(top1, top2) - Math.max(bottom1, bottom2)) / u >= LINE_OVERLAP_THRESHOLD;
2812
- }
2813
- //#endregion
2814
- //#region src/search/rects.ts
2815
- /**
2816
- * Match-range → canonical visual-line segments. Thin wrappers over the ONE
2817
- * text layout (`src/text/layout.ts`) — search owns matching and cursors,
2818
- * never segmentation, so a match highlights exactly like a selection of the
2819
- * same characters. No frame or merge logic lives here.
2820
- */
2821
- /** Canonical segments for `[charStart, charStart + charCount)`. */
2822
- function searchSegmentsForRange(snapshot, charStart, charCount) {
2823
- return textSegmentsForRange(buildPageTextLayout(snapshot), charStart, charCount);
2824
- }
2825
- /** The segments' AABBs — for consumers that genuinely want boxes. */
2826
- function searchRectsForRange(snapshot, charStart, charCount) {
2827
- return searchSegmentsForRange(snapshot, charStart, charCount).map((s) => s.rect);
2828
- }
2829
- //#endregion
2830
1900
  //#region src/search/epoch.ts
2831
1901
  /**
2832
- * The search CONTENT EPOCH: a deterministic fingerprint of everything a
1902
+ * The search content epoch: a deterministic fingerprint of everything a
2833
1903
  * search result depends on — page order/structure (`layoutVersion`) and
2834
1904
  * each page's text content (`contentVersion`), nothing else. Annotation
2835
- * and metadata churn deliberately do NOT move it, so cached search
1905
+ * and metadata churn deliberately do not move it, so cached search
2836
1906
  * responses in a collaborative document survive comment storms.
2837
1907
  *
2838
1908
  * Both sides compute it from the manifest they already hold: the client
2839
1909
  * to mint versioned search URLs, the server to validate them (mismatch →
2840
1910
  * NotFound, the standard stale-versioned-read signal). FNV-1a 64-bit —
2841
1911
  * dependency-free and stable; not cryptographic, and doesn't need to be:
2842
- * an adversary can only vary his OWN authorized query results.
1912
+ * an adversary can only vary his own authorized query results.
2843
1913
  */
2844
1914
  function searchContentEpoch(manifest) {
2845
1915
  let input = `${manifest.layoutVersion}`;
2846
- for (const page of manifest.pages) input += `|${page.state.pageObjectNumber}:${page.cache.contentVersion}`;
1916
+ for (const page of manifest.pages) input += `|${page.page.objectNumber}:${page.cache.contentVersion}`;
2847
1917
  return fnv1a64(input);
2848
1918
  }
2849
1919
  const FNV_OFFSET = 14695981039346656037n;
@@ -2858,20 +1928,108 @@ function fnv1a64(input) {
2858
1928
  return hash.toString(16).padStart(16, "0");
2859
1929
  }
2860
1930
  /**
2861
- * Canonicalize a query for CACHE KEYING: default-fold literal queries are
1931
+ * Canonicalize a query for cache keying: default-fold literal queries are
2862
1932
  * pre-folded ("Café", "café" and "CAFE" are the same search, so they
2863
1933
  * should be the same cache entry). Semantics are unchanged — the fold is
2864
1934
  * idempotent and exactly what the matcher applies to the needle anyway.
2865
1935
  * Case- or diacritic-sensitive literals and regex patterns pass through
2866
- * untouched (their raw form IS the query).
1936
+ * untouched (their raw form is the query).
2867
1937
  */
2868
- function canonicalSearchQuery(query) {
1938
+ function canonicalSearchQuery(request) {
1939
+ const query = searchQueryOf(request);
2869
1940
  if (query.regex || query.matchCase || query.matchDiacritics || query.ignoreWhitespace) return query;
2870
- const canonical = { text: require_derive.foldText(query.text).folded };
1941
+ const canonical = { text: require_calibrate.foldText(query.text).folded };
2871
1942
  if (query.wholeWord) canonical.wholeWord = true;
2872
1943
  return canonical;
2873
1944
  }
2874
1945
  //#endregion
1946
+ //#region src/mutation/WriteOptions.ts
1947
+ /** What an `opId` may be: what an HTTP header carries (`Idempotency-Key`). */
1948
+ const OP_ID = /^[\x21-\x7e]{1,255}$/;
1949
+ /**
1950
+ * The `opId` a write runs under: the caller's, or a fresh one. The caller's
1951
+ * must be 1 to 255 visible ASCII characters, on every engine; anything else
1952
+ * is refused with `InvalidArg`.
1953
+ */
1954
+ function opIdOf(options) {
1955
+ const opId = options?.opId;
1956
+ if (opId === void 0) return generateUuid();
1957
+ if (!OP_ID.test(opId)) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "opId must be 1 to 255 visible ASCII characters", { details: { field: "opId" } });
1958
+ return opId;
1959
+ }
1960
+ //#endregion
1961
+ //#region src/mutation/changeOutcome.ts
1962
+ /**
1963
+ * The refusals a change keeps under its `opId`, like a success: the document
1964
+ * or the caller refused it, so asking again gets the same answer, even once
1965
+ * the document has changed so that it would apply. Anything else (an abort, a
1966
+ * lost race with another writer, an engine failure) applied nothing, is not
1967
+ * kept, and may run again.
1968
+ */
1969
+ const KEPT_REFUSALS = /* @__PURE__ */ new Set([
1970
+ require_format.EngineErrorCode.InvalidArg,
1971
+ require_format.EngineErrorCode.NotFound,
1972
+ require_format.EngineErrorCode.Forbidden,
1973
+ require_format.EngineErrorCode.ProtectedDocument,
1974
+ require_format.EngineErrorCode.MalformedPdf,
1975
+ require_format.EngineErrorCode.PayloadTooLarge,
1976
+ require_format.EngineErrorCode.ObjectNumberUnavailable,
1977
+ require_format.EngineErrorCode.LayerFull,
1978
+ require_format.EngineErrorCode.ChangeConflict,
1979
+ require_format.EngineErrorCode.UndoUnavailable
1980
+ ]);
1981
+ /** Whether a change refused with `code` keeps that answer (see `KEPT_REFUSALS`). */
1982
+ function isKeptRefusal(code) {
1983
+ return KEPT_REFUSALS.has(code);
1984
+ }
1985
+ /**
1986
+ * What a retry under the same `opId` must send: the change's fingerprint. The
1987
+ * same ops with the same bytes give the same fingerprint whatever the order
1988
+ * of their keys; bytes count by their content.
1989
+ */
1990
+ function changeFingerprint(change) {
1991
+ const text = canonicalJson(change);
1992
+ return `${text.length.toString(36)}-${hashText(text)}`;
1993
+ }
1994
+ /** `value` as JSON with every object's keys sorted, and bytes as their length and hash. */
1995
+ function canonicalJson(value) {
1996
+ if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
1997
+ const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
1998
+ return JSON.stringify({ $bytes: `${bytes.byteLength.toString(36)}-${hashBytes(bytes)}` });
1999
+ }
2000
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
2001
+ if (value !== null && typeof value === "object") return `{${Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
2002
+ return JSON.stringify(value) ?? "null";
2003
+ }
2004
+ /** A 53-bit hash (cyrb53) of a string's UTF-16 units, in base 36. */
2005
+ function hashText(text) {
2006
+ let h1 = 3735928559;
2007
+ let h2 = 1103547991;
2008
+ for (let i = 0; i < text.length; i++) {
2009
+ const unit = text.charCodeAt(i);
2010
+ h1 = Math.imul(h1 ^ unit, 2654435761);
2011
+ h2 = Math.imul(h2 ^ unit, 1597334677);
2012
+ }
2013
+ return finish(h1, h2);
2014
+ }
2015
+ /** The same hash over bytes. */
2016
+ function hashBytes(bytes) {
2017
+ let h1 = 3735928559;
2018
+ let h2 = 1103547991;
2019
+ for (const byte of bytes) {
2020
+ h1 = Math.imul(h1 ^ byte, 2654435761);
2021
+ h2 = Math.imul(h2 ^ byte, 1597334677);
2022
+ }
2023
+ return finish(h1, h2);
2024
+ }
2025
+ function finish(h1, h2) {
2026
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
2027
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
2028
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
2029
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
2030
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
2031
+ }
2032
+ //#endregion
2875
2033
  //#region src/auth/scope/pdf-bits.ts
2876
2034
  /**
2877
2035
  * ISO 32000 user-access permission bit positions, expressed as integer
@@ -2927,144 +2085,153 @@ function decodePdfBits(raw) {
2927
2085
  };
2928
2086
  }
2929
2087
  //#endregion
2930
- //#region src/auth/scope/builders.ts
2088
+ //#region src/auth/scope/authority.ts
2931
2089
  /**
2932
- * Typed capability constants for customer-side JWT minting.
2933
- *
2934
- * The shape mirrors the dotted capability name so it reads naturally:
2935
- * caps.doc.text.copy() // "doc.text.copy"
2936
- * caps.doc.download() // "doc.download"
2937
- * caps.doc.download.flattened() // "doc.download.flattened"
2938
- * caps.doc.print.high() // "doc.print.high"
2939
- *
2940
- * Each leaf is a function returning the literal string (as `const`) so
2941
- * TypeScript catches typos at the call site. Wrappers like `download`
2942
- * and `print` are `Object.assign`'d to be callable AND carry child
2943
- * properties for the refinement capabilities.
2090
+ * Check that the caller may use `capability`: `ProtectedDocument` when a
2091
+ * signature in the document took it away, `PermissionDenied` when the
2092
+ * grants never gave it.
2944
2093
  */
2945
- const caps = { doc: {
2946
- open: () => "doc.open",
2947
- render: () => "doc.render",
2948
- text: {
2949
- select: () => "doc.text.select",
2950
- copy: () => "doc.text.copy",
2951
- search: () => "doc.text.search"
2952
- },
2953
- content: { copy: () => "doc.content.copy" },
2954
- download: Object.assign(() => "doc.download", { flattened: () => "doc.download.flattened" }),
2955
- print: Object.assign(() => "doc.print", { high: () => "doc.print.high" }),
2956
- pages: {
2957
- modify: () => "doc.pages.modify",
2958
- assemble: () => "doc.pages.assemble"
2959
- },
2960
- forms: {
2961
- read: () => "doc.forms.read",
2962
- fill: () => "doc.forms.fill",
2963
- modify: () => "doc.forms.modify"
2964
- },
2965
- annotate: {
2966
- read: () => "doc.annotate.read",
2967
- modify: () => "doc.annotate.modify"
2968
- },
2969
- metadata: { modify: () => "doc.metadata.modify" },
2970
- attachments: { modify: () => "doc.attachments.modify" },
2971
- redact: () => "doc.redact"
2972
- } };
2094
+ function authorizeCapability(authority, capability) {
2095
+ authorizeUnprotected(authority, capability);
2096
+ const { grants, protection } = authority;
2097
+ if (grants && !require_resolver.checkCapability(capability, grants.scope, grants.pdfBits, protection)) throw new require_resolver.PermissionDenied(capability, "target");
2098
+ }
2099
+ /** Whether the caller may use `capability`: what {@link authorizeCapability} checks, as a yes or no. */
2100
+ function allowsCapability(authority, capability) {
2101
+ const { grants, protection } = authority;
2102
+ if (protection && require_resolver.protectedCapabilities(protection).has(capability)) return false;
2103
+ return !grants || require_resolver.checkCapability(capability, grants.scope, grants.pdfBits, protection);
2104
+ }
2973
2105
  /**
2974
- * Builder factory for collab scope filters. Each entity:action pair
2975
- * gets a sub-object with the four filter constructors.
2976
- *
2977
- * collab.annotations.create.self() → "annotations:create:self"
2978
- * collab.annotations.create.group("legal") → "annotations:create:group=legal"
2979
- * collab.annotations.update.group("4") → "annotations:update:group=4"
2980
- * collab.annotations.delete.createdBy("u-7") → "annotations:delete:createdBy=u-7"
2981
- * collab.annotations.setGroup.group("legal") → "annotations:set-group:group=legal"
2982
- * collab.annotations.all.all() → "annotations:*:all" (action wildcard)
2983
- *
2984
- * On create, the filter is evaluated against the caller's own identity
2985
- * (no impersonation), so `:self` and `:all` trivially pass; `:group=X`
2986
- * is the meaningful filter — it constrains creation to callers whose
2987
- * default group is X.
2106
+ * Check only that no signature in the document took `capability` away
2107
+ * (`ProtectedDocument`). Annotation creates, updates and deletes take this
2108
+ * for `doc.annotate.modify`; their grants are checked per annotation.
2988
2109
  */
2989
- const collab = { annotations: {
2990
- create: makeFilterBuilder("annotations", "create"),
2991
- update: makeFilterBuilder("annotations", "update"),
2992
- delete: makeFilterBuilder("annotations", "delete"),
2993
- setGroup: makeSetGroupBuilder(),
2994
- /** Action wildcard — matches create, update, delete, AND set-group with the given filter. */
2995
- all: makeFilterBuilder("annotations", "*")
2996
- } };
2997
- function makeFilterBuilder(entity, action) {
2998
- return {
2999
- all: () => `${entity}:${action}:all`,
3000
- self: () => `${entity}:${action}:self`,
3001
- createdBy: (userId) => `${entity}:${action}:createdBy=${userId}`,
3002
- group: (groupId) => `${entity}:${action}:group=${groupId}`
2110
+ function authorizeUnprotected(authority, capability) {
2111
+ const { protection } = authority;
2112
+ if (protection && require_resolver.protectedCapabilities(protection).has(capability)) throw new require_format.EngineError(require_format.EngineErrorCode.ProtectedDocument, require_resolver.describeProtection(capability, protection));
2113
+ }
2114
+ /**
2115
+ * The capability writing an annotation of `subtype` takes. A widget belongs
2116
+ * to a form field, so creating, changing, moving or deleting one is
2117
+ * designing the form: `doc.forms.modify`. The annotation scopes
2118
+ * (`annotations:*`) never apply to it, and it has no group of its own.
2119
+ */
2120
+ function annotationWriteCapability(subtype) {
2121
+ return subtype === "widget" ? "doc.forms.modify" : "doc.annotate.modify";
2122
+ }
2123
+ /** The capabilities writing all of `annotations` takes, each once. */
2124
+ function annotationWriteCapabilities(annotations) {
2125
+ return [...new Set(annotations.map((annotation) => annotationWriteCapability(annotation.subtype)))];
2126
+ }
2127
+ /**
2128
+ * Check a widget write (see {@link annotationWriteCapability}): form design,
2129
+ * and no group to set. Returns the actor the write stamps: the editor.
2130
+ */
2131
+ function authorizeWidgetWrite(authority, groupId) {
2132
+ const { identity, grants } = authority;
2133
+ if (grants && !require_resolver.checkCapability("doc.forms.modify", grants.scope, grants.pdfBits)) throw new require_resolver.PermissionDenied("doc.forms.modify", "target");
2134
+ if (typeof groupId === "string") throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "a widget has no group of its own: a form field's group is the field's");
2135
+ const actor = {
2136
+ ...identity.userId !== void 0 ? { userId: identity.userId } : {},
2137
+ ...identity.displayName !== void 0 ? { displayName: identity.displayName } : {}
3003
2138
  };
2139
+ return actor.userId || actor.displayName ? actor : void 0;
3004
2140
  }
3005
- function makeSetGroupBuilder() {
3006
- return {
3007
- all: () => "annotations:set-group:all",
3008
- group: (groupId) => `annotations:set-group:group=${groupId}`
2141
+ /**
2142
+ * Check a create of a `subtype` annotation in `groupId` (the identity's own
2143
+ * group without one) and return the actor the write stamps: the creator, in
2144
+ * that group. A group other than the identity's takes the authority a
2145
+ * reassignment takes. A widget takes form design instead, in no group.
2146
+ */
2147
+ function authorizeAnnotationCreate(authority, subtype, groupId) {
2148
+ if (subtype === "widget") return authorizeWidgetWrite(authority, groupId);
2149
+ const { identity, grants } = authority;
2150
+ const group = groupId ?? identity.groupId;
2151
+ if (grants) {
2152
+ if (group !== void 0 && !require_resolver.checkSetGroup("annotations", group, identity.groupId, grants.scope)) throw new require_resolver.PermissionDenied(`annotations:set-group:group=${group}`, "target");
2153
+ if (!require_resolver.checkCollab("create", require_resolver.collabTargetOf({
2154
+ userId: identity.userId,
2155
+ groupId: group
2156
+ }), grants.scope, identity, grants.pdfBits)) throw new require_resolver.PermissionDenied("annotations:create", "target");
2157
+ }
2158
+ const actor = {
2159
+ ...identity.userId !== void 0 ? { userId: identity.userId } : {},
2160
+ ...group !== void 0 ? { groupId: group } : {},
2161
+ ...identity.displayName !== void 0 ? { displayName: identity.displayName } : {}
3009
2162
  };
2163
+ return actor.userId || actor.groupId || actor.displayName ? actor : void 0;
3010
2164
  }
3011
2165
  /**
3012
- * Builder for the `pdf.permissions` virtual scope. Returns the literal
3013
- * string; provided as a helper for parity with caps/collab in JWT
3014
- * construction code.
2166
+ * Check an update of the annotation `owner` describes and return the actor
2167
+ * the write stamps: the editor, and the group only when the patch reassigns
2168
+ * it. An existing group can be reassigned, never removed. A widget takes form
2169
+ * design instead, and its patch sets no group.
3015
2170
  */
3016
- const pdfPermissions = () => "pdf.permissions";
2171
+ function authorizeAnnotationUpdate(authority, owner, patchGroupId) {
2172
+ if (owner.subtype === "widget") return authorizeWidgetWrite(authority, patchGroupId);
2173
+ const { identity, grants } = authority;
2174
+ const target = require_resolver.collabTargetOf(owner);
2175
+ if (grants && !require_resolver.checkCollab("update", target, grants.scope, identity, grants.pdfBits)) throw new require_resolver.PermissionDenied("annotations:update", "target");
2176
+ if (patchGroupId === null && target.groupId !== void 0) throw new require_format.EngineError(require_format.EngineErrorCode.InvalidArg, "an annotation's group can't be removed");
2177
+ const reassigning = typeof patchGroupId === "string" && patchGroupId !== target.groupId;
2178
+ if (reassigning && grants && !require_resolver.checkSetGroup("annotations", patchGroupId, identity.groupId, grants.scope)) throw new require_resolver.PermissionDenied(`annotations:set-group:group=${patchGroupId}`, "target");
2179
+ const actor = {
2180
+ ...identity.userId !== void 0 ? { userId: identity.userId } : {},
2181
+ ...identity.displayName !== void 0 ? { displayName: identity.displayName } : {},
2182
+ ...reassigning ? { groupId: patchGroupId } : {}
2183
+ };
2184
+ return actor.userId || actor.groupId || actor.displayName ? actor : void 0;
2185
+ }
3017
2186
  /**
3018
- * Materialise `pdf.permissions` into the concrete capability list it
3019
- * would expand to under the given PDF bit configuration.
3020
- *
3021
- * Useful when the customer wants to start from PDF defaults and then
3022
- * subtract specific capabilities — easier to build the JWT scope array
3023
- * by filtering an explicit list than by composing virtual + denials.
3024
- *
3025
- * const defaults = materializePdfPermissions(pdfBits);
3026
- * const scope = [
3027
- * ...defaults.filter(s => s !== 'doc.text.copy'),
3028
- * 'doc.download',
3029
- * ];
3030
- *
3031
- * IMPORTANT: this MUST stay in sync with `addPdfPermissions` inside
3032
- * resolver.ts. A test pins them together.
2187
+ * Check a delete of every annotation it removes (an annotation goes with its
2188
+ * thread and popups): all or nothing, `PermissionDenied` naming each one
2189
+ * refused. A widget takes form design, never the annotation scopes.
3033
2190
  */
3034
- function materializePdfPermissions(b) {
3035
- const out = /* @__PURE__ */ new Set();
3036
- out.add("doc.open");
3037
- out.add("doc.render");
3038
- out.add("doc.annotate.read");
3039
- out.add("doc.forms.read");
3040
- if (b.bit5) {
3041
- out.add("doc.text.select");
3042
- out.add("doc.text.copy");
3043
- out.add("doc.text.search");
3044
- out.add("doc.content.copy");
3045
- }
3046
- if (b.bit3) out.add("doc.print");
3047
- if (b.bit12 && b.bit3) out.add("doc.print.high");
3048
- if (b.bit4) {
3049
- out.add("doc.pages.modify");
3050
- out.add("doc.redact");
3051
- out.add("doc.metadata.modify");
3052
- out.add("doc.attachments.modify");
3053
- }
3054
- if (b.bit11) out.add("doc.pages.assemble");
3055
- if (b.bit6) out.add("doc.annotate.modify");
3056
- if (b.bit6 || b.bit9) {
3057
- out.add("doc.forms.fill");
3058
- out.add("doc.sign");
3059
- }
3060
- if (b.bit6 && b.bit4) out.add("doc.forms.modify");
3061
- return [...out];
2191
+ function authorizeAnnotationDelete(authority, members) {
2192
+ const { identity, grants } = authority;
2193
+ if (!grants) return;
2194
+ const mayDesign = require_resolver.checkCapability("doc.forms.modify", grants.scope, grants.pdfBits);
2195
+ const refused = members.filter((member) => member.subtype === "widget" ? !mayDesign : !require_resolver.checkCollab("delete", require_resolver.collabTargetOf(member), grants.scope, identity, grants.pdfBits));
2196
+ if (refused.length > 0) throw new require_resolver.PermissionDenied("annotations:delete", "target", void 0, refused.map((member) => member.ref));
3062
2197
  }
3063
- //#endregion
3064
- //#region src/measure/viewport.ts
3065
- /** Last containing viewport wins. A foreign measure still wins; never guess a fallback. */
3066
- function viewportForPoint(viewports, point) {
3067
- return [...viewports].reverse().find(({ bbox: b }) => point.x >= b.left && point.x <= b.right && point.y >= b.bottom && point.y <= b.top);
2198
+ /**
2199
+ * Check that the caller may `action` (fill in, or sign) the field in
2200
+ * `groupId`: `ProtectedDocument` when a signature in the document took the
2201
+ * capability away, `PermissionDenied` naming the scope it would take when
2202
+ * the grants don't reach the field's group.
2203
+ */
2204
+ function authorizeFieldWrite(authority, groupId, action) {
2205
+ authorizeUnprotected(authority, fieldCapability(action));
2206
+ if (!allowsFieldWrite(authority, groupId, action)) throw new require_resolver.PermissionDenied(groupId === null ? fieldCapability(action) : `fields:${action}:group=${groupId}`, "target");
2207
+ }
2208
+ /** Whether the caller may `action` the field in `groupId`: what {@link authorizeFieldWrite} checks. */
2209
+ function allowsFieldWrite(authority, groupId, action) {
2210
+ const { grants, protection } = authority;
2211
+ if (protection && require_resolver.protectedCapabilities(protection).has(fieldCapability(action))) return false;
2212
+ return !grants || require_resolver.checkFieldAction(action, groupId, grants.scope, grants.pdfBits, protection);
2213
+ }
2214
+ /**
2215
+ * Whether the caller may `action` some field: what a form verb checks
2216
+ * before it knows which fields it writes. Each field is then checked in
2217
+ * the write with {@link authorizeFieldWrite}.
2218
+ */
2219
+ function allowsSomeFieldWrite(authority, action) {
2220
+ const { grants, protection } = authority;
2221
+ if (protection && require_resolver.protectedCapabilities(protection).has(fieldCapability(action))) return false;
2222
+ return !grants || require_resolver.checkAnyFieldAction(action, grants.scope, grants.pdfBits, protection);
2223
+ }
2224
+ /**
2225
+ * Check that the caller may put a form field in `groupId`: their own
2226
+ * default group always, another with `fields:set-group` for it.
2227
+ */
2228
+ function authorizeFieldGroup(authority, groupId) {
2229
+ const { identity, grants } = authority;
2230
+ if (grants && !require_resolver.checkSetGroup("fields", groupId, identity.groupId, grants.scope)) throw new require_resolver.PermissionDenied(`fields:set-group:group=${groupId}`, "target");
2231
+ }
2232
+ /** The broad capability a field action takes without a `fields:` scope. */
2233
+ function fieldCapability(action) {
2234
+ return action === "fill" ? "doc.forms.fill" : "doc.sign";
3068
2235
  }
3069
2236
  //#endregion
3070
2237
  Object.defineProperty(exports, "DEFAULT_EDGE_RESOLVER_BUDGET", {
@@ -3073,6 +2240,12 @@ Object.defineProperty(exports, "DEFAULT_EDGE_RESOLVER_BUDGET", {
3073
2240
  return DEFAULT_EDGE_RESOLVER_BUDGET;
3074
2241
  }
3075
2242
  });
2243
+ Object.defineProperty(exports, "DEFAULT_MEDIA_BOX", {
2244
+ enumerable: true,
2245
+ get: function() {
2246
+ return DEFAULT_MEDIA_BOX;
2247
+ }
2248
+ });
3076
2249
  Object.defineProperty(exports, "DEFAULT_PDF_SAVE_MODE", {
3077
2250
  enumerable: true,
3078
2251
  get: function() {
@@ -3085,244 +2258,244 @@ Object.defineProperty(exports, "EdgeResolver", {
3085
2258
  return EdgeResolver;
3086
2259
  }
3087
2260
  });
3088
- Object.defineProperty(exports, "NO_ANNOTATION_FLAGS", {
2261
+ Object.defineProperty(exports, "PDF_BITS", {
3089
2262
  enumerable: true,
3090
2263
  get: function() {
3091
- return NO_ANNOTATION_FLAGS;
2264
+ return PDF_BITS;
3092
2265
  }
3093
2266
  });
3094
- Object.defineProperty(exports, "PDF_BITS", {
2267
+ Object.defineProperty(exports, "SEARCH_SNIPPET_CONTEXT", {
3095
2268
  enumerable: true,
3096
2269
  get: function() {
3097
- return PDF_BITS;
2270
+ return SEARCH_SNIPPET_CONTEXT;
3098
2271
  }
3099
2272
  });
3100
- Object.defineProperty(exports, "SEARCH_REGEX_MAX_LENGTH", {
2273
+ Object.defineProperty(exports, "USAGE_INCOMPLETE", {
3101
2274
  enumerable: true,
3102
2275
  get: function() {
3103
- return SEARCH_REGEX_MAX_LENGTH;
2276
+ return USAGE_INCOMPLETE;
3104
2277
  }
3105
2278
  });
3106
- Object.defineProperty(exports, "SEARCH_SNIPPET_CONTEXT", {
2279
+ Object.defineProperty(exports, "allowsCapability", {
3107
2280
  enumerable: true,
3108
2281
  get: function() {
3109
- return SEARCH_SNIPPET_CONTEXT;
2282
+ return allowsCapability;
3110
2283
  }
3111
2284
  });
3112
- Object.defineProperty(exports, "UNKNOWN_WEAK_ANNOTATION_STATE", {
2285
+ Object.defineProperty(exports, "allowsFieldWrite", {
3113
2286
  enumerable: true,
3114
2287
  get: function() {
3115
- return UNKNOWN_WEAK_ANNOTATION_STATE;
2288
+ return allowsFieldWrite;
3116
2289
  }
3117
2290
  });
3118
- Object.defineProperty(exports, "USAGE_INCOMPLETE", {
2291
+ Object.defineProperty(exports, "allowsSomeFieldWrite", {
3119
2292
  enumerable: true,
3120
2293
  get: function() {
3121
- return USAGE_INCOMPLETE;
2294
+ return allowsSomeFieldWrite;
3122
2295
  }
3123
2296
  });
3124
- Object.defineProperty(exports, "appearanceImpactOf", {
2297
+ Object.defineProperty(exports, "annotationImportFacts", {
3125
2298
  enumerable: true,
3126
2299
  get: function() {
3127
- return appearanceImpactOf;
2300
+ return annotationImportFacts;
3128
2301
  }
3129
2302
  });
3130
- Object.defineProperty(exports, "assessmentOf", {
2303
+ Object.defineProperty(exports, "annotationPatchBetween", {
3131
2304
  enumerable: true,
3132
2305
  get: function() {
3133
- return assessmentOf;
2306
+ return annotationPatchBetween;
3134
2307
  }
3135
2308
  });
3136
- Object.defineProperty(exports, "buildCommentThreads", {
2309
+ Object.defineProperty(exports, "annotationWriteCapabilities", {
3137
2310
  enumerable: true,
3138
2311
  get: function() {
3139
- return buildCommentThreads;
2312
+ return annotationWriteCapabilities;
3140
2313
  }
3141
2314
  });
3142
- Object.defineProperty(exports, "buildPageTextLayout", {
2315
+ Object.defineProperty(exports, "annotationWriteCapability", {
3143
2316
  enumerable: true,
3144
2317
  get: function() {
3145
- return buildPageTextLayout;
2318
+ return annotationWriteCapability;
3146
2319
  }
3147
2320
  });
3148
- Object.defineProperty(exports, "buildSnippet", {
2321
+ Object.defineProperty(exports, "assessmentOf", {
3149
2322
  enumerable: true,
3150
2323
  get: function() {
3151
- return buildSnippet;
2324
+ return assessmentOf;
3152
2325
  }
3153
2326
  });
3154
- Object.defineProperty(exports, "canonicalSearchQuery", {
2327
+ Object.defineProperty(exports, "authorizeAnnotationCreate", {
3155
2328
  enumerable: true,
3156
2329
  get: function() {
3157
- return canonicalSearchQuery;
2330
+ return authorizeAnnotationCreate;
3158
2331
  }
3159
2332
  });
3160
- Object.defineProperty(exports, "caps", {
2333
+ Object.defineProperty(exports, "authorizeAnnotationDelete", {
3161
2334
  enumerable: true,
3162
2335
  get: function() {
3163
- return caps;
2336
+ return authorizeAnnotationDelete;
3164
2337
  }
3165
2338
  });
3166
- Object.defineProperty(exports, "changedKeys", {
2339
+ Object.defineProperty(exports, "authorizeAnnotationUpdate", {
3167
2340
  enumerable: true,
3168
2341
  get: function() {
3169
- return changedKeys;
2342
+ return authorizeAnnotationUpdate;
3170
2343
  }
3171
2344
  });
3172
- Object.defineProperty(exports, "changesAnnotationList", {
2345
+ Object.defineProperty(exports, "authorizeCapability", {
3173
2346
  enumerable: true,
3174
2347
  get: function() {
3175
- return changesAnnotationList;
2348
+ return authorizeCapability;
3176
2349
  }
3177
2350
  });
3178
- Object.defineProperty(exports, "collab", {
2351
+ Object.defineProperty(exports, "authorizeFieldGroup", {
3179
2352
  enumerable: true,
3180
2353
  get: function() {
3181
- return collab;
2354
+ return authorizeFieldGroup;
3182
2355
  }
3183
2356
  });
3184
- Object.defineProperty(exports, "combine", {
2357
+ Object.defineProperty(exports, "authorizeFieldWrite", {
3185
2358
  enumerable: true,
3186
2359
  get: function() {
3187
- return combine;
2360
+ return authorizeFieldWrite;
3188
2361
  }
3189
2362
  });
3190
- Object.defineProperty(exports, "conclude", {
2363
+ Object.defineProperty(exports, "authorizeUnprotected", {
3191
2364
  enumerable: true,
3192
2365
  get: function() {
3193
- return conclude;
2366
+ return authorizeUnprotected;
3194
2367
  }
3195
2368
  });
3196
- Object.defineProperty(exports, "createPageImageHandle", {
2369
+ Object.defineProperty(exports, "buildSnippet", {
3197
2370
  enumerable: true,
3198
2371
  get: function() {
3199
- return createPageImageHandle;
2372
+ return buildSnippet;
3200
2373
  }
3201
2374
  });
3202
- Object.defineProperty(exports, "decodeFieldRefKey", {
2375
+ Object.defineProperty(exports, "canonicalSearchQuery", {
3203
2376
  enumerable: true,
3204
2377
  get: function() {
3205
- return decodeFieldRefKey;
2378
+ return canonicalSearchQuery;
3206
2379
  }
3207
2380
  });
3208
- Object.defineProperty(exports, "decodePdfBits", {
2381
+ Object.defineProperty(exports, "changeFingerprint", {
3209
2382
  enumerable: true,
3210
2383
  get: function() {
3211
- return decodePdfBits;
2384
+ return changeFingerprint;
3212
2385
  }
3213
2386
  });
3214
- Object.defineProperty(exports, "decodeStableIdKey", {
2387
+ Object.defineProperty(exports, "changedKeys", {
3215
2388
  enumerable: true,
3216
2389
  get: function() {
3217
- return decodeStableIdKey;
2390
+ return changedKeys;
3218
2391
  }
3219
2392
  });
3220
- Object.defineProperty(exports, "decodeSubmitFormFlags", {
2393
+ Object.defineProperty(exports, "checkImageQuality", {
3221
2394
  enumerable: true,
3222
2395
  get: function() {
3223
- return decodeSubmitFormFlags;
2396
+ return checkImageQuality;
3224
2397
  }
3225
2398
  });
3226
- Object.defineProperty(exports, "dictEntries", {
2399
+ Object.defineProperty(exports, "closeExportSelection", {
3227
2400
  enumerable: true,
3228
2401
  get: function() {
3229
- return dictEntries;
2402
+ return closeExportSelection;
3230
2403
  }
3231
2404
  });
3232
- Object.defineProperty(exports, "encodeFieldRefKey", {
2405
+ Object.defineProperty(exports, "combine", {
3233
2406
  enumerable: true,
3234
2407
  get: function() {
3235
- return encodeFieldRefKey;
2408
+ return combine;
3236
2409
  }
3237
2410
  });
3238
- Object.defineProperty(exports, "encodeStableIdKey", {
2411
+ Object.defineProperty(exports, "concatAnnotationLists", {
3239
2412
  enumerable: true,
3240
2413
  get: function() {
3241
- return encodeStableIdKey;
2414
+ return concatAnnotationLists;
3242
2415
  }
3243
2416
  });
3244
- Object.defineProperty(exports, "evaluateStep", {
2417
+ Object.defineProperty(exports, "conclude", {
3245
2418
  enumerable: true,
3246
2419
  get: function() {
3247
- return evaluateStep;
2420
+ return conclude;
3248
2421
  }
3249
2422
  });
3250
- Object.defineProperty(exports, "expandTextRangeToLine", {
2423
+ Object.defineProperty(exports, "createPageImageHandle", {
3251
2424
  enumerable: true,
3252
2425
  get: function() {
3253
- return expandTextRangeToLine;
2426
+ return createPageImageHandle;
3254
2427
  }
3255
2428
  });
3256
- Object.defineProperty(exports, "expandTextRangeToWord", {
2429
+ Object.defineProperty(exports, "decodePdfBits", {
3257
2430
  enumerable: true,
3258
2431
  get: function() {
3259
- return expandTextRangeToWord;
2432
+ return decodePdfBits;
3260
2433
  }
3261
2434
  });
3262
- Object.defineProperty(exports, "foldOptionsFor", {
2435
+ Object.defineProperty(exports, "deletedAnnotationsOf", {
3263
2436
  enumerable: true,
3264
2437
  get: function() {
3265
- return foldOptionsFor;
2438
+ return deletedAnnotationsOf;
3266
2439
  }
3267
2440
  });
3268
- Object.defineProperty(exports, "invalidatesWeakIndexRefs", {
2441
+ Object.defineProperty(exports, "dictEntries", {
3269
2442
  enumerable: true,
3270
2443
  get: function() {
3271
- return invalidatesWeakIndexRefs;
2444
+ return dictEntries;
3272
2445
  }
3273
2446
  });
3274
- Object.defineProperty(exports, "isStateAnnotation", {
2447
+ Object.defineProperty(exports, "draftWritesScripts", {
3275
2448
  enumerable: true,
3276
2449
  get: function() {
3277
- return isStateAnnotation;
2450
+ return draftWritesScripts;
3278
2451
  }
3279
2452
  });
3280
- Object.defineProperty(exports, "isValidPageObjectNumber", {
2453
+ Object.defineProperty(exports, "evaluateStep", {
3281
2454
  enumerable: true,
3282
2455
  get: function() {
3283
- return isValidPageObjectNumber;
2456
+ return evaluateStep;
3284
2457
  }
3285
2458
  });
3286
- Object.defineProperty(exports, "knownWeakAnnotationState", {
2459
+ Object.defineProperty(exports, "familyOfSubtype", {
3287
2460
  enumerable: true,
3288
2461
  get: function() {
3289
- return knownWeakAnnotationState;
2462
+ return familyOfSubtype;
3290
2463
  }
3291
2464
  });
3292
- Object.defineProperty(exports, "matchLiteral", {
2465
+ Object.defineProperty(exports, "generateUuid", {
3293
2466
  enumerable: true,
3294
2467
  get: function() {
3295
- return matchLiteral;
2468
+ return generateUuid;
3296
2469
  }
3297
2470
  });
3298
- Object.defineProperty(exports, "matchPageText", {
2471
+ Object.defineProperty(exports, "generateUuidV7", {
3299
2472
  enumerable: true,
3300
2473
  get: function() {
3301
- return matchPageText;
2474
+ return generateUuidV7;
3302
2475
  }
3303
2476
  });
3304
- Object.defineProperty(exports, "matchRegex", {
2477
+ Object.defineProperty(exports, "imageSourceOfBytes", {
3305
2478
  enumerable: true,
3306
2479
  get: function() {
3307
- return matchRegex;
2480
+ return imageSourceOfBytes;
3308
2481
  }
3309
2482
  });
3310
- Object.defineProperty(exports, "materializePdfPermissions", {
2483
+ Object.defineProperty(exports, "isKeptRefusal", {
3311
2484
  enumerable: true,
3312
2485
  get: function() {
3313
- return materializePdfPermissions;
2486
+ return isKeptRefusal;
3314
2487
  }
3315
2488
  });
3316
- Object.defineProperty(exports, "normalizeAnnotationDraft", {
2489
+ Object.defineProperty(exports, "matchPageText", {
3317
2490
  enumerable: true,
3318
2491
  get: function() {
3319
- return normalizeAnnotationDraft;
2492
+ return matchPageText;
3320
2493
  }
3321
2494
  });
3322
- Object.defineProperty(exports, "normalizeAnnotationPatch", {
2495
+ Object.defineProperty(exports, "mergeAnnotationPatch", {
3323
2496
  enumerable: true,
3324
2497
  get: function() {
3325
- return normalizeAnnotationPatch;
2498
+ return mergeAnnotationPatch;
3326
2499
  }
3327
2500
  });
3328
2501
  Object.defineProperty(exports, "normalizeAttachmentFileSource", {
@@ -3331,166 +2504,166 @@ Object.defineProperty(exports, "normalizeAttachmentFileSource", {
3331
2504
  return normalizeAttachmentFileSource;
3332
2505
  }
3333
2506
  });
3334
- Object.defineProperty(exports, "parsePdfValue", {
2507
+ Object.defineProperty(exports, "opIdOf", {
3335
2508
  enumerable: true,
3336
2509
  get: function() {
3337
- return parsePdfValue;
2510
+ return opIdOf;
3338
2511
  }
3339
2512
  });
3340
- Object.defineProperty(exports, "pdfPermissions", {
2513
+ Object.defineProperty(exports, "pageAppearancesOf", {
3341
2514
  enumerable: true,
3342
2515
  get: function() {
3343
- return pdfPermissions;
2516
+ return pageAppearancesOf;
3344
2517
  }
3345
2518
  });
3346
- Object.defineProperty(exports, "pdfValueEquals", {
2519
+ Object.defineProperty(exports, "pageBoxesOf", {
3347
2520
  enumerable: true,
3348
2521
  get: function() {
3349
- return pdfValueEquals;
2522
+ return pageBoxesOf;
3350
2523
  }
3351
2524
  });
3352
- Object.defineProperty(exports, "primaryFinding", {
2525
+ Object.defineProperty(exports, "pageFieldActionsOf", {
3353
2526
  enumerable: true,
3354
2527
  get: function() {
3355
- return primaryFinding;
2528
+ return pageFieldActionsOf;
3356
2529
  }
3357
2530
  });
3358
- Object.defineProperty(exports, "refsOf", {
2531
+ Object.defineProperty(exports, "pageFormFieldOf", {
3359
2532
  enumerable: true,
3360
2533
  get: function() {
3361
- return refsOf;
2534
+ return pageFormFieldOf;
3362
2535
  }
3363
2536
  });
3364
- Object.defineProperty(exports, "resolveBinarySource", {
2537
+ Object.defineProperty(exports, "pageFormSnapshotOf", {
3365
2538
  enumerable: true,
3366
2539
  get: function() {
3367
- return resolveBinarySource;
2540
+ return pageFormSnapshotOf;
3368
2541
  }
3369
2542
  });
3370
- Object.defineProperty(exports, "restrictionsFor", {
2543
+ Object.defineProperty(exports, "pageLayoutOf", {
3371
2544
  enumerable: true,
3372
2545
  get: function() {
3373
- return restrictionsFor;
2546
+ return pageLayoutOf;
3374
2547
  }
3375
2548
  });
3376
- Object.defineProperty(exports, "restrictionsOf", {
2549
+ Object.defineProperty(exports, "pageListOf", {
3377
2550
  enumerable: true,
3378
2551
  get: function() {
3379
- return restrictionsOf;
2552
+ return pageListOf;
3380
2553
  }
3381
2554
  });
3382
- Object.defineProperty(exports, "revisionTokensEqual", {
2555
+ Object.defineProperty(exports, "pageRotationOf", {
3383
2556
  enumerable: true,
3384
2557
  get: function() {
3385
- return revisionTokensEqual;
2558
+ return pageRotationOf;
3386
2559
  }
3387
2560
  });
3388
- Object.defineProperty(exports, "richTextParagraphsFromPlainText", {
2561
+ Object.defineProperty(exports, "pageSpaceBoxesOf", {
3389
2562
  enumerable: true,
3390
2563
  get: function() {
3391
- return richTextParagraphsFromPlainText;
2564
+ return pageSpaceBoxesOf;
3392
2565
  }
3393
2566
  });
3394
- Object.defineProperty(exports, "richTextPlainText", {
2567
+ Object.defineProperty(exports, "pageWidgetOf", {
3395
2568
  enumerable: true,
3396
2569
  get: function() {
3397
- return richTextPlainText;
2570
+ return pageWidgetOf;
3398
2571
  }
3399
2572
  });
3400
- Object.defineProperty(exports, "sameEffectiveValue", {
2573
+ Object.defineProperty(exports, "parsePdfValue", {
3401
2574
  enumerable: true,
3402
2575
  get: function() {
3403
- return sameEffectiveValue;
2576
+ return parsePdfValue;
3404
2577
  }
3405
2578
  });
3406
- Object.defineProperty(exports, "searchContentEpoch", {
2579
+ Object.defineProperty(exports, "pdfFormFieldDraftOf", {
3407
2580
  enumerable: true,
3408
2581
  get: function() {
3409
- return searchContentEpoch;
2582
+ return pdfFormFieldDraftOf;
3410
2583
  }
3411
2584
  });
3412
- Object.defineProperty(exports, "searchRectsForRange", {
2585
+ Object.defineProperty(exports, "pdfRenderTargetOf", {
3413
2586
  enumerable: true,
3414
2587
  get: function() {
3415
- return searchRectsForRange;
2588
+ return pdfRenderTargetOf;
3416
2589
  }
3417
2590
  });
3418
- Object.defineProperty(exports, "searchSegmentsForRange", {
2591
+ Object.defineProperty(exports, "pdfValueEquals", {
3419
2592
  enumerable: true,
3420
2593
  get: function() {
3421
- return searchSegmentsForRange;
2594
+ return pdfValueEquals;
3422
2595
  }
3423
2596
  });
3424
- Object.defineProperty(exports, "semanticEqual", {
2597
+ Object.defineProperty(exports, "pdfWidgetPlacementOf", {
3425
2598
  enumerable: true,
3426
2599
  get: function() {
3427
- return semanticEqual;
2600
+ return pdfWidgetPlacementOf;
3428
2601
  }
3429
2602
  });
3430
- Object.defineProperty(exports, "shiftsExistingAnnotationIndices", {
2603
+ Object.defineProperty(exports, "placedWidgetOf", {
3431
2604
  enumerable: true,
3432
2605
  get: function() {
3433
- return shiftsExistingAnnotationIndices;
2606
+ return placedWidgetOf;
3434
2607
  }
3435
2608
  });
3436
- Object.defineProperty(exports, "sniffBinaryMetadata", {
2609
+ Object.defineProperty(exports, "primaryFinding", {
3437
2610
  enumerable: true,
3438
2611
  get: function() {
3439
- return sniffBinaryMetadata;
2612
+ return primaryFinding;
3440
2613
  }
3441
2614
  });
3442
- Object.defineProperty(exports, "stableStringify", {
2615
+ Object.defineProperty(exports, "refsOf", {
3443
2616
  enumerable: true,
3444
2617
  get: function() {
3445
- return stableStringify;
2618
+ return refsOf;
3446
2619
  }
3447
2620
  });
3448
- Object.defineProperty(exports, "textGlyphAt", {
2621
+ Object.defineProperty(exports, "resolvePageLayers", {
3449
2622
  enumerable: true,
3450
2623
  get: function() {
3451
- return textGlyphAt;
2624
+ return resolvePageLayers;
3452
2625
  }
3453
2626
  });
3454
- Object.defineProperty(exports, "textGlyphQuad", {
2627
+ Object.defineProperty(exports, "restrictionsFor", {
3455
2628
  enumerable: true,
3456
2629
  get: function() {
3457
- return textGlyphQuad;
2630
+ return restrictionsFor;
3458
2631
  }
3459
2632
  });
3460
- Object.defineProperty(exports, "textSegmentsForRange", {
2633
+ Object.defineProperty(exports, "restrictionsOf", {
3461
2634
  enumerable: true,
3462
2635
  get: function() {
3463
- return textSegmentsForRange;
2636
+ return restrictionsOf;
3464
2637
  }
3465
2638
  });
3466
- Object.defineProperty(exports, "validateSearchQuery", {
2639
+ Object.defineProperty(exports, "sameEffectiveValue", {
3467
2640
  enumerable: true,
3468
2641
  get: function() {
3469
- return validateSearchQuery;
2642
+ return sameEffectiveValue;
3470
2643
  }
3471
2644
  });
3472
- Object.defineProperty(exports, "validateSearchRegex", {
2645
+ Object.defineProperty(exports, "searchContentEpoch", {
3473
2646
  enumerable: true,
3474
2647
  get: function() {
3475
- return validateSearchRegex;
2648
+ return searchContentEpoch;
3476
2649
  }
3477
2650
  });
3478
- Object.defineProperty(exports, "viewportForPoint", {
2651
+ Object.defineProperty(exports, "searchQueryOf", {
3479
2652
  enumerable: true,
3480
2653
  get: function() {
3481
- return viewportForPoint;
2654
+ return searchQueryOf;
3482
2655
  }
3483
2656
  });
3484
- Object.defineProperty(exports, "wordAt", {
2657
+ Object.defineProperty(exports, "stableStringify", {
3485
2658
  enumerable: true,
3486
2659
  get: function() {
3487
- return wordAt;
2660
+ return stableStringify;
3488
2661
  }
3489
2662
  });
3490
- Object.defineProperty(exports, "wordBefore", {
2663
+ Object.defineProperty(exports, "visibleBoxesOf", {
3491
2664
  enumerable: true,
3492
2665
  get: function() {
3493
- return wordBefore;
2666
+ return visibleBoxesOf;
3494
2667
  }
3495
2668
  });
3496
2669
  Object.defineProperty(exports, "worstVerdict", {
@@ -3500,4 +2673,4 @@ Object.defineProperty(exports, "worstVerdict", {
3500
2673
  }
3501
2674
  });
3502
2675
 
3503
- //# sourceMappingURL=viewport-BWeNHZmb.cjs.map
2676
+ //# sourceMappingURL=authority-DLMNR7ff.cjs.map