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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { ActionReadBudget, AnalyzeInput, AnnotationActor, AnnotationAppearanceRenderOptions, AnnotationAppearancesResult, AnnotationCreateResult, AnnotationDeleteResult, AnnotationFlattenResult, AnnotationListMutationMeta, AnnotationListPageSnapshot, AnnotationListSnapshotAllPages, AnnotationMoveResult, AnnotationMutationKind, AnnotationRef, AnnotationStableId, AnnotationUpdateResult, AttachmentCreateResult, AttachmentDeleteResult, AttachmentFileInfo, AttachmentFileWorkerPayload, BaseVersionInfo, ChangeAnalysis, DigestAlgorithm, DocumentActionsSnapshot, DocumentEvent, DocumentEventInit, DocumentEventStream, DocumentMetadata, DocumentProtection, DocumentSecurityProbeInfo, DocumentVersionRef, EmbeddedFileItem, EmbeddedFileRef, FontIdentityInfo, FormDataFormat, FormEffect, FormEffectsResult, FormFieldDTO, FormFieldDraft, FormFieldPatch, FormFieldRef, FormFieldValue, FormImportResult, FormRepairResult, FormSetValueResult, FormSnapshot, FormWidgetRef, MetadataPatch, MetadataUpdateResult, PageDeleteResult, PageFlattenResult, PageFlattenUsage, PageGeometryRun, PageGeometrySnapshot, PageInsertBlankSpec, PageInsertResult, PageListSnapshot, PageMoveResult, PageNameInput, PageNameResult, PageNetworkRenderFormat, PageObjectNumber, PageRaster, PageRemoveNameInput, PageRenderOptions, PageRotateResult, PageRotation, PageState, PageTextSnapshot, PdfActionTree, PdfQuad, PdfRect, PdfRevision, PdfSaveMode, RedactionApplyResult, RedactionApplyScope, RevisionStructure, RevisionToken, SearchRequest, SearchSlice, SessionKind, SignatureAbortResult, SignatureCompleteInput, SignatureCompleteResult, SignatureDTO, SignaturePrepareInput, SignaturePrepared, SignatureSnapshot, SignedDocumentPolicy, WeakAnnotationState, WireAnnotationDraft, WireAnnotationPatch, WireAttachmentFile, WirePack, WireResource, WireResourceMap, WorkerRequest, WorkerResponse } from "@embedpdf/engine-core/runtime";
1
+ import { ActionReadBudget, AnalyzeInput, Annotation, AnnotationActor, AnnotationAppearanceRenderOptions, AnnotationAppearancesResult, AnnotationAuthority, AnnotationCreateResult, AnnotationDeleteResult, AnnotationDraft, AnnotationExportSelection, AnnotationFamily, AnnotationFlattenResult, AnnotationImportResult, AnnotationList, AnnotationListMutationMeta, AnnotationPatch, AnnotationRef, AnnotationReplyType, AnnotationUpdateResult, Attachment, AttachmentCreateResult, AttachmentDeleteResult, AttachmentFileInfo, AttachmentFileWorkerPayload, AttachmentRef, BaseVersionInfo, BundleImportPages, BundleLimits, ChangeAnalysis, ChangeAuthority, ChangeItem, ChangeItemType, ChangeRecordPayload, ChangeResult, CustomMetadata, CustomMetadataPatch, CustomMetadataUpdateResult, DateInput, DigestAlgorithm, DocumentActionsSnapshot, DocumentMetadata, DocumentProtection, DocumentSecurityProbeInfo, DocumentVersionRef, EngineError, FieldPosition, FontIdentityInfo, FormExportSelection, FormFieldDTO, FormFieldDisplay, FormFieldDraft, FormFieldPatch, FormFieldRef, FormFieldValue, FormImportResult, FormRepairResult, FormResetResult, FormSetValueResult, FormSnapshot, FormValuesImportDrop, FormWidget, IsoDateTime, MetadataPatch, MetadataUpdateResult, ObjectNumberRange, PageDeleteResult, PageFlattenResult, PageFlattenUsage, PageGeometryRun, PageGeometrySnapshot, PageInsertBlankSpec, PageInsertResult, PageListSnapshot, PageNameInput, PageNameResult, PageNetworkRenderFormat, PageObjectNumber, PagePosition, PageRaster, PageRef, PageRemoveNameInput, PageRenderOptions, PageReorderResult, PageRotateResult, PageTextSnapshot, PdfActionTree, PdfCoordinates, PdfDestination, PdfQuad, PdfRect, PdfRevision, PdfRotation, PdfSaveMode, RecordedOp, RedactionApplyResult, RedactionApplyScope, RevisionStructure, SearchScanRequest, SearchSlice, SerializedEngineError, SignatureCancelResult, SignatureCompleteInput, SignatureCompleteResult, SignatureDTO, SignaturePrepareInput, SignaturePrepared, SignatureSnapshot, SignedDocumentPolicy, VisibleBoxOf, WidgetAnnotation, WidgetPlacement, WireAnnotationBundle, WireAnnotationResources, WireAttachmentFile, WireFormBundle, WirePack, WireResource, WireResourceMap, WorkerRequest, WorkerResponse, WorkingSetPage, generateUuid } from "@embedpdf/engine-core/runtime";
2
2
  import { PdfFunctions, PdfRuntimeMemory, PdfRuntimeModule, Ptr } from "@embedpdf/engine-runtime";
3
+ import { PlannedValueWrite } from "@embedpdf/engine-core/validation";
3
4
  //#region src/runtime/lifecycle/bootstrap.d.ts
4
5
  /**
5
6
  * Initialize the thread-confined PDFium runtime for the calling thread. Each
@@ -15,67 +16,29 @@ declare function destroyLibrary(runtime: PdfRuntimeModule): void;
15
16
  //#endregion
16
17
  //#region src/shared/abort.d.ts
17
18
  /**
18
- * Throw if the AbortSignal has been aborted. Service code peppers calls to
19
- * this between PDFium calls so cooperative cancellation works without
20
- * needing async/await.
19
+ * Throw if the AbortSignal has been aborted. Service code calls this between
20
+ * PDFium calls. It only sees an abort the thread has received, and a worker
21
+ * thread receives messages only while it awaits: work that never pauses (see
22
+ * `Slices`) sees an abort sent while it ran only when it is done.
21
23
  */
22
24
  declare function throwIfAborted(signal: AbortSignal): void;
23
25
  //#endregion
24
- //#region src/shared/uuid.d.ts
25
- /**
26
- * Generate an RFC 4122 v4 UUID using the Web Crypto API surface that's
27
- * available in every realm we ship to: Node 19+ (global), Web Workers,
28
- * browsers (https), browsers (http/intranet/file:), and Edge runtimes.
29
- *
30
- * Why this implementation, exactly:
31
- * - `crypto.randomUUID()` was historically gated to *secure contexts*
32
- * in older browsers and we shipped that bug to production once.
33
- * `getRandomValues()` has no such restriction.
34
- * - `import { randomUUID } from 'crypto'` makes Vite externalize the
35
- * `crypto` module for browser builds and emit a build-time warning;
36
- * we then crash at runtime in the rare path that hits it.
37
- * - 16 bytes + RFC 4122 bit-twiddling is six lines and correct.
38
- *
39
- * Output is the canonical hex form: `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`,
40
- * where `y` is one of `8/9/a/b` (variant nibble = 10xx).
41
- */
42
- declare function generateUuid(): string;
43
- //#endregion
44
26
  //#region src/shared/pdf-date.d.ts
45
27
  /**
46
- * PDF date string format conversion utilities.
47
- *
48
- * PDF stores timestamps as ASCII strings shaped like
49
- * `D:YYYYMMDDHHmmSSOHH'mm'`, where `O` is `+` / `-` / `Z` (Z means
50
- * UTC and omits the offset). See ISO 32000 §7.9.4.
51
- *
52
- * The wire format used by the engine DTOs is always ISO 8601;
53
- * readers convert PDF → ISO via {@link pdfDateToIso}, writers convert
54
- * the other way via {@link formatPdfDate}.
55
- */
56
- /**
57
- * Parse a PDF date string `D:YYYYMMDDHHmmSSOHH'mm'` to an ISO 8601
58
- * string. Returns null if the input is malformed.
59
- *
60
- * When the timezone is omitted, PDF defines the relationship to UTC as
61
- * unknown. Since the engine DTO wire format requires an absolute ISO 8601
62
- * timestamp, this converter retains the pre-existing fallback of treating
63
- * an omitted timezone as UTC.
28
+ * A PDF date as ISO 8601, keeping its offset: zero reads as `Z`, and a date
29
+ * without an offset reads without one. Fields the date leaves out take the
30
+ * spec's defaults (month and day 1, the rest 0). The `D:` prefix and the
31
+ * apostrophes of the offset are optional, as readers in the wild treat them.
32
+ * Returns `null` when the text isn't a date.
64
33
  */
65
- declare function pdfDateToIso(pdf: string): string | null;
34
+ declare function pdfDateToIso(pdf: string): IsoDateTime | null;
66
35
  /**
67
- * Format a JS Date as a PDF date string `D:YYYYMMDDHHmmSSOHH'mm'`.
68
- *
69
- * Emits the offset in PDF's funny `OHH'mm'` form, where the apostrophe
70
- * after the hour is literal and the offset always carries a sign. UTC
71
- * is emitted as `+00'00'` (not `Z`) for maximum reader compatibility —
72
- * the `Z` shorthand is allowed by the spec but not universally
73
- * supported.
74
- *
75
- * Always uses the supplied Date's local fields and offset. For
76
- * server-side stamps callers typically pass `new Date()` (default).
36
+ * A moment as a PDF date. A string keeps its own offset (none if it has
37
+ * none); a `Date` is an instant, written in UTC. A fraction of a second is
38
+ * dropped: PDF dates have whole seconds. UTC is written as `+00'00'` rather
39
+ * than `Z`, which some readers don't accept.
77
40
  */
78
- declare function formatPdfDate(d?: Date): string;
41
+ declare function formatPdfDate(value?: DateInput): string;
79
42
  //#endregion
80
43
  //#region src/runtime/memory/bits.d.ts
81
44
  /**
@@ -97,51 +60,37 @@ declare const normalizeU32: typeof u32;
97
60
  /** True when `bits` grants every standard security permission. */
98
61
  declare function hasAllStandardSecurityPermissions(bits: number): boolean;
99
62
  //#endregion
100
- //#region src/events/EventHub.d.ts
63
+ //#region src/document-session/DrawingIndex.d.ts
101
64
  /**
102
- * The in-process implementation of `DocumentEventStream`, shared by both
103
- * engine shells (one hub per open `DocumentHandle`). The engine that
104
- * performs a mutation publishes here at confirmation time; the cloud
105
- * engine's remote channel will also publish here for OTHER sessions'
106
- * mutations — listeners never learn which transport delivered an event.
65
+ * An open document's stamp drawings, known by content, so stamps with the
66
+ * same artwork place one drawing. It lives in memory for as long as the
67
+ * native document does; nothing of it is written into the PDF, and a reopen
68
+ * rebuilds it from what the drawings are then.
107
69
  *
108
- * Delivery contract:
109
- * - synchronous fan-out in subscription order;
110
- * - error isolation: a throwing listener is re-thrown on a microtask
111
- * (so it surfaces to the host's unhandled-error reporting) without
112
- * blocking other listeners or failing the mutation path;
113
- * - `lastServerId` tracks the highest remote cursor seen, for the
114
- * remote channel's reconnect/backfill.
115
- */
116
- declare class EventHub implements DocumentEventStream {
117
- /** Where a throwing listener's error goes. Defaults to an async
118
- * rethrow so it reaches the host's unhandled-error reporting. */
119
- private readonly onListenerError;
120
- private readonly listeners;
121
- private serverId;
122
- constructor(
123
- /** Where a throwing listener's error goes. Defaults to an async
124
- * rethrow so it reaches the host's unhandled-error reporting. */
125
- onListenerError?: (err: unknown) => void);
126
- subscribe(listener: (event: DocumentEvent) => void): () => void;
127
- publish(event: DocumentEvent): void;
128
- lastServerId(): number | null;
129
- /** Drop every listener (handle close). Idempotent. */
130
- clear(): void;
131
- }
132
- /**
133
- * Stamps provenance onto events a mutation path produces. One per open
134
- * handle: the engine shell constructs it with the instance's `sessionId`
135
- * (and, on cloud, the authenticated `sub` when known) and hands it to the
136
- * mutation services — they publish results, never build origins.
70
+ * Both maps stay true because no writer edits a drawing in place: a stamp
71
+ * that changes gets a new wrapper, and new artwork is a new drawing. Anything
72
+ * that deletes objects or reuses object numbers (a rollback) must call
73
+ * {@link DrawingIndex.forget}.
137
74
  */
