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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/AnnotationList-CkijRHet.d.cts +27163 -0
  2. package/dist/AnnotationList-CkijRHet.d.ts +27163 -0
  3. package/dist/Change-CKoxfTNY.js +889 -0
  4. package/dist/Change-CKoxfTNY.js.map +1 -0
  5. package/dist/Change-CjImsrN0.cjs +1020 -0
  6. package/dist/Change-CjImsrN0.cjs.map +1 -0
  7. package/dist/DocumentRenderService-BfFdFSwI.cjs +120 -0
  8. package/dist/DocumentRenderService-BfFdFSwI.cjs.map +1 -0
  9. package/dist/DocumentRenderService-DV3J9lMz.js +85 -0
  10. package/dist/DocumentRenderService-DV3J9lMz.js.map +1 -0
  11. package/dist/{DocumentSecurityService-_JRjqvoI.d.ts → DocumentSecurityService-CU1M6Q7m.d.ts} +100 -112
  12. package/dist/{DocumentSecurityService-ClTZc0zO.d.cts → DocumentSecurityService-e6vp2de8.d.cts} +100 -112
  13. package/dist/LocalDocumentHandle-D02FMm-Z.cjs +53 -0
  14. package/dist/LocalDocumentHandle-D02FMm-Z.cjs.map +1 -0
  15. package/dist/LocalDocumentHandle-DAPdaOt_.js +30 -0
  16. package/dist/LocalDocumentHandle-DAPdaOt_.js.map +1 -0
  17. package/dist/LocalEngine-DFoBm4B3.d.cts +1565 -0
  18. package/dist/LocalEngine-DPriFujS.d.ts +1565 -0
  19. package/dist/PageInsertBlankInput-CXHAM7l6.cjs +17 -0
  20. package/dist/PageInsertBlankInput-CXHAM7l6.cjs.map +1 -0
  21. package/dist/PageInsertBlankInput-DhVYVJ_S.js +12 -0
  22. package/dist/PageInsertBlankInput-DhVYVJ_S.js.map +1 -0
  23. package/dist/authority-B3BklsB4.js +2239 -0
  24. package/dist/authority-B3BklsB4.js.map +1 -0
  25. package/dist/authority-DLMNR7ff.cjs +2676 -0
  26. package/dist/authority-DLMNR7ff.cjs.map +1 -0
  27. package/dist/builders-CNj0POeY.js +161 -0
  28. package/dist/builders-CNj0POeY.js.map +1 -0
  29. package/dist/builders-Cgdp_YCn.cjs +184 -0
  30. package/dist/builders-Cgdp_YCn.cjs.map +1 -0
  31. package/dist/calibrate-7T3fnP8-.js +2404 -0
  32. package/dist/calibrate-7T3fnP8-.js.map +1 -0
  33. package/dist/calibrate-DJBXnhLE.cjs +2919 -0
  34. package/dist/calibrate-DJBXnhLE.cjs.map +1 -0
  35. package/dist/charmap-Cprti1ta.js +239 -0
  36. package/dist/charmap-Cprti1ta.js.map +1 -0
  37. package/dist/charmap-aYlefQP9.cjs +304 -0
  38. package/dist/charmap-aYlefQP9.cjs.map +1 -0
  39. package/dist/conformance.cjs +16753 -5247
  40. package/dist/conformance.cjs.map +1 -1
  41. package/dist/conformance.d.cts +928 -165
  42. package/dist/conformance.d.ts +928 -165
  43. package/dist/conformance.js +16731 -5268
  44. package/dist/conformance.js.map +1 -1
  45. package/dist/format-Bp3QFSH1.cjs +574 -0
  46. package/dist/format-Bp3QFSH1.cjs.map +1 -0
  47. package/dist/format-Dgg428Kb.js +461 -0
  48. package/dist/format-Dgg428Kb.js.map +1 -0
  49. package/dist/index.cjs +330 -103
  50. package/dist/index.d.cts +2 -2
  51. package/dist/index.d.ts +2 -2
  52. package/dist/index.js +14 -5
  53. package/dist/pageRefs-B3ur7Ckm.cjs +51 -0
  54. package/dist/pageRefs-B3ur7Ckm.cjs.map +1 -0
  55. package/dist/pageRefs-DkbuxSCV.js +40 -0
  56. package/dist/pageRefs-DkbuxSCV.js.map +1 -0
  57. package/dist/public.cjs +57 -0
  58. package/dist/public.d.cts +5 -0
  59. package/dist/public.d.ts +5 -0
  60. package/dist/public.js +9 -0
  61. package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs +3646 -0
  62. package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs.map +1 -0
  63. package/dist/resolveAnnotationDraft-CUpf0SMh.js +3167 -0
  64. package/dist/resolveAnnotationDraft-CUpf0SMh.js.map +1 -0
  65. package/dist/{resolver-inAVzsWt.js → resolver-CzxmD9Ck.js} +294 -144
  66. package/dist/resolver-CzxmD9Ck.js.map +1 -0
  67. package/dist/{resolver-CYVNV4hn.cjs → resolver-D2OCtE8F.cjs} +347 -143
  68. package/dist/resolver-D2OCtE8F.cjs.map +1 -0
  69. package/dist/runtime-Cl0v56mm.d.ts +2032 -0
  70. package/dist/runtime-p1WDzDo4.d.cts +2032 -0
  71. package/dist/runtime.cjs +733 -284
  72. package/dist/runtime.cjs.map +1 -1
  73. package/dist/runtime.d.cts +5 -1348
  74. package/dist/runtime.d.ts +5 -1348
  75. package/dist/runtime.js +396 -179
  76. package/dist/runtime.js.map +1 -1
  77. package/dist/schema-C-gU4vj7.cjs +3034 -0
  78. package/dist/schema-C-gU4vj7.cjs.map +1 -0
  79. package/dist/schema-DzlUNk38.js +2081 -0
  80. package/dist/schema-DzlUNk38.js.map +1 -0
  81. package/dist/schemas-Berf1swF.cjs +2481 -0
  82. package/dist/schemas-Berf1swF.cjs.map +1 -0
  83. package/dist/schemas-D6xOGjqg.js +1654 -0
  84. package/dist/schemas-D6xOGjqg.js.map +1 -0
  85. package/dist/subtype-CG4WpIRj.js +129 -0
  86. package/dist/subtype-CG4WpIRj.js.map +1 -0
  87. package/dist/subtype-aDt5TFmK.cjs +158 -0
  88. package/dist/subtype-aDt5TFmK.cjs.map +1 -0
  89. package/dist/validation.cjs +578 -0
  90. package/dist/validation.cjs.map +1 -0
  91. package/dist/validation.d.cts +174 -0
  92. package/dist/validation.d.ts +174 -0
  93. package/dist/validation.js +571 -0
  94. package/dist/validation.js.map +1 -0
  95. package/dist/value-BFS1R8ek.cjs +435 -0
  96. package/dist/value-BFS1R8ek.cjs.map +1 -0
  97. package/dist/value-Dhc8eYcx.js +340 -0
  98. package/dist/value-Dhc8eYcx.js.map +1 -0
  99. package/dist/viewport-BJW13EGZ.js +540 -0
  100. package/dist/viewport-BJW13EGZ.js.map +1 -0
  101. package/dist/viewport-DlL5Bxbg.cjs +671 -0
  102. package/dist/viewport-DlL5Bxbg.cjs.map +1 -0
  103. package/dist/wire.cjs +1056 -379
  104. package/dist/wire.cjs.map +1 -1
  105. package/dist/wire.d.cts +15069 -725
  106. package/dist/wire.d.ts +15069 -725
  107. package/dist/wire.js +825 -239
  108. package/dist/wire.js.map +1 -1
  109. package/package.json +10 -1
  110. package/dist/AbortError-Cq2igpZ6.cjs +0 -38
  111. package/dist/AbortError-Cq2igpZ6.cjs.map +0 -1
  112. package/dist/AbortError-DHqPBnYc.js +0 -27
  113. package/dist/AbortError-DHqPBnYc.js.map +0 -1
  114. package/dist/AnnotationListSnapshot-D3mFut1e.d.cts +0 -6871
  115. package/dist/AnnotationListSnapshot-D3mFut1e.d.ts +0 -6871
  116. package/dist/Engine-Bx0NOfmd.d.cts +0 -1264
  117. package/dist/Engine-DxtpCYcP.d.ts +0 -1264
  118. package/dist/PageInsertBlankInput-CqXBmFhN.cjs +0 -449
  119. package/dist/PageInsertBlankInput-CqXBmFhN.cjs.map +0 -1
  120. package/dist/PageInsertBlankInput-cHq9-i-z.js +0 -378
  121. package/dist/PageInsertBlankInput-cHq9-i-z.js.map +0 -1
  122. package/dist/builders-Bqm8yTpi.cjs +0 -3564
  123. package/dist/builders-Bqm8yTpi.cjs.map +0 -1
  124. package/dist/builders-Dqo2X-dt.js +0 -3121
  125. package/dist/builders-Dqo2X-dt.js.map +0 -1
  126. package/dist/fold-2V29cxCV.cjs +0 -447
  127. package/dist/fold-2V29cxCV.cjs.map +0 -1
  128. package/dist/fold-CdEJTBTn.js +0 -334
  129. package/dist/fold-CdEJTBTn.js.map +0 -1
  130. package/dist/resolver-CYVNV4hn.cjs.map +0 -1
  131. package/dist/resolver-inAVzsWt.js.map +0 -1
  132. package/dist/schemas-BLARNCgx.js +0 -3113
  133. package/dist/schemas-BLARNCgx.js.map +0 -1
  134. package/dist/schemas-DIqFMmrC.cjs +0 -4468
  135. package/dist/schemas-DIqFMmrC.cjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { $u as PdfRect, An as HighlightDraft, Ju as InkList, Mc as CharMapAnchor, Xu as LinePoints, Zu as PdfPoint, n as AnnotationListSnapshotAllPages, t as AnnotationListPageSnapshot, yd as DocumentMetadata } from "./AnnotationListSnapshot-D3mFut1e.js";
2
- import { t as Engine } from "./Engine-DxtpCYcP.js";
1
+ import { E_ as Identity, Ep as CharMapAnchor, Jg as PdfRect, Nd as AnnotationResources, Ng as PageBox, Pg as PagePoint, Wm as PageTabOrder, Yg as PdfRotation, _t as LineDraft, _v as DocumentMetadata, bs as AnnotationDraft, dn as HighlightDraft, nt as InkDraft, t as AnnotationList } from "./AnnotationList-CkijRHet.js";
2
+ import { _ as Engine, t as LocalEngine, y as DocumentHandle } from "./LocalEngine-DPriFujS.js";
3
3
  //#region src/conformance/runMetadataConformance.d.ts