138
- declare class SessionEventPublisher {
139
- private readonly hub;
140
- private readonly sessionId;
141
- private readonly sub;
142
- constructor(hub: EventHub, sessionId: string, sub?: string | null);
143
- /** Publish a mutation THIS engine instance just confirmed. */
144
- publishLocal(event: DocumentEventInit): void;
75
+ declare class DrawingIndex {
76
+ /**
77
+ * Content id (the SHA-256, in hex, of a drawing's canonical bytes) → the
78
+ * drawing's object number. `null` until the first lookup builds it from
79
+ * the drawings the document's stamps place.
80
+ */
81
+ byContent: Map<string, number> | null;
82
+ /**
83
+ * SHA-256 (hex) of bytes as a caller gave them → the drawing made from
84
+ * them, so placing the same bytes again skips making the drawing.
85
+ */
86
+ readonly bySource: Map<string, number>;
87
+ /**
88
+ * SHA-256 (hex) of bytes already hashed, by the buffer holding them. The
89
+ * stamps of one import share one buffer per resource, so it is copied and
90
+ * hashed once. A fact about the bytes, so {@link forget} keeps it.
91
+ */
92
+ readonly hashes: WeakMap<ArrayBuffer, string>;
93
+ forget(): void;
145
94
  }
146
95
  //#endregion
147
96
  //#region src/document-session/lifecycle/PdfDocumentOpener.d.ts
@@ -202,38 +151,198 @@ declare class CloseStack {
202
151
  declare function openFatMemoryDocument(runtime: PdfRuntimeModule, bytes: Uint8Array, password?: string | null): OpenedPdfDocument;
203
152
  declare function openLayerDocument(runtime: PdfRuntimeModule, base: AcquiredBaseDocument, layer?: LayerSource, password?: string | null): OpenedPdfDocument;
204
153
  //#endregion
154
+ //#region src/shared/slices.d.ts
155
+ /**
156
+ * How long work runs before it lets the thread receive messages. PDFium works
157
+ * for `budgetMs` at a time - a slice of a page render or of a page load - and
158
+ * the caller awaits `between()` before it goes on, so the thread can receive
159
+ * messages meanwhile: an abort stops the work at the next slice. The result is
160
+ * that of the work done at once, however it is sliced.
161
+ */
162
+ interface Slices {
163
+ readonly budgetMs: number;
164
+ between(): Promise<void>;
165
+ }
166
+ //#endregion
167
+ //#region src/document-session/pages/PageResidency.d.ts
168
+ /**
169
+ * The parsed pages of every document on one runtime - one WASM heap, or one
170
+ * server thread - kept within a budget of bytes.
171
+ *
172
+ * Parsing a page is the costly part of using it: seconds for a page of
173
+ * millions of vector objects, where one tile of it renders in milliseconds.
174
+ * So a page stays parsed after the job that used it, for the next one, as long
175
+ * as memory allows:
176
+ * - A page costs what its parse made (`EPDFPage_GetParsedSize`) and a fixed
177
+ * overhead for the page itself.
178
+ * - Room is made before a page loads, for its size when it was last open, and
179
+ * again after every slice of a load, as the page grows. A page a job holds
180
+ * never closes, so one page may be over the budget on its own: the one a
181
+ * job needs.
182
+ * - A page bigger than the whole budget stays parsed after its job too, or
183
+ * every job on it would parse it again: of such pages, the one that would
184
+ * close last is kept, and the budget holds every other page beside it. It
185
+ * closes when the pages jobs hold need the budget's room themselves.
186
+ * - What the viewers show decides what closes first (`setWorkingSet`): pages
187
+ * in no working set, then `near` pages, then `visible` pages from the
188
+ * fewest device pixels on screen up, so a thumbnail rail's pages close
189
+ * before the main view's. Least recently used first among equals.
190
+ * - A render loads its page in slices, between which its job can be aborted.
191
+ * An aborted load is set aside half parsed, and the next job that needs the
192
+ * page goes on with it.
193
+ * - A page whose content changed since it was parsed closes when it is next
194
+ * taken (`EPDFPage_IsContentCurrent`). That sees writes made in layer
195
+ * transactions; the worker host closes a document's pages around the writes
196
+ * that change content in place.
197
+ * - A page's image cache is emptied when its last job releases it, so its next
198
+ * job renders images as a newly loaded page does.
199
+ * - Pages no job uses and no viewer shows close after a while, giving their
200
+ * memory back to the allocator.
201
+ *
202
+ * Pages belong to an owner, one per open document ({@link PagePtrPool}).
203
+ */
204
+ interface PageResidencyPolicy {
205
+ /**
206
+ * Bytes of parsed pages kept at once; a page a job holds, or one page bigger
207
+ * than the budget, may be over it alone.
208
+ */
209
+ budgetBytes: number;
210
+ /** Pages no job uses close after this many milliseconds; 0 disables the timer. */
211
+ idleMs: number;
212
+ }
213
+ declare class PageResidency {
214
+ private readonly runtime;
215
+ private readonly policy;
216
+ /**
217
+ * True while a job is between two of its steps, such as a page render
218
+ * between slices, when nothing else may use PDFium: the idle timer then
219
+ * waits another period instead of closing pages.
220
+ */
221
+ private readonly busy;
222
+ private readonly pages;
223
+ /** What each closed page cost when it was last open: the room its next load needs. */
224
+ private readonly closedSizes;
225
+ /** Per owner, per view: where the pages it shows are (see {@link setWorkingSet}). */
226
+ private readonly workingSets;
227
+ private totalBytes;
228
+ private clock;
229
+ private timer;
230
+ constructor(runtime: PdfRuntimeModule, policy?: PageResidencyPolicy,
231
+ /**
232
+ * True while a job is between two of its steps, such as a page render
233
+ * between slices, when nothing else may use PDFium: the idle timer then
234
+ * waits another period instead of closing pages.
235
+ */
236
+ busy?: () => boolean);
237
+ /** The page, loaded in one go when it isn't parsed; held until {@link release}. */
238
+ acquire(owner: object, docPtr: Ptr, pageObjectNumber: PageObjectNumber): Ptr;
239
+ /**
240
+ * The page, loaded in slices when it isn't parsed: between slices the
241
+ * caller's thread receives messages, and an abort stops the load there. An
242
+ * aborted load is set aside, to go on when a job next needs the page. Held
243
+ * until {@link release}.
244
+ */
245
+ acquireInSlices(owner: object, docPtr: Ptr, pageObjectNumber: PageObjectNumber, signal: AbortSignal, slices: Slices): Promise<Ptr>;
246
+ /** A job is done with the page; it stays parsed while there is room. */
247
+ release(owner: object, pageObjectNumber: PageObjectNumber): void;
248
+ /**
249
+ * What `view` shows of `owner`'s pages, replacing its last set; an empty set
250
+ * withdraws it. Decides which kept pages close first (see the class notes).
251
+ * Only bookkeeping: it touches no PDFium state, so it may run mid-render.
252
+ */
253
+ setWorkingSet(owner: object, view: string, pages: Iterable<{
254
+ pageObjectNumber: PageObjectNumber;
255
+ } & Pick<WorkingSetPage, 'role' | 'pixels'>>): void;
256
+ /** True while a job holds the page; an idle page is not held. */
257
+ isHeld(owner: object, pageObjectNumber: PageObjectNumber): boolean;
258
+ /** True while the page is kept, parsed or loading: a job for it parses nothing new. */
259
+ isKept(owner: object, pageObjectNumber: PageObjectNumber): boolean;
260
+ /** Closes every page of `owner`, held or not: its document closes. */
261
+ closeOwner(owner: object): void;
262
+ /** Closes the pages of `owner` no job holds: a write changed its content in place. */
263
+ closeIdleOf(owner: object): void;
264
+ /** Closes every page no job holds. */
265
+ closeIdle(): void;
266
+ /** Closes the pages no job holds and no viewer shows on screen: the idle timer. */
267
+ private closeUnshown;
268
+ /** Pages kept, held or not (tests and diagnostics). */
269
+ get size(): number;
270
+ /** What the pages kept cost, as last measured (tests and diagnostics). */
271
+ get bytes(): number;
272
+ /** The page when it is kept, unless its content changed meanwhile. */
273
+ private take;
274
+ private add;
275
+ private measure;
276
+ private expectedBytes;
277
+ /**
278
+ * Closes pages no job holds until `incoming` more bytes fit: the worst
279
+ * placed first (see the class notes), least recently used first among
280
+ * equals.
281
+ *
282
+ * A page bigger than the budget on its own may stay over it: of such pages,
283
+ * the one that would close last. So it stays parsed between the jobs that
284
+ * use it, instead of parsing again for each one. Every other page fits in
285
+ * the budget beside it; when the pages jobs hold don't, they are the pages
286
+ * over it, and the spared page closes too. A budget of 0 keeps nothing.
287
+ */
288
+ private makeRoom;
289
+ /** The page no job holds that closes first, other than `spared`. */
290
+ private firstToClose;
291
+ /**
292
+ * Of the pages bigger than the budget, the one that closes last: the best
293
+ * placed, the most recently used among equals.
294
+ */
295
+ private lastToCloseOverBudget;
296
+ /** Where the working sets have the page: the best place any view gives it. */
297
+ private placeOf;
298
+ private close;
299
+ /** Closes the page without remembering its size. */
300
+ private drop;
301
+ private armTimer;
302
+ private disarmTimer;
303
+ }
304
+ //#endregion
205
305
  //#region src/document-session/pages/PagePtrPool.d.ts
206
306
  /**
207
- * Manages pagePtr lifetime for a single open `DocumentSession`.
307
+ * The pages of one open `DocumentSession`, kept by the runtime's
308
+ * {@link PageResidency}.
208
309
  *
209
- * `acquire(pon)` returns a loaded pagePtr for the given PDF object number,
210
- * loading it via `EPDFDoc_LoadPageByObjectNumberNormalized` (rotation forced
211
- * to 0deg so render/text/geometry/annotation coordinates all share one
212
- * normalized space) if not already in the pool. `release(pon)` decrements the
213
- * refcount and closes when it reaches
214
- * zero; `closeAll()` is called by `DocumentSession.close()`.
310
+ * `acquire(pon)` returns the page with that PDF object number, loaded with
311
+ * `EPDFDoc_LoadPageByObjectNumberNormalized` when it isn't kept (rotation
312
+ * forced to 0deg, so render, text, geometry and annotation coordinates all
313
+ * share one normalized space); `acquireInSlices` loads it in slices, for a
314
+ * render. Overlapping acquires of one page share it; `release(pon)` hands it
315
+ * back to the residency, which keeps it parsed while there is room.
316
+ * `closeAll()` is called by `DocumentSession.close()`.
215
317
  *
216
- * While at least one holder is active the pagePtr is shared (refcounted),
217
- * so overlapping `acquire(pon)` calls for the same page reuse a single
218
- * load. Once the refcount reaches zero the page is closed; a later
219
- * `acquire` reloads it — there is no LRU retention across the zero-ref
220
- * boundary. A future slice could add that without changing callers.
318
+ * A session made without a residency (a one-off render of a file) gets one
319
+ * of its own that keeps nothing: its pages close when released.
221
320
  */
222
321
  declare class PagePtrPool {
223
- private readonly runtime;
224
322
  private readonly docPtr;
225
- private readonly counts;
226
- constructor(runtime: PdfRuntimeModule, docPtr: Ptr);
323
+ private readonly residency;
324
+ constructor(runtime: PdfRuntimeModule, docPtr: Ptr, residency?: PageResidency | null);
227
325
  acquire(pageObjectNumber: PageObjectNumber): Ptr;
326
+ /** {@link acquire}, loading the page in slices between which `signal` can abort. */
327
+ acquireInSlices(pageObjectNumber: PageObjectNumber, signal: AbortSignal, slices: Slices): Promise<Ptr>;
228
328
  /**
229
- * True while at least one holder has the page open. Page-STRUCTURE
230
- * mutations (delete) assert on this: thread confinement means no other
231
- * job can be mid-flight, so a held pagePtr during a structural mutation
232
- * is a leaked `acquire` (an internal bug) — the mutator fails loudly
233
- * instead of mutating under a live handle.
329
+ * True while at least one holder has the page open; a page the residency
330
+ * keeps between jobs is not held. Page-structure mutations (delete) assert
331
+ * on this: thread confinement means no other job can be mid-flight, so a
332
+ * held pagePtr during a structural mutation is a leaked `acquire` (an
333
+ * internal bug) - the mutator fails loudly instead of mutating under a live
334
+ * handle.
234
335
  */
235
336
  isHeld(pageObjectNumber: PageObjectNumber): boolean;
337
+ /** True while the residency keeps the page, parsed or loading, held or not. */
338
+ isKept(pageObjectNumber: PageObjectNumber): boolean;
236
339
  release(pageObjectNumber: PageObjectNumber): void;
340
+ /** What `view` shows of the document, replacing its last set (see {@link PageResidency}). */
341
+ setWorkingSet(view: string, pages: Iterable<{
342
+ pageObjectNumber: PageObjectNumber;
343
+ } & Pick<WorkingSetPage, 'role' | 'pixels'>>): void;
344
+ /** Closes the pages no job holds: a write changed the document's content in place. */
345
+ closeIdle(): void;
237
346
  closeAll(): void;
238
347
  }
239
348
  //#endregion
@@ -242,7 +351,7 @@ declare class PagePtrPool {
242
351
  * Record of one page within a `DocumentSession`. Built lazily by
243
352
  * `DocumentSession.ensurePageRegistry()`; the registry is only forced
244
353
  * to populate fully when a path that needs `pageIndex -> pageObjectNumber`
245
- * for the whole document is invoked (most notably `listRawAll()`).
354
+ * for the whole document is invoked (most notably a whole-document annotation list).
246
355
  *
247
356
  * Carries no `pagePtr`. Lifetime of the actual PDFium page handle is owned
248
357
  * by the `PagePtrPool`; the page record is metadata only.
@@ -254,10 +363,9 @@ interface PageRecord {
254
363
  //#endregion
255
364
  //#region src/document-session/DocumentSession.d.ts
256
365
  /**
257
- * Owns the lifecycle of a single open PDFium document and the v3
258
- * identity machinery: page registry (pageObjectNumber <-> pageIndex),
259
- * `RevisionAuthority` (per-page generation counters), and `PagePtrPool`
260
- * (refcounted pagePtr access).
366
+ * Owns the lifecycle of a single open PDFium document and the
367
+ * identity machinery: page registry (pageObjectNumber <-> pageIndex) and
368
+ * `PagePtrPool` (refcounted pagePtr access).
261
369
  *
262
370
  * Both the local browser Worker and the server worker_thread instantiate
263
371
  * this exactly the same way; the only thing that differs is the
@@ -300,6 +408,11 @@ interface SigningCompletion {
300
408
  readonly cms: Uint8Array;
301
409
  readonly result: SignatureCompleteResult;
302
410
  }
411
+ /** What every meta of a write carries: its id, and whether it can be undone. */
412
+ interface WriteStamp {
413
+ readonly opId: string;
414
+ readonly undoable: boolean;
415
+ }
303
416
  declare class DocumentSession {
304
417
  private readonly runtime;
305
418
  private docPtr;
@@ -312,33 +425,36 @@ declare class DocumentSession {
312
425
  /** pageIndex -> record */
313
426
  private readonly recordsByIndex;
314
427
  private fullyEnumerated;
315
- private revisions;
316
- private mutationSeqCounter;
428
+ /** Bumped by {@link invalidateDerived}: the version caches built from the document key on. */
429
+ private cacheSeqCounter;
430
+ /** Bumped by {@link noteEdit}: one per committed write, never inside or for an abort. */
431
+ private editsSeqCounter;
432
+ /** The open layer transaction, if any (see {@link beginTransaction}). */
433
+ private transaction;
434
+ /** Why the session can't be used anymore, after a transaction neither committed nor aborted. */
435
+ private unusableReason;
317
436
  private pages;
437
+ /**
438
+ * The runtime's parsed pages, kept between jobs. Set before the document
439
+ * loads; without it every page closes when its last holder releases it.
440
+ */
441
+ residency: PageResidency | null;
318
442
  /**
319
443
  * Whether writers honour what the document's signatures forbid (locked
320
444
  * fields, structural edits). Set at open from the engine option; the
321
445
  * main-thread guard subtracts the matching capabilities.
322
446
  */
323
447
  signedDocumentPolicy: SignedDocumentPolicy;
324
- /**
325
- * The shape the caller asked for when opening plain bytes. `layer` (the
326
- * default) opens them as an immutable base with a fresh layer; `plain`
327
- * keeps one in-memory document unless the bytes already carry a
328
- * signature, which always opens as a layer.
329
- */
330
- sessionKind: SessionKind;
331
448
  /** The password the document was opened with; signing candidates open with the same one. */
332
449
  password: string | null;
333
450
  /**
334
451
  * Whether mutations on a layer session also serialize the layer artifact
335
- * into their response (what a server persists). False for a session that
336
- * became a layer only because its document is signed (see
337
- * `WorkerHost.openSignedAware`): the caller opened plain bytes and never
338
- * asked for artifacts.
452
+ * into their response (what a server persists). False for a session opened
453
+ * from bytes (`WorkerHost.openBytesAsLayer`): the caller holds the document
454
+ * and never asked for artifacts.
339
455
  */
340
456
  persistLayerArtifact: boolean;
341
- /** The mutation sequence the current bytes were loaded at (see `hasUnsavedEdits`). */
457
+ /** The edits sequence the current bytes were loaded at (see `hasUnsavedEdits`). */
342
458
  private loadedSeq;
343
459
  /** SHA-256 (hex) of a plain session's loaded bytes, hashed once per load. */
344
460
  private plainSha256;
@@ -346,6 +462,8 @@ declare class DocumentSession {
346
462
  pendingSigning: PendingSigning | null;
347
463
  /** The last completed signing, so a replayed `complete` answers `already-completed`. */
348
464
  lastCompletion: SigningCompletion | null;
465
+ /** The stamp drawings of the open document, by content; dropped with it. */
466
+ private drawings;
349
467
  constructor(runtime: PdfRuntimeModule, sessionId?: string);
350
468
  get sessionId(): string;
351
469
  get kind(): OpenedPdfDocumentKind | null;
@@ -359,8 +477,8 @@ declare class DocumentSession {
359
477
  open(bytes: Uint8Array, password?: string | null): void;
360
478
  openFromHandle(handle: OpenedPdfDocument): void;
361
479
  /**
362
- * Whether anything was mutated since the current bytes were loaded. When
363
- * false, the loaded bytes ARE the document: a save returns them verbatim
480
+ * Whether a write was committed since the current bytes were loaded. When
481
+ * false, the loaded bytes are the document: a save returns them verbatim
364
482
  * and a signing candidate is built straight on them.
365
483
  */
366
484
  hasUnsavedEdits(): boolean;
@@ -373,8 +491,9 @@ declare class DocumentSession {
373
491
  * installs its sealed file. The session id, its page object numbers
374
492
  * (an incremental save never renumbers) and its retained resources
375
493
  * survive; the old document is closed, every page is re-pinned (its
376
- * revision bumped, its cached pointer dropped), and the mutation
377
- * sequence advances so every version-keyed cache rebuilds.
494
+ * cached pointer dropped), and both sequences
495
+ * advance, so every version-keyed cache rebuilds and the new bytes count as
496
+ * loaded.
378
497
  */
379
498
  install(handle: OpenedPdfDocument): void;
380
499
  /** The two publish fences a candidate is built on. */
@@ -382,64 +501,139 @@ declare class DocumentSession {
382
501
  /**
383
502
  * Park this session in the password-locked state: the document could not
384
503
  * be loaded because a (correct) password is missing, so the session keeps
385
- * the already-transferred bytes and waits for an unlock attempt to load
386
- * them. A locked session occupies its docId key like an open one — every
387
- * operation except the password check rejects with DocPasswordRequired.
504
+ * how to load it (the already-transferred bytes, a base's path, the layer
505
+ * to open) and waits for an unlock attempt. A locked session occupies its
506
+ * docId key like an open one — every operation except the password check
507
+ * rejects with DocPasswordRequired.
388
508
  */
389
- parkLocked(bytes: Uint8Array): void;
509
+ parkLocked(load: (password: string | null) => void): void;
390
510
  isLocked(): boolean;
391
- /** The bytes retained for a later unlock attempt. Locked sessions only. */
392
- lockedBytes(): Uint8Array;
393
- private parkedBytes;
511
+ /**
512
+ * Load a locked session with `password`. A wrong one throws
513
+ * `DocPasswordIncorrect` and leaves the session parked for another try.
514
+ */
515
+ unlockWith(password: string | null): void;
516
+ private parkedLoad;
394
517
  /** Number of pages in the document. */
395
518
  pageCount(): number;
396
519
  /**
397
520
  * Lazily enumerate every page and cache (pageObjectNumber, pageIndex).
398
- * Necessary before `listRawAll()` and any pon -> pageIndex resolution.
521
+ * Necessary before a whole-document list and any pon -> pageIndex resolution.
399
522
  */
400
523
  ensureFullPageRegistry(): void;
401
524
  /** Lookup; populates the cache for one page only on cache miss. */
402
525
  recordByObjectNumber(pageObjectNumber: PageObjectNumber): PageRecord;
526
+ /**
527
+ * Resolve a page address to its registry record — the one boundary where
528
+ * a `PageRef` becomes a page object number. Throws `NotFound` for an
529
+ * unknown page.
530
+ */
531
+ resolvePageRef(ref: PageRef): PageRecord;
532
+ /** `resolvePageRef` over a batch, preserving order. */
533
+ resolvePageRefs(refs: readonly PageRef[]): PageObjectNumber[];
403
534
  /** All page records, in display order. Forces full enumeration. */
404
535
  allRecords(): PageRecord[];
405
536
  /**
406
537
  * Drop the cached `pageIndex <-> pageObjectNumber` mapping and force a
407
538
  * fresh enumeration on next access. Called by `PagesMutator`
408
- * after `FPDF_MovePages` shuffles page positions; we keep
409
- * weak-annotation knowledge and per-page revision counters intact, both of
410
- * which are keyed by durable `pageObjectNumber` and survive a page reorder.
539
+ * after `FPDF_MovePages` shuffles page positions.
411
540
  */
412
541
  refreshPageRegistry(): void;
413
- /** Per-page liveness envelope used by annotation read/mutation results. */
414
- pageState(pageObjectNumber: PageObjectNumber): PageState;
415
- /** Set by readers as they discover whether a page has weak annotations. */
416
- recordWeakFlag(pageObjectNumber: PageObjectNumber, hasWeak: boolean): void;
417
- recordWeakAnnotationState(pageObjectNumber: PageObjectNumber, state: WeakAnnotationState): void;
418
- weakAnnotationState(pageObjectNumber: PageObjectNumber): WeakAnnotationState;
419
- /** Bump and return the new revision token; called by mutation paths. */
420
- bumpRevision(pageObjectNumber: PageObjectNumber): RevisionToken;
421
- /**
422
- * Monotonic count of successful document mutations in this session.
423
- * Version key for detached-snapshot caches (e.g. the forms model):
424
- * a cache entry built at sequence N is exactly valid while the
425
- * sequence is still N. Coarse on purpose — widgets are annotations
426
- * and page ops move widgets, so ANY mutation may affect derived
427
- * form state; per-domain counters are a later optimization.
428
- */
429
- mutationSeq(): number;
430
- /** Record one successful mutation; called by mutation paths. */
431
- noteMutation(): void;
432
- /**
433
- * Forget a page's per-session state (revision generation + weak-annotation
434
- * flag). Called by `pages.delete` after the page object is retired; the
435
- * PON is never recycled, so this is hygiene, not correctness.
436
- */
437
- dropPageState(pageObjectNumber: PageObjectNumber): void;
438
- validateRevision(token: RevisionToken): void;
542
+ /**
543
+ * The version key for caches built from the document (the forms model,
544
+ * the signature model, search text): an entry built at sequence N is
545
+ * exactly valid while the sequence is still N. Coarse on purpose: widgets
546
+ * are annotations and page edits move widgets, so any write may change
547
+ * derived form state.
548
+ */
549
+ cacheSeq(): number;
550
+ /**
551
+ * Make every cache keyed on {@link cacheSeq} build again. Writers call it
552
+ * after a write, before they read back; an abort calls it too, because a
553
+ * cache may have been built from what the transaction wrote.
554
+ */
555
+ invalidateDerived(): void;
556
+ /**
557
+ * The count of committed writes: what {@link hasUnsavedEdits} and a signing
558
+ * candidate's `editsVersion` compare.
559
+ */
560
+ editsSeq(): number;
561
+ /** Count one committed write. Called once per write, after its commit. */
562
+ noteEdit(): void;
563
+ /** Whether a layer transaction is open (see {@link beginTransaction}). */
564
+ inTransaction(): boolean;
565
+ /**
566
+ * Open a layer transaction: until {@link commitTransaction} or
567
+ * {@link abortTransaction}, every write lands in an overlay that only a
568
+ * commit keeps, and every read sees it. Only a layer document has
569
+ * transactions. `opId` names the write it runs (see {@link writeStamp}).
570
+ */
571
+ beginTransaction(opId: string): void;
572
+ /**
573
+ * What every meta of the running write carries: its `opId`, and whether
574
+ * it can be undone. A write is undoable once {@link markUndoable} said so,
575
+ * which a write does when it records its reverse.
576
+ */
577
+ writeStamp(): WriteStamp;
578
+ /** The running write records its reverse, so `{ undoOf: opId }` can undo it. */
579
+ markUndoable(): void;
580
+ /**
581
+ * Keep everything the open transaction wrote. A commit that fails can't
582
+ * say how far it got, so the session becomes unusable.
583
+ */
584
+ commitTransaction(): void;
585
+ /**
586
+ * Drop everything the open transaction wrote, and forget what this session
587
+ * learned from it. Never throws: it runs while a failure is on its way out,
588
+ * and a failed abort makes the session unusable instead.
589
+ */
590
+ abortTransaction(): void;
591
+ /**
592
+ * Who decides which object numbers a create may name: `'session'` hands
593
+ * them out ({@link reserveObjectNumbers}) and refuses a number it doesn't
594
+ * hold; `'caller'` checked every number before sending the job (a server
595
+ * that hands numbers to editing sessions).
596
+ */
597
+ objectNumberAuthority: 'session' | 'caller';
598
+ /** Numbers this session handed out that no committed create has used. */
599
+ private readonly heldObjectNumbers;
600
+ /** Held numbers the open transaction made objects at: spent when it commits. */
601
+ private objectNumbersInUse;
602
+ /** The layer's last object number: every object it has is at or below it. */
603
+ lastObjectNumber(): number;
604
+ /**
605
+ * Raise the layer's last object number to at least `last`, so the objects
606
+ * the next write makes for itself get numbers above it. A raise outlives
607
+ * an abort: numbers once passed are never handed out again.
608
+ */
609
+ raiseLastObjectNumber(last: number): void;
610
+ /**
611
+ * Hand out `count` more numbers: the layer's last object number moves up
612
+ * by `count`, so nothing the engine makes lands on them, and they are this
613
+ * session's to name objects with.
614
+ */
615
+ reserveObjectNumbers(count: number): ObjectNumberRange;
616
+ /**
617
+ * Checks the number a create names before anything is written, and counts
618
+ * it as used by the open transaction: spent when it commits, held again
619
+ * if it aborts. A session that hands numbers out refuses one it doesn't
620
+ * hold (`not-held`); a number the transaction already used is `taken`.
621
+ * Nothing else creates objects at held numbers, so a held number is free
622
+ * otherwise; the fork checks that again as it creates.
623
+ */
624
+ useObjectNumber(objectNumber: number): void;
625
+ /**
626
+ * Throws once a transaction neither committed nor aborted, so nothing reads
627
+ * or writes a document in an unknown state.
628
+ */
629
+ assertUsable(): void;
630
+ private requireTransaction;
439
631
  pagePool(): PagePtrPool;
632
+ /** The open document's stamp drawings, by content (see {@link DrawingIndex}). */
633
+ drawingIndex(): DrawingIndex;
440
634
  requireDocPtr(): Ptr;
441
635
  /**
442
- * Park a disposer to run when THIS session closes. Used by operations
636
+ * Park a disposer to run when this session closes. Used by operations
443
637
  * whose native side leaves the session document referencing another
444
638
  * resource — e.g. `pages.insert`: `FPDF_ImportPagesByIndex` does not
445
639
  * fully detach imported objects from their source document, so the
@@ -449,7 +643,6 @@ declare class DocumentSession {
449
643
  retainUntilClose(dispose: () => void): void;
450
644
  private readonly retained;
451
645
  close(): void;
452
- private requireRevisions;
453
646
  }
454
647
  //#endregion
455
648
  //#region src/document-session/lifecycle/BaseDocumentRegistry.d.ts
@@ -492,54 +685,6 @@ declare class BaseDocumentRegistry {
492
685
  private handleFor;
493
686
  }
494
687
  //#endregion
495
- //#region src/document-session/revisions/RevisionAuthority.d.ts
496
- interface RevisionAuthority {
497
- readonly docSessionId: string;
498
- token(pageObjectNumber: PageObjectNumber): RevisionToken;
499
- bump(pageObjectNumber: PageObjectNumber): RevisionToken;
500
- validate(token: RevisionToken): void;
501
- weakAnnotationState(pageObjectNumber: PageObjectNumber): WeakAnnotationState;
502
- recordWeakAnnotationState(pageObjectNumber: PageObjectNumber, state: WeakAnnotationState): void;
503
- /** Forget a page entirely (generation + weak state). Called when the page
504
- * is DELETED — its object number is retired, never recycled, so dropping
505
- * is hygiene, not correctness. */
506
- drop(pageObjectNumber: PageObjectNumber): void;
507
- clear(): void;
508
- }
509
- /**
510
- * Per-page generation counter. Backs `RevisionToken` so weak
511
- * `AnnotationRef.kind === 'index'` references can be validated against the
512
- * exact page state they were minted from.
513
- *
514
- * `bump()` is the only mutator; mutation services call it after a
515
- * structural change to the page (annotation create/delete/reorder).
516
- * Read paths never mutate the store.
517
- */
518
- declare class LocalRevisionAuthority implements RevisionAuthority {
519
- readonly docSessionId: string;
520
- private readonly generations;
521
- private readonly weakAnnotationStates;
522
- constructor(docSessionId: string);
523
- /** Returns the current generation, defaulting to 0 for first read. */
524
- current(pageObjectNumber: PageObjectNumber): number;
525
- /** Mints a fresh `RevisionToken` for the page's current generation. */
526
- token(pageObjectNumber: PageObjectNumber): RevisionToken;
527
- /**
528
- * Increments the generation counter for a page. Returns the new token.
529
- * Called by mutation services after every structural change.
530
- */
531
- bump(pageObjectNumber: PageObjectNumber): RevisionToken;
532
- /**
533
- * Strict equality check. Throws `EngineError(InvalidReference)` when the
534
- * caller's token does not match what the store currently holds.
535
- */
536
- validate(token: RevisionToken): void;
537
- weakAnnotationState(pageObjectNumber: PageObjectNumber): WeakAnnotationState;
538
- recordWeakAnnotationState(pageObjectNumber: PageObjectNumber, state: WeakAnnotationState): void;
539
- drop(pageObjectNumber: PageObjectNumber): void;
540
- clear(): void;
541
- }
542
- //#endregion
543
688
  //#region src/features/metadata/MetadataReader.d.ts
544
689
  /**
545
690
  * Synchronous, runtime-agnostic implementation of the metadata read.
@@ -555,6 +700,8 @@ declare class MetadataReader {
555
700
  private readonly session;
556
701
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
557
702
  read(signal: AbortSignal): DocumentMetadata;
703
+ /** The Info dict's custom keys: every key but the standard ones. */
704
+ readCustom(signal: AbortSignal): CustomMetadata;
558
705
  }
559
706
  //#endregion
560
707
  //#region src/features/metadata/MetadataMutator.d.ts
@@ -574,6 +721,8 @@ declare class MetadataMutator {
574
721
  private readonly session;
575
722
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
576
723
  update(patch: MetadataPatch, signal: AbortSignal): MetadataUpdateResult;
724
+ /** Set and remove custom keys, then re-read them the same way. */
725
+ updateCustom(patch: CustomMetadataPatch, signal: AbortSignal): CustomMetadataUpdateResult;
577
726
  }
578
727
  //#endregion
579
728
  //#region src/features/pages/PagesReader.d.ts
@@ -588,8 +737,14 @@ declare class PagesReader {
588
737
  private readonly runtime;
589
738
  private readonly session;
590
739
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
591
- read(signal: AbortSignal): PageListSnapshot;
740
+ read(signal: AbortSignal): PageListSnapshot<PdfCoordinates>;
592
741
  }
742
+ /**
743
+ * Each page's visible box, read the first time it is asked for: what a
744
+ * result's destinations are measured on. A page that isn't in the document
745
+ * has none.
746
+ */
747
+ declare function visibleBoxReader(runtime: PdfRuntimeModule, session: DocumentSession): VisibleBoxOf;
593
748
  //#endregion
594
749
  //#region src/features/pages/PagesMutator.d.ts
595
750
  /**
@@ -599,35 +754,26 @@ declare class PagesReader {
599
754
  * `worker_thread`) share the same code path.
600
755
  *
601
756
  * Architectural anchor — locked with the user, do not loosen without
602
- * re-reading the doc comments on `PageMoveResult` and `RevisionAuthority`:
603
- *
604
- * - Pages are addressed by their durable `pageObjectNumber`. There is
605
- * no "weak page ref" model in the engine; therefore there is no
606
- * document-level revision token, no `DocumentRevisionStore`, and no
607
- * "doc-level shouldRefetch" semantic. Anything that *is* listed
608
- * here must remain stable across every reorder permutation.
757
+ * re-reading the doc comment on `PageReorderResult`:
609
758
  *
610
- * - Per-page `RevisionToken`s do NOT bump on `move()`. The /Annots
611
- * array of each affected page is untouched (PDFium just rewrites
612
- * pointer entries in the doc-level pages tree), so weak
613
- * `AnnotationRef.kind === 'index'` references the caller is
614
- * holding remain valid across a page reorder. This is the right
615
- * semantic for an editing UI: shuffling pages must NOT silently
616
- * break a pending highlight edit.
759
+ * - Pages are addressed by their durable `pageObjectNumber`, which must
760
+ * remain stable across every reorder permutation.
617
761
  *
618
- * - Identity strengthening (the opportunistic `/NM` stamping that
619
- * applies to weak annotations on `update()` / `move()`) is also
620
- * intentionally absent here. Pages are durable by construction;
621
- * there is nothing to upgrade.
762
+ * - The /Annots array of each page is untouched by `reorder()` (PDFium just
763
+ * rewrites pointer entries in the doc-level pages tree), so every
764
+ * annotation ref the caller is holding stays valid across a page
765
+ * reorder: shuffling pages must not silently break a pending
766
+ * highlight edit.
622
767
  */
623
768
  declare class PagesMutator {
624
769
  private readonly runtime;
625
770
  private readonly session;
626
771
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
627
772
  /**
628
- * Reorder pages. Mirrors `FPDF_MovePages`: detach the supplied pages,
629
- * then re-insert them as a contiguous block at `destIndex` in the
630
- * post-removal index space, preserving caller order.
773
+ * Reorder pages: they go together, in the order given, to `position`, next
774
+ * to a neighbour page or at the start or end. Mirrors `FPDF_MovePages`,
775
+ * which detaches the pages and re-inserts them as a block at an index in
776
+ * the pages left: the neighbour's index there, or the one after it.
631
777
  *
632
778
  * Atomicity:
633
779
  * - `FPDF_MovePages` rejects atomically: if it returns false, no
@@ -635,66 +781,63 @@ declare class PagesMutator {
635
781
  * - On success we refresh the per-session page registry and read the
636
782
  * new layout back, which is what the result returns.
637
783
  *
638
- * Validation done up front (the helper repeats these checks; we do
639
- * them here for clean error messages):
640
- * - non-empty inputs;
641
- * - duplicate `pageObjectNumber`s rejected;
642
- * - every `pon` resolvable via the session's page registry;
643
- * - `destIndex` in `[0, pageCount - len]`.
784
+ * Validation done up front, for clean error messages: at least one page,
785
+ * none twice, and a neighbour that isn't one of them (`InvalidArg`); every
786
+ * page and the neighbour pages of the document (`NotFound`).
644
787
  */
645
- move(pageObjectNumbers: PageObjectNumber[], destIndex: number, signal: AbortSignal): PageMoveResult;
788
+ reorder(pages: PageRef[], position: PagePosition, signal: AbortSignal): PageReorderResult<PdfCoordinates>;
646
789
  /**
647
- * Set the ABSOLUTE display rotation of the supplied pages. Rotation is
790
+ * Set the absolute display rotation of the supplied pages. Rotation is
648
791
  * presentation metadata over normalized content (pages always load with
649
792
  * rotation forced to 0 — see `PagePtrPool`), so:
650
793
  * - no cached render, text run, or geometry coordinate changes;
651
- * - per-page `RevisionToken`s do NOT bump;
794
+ * - per-page `RevisionToken`s do not bump;
652
795
  * - page identity and order are untouched — no registry refresh.
653
796
  *
654
- * Atomicity: every PON is resolved up front (`NotFound` on caller error),
797
+ * Atomicity: every page object number is resolved up front (`NotFound` on caller error),
655
798
  * so the apply loop below operates on validated pages only and each write
656
799
  * is absolute + idempotent — a retry after an unexpected mid-loop engine
657
- * fault converges to the requested state. Abort is honored BEFORE the
800
+ * fault converges to the requested state. Abort is honored before the
658
801
  * loop, never inside it.
659
802
  */
660
- rotate(pageObjectNumbers: PageObjectNumber[], rotation: PageRotation, signal: AbortSignal): PageRotateResult;
803
+ rotate(pages: PageRef[], rotation: PdfRotation, signal: AbortSignal): PageRotateResult<PdfCoordinates>;
661
804
  /**
662
- * Delete pages. Deleted object numbers are RETIRED — the engine nulls the
805
+ * Delete pages. Deleted object numbers are retired — the engine nulls the
663
806
  * page object rather than freeing the number — so per-page state keyed by
664
- * PON can never silently attach to an unrelated future page; we still drop
665
- * the session's revision/weak entries as hygiene. Surviving pages keep
666
- * their identity and `RevisionToken`s.
807
+ * page object number can never silently attach to an unrelated future
808
+ * page. Surviving pages keep their identity, and their annotations their
809
+ * names.
667
810
  *
668
811
  * Guards:
669
812
  * - a document must keep at least one page (`InvalidArg`);
670
- * - every PON must resolve (`NotFound`);
813
+ * - every page object number must resolve (`NotFound`);
671
814
  * - no target page may have a live pooled `pagePtr`. Thread confinement
672
815
  * means no other job can be mid-flight, so a held ptr here is a leaked
673
816
  * `acquire` — an internal bug we surface loudly instead of deleting
674
817
  * under a live handle.
675
818
  *
676
- * Abort is honored BEFORE the apply loop, never inside it.
819
+ * Abort is honored before the apply loop, never inside it.
677
820
  */
678
- delete(pageObjectNumbers: PageObjectNumber[], signal: AbortSignal): PageDeleteResult;
821
+ delete(pages: PageRef[], signal: AbortSignal): PageDeleteResult<PdfCoordinates>;
679
822
  /**
680
823
  * Register `name` → page in `/Names /Pages` (create, or replace what the
681
824
  * key points at); with `replace`, drop that other key first — a rename as
682
- * one job. Named pages are LAYOUT: page identity and order are untouched
683
- * (no registry refresh, no revision bumps) and the fresh snapshot is
825
+ * one job. Named pages are layout: page identity and order are untouched
826
+ * (no registry refresh) and the fresh snapshot is
684
827
  * returned like `move()`.
685
828
  */
686
- setName(input: PageNameInput, signal: AbortSignal): PageNameResult;
829
+ setName(input: PageNameInput, signal: AbortSignal): PageNameResult<PdfCoordinates>;
687
830
  /** Remove one `/Names /Pages` registration; the page stays. */
688
- removeName(input: PageRemoveNameInput, signal: AbortSignal): PageNameResult;
689
- /** Shared input check: non-empty, no duplicate PONs. */
690
- private requireUniquePons;
831
+ removeName(input: PageRemoveNameInput, signal: AbortSignal): PageNameResult<PdfCoordinates>;
832
+ /** Shared input check: non-empty, no duplicate page object numbers. */
833
+ private requireUniquePageObjectNumbers;
691
834
  }
692
835
  //#endregion
693
836
  //#region src/features/pages/PagesExtractor.d.ts
694
837
  /**
695
- * Export a subset of pages as a standalone PDF. A READ over the session:
838
+ * Export a subset of pages as a standalone PDF. A read over the session:
696
839
  * `FPDF_ImportPagesByIndex` copies pages into a scratch document, so the
697
- * source is untouched — no revision bumps, no registry refresh, no layer
840
+ * source is untouched — no registry refresh, no layer
698
841
  * artifact. Lives next to `PagesReader`/`PagesMutator` (one file per page
699
842
  * verb family) so both worker hosts share the code path.
700
843
  */
@@ -705,10 +848,10 @@ declare class PagesExtractor {
705
848
  /**
706
849
  * Copy the given pages, in the supplied caller order, into a new
707
850
  * document and serialize it. Validation up front: non-empty, no
708
- * duplicates, every PON resolvable (`NotFound` from the session on a
851
+ * duplicates, every page object number resolvable (`NotFound` from the session on a
709
852
  * bad one). The scratch document is always closed, success or throw.
710
853
  */
711
- extract(pageObjectNumbers: PageObjectNumber[], signal: AbortSignal): {
854
+ extract(pages: PageRef[], signal: AbortSignal): {
712
855
  bytes: ArrayBuffer;
713
856
  size: number;
714
857
  };
@@ -717,9 +860,9 @@ declare class PagesExtractor {
717
860
  //#region src/features/pages/PagesInserter.d.ts
718
861
  /**
719
862
  * Insert every page of a standalone PDF into the session document. A
720
- * structural MUTATION (like move/delete): the source bytes are loaded as a
863
+ * structural mutation (like move/delete): the source bytes are loaded as a
721
864
  * throwaway PDFium document, `FPDF_ImportPagesByIndex` deep-copies its
722
- * pages in at `destIndex`, and the page registry is rebuilt. Pre-existing
865
+ * pages in at `position`, and the page registry is rebuilt. Pre-existing
723
866
  * pages keep their identity and `RevisionToken`s; the inserted copies get
724
867
  * fresh object numbers, resolved from the post-insert registry.
725
868
  */
@@ -727,43 +870,49 @@ declare class PagesInserter {
727
870
  private readonly runtime;
728
871
  private readonly session;
729
872
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
730
- insert(bytes: ArrayBuffer, destIndex: number | undefined, signal: AbortSignal): PageInsertResult;
873
+ insert(bytes: ArrayBuffer, position: PagePosition, signal: AbortSignal): PageInsertResult<PdfCoordinates>;
731
874
  /**
732
- * Create `count` blank pages of `size` at `destIndex`. Same mutation
733
- * contract as `insert`, but native creation (`FPDFPage_New`) instead of a
734
- * deep copy: no source document, so none of the retain-until-close
735
- * lifetime hazard above. Each page gets an empty content stream
736
- * (`FPDFPage_GenerateContent`) so the saved PDF renders identically in
737
- * third-party viewers.
875
+ * Create `count` blank pages of `size` at `position`. Same mutation
876
+ * contract as `insert`, but native creation (`EPDFPage_InsertBlankRaw`,
877
+ * which never loads the page) instead of a deep copy: no source document,
878
+ * so none of the retain-until-close lifetime hazard above. A blank page has
879
+ * no `/Contents`, which ISO 32000-2 defines as an empty page.
880
+ *
881
+ * `objectNumbers`, one per page, names the pages: numbers the session
882
+ * holds, each checked before the first page is made.
738
883
  */
739
- insertBlank(spec: PageInsertBlankSpec, destIndex: number | undefined, signal: AbortSignal): PageInsertResult;
884
+ insertBlank(spec: PageInsertBlankSpec, position: PagePosition, signal: AbortSignal, objectNumbers?: readonly number[]): PageInsertResult<PdfCoordinates>;
740
885
  }
741
886
  //#endregion
742
887
  //#region src/features/pages/PagesFlattener.d.ts
743
- /** Layer-safe page flattening. Content and annotation liveness change together. */
888
+ /**
889
+ * Layer-safe page flattening. Content and annotation liveness change
890
+ * together, all or nothing: a page that fails, or a cancel, aborts the whole
891
+ * flatten.
892
+ */
744
893
  declare class PagesFlattener {
745
894
  private readonly runtime;
746
895
  private readonly session;
747
896
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
748
- flatten(pageObjectNumbers: PageObjectNumber[], usage: PageFlattenUsage, signal: AbortSignal): PageFlattenResult;
897
+ flatten(pages: PageRef[], usage: PageFlattenUsage, signal: AbortSignal): PageFlattenResult;
749
898
  }
750
899
  //#endregion
751
900
  //#region src/features/redaction/RedactionApplier.d.ts
752
901
  /**
753
902
  * The destructive half of redaction (see `DocumentRedactionService` for the
754
903
  * model and the layer trust boundary). Content and annotation liveness
755
- * change together, exactly like {@link PagesFlattener} — this class mirrors
756
- * its validate-then-apply boundary and ordered-batch semantics.
904
+ * change together, exactly like {@link PagesFlattener}: all or nothing, so a
905
+ * page that fails, or a cancel, aborts the whole apply.
757
906
  *
758
907
  * Scope semantics:
759
- * - `pages`: every REDACT annotation on each listed page is applied; a
908
+ * - `pages`: every redact annotation on each listed page is applied; a
760
909
  * page with none is `unchanged`.
761
- * - `annotations`: exactly the referenced REDACT annotations are applied.
762
- * Every ref is resolved and subtype-checked BEFORE the first native
763
- * write; a non-REDACT ref rejects the whole call with `InvalidArg`.
910
+ * - `annotations`: exactly the referenced redact annotations are applied.
911
+ * Every ref is resolved and subtype-checked before the first native
912
+ * write; a non-redact ref rejects the whole call with `InvalidArg`.
764
913
  *
765
914
  * The per-page `removedAnnotationCount` is the native collateral count:
766
- * annotations other than REDACT ones removed alongside the apply (popup
915
+ * annotations other than redact ones removed alongside the apply (popup
767
916
  * cascades and detached widgets included).
768
917
  */
769
918
  declare class RedactionApplier {
@@ -773,29 +922,34 @@ declare class RedactionApplier {
773
922
  apply(scope: RedactionApplyScope, signal: AbortSignal): RedactionApplyResult;
774
923
  /**
775
924
  * Normalize the scope into an ordered per-page plan. `null` refs means
776
- * "every REDACT annotation on the page" (the `pages` scope).
925
+ * "every redact annotation on the page" (the `pages` scope).
777
926
  */
778
927
  private buildPlan;
779
928
  /**
780
- * Validate every ref BEFORE the first native write: it must resolve and
781
- * it must be a REDACT annotation. After this returns, the apply loop's
929
+ * Validate every ref before the first native write: it must resolve and
930
+ * it must be a redact annotation. After this returns, the apply loop's
782
931
  * writes begin and per-page failures are recorded, not thrown.
783
932
  */
784
933
  private preflight;
785
934
  private applyAllOnPage;
786
935
  private pageHasRedactAnnotations;
936
+ /**
937
+ * Applies `refs` in one native call: their regions are redacted together,
938
+ * so an image under several of them is read and written once.
939
+ */
787
940
  private applyRefsOnPage;
788
941
  }
789
942
  //#endregion
790
943
  //#region src/features/text/PageTextReader.d.ts
791
944
  /**
792
945
  * Per-page slow-path text reader. Acquires a `pagePtr` from the
793
- * `PagePtrPool`, opens a PDFium text page (`FPDFText_LoadPage`), extracts
794
- * the page text, and releases everything in reverse order.
946
+ * `PagePtrPool` (a page not parsed yet loads in slices, so an abort stops the
947
+ * load), opens a PDFium text page (`FPDFText_LoadPage`), extracts the page
948
+ * text, and releases everything in reverse order.
795
949
  *
796
950
  * Index spaces (see engine-core `text/charmap.ts`): `charCount` is the
797
- * CHARACTER space (`FPDFText_CountChars` — the space geometry runs tile);
798
- * `text` is the TEXT projection. The two diverge when a character
951
+ * character space (`FPDFText_CountChars` — the space geometry runs tile);
952
+ * `text` is the text projection. The two diverge when a character
799
953
  * contributes zero text units (non-printing) or two (supplementary plane);
800
954
  * `charMap` anchors encode exactly those deviations.
801
955
  *
@@ -815,7 +969,7 @@ declare class PageTextReader {
815
969
  private readonly runtime;
816
970
  private readonly session;
817
971
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
818
- read(pageObjectNumber: PageObjectNumber, signal: AbortSignal): PageTextSnapshot;
972
+ read(pageObjectNumber: PageObjectNumber, signal: AbortSignal, slices: Slices): Promise<PageTextSnapshot>;
819
973
  }
820
974
  //#endregion
821
975
  //#region src/features/geometry/PageGeometryReader.d.ts
@@ -830,9 +984,9 @@ interface RawGeometryGlyphRecord {
830
984
  /** Sampled at each text object's first glyph (run splits inherit it). */
831
985
  fontSize?: number;
832
986
  /**
833
- * Wire flags (bit 1 = space, bit 2 = empty). Synthesized-but-visible
834
- * glyphs (/ActualText pieces) stay 0, exactly like the legacy reader —
835
- * they represent real, selectable text.
987
+ * `FLAG_SPACE` and `FLAG_EMPTY`. Synthesized-but-visible glyphs
988
+ * (/ActualText pieces) stay 0, exactly like the legacy reader — they
989
+ * represent real, selectable text.
836
990
  */
837
991
  flags: number;
838
992
  /** Page-space AABB (zeroed when empty). */
@@ -844,7 +998,7 @@ interface RawGeometryGlyphRecord {
844
998
  /** Native uprightness of the char matrix (empty glyphs: true, inert). */
845
999
  upright: boolean;
846
1000
  /** Baseline angle (radians CCW, PDF y-up); 0 when upright. */
847
- rotation: number;
1001
+ baselineAngle: number;
848
1002
  /** True when the ascent vector maps opposite the rotated +y (det < 0). */
849
1003
  ascentFlip: boolean;
850
1004
  }
@@ -853,7 +1007,7 @@ interface RawGeometryGlyphRecord {
853
1007
  *
854
1008
  * Emits geometry in PDF user space (y-up edges) — the canonical engine
855
1009
  * geometry. The viewer converts to content/view space via the page geometry
856
- * matrix; this reader applies NO Y-flip or device transform.
1010
+ * matrix; this reader applies no Y-flip or device transform.
857
1011
  *
858
1012
  * One `EPDFText_GetCharGeometry` call per glyph supplies boxes, oriented
859
1013
  * cells, and flags together; `buildRunsFromRawGlyphs` then groups glyphs
@@ -865,7 +1019,12 @@ declare class PageGeometryReader {
865
1019
  private readonly runtime;
866
1020
  private readonly session;
867
1021
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
868
- read(pageObjectNumber: PageObjectNumber, signal: AbortSignal): PageGeometrySnapshot;
1022
+ /**
1023
+ * A page's text geometry. The page loads in slices when it isn't parsed, and
1024
+ * the glyph loop pauses once a slice's budget is spent, so an abort stops the
1025
+ * read at either.
1026
+ */
1027
+ read(pageObjectNumber: PageObjectNumber, signal: AbortSignal, slices: Slices): Promise<PageGeometrySnapshot<PdfCoordinates>>;
869
1028
  private readGlyphRaw;
870
1029
  }
871
1030
  /**
@@ -873,16 +1032,16 @@ declare class PageGeometryReader {
873
1032
  *
874
1033
  * Runs split on text-object change (the legacy rule) and on orientation
875
1034
  * change between classifiable glyphs (θ drift / mixed orientation). A run's
876
- * variant is decided by its FIRST classifiable glyph:
1035
+ * variant is decided by its first classifiable glyph:
877
1036
  * - empty glyphs never classify (they adopt the run's variant);
878
- * - real glyphs WITHOUT an oriented cell (singular matrix, synthesized
1037
+ * - real glyphs without an oriented cell (singular matrix, synthesized
879
1038
  * /ActualText pieces) classify as upright — box-only data degrades to
880
1039
  * exactly the legacy behavior;
881
1040
  * - real glyphs with a non-upright matrix classify as rotated.
882
1041
  * Runs that never see a classifiable glyph emit as upright (degenerate,
883
1042
  * legacy behavior).
884
1043
  */
885
- declare function buildRunsFromRawGlyphs(records: RawGeometryGlyphRecord[]): PageGeometryRun[];
1044
+ declare function buildRunsFromRawGlyphs(records: RawGeometryGlyphRecord[]): PageGeometryRun<PdfCoordinates>[];
886
1045
  //#endregion
887
1046
  //#region src/runtime/memory/structs.d.ts
888
1047
  /** Generated from `EPDF_CHAR_GEOMETRY`; pinned by native static_asserts. */
@@ -912,7 +1071,15 @@ declare class PageRenderReader {
912
1071
  private readonly runtime;
913
1072
  private readonly session;
914
1073
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
915
- render(pageObjectNumber: PageObjectNumber, options: PageRenderOptions, signal: AbortSignal): import("@embedpdf/engine-core/runtime").PageRaster;
1074
+ /**
1075
+ * Renders a page in slices, and says which area of it the pixels show.
1076
+ * Until it settles, PDFium holds the page's render: the caller must not let
1077
+ * anything else use PDFium between slices.
1078
+ */
1079
+ render(pageObjectNumber: PageObjectNumber, options: PageRenderOptions<PdfCoordinates>, signal: AbortSignal, slices: Slices): Promise<{
1080
+ raster: PageRaster;
1081
+ area: PdfRect;
1082
+ }>;
916
1083
  }
917
1084
  //#endregion
918
1085
  //#region src/features/fonts/FontRegistrar.d.ts
@@ -1038,29 +1205,38 @@ declare class AnnotationReader {
1038
1205
  constructor(runtime: PdfRuntimeModule, session: DocumentSession,
1039
1206
  /** This thread's font registry: FreeText faces read back as keys. */
1040
1207
  fonts?: FontRegistrar | undefined);
1041
- list(pageObjectNumber: PageObjectNumber, signal: AbortSignal): AnnotationListPageSnapshot;
1208
+ list(pageObjectNumber: PageObjectNumber, signal: AbortSignal): AnnotationList<PdfCoordinates>;
1042
1209
  }
1043
1210
  //#endregion
1044
1211
  //#region src/features/annotations/AnnotationAppearanceReader.d.ts
1045
1212
  /**
1046
- * Batch-renders the appearance streams (`/AP`) of every annotation on a page,
1047
- * one bitmap per requested mode. Ported from the v2 PDFium engine's
1048
- * `renderPageAnnotationsRaw` / `renderSingleAnnotAppearance`, but expressed in
1049
- * PDF user space and against the `PdfRuntimeModule` (`fn` + `mem`).
1213
+ * Batch-renders the appearances of one family of a page's annotations (its
1214
+ * annotations except widgets, or its widgets), one bitmap per mode it stores
1215
+ * (or each requested one) and per state of that mode, each labelled with
1216
+ * both, in PDF user space and against the `PdfRuntimeModule` (`fn` + `mem`).
1050
1217
  *
1051
- * Each appearance bitmap is sized to its annotation's `/Rect` scaled by
1052
- * `options.scale`. The shared raster helper handles PDFium's display matrix
1053
- * convention, so this reader stays in normalized PDF page coordinates.
1218
+ * A stored appearance (`/AP`) renders into its annotation's `/Rect`. An
1219
+ * annotation with no normal appearance renders as PDFium draws it in memory,
1220
+ * into the box that drawing takes (`EPDFAnnot_GetDrawingRect`), which can
1221
+ * reach past `/Rect`; nothing is written. One PDFium can draw only by
1222
+ * generating an appearance into the file has no raster. Each bitmap is sized
1223
+ * to its box at the scale the page has at `options.viewport`. The shared
1224
+ * raster helper handles PDFium's display matrix convention, so this reader
1225
+ * stays in normalized PDF page coordinates.
1054
1226
  */
1055
1227
  declare class AnnotationAppearanceReader {
1056
1228
  private readonly runtime;
1057
1229
  private readonly session;
1058
1230
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1059
- render(pageObjectNumber: PageObjectNumber, options: AnnotationAppearanceRenderOptions, signal: AbortSignal): AnnotationAppearancesResult;
1060
1231
  /**
1061
- * Render a single annotation appearance into its own raster. Returns `null`
1062
- * when the mode has no appearance stream (after an optional form-field AP
1063
- * generation fallback) or the render fails.
1232
+ * The page loads in slices when it isn't parsed, and the loop pauses between
1233
+ * annotations once a slice's budget is spent, so an abort stops it at either.
1234
+ */
1235
+ render(pageObjectNumber: PageObjectNumber, family: AnnotationFamily, options: AnnotationAppearanceRenderOptions, signal: AbortSignal, slices: Slices): Promise<AnnotationAppearancesResult<PdfCoordinates>>;
1236
+ /**
1237
+ * Render a single annotation appearance into its own raster, sized to
1238
+ * `rect`: the stored appearance, or the one PDFium draws in memory. Returns
1239
+ * `null` when the render fails.
1064
1240
  */
1065
1241
  private renderOne;
1066
1242
  }
@@ -1072,9 +1248,13 @@ declare class AnnotationAppearanceReader {
1072
1248
  * `EPDFAnnot_GetObjectNumber` directly off the docPtr.
1073
1249
  *
1074
1250
  * Per-subtype dispatch is the same as the full reader (both share
1075
- * `collectPageAnnotations`), so the wire shape `AnnotationDTO[]` is
1251
+ * `collectPageAnnotations`), so the wire shape `Annotation[]` is
1076
1252
  * identical between raw and full read paths for the subtypes that don't
1077
1253
  * actually need a pagePtr to materialise their fields.
1254
+ *
1255
+ * `family` builds only one family's rows: the public reads pass it
1256
+ * (`'annotations'` for `annotations.list()`, `'widgets'` for the form);
1257
+ * the change internals read every row, by its `/Annots` index.
1078
1258
  */
1079
1259
  declare class RawAnnotationReader {
1080
1260
  private readonly runtime;
@@ -1084,16 +1264,16 @@ declare class RawAnnotationReader {
1084
1264
  constructor(runtime: PdfRuntimeModule, session: DocumentSession,
1085
1265
  /** This thread's font registry: FreeText faces read back as keys. */
1086
1266
  fonts?: FontRegistrar | undefined);
1087
- listAll(signal: AbortSignal): AnnotationListSnapshotAllPages;
1088
- listOne(pageObjectNumber: PageObjectNumber, signal: AbortSignal): AnnotationListPageSnapshot;
1267
+ /** The given pages in their order, or every page in document order. */
1268
+ list(pages: readonly PageObjectNumber[] | undefined, signal: AbortSignal, family?: AnnotationFamily): AnnotationList<PdfCoordinates>;
1269
+ listOne(pageObjectNumber: PageObjectNumber, signal: AbortSignal, family?: AnnotationFamily): AnnotationList<PdfCoordinates>;
1089
1270
  }
1090
1271
  //#endregion
1091
1272
  //#region src/features/annotations/AnnotationMutator.d.ts
1092
1273
  /**
1093
1274
  * Synchronous orchestrator for `create` / `update` / `delete` annotation
1094
- * mutations. Owns the dance between PDFium calls, identity bookkeeping,
1095
- * revision bumping (only for structural ops), and the
1096
- * `AnnotationListMutationMeta` envelope every result type carries.
1275
+ * mutations. Owns the dance between PDFium calls, identity bookkeeping, and
1276
+ * the `AnnotationListMutationMeta` envelope every result type carries.
1097
1277
  *
1098
1278
  * Lives in `engine-services` (not the worker hosts) so the local Web
1099
1279
  * Worker, the Node `worker_thread` server, and any future direct-thread
@@ -1101,21 +1281,19 @@ declare class RawAnnotationReader {
1101
1281
  * them is the underlying `PdfRuntimeModule` (WASM vs native).
1102
1282
  *
1103
1283
  * Identity rules enforced here, locked with the user:
1104
- * - `create` always uses `EPDFPage_CreateAnnot` (the fork helper that
1105
- * creates an INDIRECT object) so new annotations are born durable.
1106
- * If the fork helper ever returns a direct object, we throw — never
1107
- * silently produce a weak annotation.
1108
- * - `update` is non-structural. /NM is monotonic per annotation:
1109
- * * already durable (objectNumber > 0 OR /NM present) -> NEVER touched.
1110
- * * weak (no objectNumber, no /NM) -> stamp engine-generated UUID v4.
1111
- * The patch type has no `nm` field, so the writer surface enforces
1112
- * "callers cannot rename a stable id" at the type level. Updates do
1113
- * not bump the revision.
1114
- * - `delete` is subtype-agnostic. Three native fork helpers handle the
1115
- * three ref kinds without round-tripping through index. For weak
1116
- * deletes (`AnnotationStableId | null`-shaped result), we set
1117
- * `deleted: null` so callers can detect that no durable id was
1118
- * reportable.
1284
+ * - `create` is a one-item change set on `AnnotationBatchApplier`, which
1285
+ * makes the annotation with `EPDFPage_CreateAnnotRaw` (an indirect
1286
+ * object, on a page that isn't loaded), so a new annotation is named by
1287
+ * its object number. If the fork helper ever returns a direct object, it
1288
+ * throws.
1289
+ * - Every annotation keeps its name through every write (see
1290
+ * `annotationRefOf`). No write stamps an /NM to name one, and the patch
1291
+ * type has no `nm` field, so callers cannot rename one either.
1292
+ * - `delete` is subtype-agnostic and names everything it removed.
1293
+ *
1294
+ * Every write finds its annotations from the page's dictionaries (raw
1295
+ * handles), so none parses the page's content. Each runs inside its job's
1296
+ * layer transaction: a failure after the first write aborts all of it.
1119
1297
  */
1120
1298
  declare class AnnotationMutator {
1121
1299
  private readonly runtime;
@@ -1136,69 +1314,52 @@ declare class AnnotationMutator {
1136
1314
  */
1137
1315
  fonts?: FontRegistrar | undefined);
1138
1316
  /** Build the per-write context handed to subtype writers: font resolver
1139
- * (FreeText `/DA`), doc/page pointers and binary resources (stamp). */
1317
+ * (FreeText `/DA`), the document pointer and binary resources (stamp). */
1140
1318
  private writeContext;
1141
- create(pageObjectNumber: PageObjectNumber, draft: WireAnnotationDraft, signal: AbortSignal, actor?: AnnotationActor, resources?: WireResourceMap): AnnotationCreateResult;
1142
- update(ref: AnnotationRef, patch: WireAnnotationPatch, signal: AbortSignal, actor?: AnnotationActor, resources?: WireResourceMap): AnnotationUpdateResult;
1319
+ /**
1320
+ * A one-item change set on {@link AnnotationBatchApplier}: checked before
1321
+ * the first write, made without loading its page, all or nothing. A
1322
+ * `reply` or a popup's `parent` names an annotation the document has.
1323
+ */
1324
+ create(pageObjectNumber: PageObjectNumber, draft: AnnotationDraft<PdfCoordinates>, signal: AbortSignal, options?: {
1325
+ /** Who creates it: stamped as its author. */
1326
+ readonly actor?: AnnotationActor;
1327
+ /** The bytes beside the draft, by role. */
1328
+ readonly resources?: WireAnnotationResources;
1329
+ /** The object number it gets; the next free one when absent. */
1330
+ readonly objectNumber?: number;
1331
+ }): AnnotationCreateResult<PdfCoordinates>;
1332
+ /**
1333
+ * Edit an annotation in place. `authority` is checked against the
1334
+ * annotation as it reads before the first write, and gives the actor the
1335
+ * write stamps.
1336
+ */
1337
+ update(ref: AnnotationRef, patch: AnnotationPatch<PdfCoordinates>, authority: AnnotationAuthority, signal: AbortSignal, resources?: WireAnnotationResources): AnnotationUpdateResult<PdfCoordinates>;
1143
1338
  /**
1144
1339
  * Attached widgets are views of a form field: deleting one here would
1145
1340
  * orphan the field's /Kids (the corruption class doc.forms.repair
1146
- * exists to fix). Detach first, or use the forms-plane cascade.
1341
+ * exists to fix). Remove it from its field first, or delete the field. A
1342
+ * merged field/widget is the field's own dictionary and has nothing to be
1343
+ * removed from, so only the field's delete removes it; that cascade passes
1344
+ * the field it has unlinked as `releasedFieldObjectNumber`.
1147
1345
  */
1148
1346
  private assertNotAttachedWidget;
1149
- delete(ref: AnnotationRef, signal: AbortSignal): AnnotationDeleteResult;
1150
1347
  /**
1151
- * Batch reorder of a contiguous block of annotations within a single
1152
- * page's /Annots array. Symmetric with `pages.move()` for pages.
1153
- *
1154
- * Semantics (locked with the user, mirrors `EPDFPage_MoveAnnots`):
1155
- * - Each ref in `refs` is resolved to its current /Annots index.
1156
- * The block is detached, then re-inserted at `toIndex` in the
1157
- * post-removal index space, preserving caller-supplied order.
1158
- * - Single-annotation case is `move([ref], toIndex)`. There is no
1159
- * separate single-move path; one batch primitive serves both.
1160
- * - Atomic from the caller's perspective:
1161
- * * one revision bump per batch, regardless of `refs.length`.
1162
- * * one `AnnotationListMutationMeta` envelope.
1163
- * * if `EPDFPage_MoveAnnots` rejects (returns false) the page is
1164
- * untouched and we throw `InvalidArg` without bumping.
1165
- * - Identity strengthening: each weak ref in the batch (no
1166
- * `objectNumber`, no `/NM`) is opportunistically stamped with a
1167
- * fresh engine-generated UUID v4 BEFORE the move. So
1168
- * `meta.changed` always lists durable stable ids, and the moved
1169
- * DTOs come out durable. Same monotonic `/NM` rule as `update()`.
1170
- *
1171
- * Validation rules applied here BEFORE calling the helper, so callers
1172
- * get clean errors instead of an opaque `false` return code:
1173
- * - `refs.length >= 1`.
1174
- * - All refs target the page identified by `pageObjectNumber`.
1175
- * - `toIndex >= 0` and `toIndex <= count - refs.length` (count is
1176
- * captured AFTER ref resolution, so the helper sees the same view).
1177
- * - Resolved indices have no duplicates.
1178
- *
1179
- * The `EPDFPage_MoveAnnots` helper itself enforces the same rules; the
1180
- * up-front validation is purely for a usable error surface.
1348
+ * Delete an annotation with everything that goes with it
1349
+ * ({@link deletedWith}): its replies and theirs, grouped parts, review
1350
+ * states and every popup, in one change. `authority` is checked against
1351
+ * each of them, and every check runs before the first write.
1181
1352
  */
1182
- move(pageObjectNumber: PageObjectNumber, refs: AnnotationRef[], toIndex: number, signal: AbortSignal): AnnotationMoveResult;
1353
+ delete(ref: AnnotationRef, authority: AnnotationAuthority, signal: AbortSignal): AnnotationDeleteResult;
1183
1354
  /**
1184
- * Bake (or re-bake) an annotation's `/AP` normal appearance stream from
1185
- * its current dictionary properties using PDFium's native AP generator,
1186
- * then flush the page content so the result is persisted into the
1187
- * page/object tree. Same path the v2 engine used after every
1188
- * create/update; runs for every writable subtype (shapes + text-markup).
1189
- *
1190
- * Best-effort: `EPDFAnnot_GenerateAppearance` returns false for subtypes
1191
- * PDFium has no generator for (e.g. widgets), in which case the
1192
- * annotation simply ships without a baked `/AP` and viewers synthesize
1193
- * one — so a false return is not a hard error; it is surfaced to `update`
1194
- * callers as the `generation-unavailable` appearance outcome. This step is
1195
- * non-structural: it never shifts annotation indices or bumps revisions.
1196
- */
1197
- private regenerateAppearance;
1198
- private captureOrStampStableId;
1199
- private ensureKnownWeakStateFromPage;
1200
- private recordWeakStateFromPage;
1201
- private computeHasWeakAnnotations;
1355
+ * Remove a widget of the field `doc.forms.delete` has just unlinked from
1356
+ * the field tree, including the field's own dictionary when it is a
1357
+ * merged field/widget. The cascade runs past that mutation's apply
1358
+ * boundary, so it takes no abort signal.
1359
+ */
1360
+ deleteReleasedWidget(ref: AnnotationRef, releasedFieldObjectNumber: number): AnnotationDeleteResult;
1361
+ /** An annotation of the page opened raw at `index`, closed after `body`. */
1362
+ private withAnnotAt;
1202
1363
  }
1203
1364
  //#endregion
1204
1365
  //#region src/features/annotations/AnnotationFlattener.d.ts
@@ -1206,7 +1367,7 @@ declare class AnnotationMutator {
1206
1367
  * The annotation-plane flatten verbs — `pages.flatten` for a chosen set:
1207
1368
  * in place (`flatten`) or into a new single-page document
1208
1369
  * (`exportAppearance`). Both resolve refs to native handles on the page,
1209
- * hand the SET to the fork (one candidate plan, one placement writer — the
1370
+ * hand the set to the fork (one candidate plan, one placement writer — the
1210
1371
  * same code whole-page flatten runs), and release every handle afterwards.
1211
1372
  */
1212
1373
  declare class AnnotationFlattener {
@@ -1218,126 +1379,499 @@ declare class AnnotationFlattener {
1218
1379
  bytes: ArrayBuffer;
1219
1380
  size: number;
1220
1381
  };
1221
- /** Resolve every ref on `pagePtr` (NotFound / InvalidReference from the
1222
- * resolver on caller error); on any failure, close what was opened. */
1382
+ /**
1383
+ * An annotation's `appearance` resource: what its normal appearance draws
1384
+ * apart from what its data describes, as a one-page PDF
1385
+ * (`EPDFAnnot_ExportAppearance`). Only a kind that takes an appearance
1386
+ * resource has one; a copy is created from these bytes and the data.
1387
+ */
1388
+ readAppearance(pageObjectNumber: PageObjectNumber, ref: AnnotationRef, signal: AbortSignal): {
1389
+ bytes: ArrayBuffer;
1390
+ size: number;
1391
+ };
1392
+ /** Resolve every ref on `pagePtr` (NotFound from the resolver on caller
1393
+ * error); on any failure, close what was opened. */
1223
1394
  private resolveAll;
1224
1395
  /** Marshal an `FPDF_ANNOTATION*` array for the call's duration. */
1225
1396
  private withHandleArray;
1226
1397
  private requireRefs;
1227
1398
  }
1228
1399
  //#endregion
1229
- //#region src/features/annotations/internal/mutations/computeMutationImpact.d.ts
1400
+ //#region src/features/annotations/AnnotationExporter.d.ts
1230
1401
  /**
1231
- * The kind of mutation that just happened on a page.
1232
- *
1233
- * Index-space behaviour drives whether weak (`kind: 'index'`) refs
1234
- * the client may be holding stay valid:
1235
- *
1236
- * - `update` does not touch the /Annots array at all.
1237
- * - `create` is append-only — the new annotation lands at
1238
- * `index = previousCount`, so no existing index ever shifts.
1239
- * The array grows, but every pre-existing weak ref still points
1240
- * at the same physical annotation.
1241
- * - `delete` removes an entry and shifts every later index down by
1242
- * one. Pre-existing weak refs at or after the removed index are
1243
- * now off-by-one and stale.
1244
- * - `move` detaches a contiguous block and re-inserts it, rewriting
1245
- * indices on both ends of the gap. Existing weak refs across that
1246
- * range are stale.
1247
- *
1248
- * So the doctrine for `MutationKind` is: only `delete` and `move`
1249
- * actually invalidate weak refs. `update` and `create` leave them
1250
- * alone. (A future explicit-position `createAt(index)` API would
1251
- * shift indices >= toIndex and would join the structural cohort —
1252
- * see `MutationKind` extension note in `compute()` below.)
1402
+ * `doc.annotations.export`: the annotations a selection takes
1403
+ * ({@link closeExportSelection}) and the bytes beside them, as one bundle.
1404
+ * No page is loaded or parsed: annotations are read raw, and each resource
1405
+ * is exported from a raw handle, so a large document costs dictionary reads.
1406
+ * A drawing is one resource however many stamps place it, and equal bytes
1407
+ * are one resource under their hash. A bundle is page space, like
1408
+ * everything the engine hands out: each item is measured from the top-left
1409
+ * of its page. It is checked against `limits` as it is built, in the form
1410
+ * it leaves in, so an export never makes one an import refuses.
1253
1411
  */
1254
- type MutationKind = AnnotationMutationKind;
1412
+ declare class AnnotationExporter {
1413
+ private readonly runtime;
1414
+ private readonly session;
1415
+ /** This thread's font registry: FreeText faces read back as keys. */
1416
+ private readonly fonts?;
1417
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession,
1418
+ /** This thread's font registry: FreeText faces read back as keys. */
1419
+ fonts?: FontRegistrar | undefined);
1420
+ export(selection: AnnotationExportSelection, limits: BundleLimits, signal: AbortSignal): WireAnnotationBundle;
1421
+ }
1422
+ //#endregion
1423
+ //#region src/features/annotations/AnnotationImporter.d.ts
1424
+ interface AnnotationImportRequest {
1425
+ /** In page space, as bundles are: each item converts on the page it goes to. */
1426
+ readonly bundle: WireAnnotationBundle;
1427
+ readonly pages?: BundleImportPages;
1428
+ readonly attribution: 'restore' | 'stamp';
1429
+ /**
1430
+ * The session's identity: who `'stamp'` attributes each annotation to, and
1431
+ * whose user `'restore'` records as `importedBy`.
1432
+ */
1433
+ readonly actor?: AnnotationActor;
1434
+ readonly limits: BundleLimits;
1435
+ /**
1436
+ * Checks each annotation the import will create, before anything is
1437
+ * written: what its create would need. A refusal refuses the import.
1438
+ */
1439
+ readonly authorizeCreate?: (draft: AnnotationDraft<PdfCoordinates>) => void;
1440
+ }
1255
1441
  /**
1256
- * Inputs that decide whether a mutation should make a client refetch.
1257
- *
1258
- * `pageStateBefore` is captured BEFORE the mutation. Its known
1259
- * `weakAnnotationState` drives the locked rule:
1260
- * index-shifting mutation × any weak annotation on the page ⇒ refetch.
1261
- *
1262
- * `pageStateAfter` is captured AFTER the mutation. It carries the
1263
- * bumped revision token (for index-shifting ops) and the recomputed
1264
- * weak-annotation state (for any op that might have changed it, e.g.
1265
- * opportunistic /NM stamping during update).
1266
- *
1267
- * `changed` is the list of stable ids the mutation actually touched.
1268
- * Empty for a weak delete (we have no durable id to report).
1442
+ * `doc.annotations.import`: a bundle's annotations, created in this document
1443
+ * as one change. The bundle is checked first, cheapest checks first: its
1444
+ * shape, counts and sizes against the limits, each resource against its id,
1445
+ * and each image's decoded size, before any image is decoded. Then
1446
+ * {@link planAnnotationImport} maps pages and works out what to leave out,
1447
+ * each create is measured on the page it goes to, from that page's
1448
+ * top-left, and {@link AnnotationBatchApplier} writes them, all or nothing. No
1449
+ * page is loaded: names are looked up in the page dictionaries, and
1450
+ * annotations made on raw handles.
1451
+ */
1452
+ declare class AnnotationImporter {
1453
+ private readonly runtime;
1454
+ private readonly session;
1455
+ /** This thread's font registry: FreeText faces by key. */
1456
+ private readonly fonts?;
1457
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession,
1458
+ /** This thread's font registry: FreeText faces by key. */
1459
+ fonts?: FontRegistrar | undefined);
1460
+ import(request: AnnotationImportRequest, signal: AbortSignal): AnnotationImportResult<PdfCoordinates>;
1461
+ private assertBundle;
1462
+ }
1463
+ //#endregion
1464
+ //#region src/features/annotations/internal/write/writeEmbedMetadata.d.ts
1465
+ /** The `/EMBD_Metadata` fields a restoring import writes as the bundle has them. */
1466
+ interface RestoredEmbedMetadata {
1467
+ readonly userId: string | null;
1468
+ readonly createdBy: string | null;
1469
+ readonly modifiedBy: string | null;
1470
+ readonly groupId: string | null;
1471
+ }
1472
+ //#endregion
1473
+ //#region src/features/annotations/internal/write/restoreAttribution.d.ts
1474
+ /** The attribution a restoring import writes: the fields a read returned, as they are. */
1475
+ interface RestoredAttribution extends RestoredEmbedMetadata {
1476
+ readonly author: string | null;
1477
+ readonly createdAt: string | null;
1478
+ readonly modifiedAt: string | null;
1479
+ }
1480
+ //#endregion
1481
+ //#region src/features/annotations/AnnotationBatchApplier.d.ts
1482
+ /**
1483
+ * What a link points at: another create of the same change, by its place in
1484
+ * it, so creates can refer to each other before any exists; or an annotation
1485
+ * the document has.
1269
1486
  */
1270
- interface ImpactInputs {
1271
- mutation: MutationKind;
1272
- pageStateBefore: PageState;
1273
- pageStateAfter: PageState;
1274
- changed: AnnotationStableId[];
1487
+ type BatchLinkTarget = {
1488
+ readonly planned: number;
1489
+ } | {
1490
+ readonly existing: AnnotationRef;
1491
+ };
1492
+ /** One create of a change. */
1493
+ interface BatchCreate {
1494
+ /** The page it goes on. */
1495
+ readonly page: PageRef;
1496
+ /** Its data, without its links, which are the two fields below. */
1497
+ readonly draft: AnnotationDraft<PdfCoordinates>;
1498
+ /** `reply`: the annotation it replies to, on the same page. */
1499
+ readonly replyTo?: {
1500
+ readonly to: BatchLinkTarget;
1501
+ readonly type: AnnotationReplyType;
1502
+ };
1503
+ /** For a popup: the annotation it shows, on the same page. */
1504
+ readonly parent?: BatchLinkTarget;
1505
+ /** The bytes beside the draft, by role. */
1506
+ readonly resources?: WireAnnotationResources;
1507
+ /** The object number it gets; the next free one when absent. */
1508
+ readonly objectNumber?: number;
1509
+ /**
1510
+ * Who wrote the annotation, and when: the session now, as `create`
1511
+ * stamps it, or the attribution it already had, restored as it is. A
1512
+ * stamped one is a new annotation: without an `nm` in its data it gets a
1513
+ * fresh UUIDv7 name. A restored one keeps the name it had, or none.
1514
+ */
1515
+ readonly attribution: {
1516
+ readonly kind: 'stamp';
1517
+ readonly actor?: AnnotationActor;
1518
+ } | {
1519
+ readonly kind: 'restore';
1520
+ readonly from: RestoredAttribution;
1521
+ /** The importing session's user, recorded as `importedBy`. */
1522
+ readonly importedBy?: string;
1523
+ };
1524
+ /** A copied file attachment's file dates, kept in either attribution. */
1525
+ readonly fileDates?: Pick<AttachmentFileInfo, 'createdAt' | 'modifiedAt'>;
1526
+ /** Names the create in an error, such as `import: item 3`. */
1527
+ readonly label?: string;
1528
+ }
1529
+ interface BatchCreateResult {
1530
+ /** In the order of the creates, each as it is now. */
1531
+ created: Annotation<PdfCoordinates>[];
1532
+ meta: AnnotationListMutationMeta;
1275
1533
  }
1276
1534
  /**
1277
- * Pure helper: turn a mutation outcome into the side-effect envelope
1278
- * every result type carries on its `meta` field.
1535
+ * Applies a change set as one unit: every item is checked before the first
1536
+ * write, and the writes run inside the job's layer transaction, so any
1537
+ * failure, an abort included, leaves the document as it was. It currently
1538
+ * applies annotation creates for imports.
1279
1539
  *
1280
- * The rules — locked with the user, do not change without re-reading
1281
- * the doc comment on `AnnotationListMutationMeta` and the
1282
- * `MutationKind` definition above:
1540
+ * No page is loaded or parsed. Each annotation is made on a raw handle
1541
+ * (`EPDFPage_CreateAnnotRaw`) and opened again the same way, by its place in
1542
+ * its page's `/Annots`, for each pass:
1283
1543
  *
1284
- * 1. `update` and `create` are non-invalidating.
1544
+ * 1. create every annotation in order, so each page's `/Annots` keeps it,
1545
+ * with its data and its appearance;
1546
+ * 2. link replies and popups to what they name: another create, or an
1547
+ * annotation the document has (one born inline becomes an object, which
1548
+ * a link needs, and keeps its name);
1549
+ * 3. attribute each, stamped as the session or restored as it was, last,
1550
+ * so no later write touches `/M`.
1285
1551
  *
1286
- * - `update` doesn't touch the /Annots array; indices on the page
1287
- * do not move, so weak refs the client may be holding stay
1288
- * valid. Opportunistic /NM stamping during an update is also
1289
- * non-structural — it changes the annotation's identity quality
1290
- * but not its position.
1291
- *
1292
- * - `create` is append-only. The new annotation goes at
1293
- * `index = previousCount`, so no existing index shifts and no
1294
- * pre-existing weak ref ever becomes stale. We treat `create`
1295
- * the same as `update` for impact purposes: no revision bump,
1296
- * `weakRefsInvalidated = false`, `shouldRefetch = null`.
1297
- *
1298
- * The "revisions exist solely for weak-ref authentication"
1299
- * doctrine (see `pages.move()`'s identical reasoning) means we
1300
- * deliberately do NOT bump on `create` — bumping a revision
1301
- * that nobody's weak ref depends on would erode the invariant
1302
- * and turn revisions into a generic "something changed" signal.
1303
- *
1304
- * 2. `delete` and `move` ARE index-shifting. They genuinely move
1305
- * pre-existing indices:
1306
- * - `delete` removes an annotation, shifting every later index
1307
- * down by one.
1308
- * - `move` detaches a contiguous block and re-inserts it
1309
- * elsewhere, rewriting indices on both ends of the gap.
1310
- * They invalidate weak refs iff the page actually had any weak
1311
- * refs *before* the mutation — if every annotation already had a
1312
- * durable identity, no client could possibly be holding a stale
1313
- * index, and we keep `shouldRefetch = null`.
1314
- *
1315
- * (`move` opportunistically stamps /NM on weak refs in the batch
1316
- * BEFORE the move, so the annotations actually being moved end up
1317
- * durable on the way out; but other weak annotations on the page
1318
- * still need a refetch.)
1552
+ * A create that names its object number makes the annotation at exactly
1553
+ * that number: one the session holds, checked before the first write, and
1554
+ * free in the layer, which the create itself checks.
1555
+ */
1556
+ declare class AnnotationBatchApplier {
1557
+ private readonly runtime;
1558
+ private readonly session;
1559
+ /** This thread's font registry: FreeText faces by key. */
1560
+ private readonly fonts?;
1561
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession,
1562
+ /** This thread's font registry: FreeText faces by key. */
1563
+ fonts?: FontRegistrar | undefined);
1564
+ create(creates: readonly BatchCreate[], signal: AbortSignal): BatchCreateResult;
1565
+ /**
1566
+ * A name must be free on its page (ISO 32000-2 §12.5.2): not used there,
1567
+ * and not by an earlier create of the change.
1568
+ */
1569
+ private claimName;
1570
+ /**
1571
+ * A link's target, checked before the first write: another create of the
1572
+ * change, or an annotation the document has, on the same page (ISO
1573
+ * 32000-2 §12.5.6.2). Creates only append, so its place holds.
1574
+ */
1575
+ private linkTarget;
1576
+ private writeContext;
1577
+ private withOpen;
1578
+ private withOpenExisting;
1579
+ }
1580
+ //#endregion
1581
+ //#region src/features/changes/ChangeRecord.d.ts
1582
+ /**
1583
+ * What the engine keeps of a change so it can undo it: who made it, and the
1584
+ * steps that reverse it, in the order they run (the reverses of its ops, in
1585
+ * reverse op order). Each step is guarded by what the change left: it applies
1586
+ * where the document still shows that, and is skipped, and reported, where it
1587
+ * doesn't. Running a step records the step that reverses it in turn, so the
1588
+ * undo of an undo is a redo.
1589
+ */
1590
+ interface ChangeRecord {
1591
+ /** Who made the change: only they may undo it. */
1592
+ readonly userId: string | null;
1593
+ readonly steps: readonly ReverseStep[];
1594
+ }
1595
+ type ReverseStep = ObjectsRevertStep | AnnotationRemoveStep | AnnotationRestoreStep | AnnotationReorderStep | FieldRemoveStep | FieldRestoreStep | WidgetRestoreStep | WidgetDeleteStep | CalculationOrderStep | MetadataRevertStep;
1596
+ /**
1597
+ * One dictionary a change wrote, as it was before (the capture, with the
1598
+ * objects under `deep` the layer held) and as the change left it (a shallow
1599
+ * capture, the guard). `before` is null for a dictionary the change made.
1600
+ */
1601
+ interface CapturedObject {
1602
+ readonly objectNumber: number;
1603
+ readonly deep: readonly string[];
1604
+ readonly before: Uint8Array | null;
1605
+ readonly after: Uint8Array;
1606
+ }
1607
+ /**
1608
+ * Puts back the dictionaries an op wrote, exactly as they were, when each
1609
+ * still reads as the op left it. When one doesn't, someone changed it since:
1610
+ * an annotation update, a field update or a form value write puts back, by
1611
+ * value, only what still shows what the op set (`fallback`); any other op is
1612
+ * skipped.
1613
+ */
1614
+ interface ObjectsRevertStep {
1615
+ readonly kind: 'objects.revert';
1616
+ /** What its item reports doing: the op it puts back, or its opposite (a widget's add undoes its removal). */
1617
+ readonly reports: ChangeItemType;
1618
+ readonly objects: readonly CapturedObject[];
1619
+ /** What the item reads back: the annotation, or the form fields. */
1620
+ readonly subject: {
1621
+ readonly annotation: AnnotationRef;
1622
+ } | {
1623
+ readonly fields: readonly number[];
1624
+ };
1625
+ /** Whether the op changed what the annotation shows (its update's `appearance.changed`). */
1626
+ readonly appearanceChanged?: boolean;
1627
+ readonly fallback?: RevertFallback;
1628
+ /** The rights it takes beside its op's: an import's (see {@link StepRights}). */
1629
+ readonly rights?: StepRights;
1630
+ }
1631
+ /** How a revert puts values back when the dictionaries it wrote changed since. */
1632
+ type RevertFallback = {
1633
+ readonly kind: 'annotation';
1634
+ /** The old values of the fields the update changed. */
1635
+ readonly restore: AnnotationPatch<PdfCoordinates>;
1636
+ /** The values the update left in them: a field still holding its value is put back. */
1637
+ readonly left: AnnotationPatch<PdfCoordinates>;
1638
+ } | {
1639
+ readonly kind: 'properties';
1640
+ readonly objectNumber: number;
1641
+ /** The old values of the properties a field update changed, as a patch. */
1642
+ readonly restore: FormFieldPatch;
1643
+ /** The values it left in them: a property still holding its value is put back. */
1644
+ readonly left: FormFieldPatch;
1645
+ } | {
1646
+ readonly kind: 'value';
1647
+ /** Each field the op wrote: its value before, and the value the op left. */
1648
+ readonly fields: readonly {
1649
+ readonly objectNumber: number;
1650
+ readonly before: FormFieldValue;
1651
+ readonly left: FormFieldValue;
1652
+ }[];
1653
+ };
1654
+ /**
1655
+ * Deletes what a create, an import or a restore brought back, when nobody
1656
+ * changed it since and deleting it takes nothing else: undoing a create
1657
+ * never deletes someone else's reply.
1658
+ */
1659
+ interface AnnotationRemoveStep {
1660
+ readonly kind: 'annotation.remove';
1661
+ readonly ref: AnnotationRef;
1662
+ /** What the delete takes, as the change left it: the annotation first. */
1663
+ readonly left: readonly Annotation<PdfCoordinates>[];
1664
+ /** The rights it takes: an import's (see {@link StepRights}); a delete's when absent. */
1665
+ readonly rights?: StepRights;
1666
+ }
1667
+ /**
1668
+ * The rights a step takes as a restoring import's undo or redo: what the
1669
+ * import took. For annotations, `doc.annotate.modify` and
1670
+ * `doc.annotate.import` instead of the per-annotation rules: a restoring
1671
+ * import gives annotations other people's `userId`, which a delete's rules
1672
+ * (`annotations:delete:self`) would never let the importer remove. For
1673
+ * forms, `doc.forms.import` beside `doc.forms.modify` or `doc.forms.fill`:
1674
+ * putting back who made or filled a field is restoring attribution too.
1675
+ */
1676
+ type StepRights = 'import';
1677
+ /**
1678
+ * Brings back what a delete removed: the same objects, at the same numbers and
1679
+ * positions, from the capture taken in the delete's transaction.
1680
+ */
1681
+ interface AnnotationRestoreStep {
1682
+ readonly kind: 'annotation.restore';
1683
+ readonly page: PageRef;
1684
+ /** What came back: the annotation first, then what went with it. */
1685
+ readonly members: readonly AnnotationRef[];
1686
+ readonly capture: Uint8Array;
1687
+ /** Annotations the delete unlinked a popup from, put back as they were. */
1688
+ readonly unlinked: readonly CapturedObject[];
1689
+ /** The rights it takes: those of the remove it reverses (see {@link StepRights}). */
1690
+ readonly rights?: StepRights;
1691
+ }
1692
+ /**
1693
+ * Puts rows of one family on a page back beside the neighbours they had
1694
+ * before a reorder: its reverse, and, the other way round, its redo. A row
1695
+ * runs only when it is still where the reorder left it.
1696
+ */
1697
+ interface AnnotationReorderStep {
1698
+ readonly kind: 'annotation.reorder';
1699
+ readonly page: PageRef;
1700
+ readonly family: AnnotationFamily;
1701
+ /** The rows the reorder moved, in the order it was given them. */
1702
+ readonly refs: readonly AnnotationRef[];
1703
+ /** The family's order before the reorder, bottom to top: where the rows go back to. */
1704
+ readonly before: readonly AnnotationRef[];
1705
+ /** The family's order it left. */
1706
+ readonly after: readonly AnnotationRef[];
1707
+ }
1708
+ /**
1709
+ * Takes back what a change did to the form's calculation order, by field
1710
+ * object number, where nobody changed it since (see `orderBack`): a field it
1711
+ * added leaves, one it took out comes back, one it moved goes back beside
1712
+ * its old neighbour.
1713
+ */
1714
+ interface CalculationOrderStep {
1715
+ readonly kind: 'calculations.restore';
1716
+ /** The order before the change. */
1717
+ readonly before: readonly number[];
1718
+ /** The order the change left. */
1719
+ readonly after: readonly number[];
1720
+ }
1721
+ /**
1722
+ * Brings back a widget a delete removed: onto its page at its place, and into
1723
+ * its field again when it had one. Left alone when the page is gone, the
1724
+ * widget is back already, or its field changed since.
1725
+ */
1726
+ interface WidgetRestoreStep {
1727
+ readonly kind: 'widget.restore';
1728
+ readonly page: PageRef;
1729
+ readonly widget: AnnotationRef;
1730
+ /** The widget as it was on its page, out of its field. */
1731
+ readonly capture: Uint8Array;
1732
+ /** The field it was in; `null` for a widget in no field. */
1733
+ readonly fieldObjectNumber: number | null;
1734
+ /** The field's dictionary and the widget's, before and after it left the field. */
1735
+ readonly detached: readonly CapturedObject[];
1736
+ }
1737
+ /** Deletes a widget a restore brought back, when nobody changed it since: the redo of a delete. */
1738
+ interface WidgetDeleteStep {
1739
+ readonly kind: 'widget.delete';
1740
+ readonly widget: AnnotationRef;
1741
+ readonly left: WidgetAnnotation<PdfCoordinates>;
1742
+ }
1743
+ /** Deletes a field a create or an import made, when nobody changed it since. */
1744
+ interface FieldRemoveStep {
1745
+ readonly kind: 'field.remove';
1746
+ readonly objectNumber: number;
1747
+ /** The field as the change left it. */
1748
+ readonly left: FormFieldDTO<PdfCoordinates>;
1749
+ /** The rights it takes beside `doc.forms.modify`: an import's (see {@link StepRights}). */
1750
+ readonly rights?: StepRights;
1751
+ }
1752
+ /** Brings back a field a delete removed, its widgets and the parents it pruned. */
1753
+ interface FieldRestoreStep {
1754
+ readonly kind: 'field.restore';
1755
+ readonly objectNumber: number;
1756
+ /** Its fully qualified name: the restore is skipped when another field took it. */
1757
+ readonly name: string;
1758
+ /** The pages its widgets were on: the restore is skipped when one is gone. */
1759
+ readonly pages: readonly PageRef[];
1760
+ readonly capture: Uint8Array;
1761
+ /** The rights it takes: those of the remove it reverses (see {@link StepRights}). */
1762
+ readonly rights?: StepRights;
1763
+ }
1764
+ /** Puts metadata back, entry by entry, where an entry still holds what the change set. */
1765
+ interface MetadataRevertStep {
1766
+ readonly kind: 'metadata.revert';
1767
+ readonly op: 'metadata.update' | 'metadata.updateCustom';
1768
+ readonly restore: MetadataPatch | CustomMetadataPatch;
1769
+ readonly left: MetadataPatch | CustomMetadataPatch;
1770
+ }
1771
+ //#endregion
1772
+ //#region src/features/changes/ChangeApplier.d.ts
1773
+ /** What a change did: one item per op (or per step of an undo), and the record that undoes it. */
1774
+ interface AppliedChange {
1775
+ readonly items: ChangeItem<PdfCoordinates>[];
1776
+ /** Its steps are empty when nothing was written: there is nothing to undo. */
1777
+ readonly record: ChangeRecord;
1778
+ }
1779
+ /**
1780
+ * Runs changes inside the job's layer transaction, which its caller opens and
1781
+ * ends, so a failure anywhere leaves the document as it was: a change's ops in
1782
+ * order (`apply`), or the steps of a recorded reverse (`undo`). Each op and
1783
+ * step is checked against the change's authority as it runs, as its single
1784
+ * verb's job checks it.
1319
1785
  *
1320
- * 3. When rule (2) fires, the reason is always `'weakRefsInvalidated'`.
1321
- * `'pageRebuilt'` and `'externalChange'` are reserved for higher-
1322
- * level signals the engine doesn't emit yet (insert/delete-page
1323
- * that rebuilds /Annots, watch-based refresh). Page **reorder**
1324
- * explicitly does NOT bump per-page revisions and does NOT emit
1325
- * `pageRebuilt` — see `PagesMutator`.
1786
+ * Every op records the steps that reverse it as it runs, from what it found
1787
+ * and wrote; the record lists them in reverse op order, so each step meets the
1788
+ * document as the op after it left it.
1789
+ */
1790
+ declare class ChangeApplier {
1791
+ private readonly runtime;
1792
+ private readonly session;
1793
+ private readonly fonts?;
1794
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession, fonts?: FontRegistrar | undefined);
1795
+ apply(ops: readonly RecordedOp<PdfCoordinates, WireAnnotationResources>[], authority: ChangeAuthority, signal: AbortSignal): AppliedChange;
1796
+ undo(record: ChangeRecord, authority: ChangeAuthority, signal: AbortSignal): AppliedChange;
1797
+ private context;
1798
+ }
1799
+ //#endregion
1800
+ //#region src/features/changes/ChangeLedger.d.ts
1801
+ /** What one change answered, kept under its `opId`. */
1802
+ type ChangeOutcome = {
1803
+ readonly kind: 'applied';
1804
+ readonly fingerprint: string;
1805
+ readonly result: ChangeResult<PdfCoordinates>;
1806
+ } | {
1807
+ readonly kind: 'refused';
1808
+ readonly fingerprint: string;
1809
+ readonly error: SerializedEngineError;
1810
+ };
1811
+ /** Why a change can't be undone (`UndoUnavailable`'s `details.reason`). */
1812
+ type UndoUnavailableReason = 'final-change' | 'expired';
1813
+ /**
1814
+ * What a local session keeps of its changes: every answer under its `opId`,
1815
+ * refusals included, so a retry gets the same one; and each undoable change's
1816
+ * record, until a final change ends undo for everything before it, or the
1817
+ * capture budget runs out (the oldest go first, as `expired`).
1326
1818
  *
1327
- * Future note: when an explicit-position `createAt(index)` API
1328
- * ships, add `'createAt'` to `MutationKind` and slot it into rule
1329
- * (2) — it shifts every index >= toIndex and is structural in the
1330
- * same way `delete` and `move` are.
1819
+ * The cloud keeps the same in its database; a cloud worker's session keeps
1820
+ * nothing here between requests.
1821
+ */
1822
+ declare class ChangeLedger {
1823
+ private readonly entries;
1824
+ private bytes;
1825
+ /** The answer kept under `opId`, if any. */
1826
+ outcome(opId: string): ChangeOutcome | undefined;
1827
+ /**
1828
+ * The record of the change `opId`, or why it can't be undone. Undefined for
1829
+ * a change this session has no answer for.
1830
+ */
1831
+ record(opId: string): {
1832
+ readonly record: ChangeRecord | null;
1833
+ } | {
1834
+ readonly unavailable: UndoUnavailableReason;
1835
+ } | undefined;
1836
+ /** Keeps an applied change's answer, and its record when it can be undone. */
1837
+ keepApplied(opId: string, fingerprint: string, result: ChangeResult<PdfCoordinates>, record: ChangeRecord | null): void;
1838
+ /** Keeps a refusal: asking again answers it again. */
1839
+ keepRefused(opId: string, fingerprint: string, error: SerializedEngineError): void;
1840
+ /**
1841
+ * A final change (redaction apply, flatten, signing, form repair) ended undo
1842
+ * for itself and every change before it: their records go.
1843
+ */
1844
+ endUndo(): void;
1845
+ private add;
1846
+ private trimCaptures;
1847
+ private drop;
1848
+ }
1849
+ /** The ledger of `session`'s changes. */
1850
+ declare function changeLedgerOf(session: DocumentSession): ChangeLedger;
1851
+ //#endregion
1852
+ //#region src/features/changes/recordCodec.d.ts
1853
+ /**
1854
+ * A change's record as a caller stores it: the steps as JSON, every capture's
1855
+ * bytes moved into one blob and named in the JSON by where they are in it
1856
+ * (`{ "$capture": [offset, length] }`). `capture` is null when the record
1857
+ * holds no bytes.
1331
1858
  */
1332
- declare function computeMutationImpact(inputs: ImpactInputs): AnnotationListMutationMeta;
1859
+ interface PackedChangeRecord {
1860
+ readonly reverse: string;
1861
+ readonly capture: Uint8Array | null;
1862
+ }
1863
+ /** A record, packed for storage (see `PackedChangeRecord`). */
1864
+ declare function packChangeRecord(record: ChangeRecordPayload): PackedChangeRecord;
1865
+ /** A record unpacked from its JSON and its capture blob. */
1866
+ declare function unpackChangeRecord(reverse: string, capture: Uint8Array | null): ChangeRecordPayload;
1333
1867
  //#endregion
1334
1868
  //#region src/features/attachments/AttachmentReader.d.ts
1335
1869
  /**
1336
1870
  * Read-only access to embedded files, at both of their homes: the
1337
1871
  * document catalog's `/EmbeddedFiles` name tree (list / readFile by
1338
1872
  * index) and a FileAttachment annotation's `/FS` (readAnnotationFile by
1339
- * ref). Pure READS over the session — no revision bumps, no layer
1340
- * artifacts (the `PagesExtractor` shape).
1873
+ * ref). Pure reads over the session — no layer artifacts (the
1874
+ * `PagesExtractor` shape).
1341
1875
  *
1342
1876
  * Byte delivery mirrors the request's `path?`: absent → a standalone
1343
1877
  * buffer for the transfer list (browser); present → the decoded file is
@@ -1349,11 +1883,12 @@ declare class AttachmentReader {
1349
1883
  private readonly session;
1350
1884
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1351
1885
  /** Snapshot of the `/EmbeddedFiles` name tree, in tree (key-sorted) order. */
1352
- list(signal: AbortSignal): EmbeddedFileItem[];
1886
+ list(signal: AbortSignal): Attachment[];
1353
1887
  /** Decode one document-level embedded file, addressed by key. */
1354
- readFile(ref: EmbeddedFileRef, path: string | undefined, maxDecodedBytes: number | undefined, signal: AbortSignal): AttachmentFileWorkerPayload;
1888
+ readFile(ref: AttachmentRef, path: string | undefined, maxDecodedBytes: number | undefined, signal: AbortSignal): AttachmentFileWorkerPayload;
1355
1889
  /** Decode the file embedded in a FileAttachment annotation's `/FS`. */
1356
- readAnnotationFile(pageObjectNumber: PageObjectNumber, ref: AnnotationRef, path: string | undefined, maxDecodedBytes: number | undefined, signal: AbortSignal): AttachmentFileWorkerPayload;
1890
+ /** A file attachment annotation's file; a page not parsed yet loads in slices. */
1891
+ readAnnotationFile(pageObjectNumber: PageObjectNumber, ref: AnnotationRef, path: string | undefined, maxDecodedBytes: number | undefined, signal: AbortSignal, slices: Slices): Promise<AttachmentFileWorkerPayload>;
1357
1892
  private extract;
1358
1893
  }
1359
1894
  //#endregion
@@ -1364,9 +1899,8 @@ declare class AttachmentReader {
1364
1899
  * write path (`writeAttachmentFilePayload`) with the file-attachment
1365
1900
  * annotation writer.
1366
1901
  *
1367
- * Identity is the name-tree KEY (unique by construction), so unlike
1368
- * annotations there is no weak-ref/revision bookkeeping here. Note the
1369
- * tree is key-sorted: both mutations shift OTHER entries' indices; keys
1902
+ * Identity is the name-tree key (unique by construction). Note the
1903
+ * tree is key-sorted: both mutations shift other entries' indices; keys
1370
1904
  * never move. Layer persistence (`finishMutation`) and event publication
1371
1905
  * happen in the callers, exactly like every other mutation family.
1372
1906
  */
@@ -1382,7 +1916,7 @@ declare class AttachmentMutator {
1382
1916
  */
1383
1917
  create(file: WireAttachmentFile, resources: WireResourceMap | undefined, signal: AbortSignal): AttachmentCreateResult;
1384
1918
  /** Delete an embedded file by key. Unlinks the tree entry only. */
1385
- delete(ref: EmbeddedFileRef, signal: AbortSignal): AttachmentDeleteResult;
1919
+ delete(ref: AttachmentRef, signal: AbortSignal): AttachmentDeleteResult;
1386
1920
  }
1387
1921
  //#endregion
1388
1922
  //#region src/features/attachments/internal/attachmentPrimitives.d.ts
@@ -1396,7 +1930,7 @@ declare class AttachmentMutator {
1396
1930
  * vocabulary.
1397
1931
  */
1398
1932
  declare function writeAttachmentFilePayload(fn: PdfFunctions, mem: PdfRuntimeMemory, attachmentPtr: Ptr, docPtr: Ptr, file: {
1399
- mimeType?: string;
1933
+ mimeType?: string | null;
1400
1934
  description?: string;
1401
1935
  }, resource: WireResource): void;
1402
1936
  /** Read the metadata projection of an embedded file (never its bytes). */
@@ -1418,7 +1952,7 @@ declare class SecurityReader {
1418
1952
  constructor(runtime: PdfRuntimeModule);
1419
1953
  probeFile(path: string, password?: string | null): DocumentSecurityProbeInfo;
1420
1954
  /**
1421
- * Snapshot of the live session's EFFECTIVE security state.
1955
+ * Snapshot of the live session's effective security state.
1422
1956
  * `FPDF_GetDocUserPermissions` reports the permission word in force
1423
1957
  * right now (e.g. after an owner unlock), as opposed to the declared
1424
1958
  * word the cold `probeFile` path reads.
@@ -1534,7 +2068,7 @@ declare class DocumentSaver {
1534
2068
  * `saveLayerDelta` that says whether anything reachable changed since the
1535
2069
  * layer was opened; empty bytes when nothing did (keep what you have), or
1536
2070
  * when the layer came to equal its base (then `changedSinceLoad` is true
1537
- * and the document IS the base).
2071
+ * and the document is the base).
1538
2072
  */
1539
2073
  saveLayerDeltaEx(): {
1540
2074
  bytes: ArrayBuffer;
@@ -1603,10 +2137,7 @@ declare class SignatureReader {
1603
2137
  index: number;
1604
2138
  fieldObjectNumber: number;
1605
2139
  signed: boolean;
1606
- widget: {
1607
- annotObjectNumber: number;
1608
- pageObjectNumber: number;
1609
- } | null;
2140
+ widget: FormWidget | null;
1610
2141
  };
1611
2142
  /** One signature by field object number, from the current model. */
1612
2143
  readSignatureByObjectNumber(fieldObjectNumber: number): SignatureDTO;
@@ -1631,14 +2162,14 @@ declare class SignatureReader {
1631
2162
  /**
1632
2163
  * The two-phase signing protocol on a session, local and native alike.
1633
2164
  *
1634
- * `prepare` never touches the live document: it builds a CANDIDATE — a
2165
+ * `prepare` never touches the live document: it builds a candidate — a
1635
2166
  * fresh layer over the session's own immutable base (see `openCandidate`)
1636
2167
  * — writes the signature there, saves it through a `CandidateStore`
1637
2168
  * (memory locally, a file beside the base on file-backed sessions), seals
1638
2169
  * it, and parks the saved candidate on the session. `complete` writes the
1639
2170
  * CMS into it, opens it as a new base with a fresh layer, proves the new
1640
2171
  * signature seals a whole revision, and installs it as the session's
1641
- * document. `abort` discards the candidate.
2172
+ * document. `cancel` discards the candidate.
1642
2173
  */
1643
2174
  declare class SignatureMutator {
1644
2175
  private readonly runtime;
@@ -1649,15 +2180,27 @@ declare class SignatureMutator {
1649
2180
  constructor(runtime: PdfRuntimeModule, session: DocumentSession, baseDocuments: BaseDocumentRegistry,
1650
2181
  /** Where a file-backed session's candidate is written; defaults to beside the base file. */
1651
2182
  candidatePath?: ((basePath: string, signingId: string) => string) | undefined);
1652
- prepare(input: SignaturePrepareInput): SignaturePrepared;
1653
- complete(input: SignatureCompleteInput): SignatureCompleteResult;
1654
- abort(signingId: string): SignatureAbortResult;
2183
+ /**
2184
+ * Prepare a signing of `input.field`, which `authority` must be allowed to
2185
+ * sign (its group, `fields:sign`). A field in a group, signed without a
2186
+ * `lock`, locks the rest of its group: nobody changes the buyer's fields
2187
+ * once the buyer signed.
2188
+ */
2189
+ prepare(input: SignaturePrepareInput, authority: ChangeAuthority): SignaturePrepared;
2190
+ /**
2191
+ * `opId` names the write. A signing runs outside a layer transaction (it
2192
+ * replaces the document's bytes), so it takes its id here, and it is final:
2193
+ * never undoable.
2194
+ */
2195
+ /** Complete the pending signing; `authority` must still be allowed to sign its field. */
2196
+ complete(input: SignatureCompleteInput, opId: string, authority: ChangeAuthority): SignatureCompleteResult;
2197
+ cancel(signingId: string): SignatureCancelResult;
1655
2198
  private callPrepare;
1656
2199
  /** Draw a page of `pdf` into the widget's appearance stream on the candidate. */
1657
2200
  /**
1658
2201
  * File-backed sessions (a file base in the registry: the server) persist
1659
2202
  * the candidate as a file beside the base; everything else keeps it in
1660
- * memory. The choice follows the SESSION's base, not the candidate's.
2203
+ * memory. The choice follows the session's base, not the candidate's.
1661
2204
  */
1662
2205
  private storeFor;
1663
2206
  /**
@@ -1673,7 +2216,7 @@ declare class SignatureMutator {
1673
2216
  private verifySealed;
1674
2217
  /**
1675
2218
  * The candidate the signature is authored on. A layer session reopens a
1676
- * fresh layer over ITS OWN immutable base (a registry retain, no copy of
2219
+ * fresh layer over its own immutable base (a registry retain, no copy of
1677
2220
  * the file), fed the layer it was opened with — or, with unsaved edits,
1678
2221
  * the artifact a save would write, which is cumulative: the edits and
1679
2222
  * the signature then share one revision, as Acrobat saves a
@@ -1723,14 +2266,14 @@ declare class SignatureAnalyzer {
1723
2266
  * the session's own document is the working copy. `working-copy`: a
1724
2267
  * layer session composes its immutable base with the cumulative delta
1725
2268
  * the pass emits (`EPDFDoc_OpenBaseOverlay`: no copy of the base, one
1726
- * cross-reference parse, the loaded delta's revision REPLACED, as an
2269
+ * cross-reference parse, the loaded delta's revision replaced, as an
1727
2270
  * artifact is), or — when every edit brought the layer back to its base
1728
2271
  * — a fresh layer over that base; a plain session (unsigned by law 9, so
1729
2272
  * this is exploratory) materialises a standalone incremental save and
1730
2273
  * opens it whole.
1731
2274
  */
1732
2275
  private openWorkingCopy;
1733
- /** Changed since load, nothing to write: the layer equals its base, and a fresh layer over it IS that document. */
2276
+ /** Changed since load, nothing to write: the layer equals its base, and a fresh layer over it is that document. */
1734
2277
  private freshLayerOverBase;
1735
2278
  /**
1736
2279
  * The fork's exact object diff between two revision documents,
@@ -1763,9 +2306,12 @@ interface FinalizeCandidateInput {
1763
2306
  password: string | null;
1764
2307
  }
1765
2308
  interface FinalizedCandidate {
2309
+ /** In page space: no session outlives the finalize to measure it later. */
1766
2310
  signature: SignatureDTO;
1767
2311
  protection: DocumentProtection;
1768
2312
  version: BaseVersionInfo;
2313
+ /** The sealed file's last object number: every object it has is at or below it. */
2314
+ lastObjectNumber: number;
1769
2315
  }
1770
2316
  /**
1771
2317
  * The session-less half of a durable signing. A server prepares a
@@ -1806,25 +2352,130 @@ declare function readStructure(runtime: PdfRuntimeModule, docPtr: Ptr): Revision
1806
2352
  //#endregion
1807
2353
  //#region src/features/forms/FormReader.d.ts
1808
2354
  /**
1809
- * Read side of the forms feature. All reads go through the session's
2355
+ * Read side of the forms feature. Field reads go through the session's
1810
2356
  * version-keyed model cache, so repeated reads between mutations reuse
1811
- * one native snapshot build.
2357
+ * one native snapshot build; widget rows are read from the pages.
1812
2358
  */
1813
2359
  declare class FormReader {
1814
2360
  private readonly runtime;
1815
2361
  private readonly session;
1816
- constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1817
- snapshot(signal: AbortSignal): FormSnapshot;
1818
- field(ref: FormFieldRef, signal: AbortSignal): FormFieldDTO;
2362
+ /** This thread's font registry, for the widget rows. */
2363
+ private readonly fonts?;
2364
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession,
2365
+ /** This thread's font registry, for the widget rows. */
2366
+ fonts?: FontRegistrar | undefined);
2367
+ snapshot(signal: AbortSignal): FormSnapshot<PdfCoordinates>;
2368
+ field(ref: FormFieldRef, signal: AbortSignal): FormFieldDTO<PdfCoordinates>;
2369
+ }
2370
+ //#endregion
2371
+ //#region src/features/forms/FormExporter.d.ts
2372
+ /**
2373
+ * `doc.forms.export`: the fields a selection takes, each whole with all of
2374
+ * its widgets, as one bundle in page space (see `formExportRowsOf`). It
2375
+ * reads the form as `list()` does and writes nothing. A widget is drawn
2376
+ * from its data, so the bundle holds no resources. It is checked against
2377
+ * `limits` as it is built, so an export never makes one an import refuses.
2378
+ */
2379
+ declare class FormExporter {
2380
+ private readonly runtime;
2381
+ private readonly session;
2382
+ /** This thread's font registry, for the widget rows. */
2383
+ private readonly fonts?;
2384
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession,
2385
+ /** This thread's font registry, for the widget rows. */
2386
+ fonts?: FontRegistrar | undefined);
2387
+ export(selection: FormExportSelection, limits: BundleLimits, signal: AbortSignal): WireFormBundle;
2388
+ }
2389
+ //#endregion
2390
+ //#region src/features/forms/FormImporter.d.ts
2391
+ interface FormImportRequest {
2392
+ /** In page space, as bundles are: each widget is measured on the page it goes to. */
2393
+ readonly bundle: WireFormBundle;
2394
+ readonly pages?: BundleImportPages;
2395
+ readonly attribution: 'restore' | 'stamp';
2396
+ /** Whether each field gets the value the bundle holds. */
2397
+ readonly values: boolean;
2398
+ /**
2399
+ * The session's identity: whom `'stamp'` makes each field's creator and
2400
+ * filler, and whose user `'restore'` records as `importedBy`.
2401
+ */
2402
+ readonly actor: AnnotationActor;
2403
+ readonly limits: BundleLimits;
2404
+ /** Whether scripts, submits and links are kept (`doc.forms.script`); without it they're left out. */
2405
+ readonly mayScript: boolean;
2406
+ /**
2407
+ * The group a field goes in, given the one it had in the bundle (if any):
2408
+ * checked by the caller, which refuses one it may not set.
2409
+ */
2410
+ readonly groupOf: (groupId: string | undefined) => string | undefined;
2411
+ }
2412
+ /** What a values import writes, decided before the first write. */
2413
+ interface FormValuesImportPlan {
2414
+ /** Each against the field as it is now; a write that changes nothing isn't among them. */
2415
+ readonly writes: readonly PlannedValueWrite<FormFieldDTO<PdfCoordinates>>[];
2416
+ readonly dropped: readonly FormValuesImportDrop[];
2417
+ }
2418
+ /**
2419
+ * `doc.forms.import` and `doc.forms.importValues`: a bundle's fields, copied
2420
+ * into this document or filled in from it. The bundle is checked first: its
2421
+ * shape, counts and sizes against the limits, and each resource against its
2422
+ * id. Then the core plans (`planFormImport`, `planFormValuesImport`), so all
2423
+ * that is left out is decided before the first write, and the writes go
2424
+ * through the form's own: `createField` and `setValue`.
2425
+ */
2426
+ declare class FormImporter {
2427
+ private readonly runtime;
2428
+ private readonly session;
2429
+ /** This thread's font registry, for the widget rows. */
2430
+ private readonly fonts?;
2431
+ constructor(runtime: PdfRuntimeModule, session: DocumentSession,
2432
+ /** This thread's font registry, for the widget rows. */
2433
+ fonts?: FontRegistrar | undefined);
1819
2434
  /**
1820
- * Serialize form data. Exports read the reconciled view (recovered
1821
- * fields included; on layers, promoted values win) straight from the
1822
- * document — the model cache is not involved.
1823
- */
1824
- exportData(format: FormDataFormat, signal: AbortSignal): {
1825
- format: FormDataFormat;
1826
- bytes: ArrayBuffer;
1827
- };
2435
+ * The design: each field `planFormImport` keeps, created with its widgets on
2436
+ * their mapped pages, their flags, and, with `values`, the value the
2437
+ * bundle holds; then the calculation order among them, appended to the
2438
+ * form's in bundle order; then who made and filled each, restored or
2439
+ * stamped.
2440
+ */
2441
+ import(request: FormImportRequest, signal: AbortSignal): FormImportResult<PdfCoordinates>;
2442
+ /**
2443
+ * What a values import of `bundle` writes, nothing written yet: each
2444
+ * field the bundle holds a value for, matched by full name, when it can
2445
+ * take the value (`fieldValueWriteOf`) and no signature locked it. A field
2446
+ * stored as a direct object can't be written, so it can't take the value
2447
+ * either.
2448
+ */
2449
+ planValues(bundle: WireFormBundle, limits: BundleLimits, signal: AbortSignal,
2450
+ /** Whether the session may fill the field in; one it may not is left out. */
2451
+ mayFill: (field: FormFieldDTO<PdfCoordinates>) => boolean): FormValuesImportPlan;
2452
+ /**
2453
+ * One write of a values import: the value, then, for `'restore'`, who
2454
+ * filled it in as the bundle has it. `'stamp'` leaves the session as the
2455
+ * filler, as `setValue` stamps it.
2456
+ */
2457
+ writeValue(write: PlannedValueWrite<FormFieldDTO<PdfCoordinates>>, bundle: WireFormBundle, attribution: 'restore' | 'stamp', actor: AnnotationActor, signal: AbortSignal): FormFieldDTO<PdfCoordinates>;
2458
+ /** A new widget's flags, as its row in the bundle has them: a create writes none. */
2459
+ private writeFlags;
2460
+ /** The full name of every field of the form. */
2461
+ private fieldNames;
2462
+ /** Every field of the form, as it is now. */
2463
+ private readFields;
2464
+ private readField;
2465
+ private calculationOrder;
2466
+ }
2467
+ //#endregion
2468
+ //#region src/features/forms/internal/calculationOrder.d.ts
2469
+ /**
2470
+ * The form's calculation order (`/AcroForm /CO`): the fields whose calculate
2471
+ * scripts run, in the order they run, by object number. A field's calculate
2472
+ * script keeps it in the order; the order is a list like a page's
2473
+ * annotations, moved by neighbour.
2474
+ */
2475
+ /** The calculation order before a write and as the write left it. */
2476
+ interface OrderChange {
2477
+ readonly before: readonly number[];
2478
+ readonly after: readonly number[];
1828
2479
  }
1829
2480
  //#endregion
1830
2481
  //#region src/features/forms/FormMutator.d.ts
@@ -1834,91 +2485,160 @@ declare class FormReader {
1834
2485
  * the document untouched (on layers: nothing promoted), and each success
1835
2486
  * bumps the session's mutation sequence so version-keyed caches rebuild.
1836
2487
  */
2488
+ /**
2489
+ * A form write's result as the mutator gives it: the fields, without the
2490
+ * widget rows, which the change that ran it reads (`withWidgetRows`).
2491
+ */
2492
+ type FieldWriteResult<T> = Omit<T, 'widgets'>;
1837
2493
  declare class FormMutator {
1838
2494
  private readonly runtime;
1839
2495
  private readonly session;
1840
2496
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1841
- setValue(ref: FormFieldRef, value: FormFieldValue, signal: AbortSignal): FormSetValueResult;
1842
- reset(ref: FormFieldRef, signal: AbortSignal): FormSetValueResult;
1843
- importData(data: ArrayBuffer, format: FormDataFormat | undefined, signal: AbortSignal): FormImportResult;
1844
- repair(bakeAppearances: boolean, signal: AbortSignal): FormRepairResult;
1845
2497
  /**
1846
- * Create a field and (optionally) its widgets in one composed
1847
- * transaction: native field creation, widget birth through the
1848
- * annotation plane, adoption, then field-plane setters. A failure
1849
- * mid-composition throws; earlier steps stay applied (the document is
1850
- * never inconsistent - at worst a partially configured field exists).
2498
+ * Set a field's value, as `actor` filled it in: what engine-core's rules
2499
+ * (`fieldAfterValue`) leave the field holding. A write that changes the
2500
+ * field names who filled it in; one that leaves it as it was writes
2501
+ * nothing.
2502
+ */
2503
+ setValue(ref: FormFieldRef, value: FormFieldValue, signal: AbortSignal, actor: AnnotationActor): FieldWriteResult<FormSetValueResult<PdfCoordinates>>;
2504
+ /**
2505
+ * Show, hide or keep from printing every widget of a field, as a script's
2506
+ * `Field.display` does: only the four flags it names change.
2507
+ */
2508
+ setDisplay(ref: FormFieldRef, display: FormFieldDisplay, signal: AbortSignal): FieldWriteResult<FormSetValueResult<PdfCoordinates>>;
2509
+ /**
2510
+ * Draw a text field's or dropdown's widgets with `text` (a format script's
2511
+ * result) without changing its value.
2512
+ */
2513
+ setAppearanceText(ref: FormFieldRef, text: string, signal: AbortSignal): FieldWriteResult<FormSetValueResult<PdfCoordinates>>;
2514
+ /**
2515
+ * The meta of a value write that changed no page list: the cloud layer
2516
+ * computes its own cache delta server-side.
2517
+ */
2518
+ private emptyMeta;
2519
+ /**
2520
+ * Put fields back to their default value: the ones named, or, with
2521
+ * `refs` absent, every field of the form. Fields that hold no value are
2522
+ * skipped; so, in a whole-form reset, are fields stored inline or locked
2523
+ * by a signature (named, they are refused). Every field is checked before
2524
+ * the first write. Returns the fields that changed.
1851
2525
  */
1852
- createField(draft: FormFieldDraft, signal: AbortSignal): {
1853
- field: FormFieldDTO;
2526
+ reset(refs: FormFieldRef[] | undefined, signal: AbortSignal): FieldWriteResult<FormResetResult<PdfCoordinates>>;
2527
+ /** Every field a whole-form reset puts back: those with a value, written directly, unlocked. */
2528
+ private wholeFormTargets;
2529
+ /** The named fields with a value, each once; one that can't be written is refused. */
2530
+ private namedTargets;
2531
+ repair(bakeAppearances: boolean, signal: AbortSignal): FormRepairResult;
2532
+ /**
2533
+ * Create a field and (optionally) its widgets as one change: native
2534
+ * field creation, widget birth through the annotation plane, adoption,
2535
+ * then field-plane setters, stamped as `actor` created it. Everything a
2536
+ * caller can get wrong is checked before the first write; any failure
2537
+ * after it aborts the job's layer transaction, so a rejected draft creates
2538
+ * nothing.
2539
+ */
2540
+ createField(draft: FormFieldDraft<PdfCoordinates>, signal: AbortSignal, actor: AnnotationActor, numbers?: {
2541
+ /** The field's object number; the next free one when absent. */
2542
+ readonly objectNumber?: number;
2543
+ /** Its widgets', in `draft.widgets` order; the next free ones when absent. */
2544
+ readonly widgetObjectNumbers?: readonly number[];
2545
+ }): {
2546
+ field: FormFieldDTO<PdfCoordinates>;
2547
+ calculationOrder: OrderChange | null;
1854
2548
  };
2549
+ /** EPDFForm_DeleteField: unlink the field and detach its kid widgets. */
2550
+ private nativeDeleteField;
2551
+ /** Check a widget placement before anything is written; returns its page index. */
2552
+ private preflightPlacement;
1855
2553
  /**
1856
- * Draw a PDF page into every widget of an UNSIGNED signature field: the
2554
+ * The native field node, linked into the tree, at `objectNumber` (checked
2555
+ * by the session) or the next free number.
2556
+ */
2557
+ private createFieldNode;
2558
+ /** The draft's field-plane settings, on a field just created. */
2559
+ private configureNewField;
2560
+ /**
2561
+ * Draw a PDF page into every widget of an unsigned signature field: the
1857
2562
  * visual "sign" of a viewer without a signer. The field's value stays
1858
2563
  * empty and nothing is sealed; a signed field is refused (its appearance
1859
- * is part of what the signature covers). Pages whose widgets changed are
1860
- * reported so their renders re-pin.
2564
+ * is part of what the signature covers).
1861
2565
  */
1862
- setSignatureAppearance(ref: FormFieldRef, pdf: Uint8Array, pageIndex: number, signal: AbortSignal): {
1863
- field: FormFieldDTO;
1864
- pages: PageObjectNumber[];
2566
+ setSignatureAppearance(ref: FormFieldRef, pdf: Uint8Array, signal: AbortSignal): {
2567
+ field: FormFieldDTO<PdfCoordinates>;
1865
2568
  };
1866
2569
  updateField(ref: FormFieldRef, patch: FormFieldPatch, signal: AbortSignal): {
1867
- field: FormFieldDTO;
2570
+ field: FormFieldDTO<PdfCoordinates>;
2571
+ calculationOrder: OrderChange | null;
1868
2572
  };
1869
2573
  /**
1870
- * Delete a terminal field. Widgets are DETACHED here (they become inert
1871
- * annotations); the worker host cascades their annotation deletion so
1872
- * page /Annots bookkeeping flows through the annotation feature.
2574
+ * Move fields in the calculation order: they go together, in the order
2575
+ * given, to `position`. Every field named must be in the order.
2576
+ */
2577
+ reorderCalculations(fields: readonly FormFieldRef[], position: FieldPosition, signal: AbortSignal): OrderChange & {
2578
+ moved: number[];
2579
+ };
2580
+ /**
2581
+ * Keep the calculation order in step with a field's calculate script, as
2582
+ * a write left it: a script puts the field at the end when it isn't in the
2583
+ * order, removing the script takes the field out. Returns the order before
2584
+ * and after, when it changed.
2585
+ */
2586
+ private followCalculateScript;
2587
+ /**
2588
+ * Delete a terminal field and its widgets in one mutation. The native
2589
+ * write detaches kid widgets and unlinks the field from /Fields or its
2590
+ * parent's /Kids; the cascade then removes every placed widget from its
2591
+ * page through the annotation feature, which owns /Annots bookkeeping. A
2592
+ * merged field/widget has no
2593
+ * kid to detach: its own dictionary leaves its page in the same cascade.
1873
2594
  */
1874
2595
  deleteField(ref: FormFieldRef, signal: AbortSignal): {
1875
- deletedFieldObjectNumber: number;
1876
- detachedWidgets: FormWidgetRef[];
2596
+ deleted: FormFieldRef;
2597
+ removedWidgets: FormWidget[];
2598
+ calculationOrder: OrderChange | null;
1877
2599
  };
1878
- attachWidget(ref: FormFieldRef, widget: FormWidgetRef, onState: string | undefined, signal: AbortSignal): {
1879
- field: FormFieldDTO;
2600
+ /**
2601
+ * Show a field in one more place: create the widget where the placement
2602
+ * says, styled by it, and add it to the field, as one change. A failure
2603
+ * after the first write rolls the page and the form back.
2604
+ */
2605
+ addWidget(ref: FormFieldRef, placement: WidgetPlacement<PdfCoordinates>, signal: AbortSignal, numbers?: {
2606
+ /** The new widget's object number; the next free one when absent. */
2607
+ readonly objectNumber?: number;
2608
+ /** Where a merged field's widget moves; the next free one when absent. */
2609
+ readonly splitObjectNumber?: number;
2610
+ }): {
2611
+ field: FormFieldDTO<PdfCoordinates>;
2612
+ widget: FormWidget;
1880
2613
  };
1881
- detachWidget(ref: FormFieldRef, widget: FormWidgetRef, signal: AbortSignal): {
1882
- field: FormFieldDTO;
2614
+ detachWidget(ref: FormFieldRef, widget: AnnotationRef, signal: AbortSignal): {
2615
+ field: FormFieldDTO<PdfCoordinates>;
2616
+ widget: FormWidget;
1883
2617
  };
1884
- private placementsOf;
1885
2618
  private applyOptions;
1886
2619
  private applyWideSetter;
1887
- private applyDefaultValues;
2620
+ /** Write a field's default (`/DV`) when it changes. */
2621
+ private writeDefault;
1888
2622
  private readBackField;
1889
2623
  private assertWritable;
2624
+ /** A field an earlier signature locked refuses every write (see `readFieldLocks`). */
2625
+ private assertNotLockedBySignature;
2626
+ /** Run the native value write. Returns the changed widget objnums. */
1890
2627
  /**
1891
- * A field an earlier signature froze (its FieldMDP, or the /Lock of a
1892
- * signed field) refuses every write: document-derived authority, the
1893
- * same way encryption bits are. Off under `signedDocumentPolicy:
1894
- * 'permit'`. A document whose signature model cannot be built is not
1895
- * known to be locked.
2628
+ * Write what `after` holds that `before` doesn't, as engine-core worked it
2629
+ * out: `/V`; a checkbox's or radio group's widgets on or off; a choice
2630
+ * field's selected options (`/I`); and a text or choice field's new
2631
+ * pictures (also with `redraw`, for new options). Returns the widgets whose
2632
+ * pictures changed, or `null` when the field already held it all and
2633
+ * nothing was written.
1896
2634
  */
1897
- private assertNotLockedBySignature;
1898
- /** Dispatch the typed native write. Returns the changed widget objnums. */
1899
- private applyWrite;
2635
+ private writeFieldAfter;
1900
2636
  /** Run a native write with the changed-widgets out buffer wired up. */
1901
2637
  private withChangedWidgets;
1902
2638
  /** Bump the session version, rebuild the model, and read the field back. */
1903
2639
  private readBack;
1904
2640
  }
1905
2641
  //#endregion
1906
- //#region src/features/forms/FormsEffectsApplier.d.ts
1907
- /** Ordered, non-rollback-atomic sink for one committed client script run. */
1908
- declare class FormsEffectsApplier {
1909
- private readonly runtime;
1910
- private readonly session;
1911
- constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1912
- apply(effects: FormEffect[], signal: AbortSignal): FormEffectsResult;
1913
- private preflight;
1914
- private resolve;
1915
- private readField;
1916
- private readFieldsBestEffort;
1917
- private applyNative;
1918
- private applyValue;
1919
- private withChangedWidgets;
1920
- }
1921
- //#endregion
1922
2642
  //#region src/features/forms/internal/formModelCache.d.ts
1923
2643
  /** Close and forget the session's cached model, if any. */
1924
2644
  declare function disposeFormModel(runtime: PdfRuntimeModule, session: DocumentSession): void;
@@ -1929,26 +2649,26 @@ declare function disposeFormModel(runtime: PdfRuntimeModule, session: DocumentSe
1929
2649
  * trust boundary (worker locally, server process in the cloud):
1930
2650
  *
1931
2651
  * extract — page text via the session corpus cache (version-keyed on
1932
- * `mutationSeq`, so results always reflect the CURRENT layer
2652
+ * `cacheSeq`, so results always reflect the current layer
1933
2653
  * view — text a redaction removed is unfindable),
1934
2654
  * match — the pure engine-core matcher (identical code on every
1935
2655
  * engine; parity by construction),
1936
2656
  * anchor — geometry read only for pages that actually matched, rects
1937
2657
  * via the selection line-merge (one rect per visual line).
1938
2658
  *
1939
- * Scope gating (`doc.text.search`, `doc.text.copy` for `'full'`) is the
2659
+ * Scope gating (`doc.text.search`, `doc.text.copy` for snippets) is the
1940
2660
  * caller's job — this reader assumes an authorized request.
1941
2661
  */
1942
2662
  declare class SearchReader {
1943
2663
  private readonly runtime;
1944
2664
  private readonly session;
1945
2665
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1946
- query(request: SearchRequest, signal: AbortSignal): SearchSlice;
2666
+ query(request: SearchScanRequest, signal: AbortSignal, slices: Slices): Promise<SearchSlice<PdfCoordinates>>;
1947
2667
  }
1948
2668
  //#endregion
1949
2669
  //#region src/features/actions/ActionModelReader.d.ts
1950
2670
  /** Mutable aggregate budget shared by every action model in one read job.
1951
- * Payload budgets are RESERVED before the backing buffer is allocated. */
2671
+ * Payload budgets are reserved before the backing buffer is allocated. */
1952
2672
  declare class ActionReadBudgetTracker {
1953
2673
  private readonly limits;
1954
2674
  private models;
@@ -1959,9 +2679,9 @@ declare class ActionReadBudgetTracker {
1959
2679
  constructor(limits?: ActionReadBudget);
1960
2680
  addModel(nodeCount: number): void;
1961
2681
  addScript(script: string): void;
1962
- /** Charge target entries BEFORE the target array is walked. */
2682
+ /** Charge target entries before the target array is walked. */
1963
2683
  reserveTargets(count: number): void;
1964
- /** Charge payload text BEFORE the scratch buffer for it is allocated. */
2684
+ /** Charge payload text before the scratch buffer for it is allocated. */
1965
2685
  reservePayloadBytes(length: number): void;
1966
2686
  private assertWithinBudget;
1967
2687
  }
@@ -1978,7 +2698,7 @@ declare class ActionReadBudgetTracker {
1978
2698
  * `subtype`, and the tree gains the `'payload-dropped'` warning — impossible
1979
2699
  * states stay unrepresentable while malformed documents stay readable.
1980
2700
  */
1981
- declare function readActionModel(fn: PdfFunctions, mem: PdfRuntimeMemory, docPtr: Ptr, modelPtr: Ptr, budget: ActionReadBudgetTracker): PdfActionTree | null;
2701
+ declare function readActionModel(fn: PdfFunctions, mem: PdfRuntimeMemory, docPtr: Ptr, modelPtr: Ptr, budget: ActionReadBudgetTracker): PdfActionTree<PdfDestination> | null;
1982
2702
  //#endregion
1983
2703
  //#region src/features/actions/DocumentActionsReader.d.ts
1984
2704
  /** Read catalog-owned actions. Extraction only; no action is executed. */
@@ -1986,11 +2706,11 @@ declare class DocumentActionsReader {
1986
2706
  private readonly runtime;
1987
2707
  private readonly session;
1988
2708
  constructor(runtime: PdfRuntimeModule, session: DocumentSession);
1989
- read(signal: AbortSignal): DocumentActionsSnapshot;
2709
+ read(signal: AbortSignal): DocumentActionsSnapshot<PdfDestination>;
1990
2710
  }
1991
2711
  //#endregion
1992
2712
  //#region src/worker-host/WorkerHost.d.ts
1993
- /** The image a {@link WorkerImageEncoder} produced. `bytes` must OWN its
2713
+ /** The image a {@link WorkerImageEncoder} produced. `bytes` must own its
1994
2714
  * buffer (a fresh allocation, not a pooled `Buffer` slab view) — it is
1995
2715
  * placed on the transfer manifest and moved zero-copy. */
1996
2716
  interface WorkerEncodedImage {
@@ -1999,7 +2719,7 @@ interface WorkerEncodedImage {
1999
2719
  }
2000
2720
  /**
2001
2721
  * Injected image-encode capability for the `*.renderEncoded` wire kinds.
2002
- * Dependency inversion keeps the native encoder OUT of this shared
2722
+ * Dependency inversion keeps the native encoder out of this shared
2003
2723
  * package: the cloud server's worker entry injects a sharp/libvips
2004
2724
  * implementation; browser/local entries inject nothing (they encode via
2005
2725
  * canvas) and the encoded kinds reject with `NotImplemented`.
@@ -2019,6 +2739,23 @@ interface WorkerHostOptions {
2019
2739
  * the sealed file can be published by rename.
2020
2740
  */
2021
2741
  signingCandidatePath?: (basePath: string, signingId: string) => string;
2742
+ /**
2743
+ * Bytes of decoded images kept between jobs that write nothing (see
2744
+ * {@link DecodedImageStore}). Defaults to 128 MB; 0 keeps none.
2745
+ */
2746
+ decodedImageBudgetBytes?: number;
2747
+ /**
2748
+ * Bytes of parsed pages kept between jobs, for every document on this
2749
+ * thread (see {@link PageResidency}). Defaults to
2750
+ * {@link DEFAULT_PAGE_RESIDENCY_POLICY}'s; 0 keeps none.
2751
+ */
2752
+ parsedPageBudgetBytes?: number;
2753
+ /**
2754
+ * Milliseconds a job's PDFium work runs (a page render, a page load, a loop
2755
+ * over glyphs or annotations) before it lets this thread receive messages,
2756
+ * so an abort stops it about this soon. Defaults to {@link DEFAULT_SLICE_MS}.
2757
+ */
2758
+ sliceMs?: number;
2022
2759
  }
2023
2760
  /**
2024
2761
  * The piece that runs "inside the worker": owns runtime, manages document
@@ -2054,6 +2791,21 @@ declare class WorkerHost {
2054
2791
  */
2055
2792
  private readonly fontIds;
2056
2793
  private readonly fonts;
2794
+ /** Parsed pages kept between jobs, for every session on this runtime. */
2795
+ private readonly residency;
2796
+ /** Image decodes kept between jobs that write nothing, for every session on this runtime. */
2797
+ private readonly decodedImages;
2798
+ private readonly slices;
2799
+ /**
2800
+ * True while a job's PDFium work is in progress across slices (a render, a
2801
+ * page load, a glyph loop). It pauses between slices so this thread can
2802
+ * receive an abort, but nothing else may use PDFium meanwhile (a render
2803
+ * holds its page until it ends): requests that arrive are held (see
2804
+ * {@link receive}).
2805
+ */
2806
+ private busy;
2807
+ /** Requests that arrived while a job was busy, in arrival order (see {@link takeHeld}). */
2808
+ private readonly held;
2057
2809
  private destroyed;
2058
2810
  constructor(runtime: PdfRuntimeModule,
2059
2811
  /**
@@ -2076,20 +2828,47 @@ declare class WorkerHost {
2076
2828
  */
2077
2829
  registerStartupFonts(specs: readonly StartupFontSpec[]): void;
2078
2830
  receive(msg: WorkerRequest): void;
2831
+ /** What a view shows of a document: the order its kept pages close in. Never answered. */
2832
+ private setWorkingSet;
2833
+ /** Stops a running job, or answers a held one as aborted without running it. */
2834
+ private abort;
2835
+ /** Runs held requests until one is busy across slices. */
2836
+ private drain;
2837
+ /**
2838
+ * The next held request. Requests run in arrival order, except that of page
2839
+ * renders held one after another, a render whose page is parsed runs before
2840
+ * one that must parse it. A render never passes another kind of request, nor
2841
+ * the other way round.
2842
+ */
2843
+ private takeHeld;
2844
+ /** Whether the render's page is kept, so the render parses nothing. */
2845
+ private pageIsKept;
2846
+ private run;
2847
+ /**
2848
+ * The session a job writes to, when it runs as a transaction: a write to an
2849
+ * open, usable layer document. Anything else (no such session, a locked
2850
+ * one) gets its handler's own error.
2851
+ */
2852
+ private transactedSession;
2853
+ /** Closes the pages a job of this effect may change, as no job holds them. */
2854
+ private closePagesChangedBy;
2855
+ /**
2856
+ * Answers a job with its result in page space. The handler decided which
2857
+ * buffers to move; the host relays that decision on the `resolve` envelope.
2858
+ */
2859
+ private resolve;
2860
+ /** The visible boxes of the request's document, read only when a result asks for one. */
2861
+ private visibleBoxesFor;
2079
2862
  private handleOpen;
2080
2863
  /**
2081
- * Open plain bytes into `session` in the shape it asked for. The default,
2082
- * `layer`, makes the bytes an immutable base with a fresh layer on top:
2083
- * reads fall through to the base, a write promotes only what it touches,
2084
- * a save appends exactly that, and a signature keeps its validity across
2085
- * later edits because the signature dictionary is never rewritten. The
2086
- * `plain` shape keeps one in-memory document — unless the bytes already
2087
- * carry a signature value, which always opens as a layer, because a plain
2088
- * save rewrites every loaded object into the new revision and strict
2089
- * validators reject that. Auto-layered sessions serialize no artifact per
2090
- * mutation: the caller never asked for one.
2864
+ * Open bytes into `session` as an immutable base with a fresh layer on top:
2865
+ * reads fall through to the base, a write promotes only what it touches and
2866
+ * runs as a layer transaction, a save appends exactly that, and a signature
2867
+ * keeps its validity across later edits because the signature dictionary is
2868
+ * never rewritten. The session serializes no artifact per mutation: the
2869
+ * caller holds the document, not a layer.
2091
2870
  */
2092
- private openSignedAware;
2871
+ private openBytesAsLayer;
2093
2872
  /**
2094
2873
  * What the document's signatures forbid, read at open and after an
2095
2874
  * unlock so the main-thread guard can subtract capabilities before the
@@ -2112,28 +2891,66 @@ declare class WorkerHost {
2112
2891
  * session that is never stored in `this.sessions`.
2113
2892
  */
2114
2893
  private handleSignaturesFinalizeCandidate;
2115
- private handleSignaturesAbort;
2894
+ private handleSignaturesCancel;
2116
2895
  /**
2117
- * The dispatch fence for a parked signing candidate. Only mutating kinds
2118
- * are refused; a request for a session that is not open falls through
2119
- * to its handler's own `DocNotOpen`.
2896
+ * The dispatch fence for a parked signing candidate. Only writes are
2897
+ * refused, except the signing's own completion; a request for a session
2898
+ * that is not open falls through to its handler's own `DocNotOpen`.
2120
2899
  */
2121
2900
  private assertNoPendingSigning;
2122
2901
  private handleMetadataRead;
2123
2902
  private handleMetadataUpdate;
2903
+ private handleMetadataReadCustom;
2904
+ private handleMetadataUpdateCustom;
2124
2905
  private handleActionsRead;
2125
- private handleAnnotationsListRawAll;
2126
- private handleAnnotationsListRawPage;
2127
- private handleAnnotationsListFullPage;
2906
+ private handleAnnotationsList;
2128
2907
  private handleAnnotationsRenderAppearances;
2129
2908
  private handleAnnotationsCreate;
2130
2909
  private handleAnnotationsUpdate;
2131
2910
  private handleAnnotationsDelete;
2132
2911
  private handleAnnotationsFlatten;
2133
2912
  private handleAnnotationsExportAppearance;
2134
- private handleAnnotationsMove;
2913
+ private handleAnnotationsExport;
2914
+ /**
2915
+ * An import, as a one-op change: kept under its `opId` with the record
2916
+ * that undoes it, and a retry under it gets the first answer.
2917
+ */
2918
+ private handleAnnotationsImport;
2919
+ private handleAnnotationsReadAppearance;
2920
+ private handleAnnotationsReorder;
2921
+ /**
2922
+ * `doc.apply`: one change, in the job's transaction. A change asked again
2923
+ * under its `opId` answers what it answered the first time, refusals
2924
+ * included, and a different change under it is refused. An undo runs the
2925
+ * record its change left, when the same user asks and nothing ended undo
2926
+ * since.
2927
+ */
2928
+ private handleDocumentApply;
2929
+ /**
2930
+ * A single verb as a one-op change: the same op, checks and record as
2931
+ * `doc.apply`, so its `opId` undoes it, and a retry under it gets the first
2932
+ * answer. What the handle may do was checked before the job; `authority`
2933
+ * carries who the write acts for, and what it checks per annotation.
2934
+ */
2935
+ private applyOne;
2936
+ /** A change's answer the first time, kept under its `opId`; a retry's checks. */
2937
+ private replayed;
2938
+ /**
2939
+ * Runs a change and keeps its answer under `opId`: the result and the record
2940
+ * that undoes it, or a refusal the change keeps (see `isKeptRefusal`).
2941
+ */
2942
+ private recorded;
2943
+ /**
2944
+ * A server request's changes (`POST …/changes`) as one job: each in its own
2945
+ * transaction, committed or rolled back on its own, its record handed back
2946
+ * for the server to keep. An undo of an earlier change of the same request
2947
+ * runs the record this job kept for it. One artifact for all that applied.
2948
+ */
2949
+ private handleDocumentApplyChanges;
2950
+ /** A change's ops, or the steps of the record an undo names. */
2951
+ private runChange;
2135
2952
  private handlePagesList;
2136
- private handlePagesMove;
2953
+ private handlePagesReorder;
2137
2954
  private handlePagesRotate;
2138
2955
  private handlePagesDelete;
2139
2956
  private handlePagesSetName;
@@ -2148,26 +2965,47 @@ declare class WorkerHost {
2148
2965
  private handleAttachmentsCreate;
2149
2966
  private handleAttachmentsDelete;
2150
2967
  private handleAnnotationsReadFile;
2968
+ private handleMeasureViewports;
2969
+ private handleMeasureSetScale;
2151
2970
  private handlePieceInfoRead;
2152
2971
  private handlePieceInfoUpdate;
2153
2972
  private handlePieceInfoApplications;
2154
- private handlePieceInfoClear;
2973
+ private handlePieceInfoDelete;
2155
2974
  private handlePagesText;
2156
2975
  private handlePagesGeometry;
2157
2976
  private handleSearchQuery;
2158
2977
  private handlePagesRender;
2159
- private receiveEncoded;
2978
+ /**
2979
+ * The work of a job that pauses between slices, or null for one that runs in
2980
+ * one go: renders, and the reads that load a page (its text, geometry,
2981
+ * annotation appearances, measurement viewports, an annotation's file) or
2982
+ * many (a search). Writes never pause: a read must never see half of one.
2983
+ */
2984
+ private slicedWork;
2985
+ /**
2986
+ * A job that pauses between slices of its PDFium work, so this thread can
2987
+ * receive an abort meanwhile. Until that work ends (`busy`), requests are
2988
+ * held. An encoded kind then encodes while other requests run.
2989
+ */
2990
+ private runSliced;
2160
2991
  private encodeRaster;
2161
- private handlePagesRenderEncoded;
2162
- private handleDocumentRenderPageFileEncoded;
2163
- private handleAnnotationsRenderAppearancesEncoded;
2992
+ /** The encoded reply of a page render's raw one. */
2993
+ private encodeRendered;
2994
+ /** The encoded reply of an appearance render's raw one, its rasters encoded one by one. */
2995
+ private encodeAppearances;
2996
+ /**
2997
+ * A rewrite drops every revision, and with them every signature: a signed
2998
+ * document refuses it unless the session permits breaking signatures
2999
+ * (`signedDocumentPolicy: 'permit'`). One rule for both engines.
3000
+ */
3001
+ private assertRewriteKeepsSignatures;
2164
3002
  private handleDocumentSaveBuffer;
2165
3003
  private handleDocumentSaveFile;
2166
3004
  private handleDocumentSaveLayerBuffer;
2167
3005
  private handleDocumentProbeSecurityFile;
2168
3006
  /**
2169
3007
  * One-shot file render (the derived-artifact warmer's producer): open the
2170
- * base from a file path into a TRANSIENT session — never stored in
3008
+ * base from a file path into a transient session — never stored in
2171
3009
  * `this.sessions`, so it can't collide with (or leak into) live document
2172
3010
  * sessions — resolve the display index to its durable page object number,
2173
3011
  * render, close. Shares the base parse with concurrent ad-hoc opens of
@@ -2183,7 +3021,7 @@ declare class WorkerHost {
2183
3021
  private handleFontsClearFallbacks;
2184
3022
  private handleFontsClear;
2185
3023
  /**
2186
- * Close exactly ONE layer session — the reload seam for layer-session
3024
+ * Close exactly one layer session — the reload seam for layer-session
2187
3025
  * freshness. The base document's refcount releases through the session's
2188
3026
  * close stack, so sibling layer sessions (and the base session) are
2189
3027
  * untouched. Idempotent: closing an absent session is a no-op ack,
@@ -2205,19 +3043,28 @@ declare class WorkerHost {
2205
3043
  private handleFormsList;
2206
3044
  private handleFormsSetValue;
2207
3045
  private handleFormsReset;
2208
- private handleFormsApplyEffects;
2209
3046
  private handleFormsExport;
3047
+ /**
3048
+ * A design import, as a one-op change: kept under its `opId` with the
3049
+ * record that undoes it, and a retry under it gets the first answer.
3050
+ */
2210
3051
  private handleFormsImport;
3052
+ /** A values import, as a one-op change (see {@link handleFormsImport}). */
3053
+ private handleFormsImportValues;
2211
3054
  private handleFormsRepair;
2212
3055
  private handleFormsCreateField;
2213
3056
  private handleFormsUpdateField;
2214
3057
  private handleFormsSetSignatureAppearance;
2215
3058
  private handleFormsDeleteField;
2216
- private handleFormsAttachWidget;
3059
+ private handleFormsAddWidget;
2217
3060
  private handleFormsDetachWidget;
3061
+ private handleFormsUpdateWidget;
3062
+ private handleFormsDeleteWidget;
3063
+ private handleFormsReorderCalculations;
3064
+ private handleFormsReorderWidgets;
2218
3065
  private finishMutation;
2219
3066
  private saveLayerArtifact;
2220
3067
  }
2221
3068
  //#endregion
2222
- export { ALL_STANDARD_SECURITY_PERMISSIONS, type AcquiredBaseDocument, ActionReadBudgetTracker, AnnotationAppearanceReader, AnnotationFlattener, AnnotationMutator, AnnotationReader, AttachmentMutator, AttachmentReader, BaseDocumentRegistry, CandidateFinalizer, CloseStack, DIGEST_CODE, DocumentActionsReader, PagesMutator as DocumentPagesMutator, PagesMutator, DocumentSaver, DocumentSession, EPDF_CHAR_GEOMETRY_LAYOUT, EventHub, FontRegistrar, FormMutator, FormReader, FormsEffectsApplier, type ImpactInputs, type LayerSource, LocalRevisionAuthority, MetadataMutator, MetadataReader, type MutationKind, type OpenedPdfDocument, type OpenedPdfDocumentKind, PageGeometryReader, PagesReader as PageLayoutReader, PagesReader, PagePtrPool, PageRenderReader as PageRasterReader, PageRenderReader, type PageRecord, PageTextReader, PagesExtractor, PagesFlattener, PagesInserter, RawAnnotationReader, type RawGeometryGlyphRecord, RedactionApplier, type RevisionAuthority, SearchReader, SecurityReader, SessionEventPublisher, SignatureAnalyzer, SignatureMutator, SignatureReader, type StartupFontSpec, type WorkerEncodedImage, WorkerHost, type WorkerHostOptions, type WorkerImageEncoder, acquireSignatureModel, buildRunsFromRawGlyphs, computeMutationImpact, destroyLibrary, disposeFormModel, disposeSignatureModel, ensureInitialized, formatPdfDate, generateUuid, hasAllStandardSecurityPermissions, hasSignedSignature, normalizeU32, openFatMemoryDocument, openLayerDocument, pdfDateToIso, readActionModel, readAttachmentFileInfo, readRevisions, readSignaturesFromModel, readStructure, throwIfAborted, writeAttachmentFilePayload };
3069
+ export { ALL_STANDARD_SECURITY_PERMISSIONS, type AcquiredBaseDocument, ActionReadBudgetTracker, AnnotationAppearanceReader, AnnotationBatchApplier, AnnotationExporter, AnnotationFlattener, AnnotationImporter, AnnotationMutator, AnnotationReader, type AppliedChange, AttachmentMutator, AttachmentReader, BaseDocumentRegistry, type BatchCreate, type BatchCreateResult, CandidateFinalizer, ChangeApplier, ChangeLedger, type ChangeOutcome, type ChangeRecord, CloseStack, DIGEST_CODE, DocumentActionsReader, PagesMutator as DocumentPagesMutator, PagesMutator, DocumentSaver, DocumentSession, EPDF_CHAR_GEOMETRY_LAYOUT, FontRegistrar, FormExporter, FormImporter, FormMutator, FormReader, type LayerSource, MetadataMutator, MetadataReader, type OpenedPdfDocument, type OpenedPdfDocumentKind, type PackedChangeRecord, PageGeometryReader, PagesReader as PageLayoutReader, PagesReader, PagePtrPool, PageRenderReader as PageRasterReader, PageRenderReader, type PageRecord, PageTextReader, PagesExtractor, PagesFlattener, PagesInserter, RawAnnotationReader, type RawGeometryGlyphRecord, RedactionApplier, type ReverseStep, SearchReader, SecurityReader, SignatureAnalyzer, SignatureMutator, SignatureReader, type StartupFontSpec, type UndoUnavailableReason, type WorkerEncodedImage, WorkerHost, type WorkerHostOptions, type WorkerImageEncoder, acquireSignatureModel, buildRunsFromRawGlyphs, changeLedgerOf, destroyLibrary, disposeFormModel, disposeSignatureModel, ensureInitialized, formatPdfDate, generateUuid, hasAllStandardSecurityPermissions, hasSignedSignature, normalizeU32, openFatMemoryDocument, openLayerDocument, packChangeRecord, pdfDateToIso, readActionModel, readAttachmentFileInfo, readRevisions, readSignaturesFromModel, readStructure, throwIfAborted, unpackChangeRecord, visibleBoxReader, writeAttachmentFilePayload };
2223
3070
  //# sourceMappingURL=index.d.ts.map