4
4
  interface ConformanceTestRunner {
5
5
  describe(name: string, fn: () => void): void;
@@ -54,6 +54,10 @@ interface ConformanceOptions {
54
54
  */
55
55
  declare function runMetadataConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
56
56
  //#endregion
57
+ //#region src/conformance/runMeasurementConformance.d.ts
58
+ /** Shared numerical, persistence and calibration contract for local and HTTP engines. */
59
+ declare function runMeasurementConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
60
+ //#endregion
57
61
  //#region src/conformance/runAnnotationReadConformance.d.ts
58
62
  /**
59
63
  * Expected per-fixture annotation knowledge. The harness asserts against
@@ -86,10 +90,10 @@ interface AnnotationReadConformanceFixture extends ConformanceFixture {
86
90
  /** At least this many `'caret'` annotations on the page. Defaults to 0. */
87
91
  minCaretCount?: number;
88
92
  /**
89
- * `true` if the fixture has at least one weak annotation (no /NM, direct
90
- * object). Drives the weak-ref + revision tests.
93
+ * `true` if the fixture page has at least one annotation born inline (a
94
+ * dictionary in `/Annots`, no object number). Drives the `baseIndex` tests.
91
95
  */
92
- expectsWeakAnnotation: boolean;
96
+ expectsInlineAnnotation: boolean;
93
97
  }
94
98
  interface AnnotationConformanceOptions extends Omit<ConformanceOptions, 'fixture'> {
95
99
  fixture: AnnotationReadConformanceFixture;
@@ -103,7 +107,7 @@ interface ActionsConformanceFixtures {
103
107
  annotation: ConformanceFixture;
104
108
  field: ConformanceFixture;
105
109
  /** `action_payloads.pdf` — every executable payload shape on one page of
106
- * /NM-keyed links. REQUIRED on both flavours: payload parity is a gate. */
110
+ * /NM-keyed links. Required on both flavours: payload parity is a gate. */
107
111
  payloads: ConformanceFixture;
108
112
  /** `open_action_dest.pdf` — a destination-form catalog `/OpenAction`. */
109
113
  openDestination: ConformanceFixture;
@@ -131,46 +135,37 @@ declare function runActionsConformance(runner: ConformanceTestRunner, opts: Acti
131
135
  interface AnnotationMutationConformanceFixture extends ConformanceFixture {
132
136
  /** PDF object number of the page used by the mutation tests. */
133
137
  pageObjectNumber: number;
134
- /** Page already has at least one weak annotation (no /NM, direct object). */
135
- expectsWeakAnnotation: boolean;
138
+ /** The page has at least one annotation born inline (a dictionary in `/Annots`, no object number). */
139
+ expectsInlineAnnotation: boolean;
136
140
  /**
137
- * QuadPoints to use for the create() smoke test. Coordinates are in
138
- * PDF user space; pick a small rectangle that fits anywhere on the
139
- * fixture page so we don't flake on different page sizes.
141
+ * QuadPoints to use for the create() smoke test, in page space; pick a
142
+ * small rectangle that fits anywhere on the fixture page so we don't
143
+ * flake on different page sizes.
140
144
  */
141
145
  createQuad?: HighlightDraft['quadPoints'];
142
146
  /**
143
- * `/Rect` to use for the shape (circle/square) create tests. PDF user
147
+ * The box to use for the shape (circle/square) create tests, in page
144
148
  * space; pick a small box that fits anywhere on the fixture page.
145
149
  */
146
- createShapeRect?: PdfRect;
150
+ createShapeRect?: PageBox;
147
151
  /**
148
- * `/Vertices` to use for the polygon/polyline create tests. PDF user
149
- * space; must fit inside `createShapeRect` (the rect the engine bakes
150
- * the appearance into). Defaults to a small triangle.
152
+ * `/Vertices` to use for the polygon/polyline create tests, in page
153
+ * space; must fit inside `createShapeRect`. Defaults to a small triangle.
151
154
  */
152
- createVertices?: PdfPoint[];
155
+ createVertices?: PagePoint[];
153
156
  /**
154
- * `/L` endpoints to use for the line create test. PDF user space; must
155
- * fit inside `createShapeRect`. Defaults to the rect's diagonal.
157
+ * `/L` endpoints to use for the line create test, in page space; must fit
158
+ * inside `createShapeRect`. Defaults to the rect's diagonal.
156
159
  */
157
- createLinePoints?: LinePoints;
160
+ createLinePoints?: LineDraft['linePoints'];
158
161
  /**
159
- * `/InkList` strokes to use for the ink create test. PDF user space; must
162
+ * `/InkList` strokes to use for the ink create test, in page space; must
160
163
  * fit inside `createShapeRect`. Defaults to a single short stroke.
161
164
  */
162
- createInkList?: InkList;
165
+ createInkList?: InkDraft['inkList'];
163
166
  }
164
167
  interface AnnotationMutationConformanceOptions extends Omit<ConformanceOptions, 'fixture'> {
165
168
  fixture: AnnotationMutationConformanceFixture;
166
- /**
167
- * `true` for engines that expose the raw RGBA appearance rasters
168
- * (`renderAppearances`). When set, the shape-create test additionally
169
- * asserts that creating a shape produces a baked `/AP` the appearance
170
- * reader can rasterize. The cloud engine ships encoded images instead
171
- * and leaves this `false`.
172
- */
173
- supportsAppearanceRasters?: boolean;
174
169
  }
175
170
  /**
176
171
  * Mutation conformance suite. Mirrors the read suite: tests are written
@@ -179,22 +174,220 @@ interface AnnotationMutationConformanceOptions extends Omit<ConformanceOptions,
179
174
  * @cloudpdf/server) implementations must pass identically.
180
175
  *
181
176
  * The locked rules being verified here:
182
- * - `create` is append-only: PDFium drops the new annotation at
183
- * `index = previousCount`, so no existing index shifts. Treated
184
- * as non-invalidating — revisions do NOT bump and weak refs
185
- * captured before the create remain valid.
186
- * - `update` is non-structural; revisions do NOT bump.
187
- * - Opportunistic /NM stamp upgrades a weak annotation's ref to
188
- * `kind: 'nm'` on update; an already-durable annotation's /NM is
189
- * NEVER touched.
190
- * - `delete` and `move` are the only index-shifting ops. They bump
191
- * the per-page revision and, on a page that had weak refs before
192
- * the mutation, surface `shouldRefetch: 'weakRefsInvalidated'`.
177
+ * - `create` is append-only: the new annotation paints on top of the
178
+ * page's others, and none of them moves.
179
+ * - Every annotation keeps its name through every write: an object
180
+ * number, or the `baseIndex` of one born inline in the file. No write
181
+ * stamps an /NM to name one.
193
182
  * - Abort propagates as `AbortError` even before the worker
194
183
  * responds.
195
184
  */
196
185
  declare function runAnnotationMutationConformance(runner: ConformanceTestRunner, opts: AnnotationMutationConformanceOptions): void;
197
186
  //#endregion
187
+ //#region src/conformance/runAnnotationAttributionConformance.d.ts
188
+ /** Who a session is and what it may do: open-time options locally, token claims on the cloud. */
189
+ interface AttributionSession {
190
+ readonly scope: ReadonlyArray<string>;
191
+ readonly identity: Identity;
192
+ }
193
+ interface AnnotationAttributionConformanceOptions {
194
+ label: string;
195
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
196
+ makeEngine: () => Promise<Engine> | Engine;
197
+ /**
198
+ * Open the authoring document, a document with annotations another tool
199
+ * wrote, as `session`. With `from`, the new session continues that open
200
+ * session's document and sees what it wrote (locally its downloaded bytes,
201
+ * on the cloud the same layer); the suite closes `from` afterwards.
202
+ */
203
+ openAs: (engine: Engine, session: AttributionSession, from?: DocumentHandle) => Promise<DocumentHandle>;
204
+ }
205
+ /**
206
+ * Attribution on both engines with the same identities:
207
+ * `create` stamps it from the session and ignores what the data says,
208
+ * `update` stamps only `modifiedAt` and `modifiedBy`, and a `groupId` other than
209
+ * the session's own passes the set-group check or is refused. The session's
210
+ * whole identity reaches `doc.security.identity`; only the name and the ids
211
+ * reach the annotation.
212
+ */
213
+ declare function runAnnotationAttributionConformance(runner: ConformanceTestRunner, opts: AnnotationAttributionConformanceOptions): void;
214
+ //#endregion
215
+ //#region src/conformance/runAnnotationResourceConformance.d.ts
216
+ /**
217
+ * The documents the suite opens, each a fresh copy:
218
+ *
219
+ * - `authoring`: any document; stamps are created on its first page.
220
+ * - `acrobat-stamps`: `stamp-opacity-acrobat.pdf`
221
+ * (`packages/engine/main/test/fixtures`): two stamps Acrobat saved at about
222
+ * 30% opacity, each appearance `/R0 gs /MWFOForm Do` with /R0 repeating the
223
+ * stamp's /CA.
224
+ * - `acrobat-rewrapped`: `stamp-rewrapped-acrobat.pdf`, stamps as EmbedPDF
225
+ * writes them after other tools edited them: `rewrapped-a`, turned 30
226
+ * degrees and set to 60% in Acrobat, under Acrobat's layer and four forms
227
+ * that only draw our wrapper; `rewrapped-b`, set to 100% in Acrobat, under
228
+ * one such form; `rewrapped-c`, our 35% layer with /CA dropped, 300 x 120.
229
+ * Each draws a 200 x 100 drawing, opaque where it paints.
230
+ * - `acrobat-roundtrip-60`, `acrobat-roundtrip-100`:
231
+ * `stamp-roundtrip-acrobat-{60,100}.pdf`, an "Approved" stamp made in the
232
+ * viewer, turned and set to 40%, then set to 60% and to 100% in Acrobat
233
+ * 26.2. Acrobat moves the form around our wrapper by 0.0008 pt, its box a
234
+ * fraction off the origin in its own arithmetic.
235
+ */
236
+ type AnnotationResourceFixture = 'authoring' | 'acrobat-stamps' | 'acrobat-rewrapped' | 'acrobat-roundtrip-60' | 'acrobat-roundtrip-100';
237
+ interface AnnotationResourceConformanceOptions {
238
+ label: string;
239
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
240
+ makeEngine: () => Promise<Engine> | Engine;
241
+ /** Open a fresh copy of a fixture, with `doc.download` among its scopes. */
242
+ open: (engine: Engine, fixture: AnnotationResourceFixture) => Promise<DocumentHandle>;
243
+ /** Open a fresh copy of a fixture with `scope` only: the import's rights tests run with it. */
244
+ openScoped?: (engine: Engine, fixture: AnnotationResourceFixture, scope: readonly string[]) => Promise<DocumentHandle>;
245
+ }
246
+ /**
247
+ * Resources beside an annotation's data, on both engines: `downloadResource`
248
+ * returns what `create(data, resources)` needs to make the same annotation
249
+ * again. A stamp's appearance is its drawing without the fit, rotation and
250
+ * opacity its data describes; a stamp another tool made is drawn as shown,
251
+ * in its own box, without a layer that only repeats its /CA. Stamps with
252
+ * the same artwork place one drawing. The opacity is painted once:
253
+ * in a copy, after a resize, and after a change.
254
+ */
255
+ declare function runAnnotationResourceConformance(runner: ConformanceTestRunner, opts: AnnotationResourceConformanceOptions): void;
256
+ //#endregion
257
+ //#region src/conformance/runAnnotationExportConformance.d.ts
258
+ /**
259
+ * `doc.annotations.export` on both engines: the annotations as a raw read
260
+ * returns them, each resource once under its hash, the same bytes
261
+ * `downloadResource` returns, and a selection with what it points at and, by
262
+ * default, its threads.
263
+ */
264
+ declare function runAnnotationExportConformance(runner: ConformanceTestRunner, opts: AnnotationResourceConformanceOptions): void;
265
+ //#endregion
266
+ //#region src/conformance/runAnnotationImportConformance.d.ts
267
+ /**
268
+ * `doc.annotations.import` on both engines, in `stamp` mode: a bundle comes
269
+ * back as the export had it, one fact per annotation as one transaction,
270
+ * what can't be carried left out and reported, pages mapped, and a refusal
271
+ * before anything is written. An import is a change: undone and redone with
272
+ * `doc.apply({ undoOf })`, answered again on a retry, and a restoring
273
+ * import's undo takes the import's rights.
274
+ */
275
+ declare function runAnnotationImportConformance(runner: ConformanceTestRunner, opts: AnnotationResourceConformanceOptions): void;
276
+ //#endregion
277
+ //#region src/conformance/runAnnotationTransferConformance.d.ts
278
+ interface AnnotationTransferConformanceOptions {
279
+ label: string;
280
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
281
+ makeEngine: () => Promise<Engine> | Engine;
282
+ /**
283
+ * The documents to round-trip. `'authoring'` is a document without
284
+ * annotations, which the suite fills with one annotation of every kind a
285
+ * create makes, two stamps sharing a drawing, a named comment with its
286
+ * popup and a reply, and a link.
287
+ */
288
+ fixtures: readonly string[];
289
+ /** Open a fresh copy of a fixture, with every scope, `doc.annotate.import` included. */
290
+ open: (engine: Engine, fixture: string) => Promise<DocumentHandle>;
291
+ }
292
+ /**
293
+ * Check annotation transfer over every fixture on both engines. The source
294
+ * bundle is imported into a copy without annotations, then exported again:
295
+ *
296
+ * - The normalized exports agree after unsupported fields are dropped, and
297
+ * every resource has the same bytes under the same id. A copy is drawn from
298
+ * its data, as Acrobat's FDF and XFDF carry it: only a stamp carries its
299
+ * drawing. So where the data is points, the box is the frame of our
300
+ * drawing, which holds the points, and an icon's box is our icon at the
301
+ * same anchor;
302
+ * - Restored attribution is the source's, while stamped attribution is
303
+ * the session's;
304
+ * - Every page keeps the source bundle's annotation order;
305
+ * - Replies, popups, their parents and link destinations point at the copies;
306
+ * - A stamp draws as its source, and in our own file every kind does,
307
+ * an edge moved by a fraction of a pixel aside (Acrobat moves a stamp's
308
+ * form by a fraction of a point, which the drawing leaves out);
309
+ * - A second import names nothing twice and adds no drawing already present.
310
+ * Export golden files and forced-failure tests cover the remaining cases.
311
+ */
312
+ declare function runAnnotationTransferConformance(runner: ConformanceTestRunner, opts: AnnotationTransferConformanceOptions): void;
313
+ //#endregion
314
+ //#region src/conformance/creatables.d.ts
315
+ /** A create's two arguments. */
316
+ interface Creatable {
317
+ data: AnnotationDraft;
318
+ resources?: AnnotationResources;
319
+ }
320
+ /** A note's or a file's icon at its usual 20 × 20, by its top-left corner. */
321
+ declare function iconRect(x: number, y: number): PageBox;
322
+ /** One create for every kind the engine can create, inside a box near the page's top-left. */
323
+ declare function creatables(): Creatable[];
324
+ //#endregion
325
+ //#region src/conformance/runDateConformance.d.ts
326
+ interface DateConformanceOptions {
327
+ label: string;
328
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
329
+ makeEngine: () => Promise<Engine> | Engine;
330
+ /**
331
+ * Open a fresh copy of `dates.pdf` (`packages/engine/main/test/fixtures`):
332
+ * one page (object 3) whose squares carry known `/CreationDate` and `/M`
333
+ * values, a file attachment annotation and an embedded file whose
334
+ * `/Params` carry dates, and an Info dictionary with dates.
335
+ */
336
+ open: (engine: Engine) => Promise<DocumentHandle>;
337
+ }
338
+ /**
339
+ * Dates on both engines: a PDF date reads as ISO 8601 with the offset it was
340
+ * written with, a read written back is the same moment in the same zone, a
341
+ * write takes the string or a `Date`, and a date that isn't one reads `null`.
342
+ */
343
+ declare function runDateConformance(runner: ConformanceTestRunner, opts: DateConformanceOptions): void;
344
+ //#endregion
345
+ //#region src/conformance/runAnnotationDeclarationConformance.d.ts
346
+ interface AnnotationDeclarationFixture {
347
+ /** Stable id used for the local engine; cloud uses its own id. */
348
+ id: string;
349
+ /** Bytes for the local engine. */
350
+ bytes: () => Uint8Array | Promise<Uint8Array>;
351
+ /** Override the cloud-side id. Defaults to `id`. */
352
+ cloudId?: string;
353
+ }
354
+ interface AnnotationDeclarationConformanceOptions {
355
+ label: string;
356
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
357
+ makeEngine: () => Promise<Engine> | Engine;
358
+ /** Engine 'kind' for opening: 'bytes' for local, 'id' for cloud. */
359
+ openKind: 'bytes' | 'id';
360
+ /**
361
+ * A document without inline annotations. One annotation of every kind the
362
+ * engine can create is added to its first page, then read back.
363
+ */
364
+ authoring: AnnotationDeclarationFixture;
365
+ /** Documents whose annotations are read as they are. */
366
+ documents: readonly AnnotationDeclarationFixture[];
367
+ /** A document with a link whose target a write can't make (a script, a viewer command). */
368
+ readOnlyLink: AnnotationDeclarationFixture;
369
+ }
370
+ /**
371
+ * Every annotation both engines read, from real documents and freshly created
372
+ * ones, is checked against its kind declaration.
373
+ */
374
+ declare function runAnnotationDeclarationConformance(runner: ConformanceTestRunner, opts: AnnotationDeclarationConformanceOptions): void;
375
+ //#endregion
376
+ //#region src/conformance/annotationReadDrift.d.ts
377
+ /**
378
+ * Compares annotation reads with their kind declarations. Each difference is
379
+ * one line, `<field>: <problem> (<kinds>)`:
380
+ *
381
+ * - `missing`: the declaration has the field, the read doesn't;
382
+ * - `invalid`: the read has a value the declaration doesn't accept;
383
+ * - `undeclared`: the read has a field the declaration doesn't know.
384
+ *
385
+ * `<kinds>` is `every kind` when the difference holds for every kind read,
386
+ * otherwise the affected subtypes. Lines are sorted, so two engines reading
387
+ * the same documents produce the same list.
388
+ */
389
+ declare function annotationReadDriftOf(reads: readonly unknown[]): string[];
390
+ //#endregion
198
391
  //#region src/conformance/runFormConformance.d.ts
199
392
  /**
200
393
  * The three fixtures the forms suite needs. Shapes (names, object numbers,
@@ -206,7 +399,7 @@ declare function runAnnotationMutationConformance(runner: ConformanceTestRunner,
206
399
  * `opt_check` (/Opt "Alpha", on-state "On"), hierarchical text field
207
400
  * `billing.name`.
208
401
  * - `orphanWidgets` — orphan_widgets.pdf: `linked_text` in /AcroForm
209
- * /Fields plus RECOVERED `orphan_check` and `orphan_radio`.
402
+ * /Fields plus recovered `orphan_check` and `orphan_radio`.
210
403
  * - `choiceFields` — listbox_form.pdf: `Listbox_MultiSelect` (options
211
404
  * Apple..), `Listbox_SingleSelect`.
212
405
  */
@@ -224,6 +417,12 @@ interface FormConformanceFixtures {
224
417
  * same bytes under a suffixed id the way the bytes transport does.
225
418
  */
226
419
  importTarget?: ConformanceFixture;
420
+ /**
421
+ * Another independent copy of `toggleFields`, whose merged field/widgets
422
+ * the delete test removes. Required for `openKind: 'id'` (cloud): the
423
+ * deletes are durable server state the other tests must not see.
424
+ */
425
+ deleteTarget?: ConformanceFixture;
227
426
  }
228
427
  interface FormConformanceOptions extends Omit<ConformanceOptions, 'fixture'> {
229
428
  fixtures: FormConformanceFixtures;
@@ -235,7 +434,7 @@ declare function runFormConformance(runner: ConformanceTestRunner, opts: FormCon
235
434
  * Per-fixture knowledge for the annotation appearance conformance suite. The
236
435
  * harness asserts thresholds (not exact wire content) so the same suite runs
237
436
  * unchanged against the local and cloud engines and proves they emit the same
238
- * set of appearances — including weak (index-only) annotations.
437
+ * set of appearances, including those of annotations born inline in the file.
239
438
  */
240
439
  interface AnnotationAppearanceConformanceFixture extends ConformanceFixture {
241
440
  /** PDF object number of the page whose appearances are rendered. */
@@ -243,21 +442,14 @@ interface AnnotationAppearanceConformanceFixture extends ConformanceFixture {
243
442
  /** At least this many `/AP` appearances are expected on that page. */
244
443
  minAppearanceCount: number;
245
444
  /**
246
- * `true` when the page has at least one weak (index-only) annotation that
247
- * carries an appearance stream. The whole point of this suite: that weak
248
- * appearance must still be emitted (it used to be dropped on the wire).
445
+ * `true` when the page has at least one annotation born inline in the
446
+ * file (named by `baseIndex`) that carries an appearance stream: it must
447
+ * be emitted too, under that name.
249
448
  */
250
- expectsWeakAppearance: boolean;
449
+ expectsInlineAppearance: boolean;
251
450
  }
252
451
  interface AnnotationAppearanceConformanceOptions extends Omit<ConformanceOptions, 'fixture'> {
253
452
  fixture: AnnotationAppearanceConformanceFixture;
254
- /**
255
- * `true` for engines that expose the raw RGBA rasters (`renderAppearances`).
256
- * The local engine's encoder needs a browser Canvas, so under node it can
257
- * only be exercised via the raw rasters; the cloud engine ships encoded
258
- * images (`renderAppearanceImages`) and leaves this `false`.
259
- */
260
- supportsRawRasters?: boolean;
261
453
  }
262
454
  declare function runAnnotationAppearanceConformance(runner: ConformanceTestRunner, opts: AnnotationAppearanceConformanceOptions): void;
263
455
  //#endregion
@@ -269,43 +461,32 @@ declare function runAnnotationAppearanceConformance(runner: ConformanceTestRunne
269
461
  * harness can exercise reorder permutations meaningfully.
270
462
  */
271
463
  interface PageReorderConformanceFixture extends ConformanceFixture {
272
- /** Stable, durable PONs of (at least) three distinct pages. Order
464
+ /** Stable, durable page object numbers of (at least) three distinct pages. Order
273
465
  * here is the *intended caller-visible* order, not the on-disk
274
466
  * order; the suite reads `pages.list()` first and works in document
275
467
  * order. */
276
- ponsForReorderTest?: number[];
277
- /**
278
- * Page used for the weak-ref-survival assertion. Defaults to the
279
- * first PON in `ponsForReorderTest`. The fixture page must already
280
- * have at least one weak annotation (no /NM, direct object) so the
281
- * harness can capture an `index`-kind ref before the page move and
282
- * re-use it after.
283
- */
284
- weakRefHostPon?: number;
285
- /** Quad to use for the `create()` step that produces a stable ref
286
- * the suite uses. */
287
- createQuad?: HighlightDraft['quadPoints'];
468
+ pageObjectNumbersForReorderTest?: number[];
288
469
  }
289
470
  interface PageReorderConformanceOptions extends Omit<ConformanceOptions, 'fixture'> {
290
471
  fixture: PageReorderConformanceFixture;
291
472
  }
292
473
  /**
293
474
  * Page reorder conformance suite. Verifies the architectural invariants
294
- * locked with the user, do NOT loosen these without re-reading
295
- * `PageMoveResult` and `DocumentPagesMutator`:
475
+ * locked with the user, do not loosen these without re-reading
476
+ * `PageReorderResult` and `DocumentPagesMutator`:
296
477
  *
297
478
  * 1. `pages.list()` returns every page in display order, addressed
298
479
  * by indirect `pageObjectNumber`.
299
- * 2. `pages.move()` returns the full new order + geometry via
480
+ * 2. `pages.reorder()` puts the pages next to a neighbour (or at the start
481
+ * or end) and returns the full new order + geometry via
300
482
  * `result.layout` (the same shape `pages.list()` returns). There is
301
483
  * no document-level revision; the wire never asks the caller for one.
302
- * 3. Index-based annotation refs survive a page reorder. This is the
303
- * whole reason per-page revisions stay put across a move: a user
304
- * shuffling pages mid-edit must not lose a pending highlight. (The
305
- * "move never bumps a RevisionToken" invariant is asserted directly
306
- * in the annotation mutation suite, where revision liveness lives.)
307
- * 4. Invalid inputs (duplicate PONs, unknown PONs, out-of-range
308
- * `destIndex`) reject with `InvalidArg`.
484
+ * 3. Annotation names survive a page reorder: a user shuffling pages
485
+ * mid-edit must not lose a pending highlight, so a `baseIndex` ref
486
+ * captured before the reorder works after it.
487
+ * 4. A page named twice, or a neighbour that is one of the moved pages,
488
+ * rejects with `InvalidArg`; an unknown page or neighbour with
489
+ * `NotFound`.
309
490
  * 5. Abort propagates as `AbortError`.
310
491
  *
311
492
  * Both local (worker host + WASM) and cloud (HTTP + @cloudpdf/server)
@@ -316,17 +497,17 @@ declare function runPageReorderConformance(runner: ConformanceTestRunner, opts:
316
497
  //#region src/conformance/runPageRotateConformance.d.ts
317
498
  /**
318
499
  * Page rotate conformance suite. Verifies the architectural invariants —
319
- * do NOT loosen these without re-reading `PageRotateResult`:
500
+ * do not loosen these without re-reading `PageRotateResult`:
320
501
  *
321
- * 1. Rotation is ABSOLUTE and idempotent: `rotate(pons, 90)` twice is
502
+ * 1. Rotation is absolute and idempotent: `rotate(pons, 90)` twice is
322
503
  * `rotate(pons, 90)` once. The wire never speaks "turn by".
323
- * 2. Rotation is presentation metadata over NORMALIZED content: the
504
+ * 2. Rotation is presentation metadata over normalized content: the
324
505
  * layout's `width`/`height` stay un-rotated, order and identity are
325
- * untouched, and per-page `RevisionToken`s survive (an index-based
326
- * annotation ref captured before the rotate works after it).
506
+ * untouched, and annotation names survive (a `baseIndex` ref captured
507
+ * before the rotate works after it).
327
508
  * 3. The result returns the full new `layout`; a subsequent `list()`
328
509
  * agrees with it.
329
- * 4. Invalid inputs (bad rotation value, duplicate PONs, unknown PONs)
510
+ * 4. Invalid inputs (bad rotation value, duplicate page object numbers, unknown page object numbers)
330
511
  * reject with `InvalidArg` / `NotFound`.
331
512
  * 5. Abort propagates as `AbortError`.
332
513
  *
@@ -335,20 +516,248 @@ declare function runPageReorderConformance(runner: ConformanceTestRunner, opts:
335
516
  */
336
517
  declare function runPageRotateConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
337
518
  //#endregion
519
+ //#region src/conformance/runAnnotationRotationConformance.d.ts
520
+ type AnnotationRotationFixture =
521
+ /** A one-page document without annotations. */
522
+ 'authoring' |
523
+ /** A stamp Acrobat turned 36° clockwise: `/Rotate -36`, the turn in its appearance. */
524
+ 'acrobat-stamp-rotated' |
525
+ /** A text box Acrobat added on a turned page (`/Rotate 90`), and one upright. */
526
+ 'acrobat-freetext-rotated';
527
+ interface AnnotationRotationConformanceOptions {
528
+ label: string;
529
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
530
+ makeEngine: () => Promise<Engine> | Engine;
531
+ /** Open a fresh copy of a fixture. */
532
+ open: (engine: Engine, fixture: AnnotationRotationFixture) => Promise<DocumentHandle>;
533
+ }
534
+ /**
535
+ * An annotation's `rotation` is degrees clockwise, as every rotation in the
536
+ * API, on both engines. What's drawn decides it: a turn another app made
537
+ * (Acrobat's `/Rotate` stamps and quarter-turned text boxes) reads as ours
538
+ * does, with the `box` the drawing turns, and a write keeps it. A turned
539
+ * box's `rect` is the upright box around all it draws, which the engine
540
+ * works out.
541
+ */
542
+ declare function runAnnotationRotationConformance(runner: ConformanceTestRunner, opts: AnnotationRotationConformanceOptions): void;
543
+ //#endregion
544
+ //#region src/conformance/runPageRenderConformance.d.ts
545
+ /**
546
+ * A 600 × 800 page cropped to [100 200 500 600], so its visible area does
547
+ * not start at 0,0, with a black square at 200..300 × 300..400 in file
548
+ * coordinates (the coordinates annotation rects use).
549
+ */
550
+ declare const CROP_OFFSET_PDF: Uint8Array<ArrayBufferLike>;
551
+ interface PageRenderConformanceOptions {
552
+ label: string;
553
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
554
+ makeEngine: () => Promise<Engine> | Engine;
555
+ /** Open a fresh copy of {@link CROP_OFFSET_PDF}. */
556
+ open: (engine: Engine) => Promise<DocumentHandle>;
557
+ }
558
+ /**
559
+ * Page images on both engines: an image reports the size of its pixels and
560
+ * where they are on the page (its transform, the one `pageTransform` gives
561
+ * without rendering), a target rect is in page space like every other place
562
+ * (so a page whose crop box doesn't start at 0,0 renders the area asked
563
+ * for), `blob()` is the image's bytes, and quality goes from 0 to 1.
564
+ */
565
+ declare function runPageRenderConformance(runner: ConformanceTestRunner, opts: PageRenderConformanceOptions): void;
566
+ //#endregion
567
+ //#region src/conformance/pageSpaceFixtures.d.ts
568
+ /**
569
+ * Small PDFs whose page boxes are odd in every way ISO 32000 allows, and in
570
+ * the ways broken files are odd, each with its answer written out by hand:
571
+ * the visible page box, the five boxes, the turn and the unit size. The
572
+ * answers come from the standard, never from our code, so a check against
573
+ * them is a check of our code:
574
+ * - ISO 32000-1 Table 30 and §7.7.3.4: which boxes a page inherits;
575
+ * - ISO 32000-1 §7.9.5: a box's corners in any order;
576
+ * - ISO 32000-1 §14.11.2: the defaults, and every box inside the media box.
577
+ * For broken files the standard has no answer; those carry our rule and say
578
+ * so.
579
+ *
580
+ * Each page draws black squares (`marks`) and words at known places in file
581
+ * coordinates, so a render shows where the page's corner really is.
582
+ */
583
+ /** A word drawn in Helvetica, its baseline starting at `x, y` (file coordinates). */
584
+ interface PageSpaceWord {
585
+ text: string;
586
+ x: number;
587
+ y: number;
588
+ size: number;
589
+ }
590
+ /** A destination: a spot on a page of the same file, as the file writes it, with its answer. */
591
+ interface PageSpaceGoTo {
592
+ /** The index of the page it goes to. */
593
+ toPage: number;
594
+ /** The destination array after the page reference, as the file writes it. */
595
+ view: string;
596
+ /**
597
+ * The destination measured from the top-left of the target page's visible
598
+ * box (x from its left edge, y from its top edge); `null` keeps the current
599
+ * value.
600
+ */
601
+ expected: {
602
+ kind: 'xyz' | 'fitH' | 'fitV' | 'fitR';
603
+ x?: number | null;
604
+ y?: number | null;
605
+ width?: number;
606
+ height?: number;
607
+ };
608
+ }
609
+ /** A link on a page to a spot on another page of the same file. */
610
+ interface PageSpaceLink extends PageSpaceGoTo {
611
+ /** Where the link sits, in file coordinates. */
612
+ rect: PdfRect;
613
+ }
614
+ interface PageSpaceBoxes {
615
+ media: PdfRect;
616
+ crop: PdfRect;
617
+ bleed: PdfRect;
618
+ trim: PdfRect;
619
+ art: PdfRect;
620
+ }
621
+ interface PageSpaceFixturePage {
622
+ /** The page dictionary's own entries: boxes, `/Rotate`, `/UserUnit`. */
623
+ entries: string;
624
+ marks: PdfRect[];
625
+ words: PageSpaceWord[];
626
+ links?: PageSpaceLink[];
627
+ /** The page's open action (`/AA /O`): a go-to. */
628
+ openAction?: PageSpaceGoTo;
629
+ expected: {
630
+ /** The visible page box: what the page shows, in file coordinates. */
631
+ visible: PdfRect;
632
+ /** The five boxes as ISO defines them: defaults applied, reduced to the media box. */
633
+ boxes: PageSpaceBoxes;
634
+ rotation: PdfRotation;
635
+ userUnit: number;
636
+ /** The order its annotations take focus in (`/Tabs`); none when the page sets none. */
637
+ tabOrder?: PageTabOrder;
638
+ };
639
+ }
640
+ interface PageSpaceFixture {
641
+ name: string;
642
+ /** The rule the fixture checks. */
643
+ about: string;
644
+ /**
645
+ * `iso`: the answers are the standard's. `recovery`: the file is broken and
646
+ * the answers are our rule, which is what Acrobat shows.
647
+ */
648
+ source: 'iso' | 'recovery';
649
+ /** Entries of the page tree node, inherited by pages that don't set them. */
650
+ treeEntries?: string;
651
+ /** The catalog's `/OpenAction` as a destination: where the document opens. */
652
+ openDestination?: PageSpaceGoTo;
653
+ /** A text field on the first page whose calculate script goes on (`/Next`) to a destination. */
654
+ fieldAction?: PageSpaceGoTo;
655
+ pages: PageSpaceFixturePage[];
656
+ bytes: Uint8Array;
657
+ }
658
+ declare const PAGE_SPACE_FIXTURES: readonly PageSpaceFixture[];
659
+ //#endregion
660
+ //#region src/conformance/runPageSpaceConformance.d.ts
661
+ interface PageSpaceConformanceOptions {
662
+ label: string;
663
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
664
+ makeEngine: () => Promise<Engine> | Engine;
665
+ /** Open a fresh copy of `fixture`. */
666
+ open: (engine: Engine, fixture: PageSpaceFixture) => Promise<DocumentHandle>;
667
+ /** Only these fixtures (by name); all when left out. */
668
+ only?: readonly string[];
669
+ }
670
+ /**
671
+ * Where things really are on a page, checked against what the renderer
672
+ * draws. An unturned render starts at the top-left of the visible page, so a
673
+ * position measured from there, times the scale, is a pixel. The fixtures
674
+ * write their answers out by hand from ISO 32000; this suite converts the
675
+ * engine's values with those answers, never with engine code, so it checks
676
+ * the engine's boxes, the renderer and every position the API returns.
677
+ */
678
+ declare function runPageSpaceConformance(runner: ConformanceTestRunner, opts: PageSpaceConformanceOptions): void;
679
+ //#endregion
680
+ //#region src/conformance/runColorConformance.d.ts
681
+ /**
682
+ * One page with a square whose border color the file stores as gray
683
+ * (`/C [0.5]`) and whose fill it stores as CMYK (`/IC [0 0 0 1]`).
684
+ */
685
+ declare const COLOR_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
686
+ interface ColorConformanceOptions {
687
+ label: string;
688
+ makeEngine: () => Promise<Engine> | Engine;
689
+ /** Open {@link COLOR_FIXTURE_PDF}, fresh for each call. */
690
+ open: (engine: Engine) => Promise<DocumentHandle>;
691
+ }
692
+ /**
693
+ * Colors are `'#rrggbb'`: lowercase when the engine gives one, either case
694
+ * when it takes one. A gray or CMYK color reads as its sRGB equivalent and,
695
+ * sent back unchanged, stays as the file stores it.
696
+ */
697
+ declare function runColorConformance(runner: ConformanceTestRunner, opts: ColorConformanceOptions): void;
698
+ //#endregion
699
+ //#region src/conformance/runAppearanceStatesConformance.d.ts
700
+ /**
701
+ * One 200 × 200 page with a check box that is off and stores every mode:
702
+ * normal Yes red and Off green, down Yes blue and Off yellow, rollover Yes
703
+ * black. Beside it, a square whose normal appearance is one stream (cyan).
704
+ * Each appearance fills its box, so a picture's middle names its state.
705
+ */
706
+ declare const APPEARANCE_STATES_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
707
+ interface AppearanceStatesConformanceOptions {
708
+ label: string;
709
+ makeEngine: () => Promise<Engine> | Engine;
710
+ /** Open {@link APPEARANCE_STATES_FIXTURE_PDF}, fresh for each call. */
711
+ open: (engine: Engine) => Promise<DocumentHandle>;
712
+ }
713
+ /**
714
+ * Appearance states and modes, and form fields in page renders, the same on
715
+ * every engine. A page's appearance batch holds every appearance an
716
+ * annotation stores: each mode it has, and every state of each mode, each
717
+ * image labelled with both, whatever `/AS` says. The annotation's
718
+ * `appearanceState` says which state it shows. Each family has its own
719
+ * batch: `page.annotations` the square's, `page.forms` the check box's, and
720
+ * neither holds the other's. A page picture draws what its two options ask
721
+ * for, the annotations and the form fields, each without the other.
722
+ */
723
+ declare function runAppearanceStatesConformance(runner: ConformanceTestRunner, opts: AppearanceStatesConformanceOptions): void;
724
+ //#endregion
725
+ //#region src/conformance/runDrawingDetailsConformance.d.ts
726
+ /**
727
+ * One 300 × 300 page with what other apps write and the engine only reads:
728
+ * a typewriter box (`/IT /FreeTextTypeWriter`) and a square with a beveled
729
+ * border.
730
+ */
731
+ declare const DRAWING_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
732
+ /** The fixtures the suite opens: its own, and Acrobat's text boxes (`freetext-rotated-acrobat.pdf`). */
733
+ type DrawingDetailsFixture = 'drawing' | 'acrobat-freetext';
734
+ interface DrawingDetailsConformanceOptions {
735
+ label: string;
736
+ makeEngine: () => Promise<Engine> | Engine;
737
+ /** A fresh copy of `fixture` for each call: the tests write to their documents. */
738
+ open: (engine: Engine, fixture: DrawingDetailsFixture) => Promise<DocumentHandle>;
739
+ }
740
+ /**
741
+ * How annotations draw and what a write takes: free text colors and
742
+ * defaults, dashes, the points a shape needs, printing, a note's `open` and
743
+ * its popup, and links.
744
+ */
745
+ declare function runDrawingDetailsConformance(runner: ConformanceTestRunner, opts: DrawingDetailsConformanceOptions): void;
746
+ //#endregion
338
747
  //#region src/conformance/runPageDeleteConformance.d.ts
339
748
  /**
340
749
  * Page delete conformance suite. Verifies the architectural invariants —
341
- * do NOT loosen these without re-reading `PageDeleteResult`:
750
+ * do not loosen these without re-reading `PageDeleteResult`:
342
751
  *
343
752
  * 1. `pages.delete()` removes exactly the listed pages and returns the
344
753
  * full surviving layout; a subsequent `list()` agrees.
345
- * 2. Deleted PONs are RETIRED: addressing a deleted page afterwards is a
754
+ * 2. Deleted page object numbers are retired: addressing a deleted page afterwards is a
346
755
  * clean `NotFound` (an API-level error, never native-memory danger).
347
- * 3. SURVIVING pages keep identity and `RevisionToken`s — an index-based
348
- * annotation ref on an unrelated page survives a neighbour's deletion.
756
+ * 3. Surviving pages keep identity, and their annotations keep their
757
+ * names — a `baseIndex` ref survives a neighbour's deletion.
349
758
  * 4. Deleting every page is rejected (`InvalidArg`) — a document keeps at
350
759
  * least one page.
351
- * 5. Duplicate / unknown PONs reject with `InvalidArg` / `NotFound`;
760
+ * 5. Duplicate / unknown page object numbers reject with `InvalidArg` / `NotFound`;
352
761
  * abort propagates as `AbortError`.
353
762
  *
354
763
  * Both local (worker host + WASM) and cloud (HTTP + @cloudpdf/server)
@@ -358,7 +767,7 @@ declare function runPageDeleteConformance(runner: ConformanceTestRunner, opts: C
358
767
  //#endregion
359
768
  //#region src/conformance/runNamedPagesConformance.d.ts
360
769
  /**
361
- * Named-pages conformance: the catalog's `/Names /Pages` registry as LAYOUT
770
+ * Named-pages conformance: the catalog's `/Names /Pages` registry as layout
362
771
  * data. Locks the invariants both engines must share — do not loosen
363
772
  * without re-reading `NamedPageEntry` and `PageNameResult`:
364
773
  *
@@ -366,7 +775,7 @@ declare function runPageDeleteConformance(runner: ConformanceTestRunner, opts: C
366
775
  * registration resolves to a page in `pages`, a template, or is
367
776
  * reported dangling — never silently dropped.
368
777
  * 2. `pages.setName()` creates or replaces by decoded key (idempotent),
369
- * `replace` renames in one job, and the result IS the new layout.
778
+ * `replace` renames in one job, and the result is the new layout.
370
779
  * 3. `pages.removeName()` drops the registration and nothing else.
371
780
  * 4. Deleting a page removes every registration pointing at it — inside
372
781
  * the delete, with no plugin in the loop.
@@ -393,7 +802,7 @@ declare function runAnnotationFlattenConformance(runner: ConformanceTestRunner,
393
802
  //#region src/conformance/runAnnotationAppearanceExportConformance.d.ts
394
803
  /**
395
804
  * `page(pon).annotations.exportAppearance(refs)`: the chosen annotations'
396
- * appearances as ONE single-page PDF sized to their union /Rect. Locks:
805
+ * appearances as one single-page PDF sized to their union /Rect. Locks:
397
806
  * 1. bytes are a PDF; the source page is untouched (no mutation, no
398
807
  * revision bump);
399
808
  * 2. re-opened (local engines), the page is exactly the union size and the
@@ -408,30 +817,39 @@ declare function runAnnotationAppearanceExportConformance(runner: ConformanceTes
408
817
  declare function runPageFlattenConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
409
818
  //#endregion
410
819
  //#region src/conformance/runRedactionApplyConformance.d.ts
820
+ /**
821
+ * One 300-point page with neither /Contents nor /Resources, as some
822
+ * generators write a blank page.
823
+ */
824
+ declare const BARE_PAGE_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
825
+ interface RedactionApplyConformanceOptions extends ConformanceOptions {
826
+ /** Open {@link BARE_PAGE_FIXTURE_PDF}, fresh for each call, with write access. */
827
+ openBarePage: (engine: Engine) => Promise<DocumentHandle>;
828
+ }
411
829
  /**
412
830
  * Transport-neutral coverage for the destructive redaction-apply rail.
413
- * Runs against a fixture page with NO pre-existing annotations so the
831
+ * Runs against a fixture page with no pre-existing annotations so the
414
832
  * collateral counts are exact. Content-removal fidelity itself is pinned by
415
833
  * the native embeddertests; this suite pins the transport contract: scopes,
416
- * statuses, counts, revision bumps, events, and preflight rejection.
834
+ * statuses, counts, affected pages, events, and preflight rejection.
417
835
  */
418
- declare function runRedactionApplyConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
836
+ declare function runRedactionApplyConformance(runner: ConformanceTestRunner, opts: RedactionApplyConformanceOptions): void;
419
837
  //#endregion
420
838
  //#region src/conformance/runPageExtractConformance.d.ts
421
839
  /**
422
- * Page extract conformance suite. `pages.extract?` is OPTIONAL on the
840
+ * Page extract conformance suite. `pages.extract?` is optional on the
423
841
  * contract (the `downloadLayer?` pattern) — the suite runs only when the
424
842
  * implementation provides it, so a cloud engine that has not shipped the
425
843
  * server endpoint yet skips cleanly instead of failing.
426
844
  *
427
845
  * Invariants:
428
846
  * 1. `pages.extract([pon])` returns standalone PDF bytes (magic header).
429
- * 2. The SOURCE document is untouched: identical layout before/after,
847
+ * 2. The source document is untouched: identical layout before/after,
430
848
  * and page revisions do not bump (extract is a read).
431
849
  * 3. Caller order is preserved and page geometry survives the copy
432
850
  * (asserted by re-opening the extracted bytes — bytes-open engines
433
851
  * only, since a cloud engine cannot re-open loose bytes).
434
- * 4. Empty / duplicate PONs reject `InvalidArg`; unknown PONs reject
852
+ * 4. Empty / duplicate page object numbers reject `InvalidArg`; unknown page object numbers reject
435
853
  * `NotFound` (or `InvalidArg`); abort propagates as `AbortError`.
436
854
  */
437
855
  declare function runPageExtractConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
@@ -439,12 +857,12 @@ declare function runPageExtractConformance(runner: ConformanceTestRunner, opts:
439
857
  //#region src/conformance/runAttachmentConformance.d.ts
440
858
  /**
441
859
  * Attachment conformance suite. Fixture requirement: a document whose
442
- * `/EmbeddedFiles` name tree contains AT LEAST ONE embedded file.
860
+ * `/EmbeddedFiles` name tree contains at least one embedded file.
443
861
  *
444
- * Both attachment surfaces are OPTIONAL on the contract (the
862
+ * Both attachment surfaces are optional on the contract (the
445
863
  * `downloadLayer?` pattern), probed independently and skipped cleanly:
446
864
  * - `doc.attachments?` — document-level EmbeddedFiles (list/download)
447
- * - `page.annotations.downloadFile?` — annotation-level file bytes
865
+ * - `page.annotations.downloadResource(ref, 'file')` — annotation-level file bytes
448
866
  *
449
867
  * Invariants:
450
868
  * 1. `list()` reflects the name tree: positional indices, non-empty
@@ -454,83 +872,335 @@ declare function runPageExtractConformance(runner: ConformanceTestRunner, opts:
454
872
  * 3. Unknown indices reject with an `EngineError`.
455
873
  * 4. A created file-attachment annotation round-trips its file:
456
874
  * metadata inline on the DTO (never bytes), bytes byte-identical
457
- * through `downloadFile(ref)`.
875
+ * as its `file` resource.
458
876
  * 5. A created text (sticky-note) annotation round-trips icon + color.
459
877
  */
460
878
  declare function runAttachmentConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
461
879
  //#endregion
462
880
  //#region src/conformance/runPageInsertConformance.d.ts
463
- /**
464
- * Page insert conformance. `pages.insert` is a REQUIRED member — this suite
465
- * runs unconditionally on every engine, so an implementation that loses the
466
- * verb fails loudly instead of being skipped past.
467
- *
468
- * Invariants:
469
- * 1. Every page of the source bytes is COPIED in at `destIndex` (omitted →
470
- * append), in source order; the result lists the fresh PONs in
471
- * insertion order and they agree with the returned layout.
472
- * 2. Pre-existing pages keep their identity: same PONs before and after,
473
- * in the expected positions (an insert never invalidates neighbours).
474
- * 3. The mutation persists through save → re-open (bytes engines only).
475
- * 4. Empty bytes / malformed bytes / out-of-range destIndex reject with
476
- * InvalidArg / MalformedPdf, leaving the document untouched.
477
- */
478
881
  declare function runPageInsertConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
479
882
  //#endregion
480
883
  //#region src/conformance/runPageInsertBlankConformance.d.ts
481
884
  /**
482
- * Blank-page insert conformance. `pages.insertBlank` is a REQUIRED member —
885
+ * Blank-page insert conformance. `pages.insertBlank` is a required member —
483
886
  * the suite runs unconditionally on every engine (no fixture bytes are
484
887
  * needed at all, which is the point of the verb), so an implementation that
485
888
  * loses the verb fails loudly instead of being skipped past.
486
889
  *
487
890
  * Invariants:
488
- * 1. `count` blank pages of exactly `size` appear at `destIndex` (omitted
489
- * → append), rotation 0; the result lists their fresh PONs in insertion
891
+ * 1. `count` blank pages of exactly `size` appear at `toIndex` (omitted
892
+ * → append), rotation 0; the result lists their fresh page object numbers in insertion
490
893
  * order and they agree with the returned layout.
491
- * 2. Pre-existing pages keep their identity: same PONs before and after,
894
+ * 2. Pre-existing pages keep their identity: same page object numbers before and after,
492
895
  * in the expected positions (an insert never invalidates neighbours).
493
896
  * 3. The mutation persists through save → re-open (bytes engines only).
494
897
  * 4. Non-positive size / count outside [1, PAGE_INSERT_BLANK_MAX_COUNT] /
495
- * out-of-range destIndex reject with InvalidArg, leaving the document
898
+ * out-of-range toIndex reject with InvalidArg, leaving the document
496
899
  * untouched.
497
900
  */
498
901
  declare function runPageInsertBlankConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
499
902
  //#endregion
903
+ //#region src/conformance/runObjectNumberConformance.d.ts
904
+ /**
905
+ * Two pages and a form. The first page holds a merged text field and widget
906
+ * (object 5, `merged`), the second nothing.
907
+ */
908
+ declare const OBJECT_NUMBER_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
909
+ interface ObjectNumberConformanceOptions {
910
+ label: string;
911
+ makeEngine: () => Promise<Engine> | Engine;
912
+ /** Open {@link OBJECT_NUMBER_FIXTURE_PDF}, fresh for each call, with write access. */
913
+ open: (engine: Engine) => Promise<DocumentHandle>;
914
+ }
915
+ /**
916
+ * Names at birth, and the order and ids of writes:
917
+ *
918
+ * - A create that names an object number from the document's pool makes its
919
+ * object at exactly that number, so its ref is known before the engine
920
+ * answers; what the engine makes for itself lands elsewhere.
921
+ * - A number the session doesn't hold is refused `not-held`; one a call uses
922
+ * twice is `taken`. A refused call changes nothing.
923
+ * - Writes made without waiting apply in the order they were made: a page
924
+ * named at birth takes an annotation at once.
925
+ * - Every event of a write carries its `opId` as `origin.tx`.
926
+ */
927
+ declare function runObjectNumberConformance(runner: ConformanceTestRunner, opts: ObjectNumberConformanceOptions): void;
928
+ //#endregion
929
+ //#region src/conformance/runChangeConformance.d.ts
930
+ /**
931
+ * Two pages and a form. The first page holds, from the file: a blue square
932
+ * by Ann (object 5); Ann's note (6) with its popup (7) and Bea's reply to it
933
+ * (8); and a merged text field and widget `name` with the value `Ann` (9).
934
+ * The second page is empty. Both share an empty content stream (10).
935
+ */
936
+ declare const CHANGE_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
937
+ interface ChangeConformanceOptions {
938
+ label: string;
939
+ makeEngine: () => Promise<Engine> | Engine;
940
+ /** Open {@link CHANGE_FIXTURE_PDF}, fresh for each call, with write access. */
941
+ open: (engine: Engine) => Promise<DocumentHandle>;
942
+ /**
943
+ * Open the document `doc` is on as another user, with write access. A local
944
+ * session has one user, so without it the test of who may undo is skipped.
945
+ */
946
+ openAsAnotherUser?: (engine: Engine, doc: DocumentHandle) => Promise<DocumentHandle>;
947
+ }
948
+ /**
949
+ * `doc.apply`, one user action as one change:
950
+ *
951
+ * - The ops apply in order, all or none, as one write: one `opId`, one burst
952
+ * of events sharing it. Later ops name what earlier ones create, by the
953
+ * numbers the creates were given.
954
+ * - `{ undoOf }` reverses a change as the engine recorded it, op by op in
955
+ * reverse order, where the document still shows what the change set; what
956
+ * it leaves alone is reported as `skipped`. Every op type round-trips:
957
+ * undo gives exactly the state before, the undo's undo the state after.
958
+ * - A deleted annotation comes back as the same objects, with the layer's
959
+ * edits, its popup and its replies.
960
+ * - `expect` refuses a change whose assumption doesn't hold.
961
+ * - Every answer is kept under its `opId`, refusals included: asking again
962
+ * answers the same, and a different change under it is refused.
963
+ * - A redaction apply ends undo for the changes before it.
964
+ */
965
+ declare function runChangeConformance(runner: ConformanceTestRunner, opts: ChangeConformanceOptions): void;
966
+ //#endregion
967
+ //#region src/conformance/pdfOf.d.ts
968
+ /** A PDF file of `objects`, numbered from 1, with its cross-reference table. */
969
+ declare function pdfOf(objects: readonly string[]): Uint8Array;
970
+ //#endregion
971
+ //#region src/conformance/runPermissionConformance.d.ts
972
+ /**
973
+ * The two read families, through three tokens: one that may do everything,
974
+ * one that may only fill the form, one that may only comment. Each token
975
+ * reads, sees and writes its own family, and is refused the other one, on
976
+ * every surface: the lists, the appearance images, the page pictures, the
977
+ * writes, and (on an engine where sessions share a document) the events of
978
+ * another session's writes. A widget is the form's: the annotation verbs
979
+ * refuse one as the wrong family (`InvalidArg`), whatever the token.
980
+ *
981
+ * It runs on {@link CHANGE_FIXTURE_PDF}: a square (5), a note (6) with its
982
+ * popup (7) and a reply (8), and the text field `name`, merged with its
983
+ * widget (9), all on the first page.
984
+ */
985
+ declare const PERMISSION_TOKENS: {
986
+ readonly everything: readonly ["*"];
987
+ readonly fill: readonly ["doc.open", "doc.render", "doc.forms.fill"];
988
+ readonly comment: readonly ["doc.open", "doc.render", "doc.annotate.modify"];
989
+ };
990
+ type PermissionToken = keyof typeof PERMISSION_TOKENS;
991
+ interface PermissionConformanceOptions {
992
+ label: string;
993
+ makeEngine: () => Promise<Engine> | Engine;
994
+ /** Open a fresh copy of {@link CHANGE_FIXTURE_PDF} with `scope`. */
995
+ open: (engine: Engine, scope: readonly string[]) => Promise<DocumentHandle>;
996
+ /**
997
+ * Open the document `doc` is on again, on `engine`, with `scope`: another
998
+ * user's session. Every open is its own session, even on the same engine.
999
+ * A local document is its own open's alone, so without it the events test
1000
+ * is skipped.
1001
+ */
1002
+ openSameDocument?: (engine: Engine, doc: DocumentHandle, scope: readonly string[]) => Promise<DocumentHandle>;
1003
+ }
1004
+ declare function runPermissionConformance(runner: ConformanceTestRunner, opts: PermissionConformanceOptions): void;
1005
+ //#endregion
1006
+ //#region src/conformance/runFormScriptConformance.d.ts
1007
+ /**
1008
+ * A field's scripts, written through `create` and `update`, and the form's
1009
+ * calculation order, which follows the calculate scripts and moves by
1010
+ * neighbour; a widget's actions, written through its placement and
1011
+ * `updateWidget`. Undo puts them back. A field event takes JavaScript only;
1012
+ * writing a script, a submit or a link takes `doc.forms.script`, while the
1013
+ * actions that only change the view (go to, reset, show and hide) need
1014
+ * `doc.forms.modify` alone.
1015
+ *
1016
+ * It runs on {@link CHANGE_FIXTURE_PDF}: the text field `name`, merged with
1017
+ * its widget (9) on the first page, and no calculation order.
1018
+ */
1019
+ interface FormScriptConformanceOptions {
1020
+ label: string;
1021
+ makeEngine: () => Promise<Engine> | Engine;
1022
+ /** Open a fresh copy of {@link CHANGE_FIXTURE_PDF} with `scope`. */
1023
+ open: (engine: Engine, scope: readonly string[]) => Promise<DocumentHandle>;
1024
+ }
1025
+ declare function runFormScriptConformance(runner: ConformanceTestRunner, opts: FormScriptConformanceOptions): void;
1026
+ //#endregion
1027
+ //#region src/conformance/runFormAttributionConformance.d.ts
1028
+ /**
1029
+ * Who made a field and who filled it in, on both engines with the same
1030
+ * identities. The engine stamps it from the session: `create` names the
1031
+ * creator, a write that changes a value names its filler (a script's value
1032
+ * counts as filled by the user whose fill ran it), a reset and an anonymous
1033
+ * fill name nobody, and an undo puts the previous filler back. A widget row
1034
+ * reports no attribution: the field holds it.
1035
+ *
1036
+ * It runs on {@link CHANGE_FIXTURE_PDF}: the text field `name`, merged with
1037
+ * its widget (9), holding `Ann`.
1038
+ */
1039
+ interface FormAttributionConformanceOptions {
1040
+ label: string;
1041
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
1042
+ makeEngine: () => Promise<Engine> | Engine;
1043
+ /**
1044
+ * Open a fresh copy of {@link CHANGE_FIXTURE_PDF} as `session`. With
1045
+ * `from`, the new session continues that open session's document and sees
1046
+ * what it wrote (locally its downloaded bytes, on the cloud the same
1047
+ * layer); the suite closes `from` afterwards.
1048
+ */
1049
+ openAs: (engine: Engine, session: AttributionSession, from?: DocumentHandle) => Promise<DocumentHandle>;
1050
+ }
1051
+ declare function runFormAttributionConformance(runner: ConformanceTestRunner, opts: FormAttributionConformanceOptions): void;
1052
+ //#endregion
1053
+ //#region src/conformance/runFormTransferConformance.d.ts
1054
+ /**
1055
+ * `doc.forms.export`, `doc.forms.import` and `doc.forms.importValues` on
1056
+ * both engines. A design import copies whole fields, with their widgets,
1057
+ * values, scripts, actions and calculation order, so the copy exports as
1058
+ * its source, apart from refs, object numbers and the attribution its mode
1059
+ * writes. A values import fills fields of the same name and reports what it
1060
+ * leaves out. Both are one change each: undone and redone with
1061
+ * `doc.apply({ undoOf })`, answered again on a retry, refused before
1062
+ * anything is written.
1063
+ *
1064
+ * It runs on {@link TWO_PAGES_PDF}: two empty pages (3 and 4) and no form.
1065
+ */
1066
+ interface FormTransferConformanceOptions {
1067
+ label: string;
1068
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
1069
+ makeEngine: () => Promise<Engine> | Engine;
1070
+ /** Open a fresh document of `bytes` as `session`. */
1071
+ openAs: (engine: Engine, session: AttributionSession, bytes: Uint8Array) => Promise<DocumentHandle>;
1072
+ }
1073
+ /** Two empty 300 × 300 pages (3 and 4), and no form. */
1074
+ declare const TWO_PAGES_PDF: Uint8Array<ArrayBufferLike>;
1075
+ declare function runFormTransferConformance(runner: ConformanceTestRunner, opts: FormTransferConformanceOptions): void;
1076
+ //#endregion
1077
+ //#region src/conformance/runWidgetRotationConformance.d.ts
1078
+ /**
1079
+ * A widget turns as a box kind does: `box` is the frame its contents are
1080
+ * laid out in, `rotation` a quarter turn, clockwise (`/MK /R`, which the
1081
+ * file holds counterclockwise), and `rect` where it stands on the page, the
1082
+ * box turned. On both engines: Acrobat's turned fields read so, a placement
1083
+ * turns a new field, an update turns, moves and resizes one, undo puts it
1084
+ * back, and a form bundle carries the turn.
1085
+ *
1086
+ * It runs on {@link TURNED_FIELDS_PDF}.
1087
+ */
1088
+ interface WidgetRotationConformanceOptions {
1089
+ label: string;
1090
+ /** Build a fresh engine for this suite. The suite tears it down at the end. */
1091
+ makeEngine: () => Promise<Engine> | Engine;
1092
+ /** Open a fresh document of `bytes` with every scope. */
1093
+ open: (engine: Engine, bytes: Uint8Array) => Promise<DocumentHandle>;
1094
+ }
1095
+ /**
1096
+ * Two 300 × 300 pages: the first upright (3), the second shown turned 90°
1097
+ * (`/Rotate 90`, page 4). Text fields with `/MK /R` as Acrobat writes it,
1098
+ * counterclockwise: `upright` (none), `r90` (90, the text runs up the page),
1099
+ * `r180`, `r270`, `odd` (45, which no viewer draws turned), `negative`
1100
+ * (-90, the same turn as 270), and `on-turned` on the turned page (90: an
1101
+ * upright field there, as Acrobat adds one).
1102
+ */
1103
+ declare const TURNED_FIELDS_PDF: Uint8Array<ArrayBufferLike>;
1104
+ declare function runWidgetRotationConformance(runner: ConformanceTestRunner, opts: WidgetRotationConformanceOptions): void;
1105
+ //#endregion
1106
+ //#region src/conformance/runWidgetFindingConformance.d.ts
1107
+ interface WidgetFindingCase {
1108
+ /** What the file does. */
1109
+ readonly does: string;
1110
+ readonly pdf: Uint8Array;
1111
+ /** The page whose `/Annots` holds the widget, or `null` when no page shows it. */
1112
+ readonly page: number | null;
1113
+ /** The name of the field the form reads the widget into, or `null` for a widget in no field. */
1114
+ readonly field: string | null;
1115
+ }
1116
+ /** The cases, by name. Object 3 is the first page in each. */
1117
+ declare const WIDGET_FINDING_CASES: {
1118
+ readonly 'annots-only': {
1119
+ readonly does: "a merged field/widget in a page's /Annots that the field tree doesn't list";
1120
+ readonly pdf: Uint8Array<ArrayBufferLike>;
1121
+ readonly page: 3;
1122
+ readonly field: "orphan";
1123
+ };
1124
+ readonly inline: {
1125
+ readonly does: "a widget written straight into a page's /Annots, as no object of its own";
1126
+ readonly pdf: Uint8Array<ArrayBufferLike>;
1127
+ readonly page: 3;
1128
+ readonly field: null;
1129
+ };
1130
+ readonly 'no-acroform': {
1131
+ readonly does: "a field widget in a file that has no /AcroForm at all";
1132
+ readonly pdf: Uint8Array<ArrayBufferLike>;
1133
+ readonly page: 3;
1134
+ readonly field: "lonely";
1135
+ };
1136
+ readonly 'stale-p': {
1137
+ readonly does: "a widget in the second page's /Annots whose /P names the first page";
1138
+ readonly pdf: Uint8Array<ArrayBufferLike>;
1139
+ readonly page: 4;
1140
+ readonly field: "moved";
1141
+ };
1142
+ readonly 'deleted-page': {
1143
+ readonly does: "a field whose widget was on a page the document no longer has";
1144
+ readonly pdf: Uint8Array<ArrayBufferLike>;
1145
+ readonly page: null;
1146
+ readonly field: "gone";
1147
+ };
1148
+ readonly 'no-field-type': {
1149
+ readonly does: "a widget with no field type anywhere on its /Parent chain";
1150
+ readonly pdf: Uint8Array<ArrayBufferLike>;
1151
+ readonly page: 3;
1152
+ readonly field: null;
1153
+ };
1154
+ };
1155
+ type WidgetFindingCaseName = keyof typeof WIDGET_FINDING_CASES;
1156
+ interface WidgetFindingConformanceOptions {
1157
+ label: string;
1158
+ makeEngine: () => Promise<Engine> | Engine;
1159
+ /** Open `pdf` with every right, as a document of its own. */
1160
+ open: (engine: Engine, pdf: Uint8Array, name: WidgetFindingCaseName) => Promise<DocumentHandle>;
1161
+ }
1162
+ declare function runWidgetFindingConformance(runner: ConformanceTestRunner, opts: WidgetFindingConformanceOptions): void;
1163
+ //#endregion
500
1164
  //#region src/conformance/runPieceInfoConformance.d.ts
1165
+ interface PieceInfoConformanceOptions {
1166
+ label: string;
1167
+ /** Build a fresh local engine for this suite. The suite tears it down at the end. */
1168
+ makeEngine: () => Promise<LocalEngine> | LocalEngine;
1169
+ fixture: ConformanceFixture;
1170
+ }
501
1171
  /**
502
- * `/PieceInfo` conformance (doc + page level). The service is OPTIONAL on
503
- * the contract (`downloadLayer?` pattern) — the suite runs only where the
504
- * implementation provides it, so an engine that has not shipped it yet
505
- * skips cleanly.
1172
+ * `/PieceInfo` conformance (doc + page level), on the local engine: the
1173
+ * only one with `/PieceInfo`.
506
1174
  *
507
1175
  * Invariants:
508
1176
  * 1. The full value vocabulary round-trips at both levels: string,
509
1177
  * number, boolean, name, string-array — read back with the same tags.
510
- * 2. Writes PERSIST: download() → re-open → identical read (bytes-open
511
- * engines only, since cloud cannot re-open loose bytes).
1178
+ * 2. Writes persist: download() → re-open → identical read.
512
1179
  * 3. `null` deletes a key; sibling keys and sibling applications
513
1180
  * survive both key deletes and whole-entry clears.
514
1181
  * 4. An absent application reads as `null`; `applications()` enumerates
515
- * what is present; every write refreshes `lastModified`.
1182
+ * what is present; every write refreshes `modifiedAt`.
516
1183
  */
517
- declare function runPieceInfoConformance(runner: ConformanceTestRunner, opts: ConformanceOptions): void;
1184
+ declare function runPieceInfoConformance(runner: ConformanceTestRunner, opts: PieceInfoConformanceOptions): void;
518
1185
  //#endregion
519
1186
  //#region src/conformance/runDocumentEventsConformance.d.ts
520
1187
  /**
521
1188
  * Document event stream conformance suite. Verifies the invariants the
522
- * collaboration design rests on — do NOT loosen these without re-reading
1189
+ * collaboration design rests on — do not loosen these without re-reading
523
1190
  * `DocumentEvent`:
524
1191
  *
525
- * 1. EXACTLY ONCE: every confirmed mutation produces exactly one event,
1192
+ * 1. Exactly once: every confirmed mutation produces exactly one event,
526
1193
  * in mutation order, regardless of engine (local worker or cloud HTTP).
527
- * 2. GROUND TRUTH: a failed mutation publishes nothing; events fire only
1194
+ * 2. Ground truth: a failed mutation publishes nothing; events fire only
528
1195
  * after confirmation.
529
- * 3. RESULTS RIDE VERBATIM: each event embeds the result the caller
1196
+ * 3. Results ride verbatim: each event embeds the result the caller
530
1197
  * received, deep-equal field for field.
531
- * 4. PROVENANCE: own mutations are `origin.kind: 'local'` with a stable
532
- * per-engine-instance `sessionId`.
533
- * 5. Unsubscribe stops delivery.
1198
+ * 4. Provenance: own mutations are `origin.kind: 'local'` with the
1199
+ * handle's one `sessionId`.
1200
+ * 5. Unsubscribe stops delivery; `on(type)` hears only its type.
1201
+ * 6. Published before settlement: the event for a session's own mutation
1202
+ * reaches subscribers before the mutation's promise settles, so a
1203
+ * caller that awaits the mutation already sees state derived from it.
534
1204
  *
535
1205
  * Both local (worker host + WASM) and cloud (HTTP + @cloudpdf/server)
536
1206
  * implementations must pass identically.
@@ -564,11 +1234,11 @@ declare function runPageTextConformance(runner: ConformanceTestRunner, opts: Pag
564
1234
  //#endregion
565
1235
  //#region src/conformance/runTextDivergenceConformance.d.ts
566
1236
  /**
567
- * Index-space divergence conformance: the cases where a page's CHARACTER
1237
+ * Index-space divergence conformance: the cases where a page's character
568
1238
  * space (what geometry tiles and selection/search ranges address) and its
569
- * extracted TEXT differ — and the `charMap` contract that bridges them.
1239
+ * extracted text differ — and the `charMap` contract that bridges them.
570
1240
  *
571
- * Fixtures pin EXACT values (text, charCount, anchors) verified against the
1241
+ * Fixtures pin exact values (text, charCount, anchors) verified against the
572
1242
  * wasm runtime, so both engines must produce byte-identical snapshots:
573
1243
  *
574
1244
  * - a non-printing character occupying a character slot with no text unit
@@ -577,17 +1247,17 @@ declare function runPageTextConformance(runner: ConformanceTestRunner, opts: Pag
577
1247
  * characters in lockstep with the text, identity map, real segments);
578
1248
  * - supplementary-plane text via a ToUnicode surrogate pair
579
1249
  * (`embedpdf_astral_tounicode.pdf` — two lockstep surrogate entries,
580
- * identity map, astral content SURVIVES extraction).
1250
+ * identity map, astral content survives extraction).
581
1251
  *
582
- * The search probe closes the loop: a hit's `charStart`/`charCount` must be
583
- * CHARACTER-space (segments join geometry), and slicing the snapshot by the
1252
+ * The search probe closes the loop: a hit's `start`/`count` must be
1253
+ * character-space (segments join geometry), and slicing the snapshot by the
584
1254
  * hit's range must reproduce the matched text exactly — the law that makes
585
1255
  * select-this-match and copy compose.
586
1256
  */
587
1257
  interface TextDivergenceConformanceFixture extends ConformanceFixture {
588
1258
  /** PDF indirect object number of the probed page. */
589
1259
  pageObjectNumber: number;
590
- /** The EXACT extracted text (UTF-16 faithful). */
1260
+ /** The exact extracted text (UTF-16 faithful). */
591
1261
  exactText: string;
592
1262
  /** The character-space size (may differ from `exactText.length`). */
593
1263
  expectedCharCount: number;
@@ -595,15 +1265,15 @@ interface TextDivergenceConformanceFixture extends ConformanceFixture {
595
1265
  expectedCharMap: ReadonlyArray<CharMapAnchor> | null;
596
1266
  /** Char-space slice expectations (half-open). */
597
1267
  slices?: ReadonlyArray<{
598
- from: number;
599
- to: number;
1268
+ start: number;
1269
+ count: number;
600
1270
  text: string;
601
1271
  }>;
602
- /** Literal search probe with expected CHARACTER-space hit range. */
1272
+ /** Literal search probe with expected character-space hit range. */
603
1273
  search?: {
604
1274
  query: string;
605
- charStart: number;
606
- charCount: number;
1275
+ start: number;
1276
+ count: number;
607
1277
  matchedText: string;
608
1278
  };
609
1279
  }
@@ -617,7 +1287,7 @@ type TextDivergenceCase = Omit<TextDivergenceConformanceFixture, 'id' | 'bytes'
617
1287
  resource: string;
618
1288
  };
619
1289
  /**
620
- * THE pinned divergence cases — one table, consumed by both engines' suites
1290
+ * The pinned divergence cases — one table, consumed by both engines' suites
621
1291
  * so expected values cannot drift. Values verified against the wasm runtime
622
1292
  * (see each case). All fixtures are single-page; the page's indirect object
623
1293
  * number is 3 in each.
@@ -641,7 +1311,7 @@ interface PageGeometryOrientationFixture extends ConformanceFixture {
641
1311
  kind: 'empty-only';
642
1312
  } | {
643
1313
  kind: 'rotated';
644
- /** Baseline angles (radians, CCW, PDF y-up) rotated runs may carry. */
1314
+ /** Turns (degrees clockwise) rotated runs may carry. */
645
1315
  rotations: number[];
646
1316
  ascentFlip: boolean;
647
1317
  /**
@@ -681,21 +1351,16 @@ interface SearchConformanceOptions extends Omit<ConformanceOptions, 'fixture'> {
681
1351
  }
682
1352
  declare function runSearchConformance(runner: ConformanceTestRunner, opts: SearchConformanceOptions): void;
683
1353
  //#endregion
684
- //#region src/conformance/diffAnnotationListSnapshot.d.ts
1354
+ //#region src/conformance/diffAnnotationList.d.ts
685
1355
  /**
686
1356
  * Returns a list of human-readable difference strings between two
687
- * snapshots. Empty array means parity. Used by `engine-node.ts` to assert
688
- * that local and cloud emit identical annotations for the same fixture.
689
- *
690
- * `RevisionToken.docSessionId` is intentionally NOT compared — sessions
691
- * are disjoint. We do compare `pageObjectNumber` and `generation` (which
692
- * should be 0 for fresh reads on both sides). Page order is no longer part
693
- * of `PageState`; it lives in `PageLayout.index` (see `pages.list()`).
694
- * `auditHead` is likewise not compared: it is a cloud-only reconciliation
695
- * cursor, absent on local snapshots by design.
1357
+ * `annotations.list()` results, page by page. Empty array means parity.
1358
+ * Used by `engine-node.ts` to assert that local and cloud emit identical
1359
+ * annotations for the same fixture: the same pages, and on each the same
1360
+ * annotations under the same names. `auditHead` is not compared: it is a
1361
+ * cloud-only reconciliation cursor, absent on local lists by design.
696
1362
  */
697
- declare function diffAnnotationListSnapshot(a: AnnotationListPageSnapshot, b: AnnotationListPageSnapshot): string[];
698
- declare function diffAnnotationListSnapshotAll(a: AnnotationListSnapshotAllPages, b: AnnotationListSnapshotAllPages): string[];
1363
+ declare function diffAnnotationList(a: AnnotationList, b: AnnotationList): string[];
699
1364
  //#endregion
700
1365
  //#region src/conformance/runSignatureConformance.d.ts
701
1366
  interface SignatureConformanceFixtures {
@@ -744,12 +1409,12 @@ interface SignatureConformanceOptions {
744
1409
  fixtures: SignatureConformanceFixtures;
745
1410
  }
746
1411
  /**
747
- * Digital signatures, read side. The service is OPTIONAL on the contract
1412
+ * Digital signatures, read side. The service is optional on the contract
748
1413
  * (`downloadLayer?` pattern) — the suite runs only where the engine
749
1414
  * provides it.
750
1415
  *
751
1416
  * Invariants:
752
- * 1. Byte facts describe the LOADED bytes: a revision is a byte prefix,
1417
+ * 1. Byte facts describe the loaded bytes: a revision is a byte prefix,
753
1418
  * a whole-revision signature seals exactly one, the digest over its
754
1419
  * `/ByteRange` is what the file's bytes hash to, and an unsaved edit
755
1420
  * changes none of it.
@@ -764,5 +1429,103 @@ interface SignatureConformanceOptions {
764
1429
  */
765
1430
  declare function runSignatureConformance(runner: ConformanceTestRunner, opts: SignatureConformanceOptions): void;
766
1431
  //#endregion
767
- export { type ActionsConformanceFixtures, type ActionsConformanceOptions, type AnnotationAppearanceConformanceFixture, type AnnotationAppearanceConformanceOptions, type AnnotationConformanceOptions, type AnnotationMutationConformanceFixture, type AnnotationMutationConformanceOptions, type AnnotationReadConformanceFixture, type ConformanceExpect, type ConformanceFixture, type ConformanceOptions, type ConformanceTestRunner, type FormConformanceFixtures, type FormConformanceOptions, type PageGeometryOrientationFixture, type PageGeometryOrientationOptions, type PageReorderConformanceFixture, type PageReorderConformanceOptions, type PageTextConformanceFixture, type PageTextConformanceOptions, type SearchConformanceFixture, type SearchConformanceOptions, type SignatureConformanceFixtures, type SignatureConformanceOptions, TEXT_DIVERGENCE_CASES, type TextDivergenceCase, type TextDivergenceConformanceFixture, type TextDivergenceConformanceOptions, diffAnnotationListSnapshot, diffAnnotationListSnapshotAll, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationAppearanceExportConformance, runAnnotationFlattenConformance, runAnnotationMutationConformance, runAnnotationReadConformance, runAttachmentConformance, runDocumentEventsConformance, runFormConformance, runMetadataConformance, runNamedPagesConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageGeometryOrientationConformance, runPageInsertBlankConformance, runPageInsertConformance, runPageReorderConformance, runPageRotateConformance, runPageTextConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance, runSignatureConformance, runTextDivergenceConformance };
1432
+ //#region src/conformance/runAnnotationPredictionConformance.d.ts
1433
+ /** One blank US Letter page. */
1434
+ declare const PREDICTION_FIXTURE_PDF: Uint8Array<ArrayBufferLike>;
1435
+ interface AnnotationPredictionConformanceOptions {
1436
+ label: string;
1437
+ makeEngine: () => Promise<Engine> | Engine;
1438
+ /** Open {@link PREDICTION_FIXTURE_PDF}. Called once; every case adds its own annotation. */
1439
+ open: (engine: Engine) => Promise<DocumentHandle>;
1440
+ }
1441
+ /**
1442
+ * What a write does, predicted: `applyAnnotationPatch(read, patch)` equals the
1443
+ * annotation the engine reads back after `update(ref, patch)`, and
1444
+ * `annotationOfDraft(draft, context)` the one it reads back after
1445
+ * `create(draft)`, on every field but the ones the engine works out from its
1446
+ * drawing or stamps on each write.
1447
+ * A viewer shows a pending change this way, so the prediction is what the
1448
+ * user sees until the engine answers.
1449
+ */
1450
+ declare function runAnnotationPredictionConformance(runner: ConformanceTestRunner, opts: AnnotationPredictionConformanceOptions): void;
1451
+ //#endregion
1452
+ //#region src/conformance/runFormPredictionConformance.d.ts
1453
+ interface FormPredictionConformanceOptions {
1454
+ label: string;
1455
+ makeEngine: () => Promise<Engine> | Engine;
1456
+ /** Open `PREDICTION_FIXTURE_PDF` (one blank page). Called once; every case adds its own field. */
1457
+ open: (engine: Engine) => Promise<DocumentHandle>;
1458
+ }
1459
+ /**
1460
+ * What a form op leaves, predicted (`forms/predict.ts`): each op of every
1461
+ * family run against a fresh field, and the field and its widgets' rows the
1462
+ * engine reads back compared with the prediction, field by field. What the
1463
+ * engine stamps on a write (dates, who filled it in), its fonts from the
1464
+ * document's default appearance and the names it mints are left out.
1465
+ */
1466
+ declare function runFormPredictionConformance(runner: ConformanceTestRunner, opts: FormPredictionConformanceOptions): void;
1467
+ //#endregion
1468
+ //#region src/conformance/runFieldGroupsConformance.d.ts
1469
+ /**
1470
+ * Field groups: who fills in and signs which fields. On a buyer and seller
1471
+ * contract, the same on every engine:
1472
+ * - each signer fills in and signs only their own group's fields;
1473
+ * - a field a script calculates moves with a fill of the signer's own;
1474
+ * - a designer puts fields in their own group, or one they may set;
1475
+ * - a values import leaves out the fields the token may not fill;
1476
+ * - signing a grouped signature field locks the rest of its group.
1477
+ *
1478
+ * It runs on {@link FIELD_GROUPS_PDF}.
1479
+ */
1480
+ /**
1481
+ * One page with a buyer's and a seller's fields, each field's group in its
1482
+ * `/EMBD_Metadata /GroupID`: `buyer_name` and `buyer_price` (buyer),
1483
+ * `seller_name` (seller), `total` (no group; a calculate script, in the
1484
+ * calculation order), and the signature fields `buyer_sig` (buyer) and
1485
+ * `seller_sig` (seller).
1486
+ */
1487
+ declare const FIELD_GROUPS_PDF: Uint8Array<ArrayBufferLike>;
1488
+ /** The tokens of the contract's parties. */
1489
+ declare const FIELD_GROUP_TOKENS: {
1490
+ /** The buyer: fills in and signs the buyer's fields. */
1491
+ readonly buyer: {
1492
+ readonly scope: readonly ["doc.open", "doc.render", "fields:fill:group=buyer", "fields:sign:group=buyer"];
1493
+ readonly identity: {
1494
+ readonly userId: "alice";
1495
+ readonly displayName: "Alice";
1496
+ };
1497
+ };
1498
+ /** The seller, as a share link would grant it: everything on the seller's fields. */
1499
+ readonly seller: {
1500
+ readonly scope: readonly ["doc.open", "doc.render", "fields:*:group=seller"];
1501
+ readonly identity: {};
1502
+ };
1503
+ /** The designer: her fields go in legal; she may put fields in the buyer's group too. */
1504
+ readonly designer: {
1505
+ readonly scope: readonly ["doc.open", "doc.render", "doc.forms.modify", "fields:set-group:group=buyer"];
1506
+ readonly identity: {
1507
+ readonly userId: "lisa";
1508
+ readonly groupId: "legal";
1509
+ };
1510
+ };
1511
+ readonly everything: {
1512
+ readonly scope: readonly ["*"];
1513
+ readonly identity: {};
1514
+ };
1515
+ };
1516
+ type FieldGroupToken = keyof typeof FIELD_GROUP_TOKENS;
1517
+ interface FieldGroupsConformanceOptions {
1518
+ label: string;
1519
+ makeEngine: () => Promise<Engine> | Engine;
1520
+ /** Open a fresh copy of `bytes` (default {@link FIELD_GROUPS_PDF}) with `scope`, acting for `identity`. */
1521
+ open: (engine: Engine, scope: readonly string[], identity: Identity, bytes?: Uint8Array) => Promise<DocumentHandle>;
1522
+ /**
1523
+ * Whether a signing completes on this engine with a placeholder CMS, so
1524
+ * the lock it installs can be read back. Default `true`.
1525
+ */
1526
+ completesSignings?: boolean;
1527
+ }
1528
+ declare function runFieldGroupsConformance(runner: ConformanceTestRunner, opts: FieldGroupsConformanceOptions): void;
1529
+ //#endregion
1530
+ export { APPEARANCE_STATES_FIXTURE_PDF, type ActionsConformanceFixtures, type ActionsConformanceOptions, type AnnotationAppearanceConformanceFixture, type AnnotationAppearanceConformanceOptions, type AnnotationAttributionConformanceOptions, type AnnotationConformanceOptions, type AnnotationDeclarationConformanceOptions, type AnnotationDeclarationFixture, type AnnotationMutationConformanceFixture, type AnnotationMutationConformanceOptions, type AnnotationPredictionConformanceOptions, type AnnotationReadConformanceFixture, type AnnotationResourceConformanceOptions, type AnnotationResourceFixture, type AnnotationRotationConformanceOptions, type AnnotationRotationFixture, type AnnotationTransferConformanceOptions, type AppearanceStatesConformanceOptions, type AttributionSession, BARE_PAGE_FIXTURE_PDF, CHANGE_FIXTURE_PDF, COLOR_FIXTURE_PDF, CROP_OFFSET_PDF, type ChangeConformanceOptions, type ColorConformanceOptions, type ConformanceExpect, type ConformanceFixture, type ConformanceOptions, type ConformanceTestRunner, type Creatable, DRAWING_FIXTURE_PDF, type DateConformanceOptions, type DrawingDetailsConformanceOptions, type DrawingDetailsFixture, FIELD_GROUPS_PDF, FIELD_GROUP_TOKENS, type FieldGroupToken, type FieldGroupsConformanceOptions, type FormAttributionConformanceOptions, type FormConformanceFixtures, type FormConformanceOptions, type FormPredictionConformanceOptions, type FormScriptConformanceOptions, type FormTransferConformanceOptions, OBJECT_NUMBER_FIXTURE_PDF, type ObjectNumberConformanceOptions, PAGE_SPACE_FIXTURES, PERMISSION_TOKENS, PREDICTION_FIXTURE_PDF, type PageGeometryOrientationFixture, type PageGeometryOrientationOptions, type PageRenderConformanceOptions, type PageReorderConformanceFixture, type PageReorderConformanceOptions, type PageSpaceConformanceOptions, type PageSpaceFixture, type PageSpaceFixturePage, type PageTextConformanceFixture, type PageTextConformanceOptions, type PermissionConformanceOptions, type PermissionToken, type PieceInfoConformanceOptions, type RedactionApplyConformanceOptions, type SearchConformanceFixture, type SearchConformanceOptions, type SignatureConformanceFixtures, type SignatureConformanceOptions, TEXT_DIVERGENCE_CASES, TURNED_FIELDS_PDF, TWO_PAGES_PDF, type TextDivergenceCase, type TextDivergenceConformanceFixture, type TextDivergenceConformanceOptions, WIDGET_FINDING_CASES, type WidgetFindingCase, type WidgetFindingCaseName, type WidgetFindingConformanceOptions, type WidgetRotationConformanceOptions, annotationReadDriftOf, creatables, diffAnnotationList, iconRect, pdfOf, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationAppearanceExportConformance, runAnnotationAttributionConformance, runAnnotationDeclarationConformance, runAnnotationExportConformance, runAnnotationFlattenConformance, runAnnotationImportConformance, runAnnotationMutationConformance, runAnnotationPredictionConformance, runAnnotationReadConformance, runAnnotationResourceConformance, runAnnotationRotationConformance, runAnnotationTransferConformance, runAppearanceStatesConformance, runAttachmentConformance, runChangeConformance, runColorConformance, runDateConformance, runDocumentEventsConformance, runDrawingDetailsConformance, runFieldGroupsConformance, runFormAttributionConformance, runFormConformance, runFormPredictionConformance, runFormScriptConformance, runFormTransferConformance, runMeasurementConformance, runMetadataConformance, runNamedPagesConformance, runObjectNumberConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageGeometryOrientationConformance, runPageInsertBlankConformance, runPageInsertConformance, runPageRenderConformance, runPageReorderConformance, runPageRotateConformance, runPageSpaceConformance, runPageTextConformance, runPermissionConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance, runSignatureConformance, runTextDivergenceConformance, runWidgetFindingConformance, runWidgetRotationConformance };
768
1531
  //# sourceMappingURL=conformance.d.ts.map