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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/{AbortError-CRSooKsk.cjs → AbortError-Cq2igpZ6.cjs} +2 -2
  2. package/dist/{AbortError-CRSooKsk.cjs.map → AbortError-Cq2igpZ6.cjs.map} +1 -1
  3. package/dist/{AbortError-8-9JAX1Y.js → AbortError-DHqPBnYc.js} +2 -2
  4. package/dist/{AbortError-8-9JAX1Y.js.map → AbortError-DHqPBnYc.js.map} +1 -1
  5. package/dist/{AnnotationListSnapshot-C1U3XUxP.d.cts → AnnotationListSnapshot-D3mFut1e.d.cts} +1323 -214
  6. package/dist/{AnnotationListSnapshot-C1U3XUxP.d.ts → AnnotationListSnapshot-D3mFut1e.d.ts} +1323 -214
  7. package/dist/{DocumentSecurityService-6rhztZlz.d.cts → DocumentSecurityService-ClTZc0zO.d.cts} +2 -2
  8. package/dist/{DocumentSecurityService-7nL9aY75.d.ts → DocumentSecurityService-_JRjqvoI.d.ts} +2 -2
  9. package/dist/{Engine-D3868xIU.d.cts → Engine-Bx0NOfmd.d.cts} +219 -11
  10. package/dist/{Engine-CWnAKccD.d.ts → Engine-DxtpCYcP.d.ts} +219 -11
  11. package/dist/{PageInsertBlankInput-CzShuSJI.cjs → PageInsertBlankInput-CqXBmFhN.cjs} +37 -2
  12. package/dist/PageInsertBlankInput-CqXBmFhN.cjs.map +1 -0
  13. package/dist/{PageInsertBlankInput-WKY0tr6S.js → PageInsertBlankInput-cHq9-i-z.js} +37 -2
  14. package/dist/PageInsertBlankInput-cHq9-i-z.js.map +1 -0
  15. package/dist/{builders-Do6_4KnS.cjs → builders-Bqm8yTpi.cjs} +1483 -11
  16. package/dist/builders-Bqm8yTpi.cjs.map +1 -0
  17. package/dist/{builders-Cdz34nbF.js → builders-Dqo2X-dt.js} +1364 -12
  18. package/dist/builders-Dqo2X-dt.js.map +1 -0
  19. package/dist/conformance.cjs +1630 -107
  20. package/dist/conformance.cjs.map +1 -1
  21. package/dist/conformance.d.cts +117 -3
  22. package/dist/conformance.d.ts +117 -3
  23. package/dist/conformance.js +1627 -108
  24. package/dist/conformance.js.map +1 -1
  25. package/dist/{fold-C7I5GpBz.cjs → fold-2V29cxCV.cjs} +2 -2
  26. package/dist/{fold-C7I5GpBz.cjs.map → fold-2V29cxCV.cjs.map} +1 -1
  27. package/dist/{fold-BfyxxwS6.js → fold-CdEJTBTn.js} +2 -2
  28. package/dist/{fold-BfyxxwS6.js.map → fold-CdEJTBTn.js.map} +1 -1
  29. package/dist/index.cjs +33 -4
  30. package/dist/index.d.cts +2 -2
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +5 -5
  33. package/dist/{resolver-D-jumvgM.cjs → resolver-CYVNV4hn.cjs} +216 -10
  34. package/dist/resolver-CYVNV4hn.cjs.map +1 -0
  35. package/dist/{resolver-CHG4waXL.js → resolver-inAVzsWt.js} +157 -11
  36. package/dist/resolver-inAVzsWt.js.map +1 -0
  37. package/dist/runtime.cjs +34 -5
  38. package/dist/runtime.d.cts +253 -5
  39. package/dist/runtime.d.ts +253 -5
  40. package/dist/runtime.js +6 -6
  41. package/dist/{schemas-XGrNVMDd.js → schemas-BLARNCgx.js} +469 -7
  42. package/dist/schemas-BLARNCgx.js.map +1 -0
  43. package/dist/{schemas-CS5iPpX5.cjs → schemas-DIqFMmrC.cjs} +672 -6
  44. package/dist/schemas-DIqFMmrC.cjs.map +1 -0
  45. package/dist/wire.cjs +250 -12
  46. package/dist/wire.cjs.map +1 -1
  47. package/dist/wire.d.cts +444 -4
  48. package/dist/wire.d.ts +444 -4
  49. package/dist/wire.js +214 -13
  50. package/dist/wire.js.map +1 -1
  51. package/package.json +1 -1
  52. package/dist/PageInsertBlankInput-CzShuSJI.cjs.map +0 -1
  53. package/dist/PageInsertBlankInput-WKY0tr6S.js.map +0 -1
  54. package/dist/builders-Cdz34nbF.js.map +0 -1
  55. package/dist/builders-Do6_4KnS.cjs.map +0 -1
  56. package/dist/resolver-CHG4waXL.js.map +0 -1
  57. package/dist/resolver-D-jumvgM.cjs.map +0 -1
  58. package/dist/schemas-CS5iPpX5.cjs.map +0 -1
  59. package/dist/schemas-XGrNVMDd.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { $i as AnnotationUpdateResult, $r as FormEffectsResult, Dr as SearchRequest, Fa as AttachmentDeleteResult, Fn as RedactionApplyScope, Fo as FontKey, Gn as PageDeleteResult, Gr as FormFieldUpdateResult, Ho as AnnotationAppearanceImagesResult, Hr as FormDataExport, Ic as AnnotationRef, Io as FontSpec, Jr as FormSetValueResult, Js as MetadataPatch, Ki as FormFieldRef, Kr as FormImportResult, Mn as MetadataUpdateResult, Ni as FormFieldDTO, Or as SearchSlice, Pa as AttachmentCreateResult, Pn as RedactionApplyResult, Po as FontHandle, Qi as AnnotationMoveResult, Ro as PdfSaveMode, Ts as PageTextSnapshot, Un as PageInsertBlankSpec, Ur as FormFieldCreateResult, Vn as PageFlattenUsage, Vo as AnnotationAppearanceImageOptions, Wn as PageInsertResult, Wr as FormFieldDeleteResult, Ws as PageListSnapshot, Xi as AnnotationCreateResult, Xn as PageMoveResult, Xr as FormEffect, Yo as AnnotationAppearancesResult, Yr as FormWidgetLinkResult, Ys as OpenInput, Za as AttachmentContent, Zi as AnnotationDeleteResult, _i as FormFieldDraft, ai as FormSubmissionRequest, ci as FormFieldValue, di as FormFieldPatch, ds as PageRenderOptions, eo as AttachmentFileSource, fl as PdfRotation, hl as DocumentActionsSnapshot, jl as DocumentMetadata, kl as PageObjectNumber, l as AnnotationPatch, ms as PageRenderViewport, n as AnnotationListSnapshotAllPages, nc as OpenOptions, no as EmbeddedFileRef, o as AnnotationDraft, qi as FormWidgetRef, qn as PageRotateResult, qo as AnnotationAppearanceRenderOptions, qr as FormRepairResult, ri as FormSubmissionReceipt, rs as PageImageOptions, si as FormDataFormat, ss as PageRaster, t as AnnotationListPageSnapshot, to as EmbeddedFileItem, ts as PageImageHandle, vs as PageGeometrySnapshot, wi as FormSnapshot, zn as PageFlattenResult } from "./AnnotationListSnapshot-C1U3XUxP.js";
2
- import { g as AbortablePromise, s as DocumentSecurityService } from "./DocumentSecurityService-7nL9aY75.js";
1
+ import { $n as PageNameResult, $r as FormImportResult, Ac as PageTextSnapshot, Bl as SignaturePrepareInput, Bn as PageDeleteResult, Ca as FormSnapshot, Cc as PageGeometrySnapshot, Cl as BaseVersionInfo, Do as AttachmentCreateResult, Fl as SignatureCompleteInput, Fn as RedactionApplyScope, Fr as SearchSlice, Ga as AnnotationCreateResult, Gs as FontSpec, Hn as PageRotateResult, Hs as FontHandle, Il as SignatureCompleteResult, Ja as AnnotationUpdateResult, Jo as EmbeddedFileItem, Js as PdfSaveMode, Ka as AnnotationDeleteResult, Ma as FormFieldDTO, Mi as AnalyzeInput, Mn as MetadataUpdateResult, Nl as SignatureAbortResult, Oo as AttachmentDeleteResult, Pi as ChangeAnalysis, Pl as SignatureAppearanceInput, Pn as RedactionApplyResult, Pr as SearchRequest, Qi as FormEffectsResult, Qn as PageFlattenUsage, Qr as FormFieldUpdateResult, Rn as PageInsertBlankSpec, Vl as SignaturePrepared, Wl as SignatureSnapshot, Wo as AttachmentContent, Ws as FontKey, Xn as PageFlattenResult, Xr as FormFieldCreateResult, Xs as AnnotationAppearanceImageOptions, Yc as PageListSnapshot, Yi as FormEffect, Yo as EmbeddedFileRef, Yr as FormDataExport, Zr as FormFieldDeleteResult, Zs as AnnotationAppearanceImagesResult, _d as PageObjectNumber, cc as PageImageOptions, ed as PdfRotation, ei as FormRepairResult, er as PageNameInput, eu as FormFieldRef, fc as PageRaster, ga as FormFieldDraft, gc as PageRenderOptions, ia as FormSubmissionRequest, ic as AnnotationAppearancesResult, l as AnnotationPatch, ll as OpenOptions, n as AnnotationListSnapshotAllPages, na as FormSubmissionReceipt, nc as AnnotationAppearanceRenderOptions, ni as FormWidgetLinkResult, nl as OpenInput, o as AnnotationDraft, oa as FormDataFormat, oc as PageImageHandle, qa as AnnotationMoveResult, qn as AnnotationFlattenResult, qo as AttachmentFileSource, rd as DocumentActionsSnapshot, rr as PageMoveResult, sa as FormFieldValue, t as AnnotationListPageSnapshot, ti as FormSetValueResult, tl as MetadataPatch, tr as PageRemoveNameInput, tu as FormWidgetRef, ua as FormFieldPatch, wl as DigestAlgorithm, wu as AnnotationRef, yc as PageRenderViewport, yd as DocumentMetadata, zn as PageInsertResult } from "./AnnotationListSnapshot-D3mFut1e.js";
2
+ import { g as AbortablePromise, s as DocumentSecurityService } from "./DocumentSecurityService-_JRjqvoI.js";
3
3
  //#region src/engine/DocumentActionsService.d.ts
4
4
  /** Lazy, catalog-owned PDF action read. This API extracts; it never executes. */
5
5
  interface DocumentActionsService {
@@ -83,6 +83,31 @@ interface DocumentAttachmentsService {
83
83
  delete?(ref: EmbeddedFileRef): AbortablePromise<AttachmentDeleteResult>;
84
84
  }
85
85
  //#endregion
86
+ //#region src/engine/DocumentFontSettings.d.ts
87
+ /**
88
+ * How much of a registered font's program the appearances authored in this
89
+ * document carry. `default` subsets annotation text (FreeText, redaction
90
+ * labels) and embeds form-field text whole; `subset` and `full` apply to
91
+ * both. A font whose licence forbids subsetting is embedded whole under
92
+ * every policy; programs already in the document are never re-embedded.
93
+ */
94
+ type FontEmbeddingPolicy = 'default' | 'subset' | 'full';
95
+ /**
96
+ * Per-document font and text-layout settings: session state of this
97
+ * document handle, never written to the file, applied to appearances
98
+ * generated after the call. Local engine only (`doc.fonts` is undefined on
99
+ * engines that do not lay text out in-process).
100
+ */
101
+ interface DocumentFontSettings {
102
+ setEmbeddingPolicy(policy: FontEmbeddingPolicy): AbortablePromise<void>;
103
+ /**
104
+ * Latin typographic features (kerning, ligatures) when shaping rich text.
105
+ * Off by default: Acrobat's appearances show plain advance widths and no
106
+ * ligatures, and parity with what Acrobat draws wins.
107
+ */
108
+ setTypographicFeatures(enabled: boolean): AbortablePromise<void>;
109
+ }
110
+ //#endregion
86
111
  //#region src/engine/DocumentFormsService.d.ts
87
112
  /** Options for {@link DocumentFormsService.repair}. */
88
113
  interface FormRepairOptions {
@@ -181,6 +206,14 @@ interface DocumentFormsService {
181
206
  * `form.fieldUpdated`.
182
207
  */
183
208
  updateField(ref: FormFieldRef, patch: FormFieldPatch): AbortablePromise<FormFieldUpdateResult>;
209
+ /**
210
+ * Draw a PDF page into every widget of an UNSIGNED signature field — the
211
+ * visual "sign" of a viewer that has no signer. The field's value stays
212
+ * empty and nothing is sealed; a signed field is refused. Gated by
213
+ * `doc.forms.fill`. Emits `form.fieldUpdated`. Absent on engines that
214
+ * cannot draw appearances.
215
+ */
216
+ setSignatureAppearance?(ref: FormFieldRef, appearance: SignatureAppearanceInput): AbortablePromise<FormFieldUpdateResult>;
184
217
  /**
185
218
  * Delete a terminal field and cascade: every widget is removed from its
186
219
  * page, the field leaves the tree, and empty ancestors are pruned.
@@ -262,6 +295,25 @@ interface DocumentPagesService {
262
295
  * recycled; surviving pages keep their identity and revisions.
263
296
  */
264
297
  delete(pageObjectNumbers: PageObjectNumber[]): AbortablePromise<PageDeleteResult>;
298
+ /**
299
+ * Register `name` → page in the catalog's `/Names /Pages` tree (create,
300
+ * or replace what an existing key points at); with `replace`, drop that
301
+ * other key in the same job (rename). Named pages are LAYOUT — read them
302
+ * back from `list().namedPages` — so this is a page-structure mutation:
303
+ * `docVersion` + `layoutVersion` advance, per-page pins do not. Gated like
304
+ * `move` (`doc.pages.assemble`). Optional while transports ship;
305
+ * feature-detect with `pages.setName !== undefined`.
306
+ *
307
+ * Rejects with `InvalidArg` for an empty name and `NotFound` for a page
308
+ * that is not in the page tree (hidden templates included).
309
+ */
310
+ setName?(input: PageNameInput): AbortablePromise<PageNameResult>;
311
+ /**
312
+ * Remove one `/Names /Pages` registration; the page itself is untouched.
313
+ * `NotFound` when no registration has that decoded key. Page DELETION
314
+ * removes registrations by itself — callers never need to pair the two.
315
+ */
316
+ removeName?(input: PageRemoveNameInput): AbortablePromise<PageNameResult>;
265
317
  /**
266
318
  * Paint eligible annotation appearances into page content and remove only
267
319
  * those annotations that were painted. This changes content and annotation
@@ -320,9 +372,12 @@ interface DocumentPagesService {
320
372
  * rewrites THIS LAYER's bytes only. The immutable base document still
321
373
  * contains the original content — byte-perfect original recovery is a
322
374
  * feature of the storage model, not a leak. Redacted content becomes truly
323
- * unrecoverable only in what leaves the system: a layer download/export, or
324
- * a local document saved after apply. Surfaces that promise "permanent
325
- * removal" must scope that promise to the exported artifact.
375
+ * unrecoverable only in a full-rewrite export after apply. Callers MUST select
376
+ * `download({ mode: 'rewrite' })` (or the corresponding file/cloud save mode)
377
+ * before sharing the redacted PDF. The default incremental download preserves
378
+ * the original content in prior revisions; apply does not change that default.
379
+ * Surfaces that promise "permanent removal" must scope that promise to the
380
+ * full-rewrite exported artifact.
326
381
  *
327
382
  * Applying is irreversible within the layer (no undo). Emits a
328
383
  * `redaction.applied` document event; content-scope raster invalidation and
@@ -469,6 +524,64 @@ interface DocumentSearchService {
469
524
  query(request: SearchRequest): AbortablePromise<SearchSlice>;
470
525
  }
471
526
  //#endregion
527
+ //#region src/engine/DocumentSignaturesService.d.ts
528
+ /**
529
+ * Digital signatures on the document: the read side (what is signed, what
530
+ * it seals, what it forbids) and the two-phase signing protocol. The
531
+ * engine does the PDF job — candidate file, digest, sealing — and never
532
+ * holds a key; the CMS comes from the caller (`@embedpdf/core-signature`
533
+ * builds one from a raw signer, or a signing service returns it).
534
+ *
535
+ * Every read here describes the bytes the document was loaded from
536
+ * (for a layer document: base + the delta it was opened with). Unsaved
537
+ * edits are not part of any revision until `prepare` snapshots them.
538
+ *
539
+ * Reads ride `doc.forms.read`; `revisionBytes` rides `doc.download`;
540
+ * `prepare`/`complete`/`abort` ride `doc.sign`, a certification
541
+ * additionally `doc.sign.certify`.
542
+ */
543
+ interface DocumentSignaturesService {
544
+ /** Revisions, every signature field with its signed state, and the protection in force. */
545
+ list(): AbortablePromise<SignatureSnapshot>;
546
+ /** The DER `/Contents` of a signed field (exactly `contentsSize` bytes, padding stripped). `NotFound` for an unsigned field. */
547
+ contents(field: FormFieldRef): AbortablePromise<Uint8Array>;
548
+ /**
549
+ * Hash the signed field's `/ByteRange` with `algorithm`, straight from
550
+ * the loaded bytes. What a CMS verifier compares its message digest to.
551
+ */
552
+ digest(field: FormFieldRef, algorithm: DigestAlgorithm): AbortablePromise<Uint8Array>;
553
+ /** The exact bytes of revision `revisionIndex` (`[0, end)`): what a signature over it signed. */
554
+ revisionBytes(revisionIndex: number): AbortablePromise<Uint8Array>;
555
+ /**
556
+ * What changed after a signature (or after any revision), judged
557
+ * revision by revision against the restrictions in force: every object
558
+ * whose cross-reference mapping moved, with its value and every
559
+ * reference to it in both revisions, and the rule that explains each —
560
+ * or the reference no rule explains. `until: 'working-copy'` snapshots
561
+ * unsaved edits as one more revision first. A truncated value is never
562
+ * permitted; a broken chain is `indeterminate`.
563
+ */
564
+ analyze(input: AnalyzeInput): AbortablePromise<ChangeAnalysis>;
565
+ /**
566
+ * Build the candidate (unsaved edits become their own revision, the
567
+ * signature lands in a fresh update over it), seal it, and return the
568
+ * digest to sign. The live document is untouched and read-only until
569
+ * `complete` or `abort` (`SigningPending`). Refusals are
570
+ * `SignatureRefused` with the reason in the message.
571
+ */
572
+ prepare(input: SignaturePrepareInput): AbortablePromise<SignaturePrepared>;
573
+ /**
574
+ * Write the CMS into the candidate and install the sealed bytes as the
575
+ * document's new version. `expectedVersion` must be what `prepare`
576
+ * returned (`SigningVersionMismatch` otherwise). Idempotent: a replay
577
+ * with the same CMS answers `already-completed`. Emits
578
+ * `signature.completed` and `document.versioned`.
579
+ */
580
+ complete(input: SignatureCompleteInput): AbortablePromise<SignatureCompleteResult>;
581
+ /** Discard a pending candidate. */
582
+ abort(signingId: string): AbortablePromise<SignatureAbortResult>;
583
+ }
584
+ //#endregion
472
585
  //#region src/engine/MetadataService.d.ts
473
586
  interface MetadataService {
474
587
  read(): AbortablePromise<DocumentMetadata>;
@@ -539,6 +652,30 @@ interface PageAnnotationsService {
539
652
  * space, in `[0, count - refs.length]`.
540
653
  */
541
654
  move(refs: AnnotationRef[], toIndex: number): AbortablePromise<AnnotationMoveResult>;
655
+ /**
656
+ * Flatten the given annotations of THIS page into its content —
657
+ * `pages.flatten` for a chosen set. Painted annotations are removed from
658
+ * the page; ones that are ineligible (hidden for `usage`, Popups, no
659
+ * usable appearance) stay and report `skipped`, so a caller can say
660
+ * "2 of 3 flattened". A content + annotation MUTATION of this page: its
661
+ * content and annotation pins advance, layout does not; a
662
+ * `annotations.flattened` event is published when anything was applied.
663
+ * Gated like `pages.flatten` (`doc.pages.modify` + `doc.annotate.modify`).
664
+ * `InvalidArg` for a ref on another page; `NotFound` for an unknown ref.
665
+ * Optional while transports ship — feature-detect.
666
+ */
667
+ flatten?(refs: AnnotationRef[], usage?: PageFlattenUsage): AbortablePromise<AnnotationFlattenResult>;
668
+ /**
669
+ * Flatten the normal appearances of the given annotations of this page
670
+ * into a NEW single-page PDF sized to their union `/Rect` — vector,
671
+ * positions preserved, exactly as this page shows them. The same plan as
672
+ * `flatten` aimed at a fresh page; the source document is untouched. A
673
+ * derived READ that egresses content, so it is gated by `doc.download`
674
+ * like `pages.extract`. All-or-nothing: `InvalidArg` when any ref is not
675
+ * on this page, hidden, or has no appearance (a stamp silently missing a
676
+ * part would be worse than an error). Optional — feature-detect.
677
+ */
678
+ exportAppearance?(refs: AnnotationRef[]): AbortablePromise<Uint8Array>;
542
679
  }
543
680
  //#endregion
544
681
  //#region src/engine/PageGeometryService.d.ts
@@ -745,6 +882,9 @@ type DocumentEvent = ({
745
882
  pageObjectNumber: PageObjectNumber;
746
883
  origin: EventOrigin;
747
884
  } & AnnotationMoveResult) | ({
885
+ type: 'annotations.flattened';
886
+ origin: EventOrigin;
887
+ } & AnnotationFlattenResult) | ({
748
888
  type: 'pages.moved';
749
889
  /** Locally: the moved block. Remotely the audit row only records the
750
890
  * outcome, so this is the full new order — consumers should read
@@ -769,6 +909,13 @@ type DocumentEvent = ({
769
909
  destIndex?: number;
770
910
  origin: EventOrigin;
771
911
  } & PageInsertResult) | ({
912
+ type: 'pages.named';
913
+ /** The decoded key that was registered, renamed, or removed. */
914
+ name: string;
915
+ /** The page it now points at; `null` when the registration was removed. */
916
+ pageObjectNumber: PageObjectNumber | null;
917
+ origin: EventOrigin;
918
+ } & PageNameResult) | ({
772
919
  type: 'attachment.created';
773
920
  origin: EventOrigin;
774
921
  } & AttachmentCreateResult) | ({
@@ -813,6 +960,30 @@ type DocumentEvent = ({
813
960
  type: 'redaction.applied';
814
961
  origin: EventOrigin;
815
962
  } & RedactionApplyResult) | {
963
+ /** A signing candidate was parked: the document is read-only until it completes or aborts. */
964
+ type: 'signature.prepared';
965
+ signingId: string;
966
+ field: FormFieldRef;
967
+ origin: EventOrigin;
968
+ } | ({
969
+ /** The sealed bytes are installed; `version` is what they became. */
970
+ type: 'signature.completed';
971
+ signingId: string;
972
+ origin: EventOrigin;
973
+ } & SignatureCompleteResult) | {
974
+ type: 'signature.aborted';
975
+ signingId: string;
976
+ origin: EventOrigin;
977
+ } | {
978
+ /**
979
+ * The session moved to a new saved version (a completed signature,
980
+ * here or in another session). Byte-level facts — revisions,
981
+ * coverage, digests, verdicts — must be re-read.
982
+ */
983
+ type: 'document.versioned';
984
+ version: BaseVersionInfo;
985
+ origin: EventOrigin;
986
+ } | {
816
987
  /**
817
988
  * Cloud only: the live event stream fell too far behind to replay
818
989
  * (the server's SSE `full-refresh`) — state derived from earlier
@@ -883,6 +1054,13 @@ interface DocumentHandle {
883
1054
  readonly attachments?: DocumentAttachmentsService;
884
1055
  /** The document's interactive form (AcroForm): fields, values, interchange. */
885
1056
  readonly forms: DocumentFormsService;
1057
+ /**
1058
+ * Font embedding and text layout settings of this document (session
1059
+ * state). Optional: the local engine implements it; engines that do not
1060
+ * lay text out in-process omit it — feature-detect with
1061
+ * `doc.fonts !== undefined`.
1062
+ */
1063
+ readonly fonts?: DocumentFontSettings;
886
1064
  /**
887
1065
  * CATALOG-level `/PieceInfo` private application data (ISO 32000 §14.5)
888
1066
  * — e.g. a stamp library's display name. Optional: the local engine
@@ -916,6 +1094,13 @@ interface DocumentHandle {
916
1094
  * ship it — feature-detect with `doc.redaction !== undefined`.
917
1095
  */
918
1096
  readonly redaction?: DocumentRedactionService;
1097
+ /**
1098
+ * Digital signatures: the read side (revisions, signed fields, the
1099
+ * protection they impose) and the two-phase signing protocol. Optional
1100
+ * while engines ship it — feature-detect with
1101
+ * `doc.signatures !== undefined`.
1102
+ */
1103
+ readonly signatures?: DocumentSignaturesService;
919
1104
  /**
920
1105
  * The document's event stream — every confirmed mutation, exactly once,
921
1106
  * identical shape on local and cloud engines (see `DocumentEvent`). The
@@ -931,13 +1116,29 @@ interface DocumentHandle {
931
1116
  download(opts?: {
932
1117
  mode?: PdfSaveMode;
933
1118
  }): AbortablePromise<Uint8Array>;
1119
+ /**
1120
+ * Local Node engines only: write the document to a local file without
1121
+ * moving its bytes through JS. An untouched session (no unsaved edits,
1122
+ * incremental mode) is streamed out verbatim — for a signed document,
1123
+ * exactly as sealed. Absent on engines that cannot reach a filesystem.
1124
+ */
1125
+ downloadToFile?(path: string, opts?: {
1126
+ mode?: PdfSaveMode;
1127
+ }): AbortablePromise<void>;
1128
+ /**
1129
+ * The saved version this session is on: SHA-256 and length of the
1130
+ * loaded bytes (for a layer session, of its base). Changes only when
1131
+ * a signature completes. Optional while engines ship it.
1132
+ */
1133
+ version?(): AbortablePromise<BaseVersionInfo>;
934
1134
  /**
935
1135
  * Export JUST this document's LAYER as a self-contained artifact (the small
936
1136
  * overlay diff over the immutable base) — re-openable later via
937
- * `OpenInputLayerBytes` with `{ kind: 'artifact', bytes }`. Optional: only the
938
- * local engine, opened WITH a layer (`layerBytes`), supports it; the cloud
939
- * engine manages layers server-side and omits it. Rejects if the document was
940
- * opened without a layer.
1137
+ * `OpenInputLayerBytes` with `{ kind: 'artifact', bytes }`. Optional: the
1138
+ * local engine supports it for every session it opens as a layer (the
1139
+ * default `sessionKind`, and any `layerBytes` open); the cloud engine
1140
+ * manages layers server-side and omits it. Rejects on a session opened
1141
+ * with `sessionKind: 'plain'`.
941
1142
  */
942
1143
  downloadLayer?(): AbortablePromise<Uint8Array>;
943
1144
  close(): AbortablePromise<void>;
@@ -985,6 +1186,13 @@ interface FontService {
985
1186
  clearFallbacks(): AbortablePromise<void>;
986
1187
  /** Unregister every font and reset the fallback chain. */
987
1188
  clear(): AbortablePromise<void>;
1189
+ /**
1190
+ * Assert that the application holds a licence permitting editing with a
1191
+ * preview-and-print font, so FreeText authoring and missing-glyph fallback
1192
+ * may use it. Resolves to the updated handle (`editingAuthorized: true`).
1193
+ * Rejects with `InvalidArg` for an unregistered key.
1194
+ */
1195
+ authorizeEditing(font: FontHandle | FontKey): AbortablePromise<FontHandle>;
988
1196
  /** Currently registered fonts, in registration order. */
989
1197
  list(): readonly FontHandle[];
990
1198
  }
@@ -1052,5 +1260,5 @@ interface Engine {
1052
1260
  */
1053
1261
  type EngineFactory = () => Engine;
1054
1262
  //#endregion
1055
- export { DocumentPagesService as A, CONTINUOUS_RENDER_POLICY as C, snapFullPageViewport as D, snapAppearanceScale as E, WeakAnnotationEditSession as F, DocumentActionsService as I, FormRepairOptions as M, DocumentAttachmentsService as N, snapTileScale as O, DocumentAnnotationsService as P, DocumentSearchService as S, EngineRenderPolicy as T, PageTextService as _, DocumentHandle as a, PageAnnotationsService as b, DocumentEventInit as c, PageHandle as d, PieceInfoService as f, PieceInfoSnapshot as g, PieceInfoPatchValue as h, DocumentCapabilities as i, DocumentFormsService as j, DocumentRedactionService as k, DocumentEventType as l, PieceInfoPatch as m, EngineFactory as n, DocumentEventStream as o, PieceInfoEntry as p, FontService as r, DocumentEvent as s, Engine as t, EventOrigin as u, PageRenderService as v, DocumentRenderService as w, MetadataService as x, PageGeometryService as y };
1056
- //# sourceMappingURL=Engine-CWnAKccD.d.ts.map
1263
+ export { DocumentRedactionService as A, DocumentSearchService as C, snapAppearanceScale as D, EngineRenderPolicy as E, FontEmbeddingPolicy as F, DocumentAttachmentsService as I, DocumentAnnotationsService as L, DocumentFormsService as M, FormRepairOptions as N, snapFullPageViewport as O, DocumentFontSettings as P, WeakAnnotationEditSession as R, DocumentSignaturesService as S, DocumentRenderService as T, PageTextService as _, DocumentHandle as a, PageAnnotationsService as b, DocumentEventInit as c, PageHandle as d, PieceInfoService as f, PieceInfoSnapshot as g, PieceInfoPatchValue as h, DocumentCapabilities as i, DocumentPagesService as j, snapTileScale as k, DocumentEventType as l, PieceInfoPatch as m, EngineFactory as n, DocumentEventStream as o, PieceInfoEntry as p, FontService as r, DocumentEvent as s, Engine as t, EventOrigin as u, PageRenderService as v, CONTINUOUS_RENDER_POLICY as w, MetadataService as x, PageGeometryService as y, DocumentActionsService as z };
1264
+ //# sourceMappingURL=Engine-DxtpCYcP.d.ts.map
@@ -70,7 +70,42 @@ const EngineErrorCode = {
70
70
  * at all) and `NotFound` (a well-formed page/annotation/etc. simply
71
71
  * doesn't exist).
72
72
  */
73
- MalformedPdf: "MalformedPdf"
73
+ MalformedPdf: "MalformedPdf",
74
+ /**
75
+ * A signing candidate is parked on this session: every mutation is
76
+ * refused until `signatures.complete` or `signatures.abort`.
77
+ */
78
+ SigningPending: "SigningPending",
79
+ /** The candidate's TTL elapsed; prepare again. */
80
+ SigningExpired: "SigningExpired",
81
+ /**
82
+ * `complete` presented a version the candidate was not built on (the
83
+ * document or the layer moved since `prepare`). The CMS cannot be
84
+ * reused: it signs a digest of bytes that will never be the head.
85
+ * Prepare again on the current version.
86
+ */
87
+ SigningVersionMismatch: "SigningVersionMismatch",
88
+ /**
89
+ * The engine refused to author the signature: the field is signed,
90
+ * read-only, or locked by an earlier signature; a certification is
91
+ * requested after a signature exists; the seed value requires what the
92
+ * engine does not implement; the request is inconsistent. The message
93
+ * names the reason.
94
+ */
95
+ SignatureRefused: "SignatureRefused",
96
+ /**
97
+ * A signature already in the document forbids this change (a
98
+ * certification's permission, a FieldMDP or `/Lock`, or the approval
99
+ * baseline). The message names the signature and the restriction. The
100
+ * engine option `signedDocumentPolicy: 'permit'` disables the guard.
101
+ */
102
+ ProtectedDocument: "ProtectedDocument",
103
+ /**
104
+ * The layer is built on a base version that is no longer the
105
+ * document's head (another signing completed); it can be read and
106
+ * edited but not signed from.
107
+ */
108
+ StaleBase: "StaleBase"
74
109
  };
75
110
  //#endregion
76
111
  //#region src/annotation/subtype.ts
@@ -411,4 +446,4 @@ Object.defineProperty(exports, "subtypeFromCode", {
411
446
  }
412
447
  });
413
448
 
414
- //# sourceMappingURL=PageInsertBlankInput-CzShuSJI.cjs.map
449
+ //# sourceMappingURL=PageInsertBlankInput-CqXBmFhN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageInsertBlankInput-CqXBmFhN.cjs","names":[],"sources":["../src/errors/EngineErrorCode.ts","../src/annotation/subtype.ts","../src/text/charmap.ts","../src/mutation/PageInsertBlankInput.ts"],"sourcesContent":["export const EngineErrorCode = {\n Unknown: 'Unknown',\n InvalidArg: 'InvalidArg',\n DocNotOpen: 'DocNotOpen',\n DocOpenFailed: 'DocOpenFailed',\n DocPasswordRequired: 'DocPasswordRequired',\n DocPasswordIncorrect: 'DocPasswordIncorrect',\n /**\n * A share-token exchange (`open({ kind: 'share' })`) needs the grant's\n * passphrase: none was supplied, or the supplied one was rejected —\n * indistinguishable by design, mirroring the server. Prompt and retry\n * with `sharePassword`. Distinct from `DocPasswordRequired`, which is\n * about the PDF's own encryption password.\n */\n SharePasswordRequired: 'SharePasswordRequired',\n Aborted: 'Aborted',\n Network: 'Network',\n Unauthenticated: 'Unauthenticated',\n Forbidden: 'Forbidden',\n NotFound: 'NotFound',\n WireFormat: 'WireFormat',\n RuntimeUnavailable: 'RuntimeUnavailable',\n /**\n * Annotation/page reference could not be resolved. Surfaced when:\n * - `AnnotationRef.kind === 'objectNumber'` but the document has no\n * annotation with that indirect object number on the addressed page\n * - `AnnotationRef.kind === 'nm'` but no annotation on the page has\n * that `/NM`\n * - `AnnotationRef.kind === 'index'` but the supplied `RevisionToken`\n * does not match the current per-page generation\n * - `AnnotationRef.kind === 'index'` but `index` is out of range for\n * the page's current annotation count\n */\n InvalidReference: 'InvalidReference',\n /**\n * A weak-annotation structural edit (delete/move) was attempted while the\n * caller does not have sole active edit presence for that page. This is a\n * collaboration guard for index-addressed annotations; stable pages and\n * non-shifting annotation updates do not use it.\n */\n WeakAnnotationSessionConflict: 'WeakAnnotationSessionConflict',\n /**\n * A layer write lost its optimistic-concurrency check: the layer's\n * durable version advanced (another writer — typically another server\n * replica — committed) between the operation's prepare and its commit,\n * and the server exhausted its rebase-and-retry budget. Retryable: the\n * operation was NOT applied; re-issue it against the new state.\n */\n LayerVersionConflict: 'LayerVersionConflict',\n /**\n * The requested operation is typed but not yet wired in this engine\n * version. The wire shape stays valid; clients can detect this with\n * `EngineError.is(err, EngineErrorCode.NotImplemented)` and degrade\n * gracefully.\n */\n NotImplemented: 'NotImplemented',\n /**\n * The PDF was loadable enough for PDFium to parse, but violates a\n * structural invariant the engine relies on. Surfaced for the rare\n * spec-violating PDF that ships a direct (non-indirect) page\n * dictionary in the /Pages tree — see `EPDFPage_GetObjectNumber`\n * returning `0`. PDFium creation paths (`FPDFPage_New`,\n * `EPDFPage_CreateAnnot`) always produce indirect objects, so this\n * error code is intentionally not used for engine-produced state;\n * it specifically signals \"this input file is broken in a way we\n * cannot work around without compromising stable identity\".\n *\n * Distinct from `DocOpenFailed` (PDFium refused to load the bytes\n * at all) and `NotFound` (a well-formed page/annotation/etc. simply\n * doesn't exist).\n */\n MalformedPdf: 'MalformedPdf',\n /**\n * A signing candidate is parked on this session: every mutation is\n * refused until `signatures.complete` or `signatures.abort`.\n */\n SigningPending: 'SigningPending',\n /** The candidate's TTL elapsed; prepare again. */\n SigningExpired: 'SigningExpired',\n /**\n * `complete` presented a version the candidate was not built on (the\n * document or the layer moved since `prepare`). The CMS cannot be\n * reused: it signs a digest of bytes that will never be the head.\n * Prepare again on the current version.\n */\n SigningVersionMismatch: 'SigningVersionMismatch',\n /**\n * The engine refused to author the signature: the field is signed,\n * read-only, or locked by an earlier signature; a certification is\n * requested after a signature exists; the seed value requires what the\n * engine does not implement; the request is inconsistent. The message\n * names the reason.\n */\n SignatureRefused: 'SignatureRefused',\n /**\n * A signature already in the document forbids this change (a\n * certification's permission, a FieldMDP or `/Lock`, or the approval\n * baseline). The message names the signature and the restriction. The\n * engine option `signedDocumentPolicy: 'permit'` disables the guard.\n */\n ProtectedDocument: 'ProtectedDocument',\n /**\n * The layer is built on a base version that is no longer the\n * document's head (another signing completed); it can be read and\n * edited but not signed from.\n */\n StaleBase: 'StaleBase',\n} as const;\n\nexport type EngineErrorCode = (typeof EngineErrorCode)[keyof typeof EngineErrorCode];\n","/**\n * Closed-world string catalog of annotation subtypes the v3 wire format knows\n * about. The TypeScript discriminated union `AnnotationDTO` is keyed on this\n * literal type. PDFium's integer subtype code is mapped onto these strings\n * by the reader registry.\n *\n * `unsupported` is the forward-compat fallback for any PDF subtype that\n * the engine has not implemented a reader for yet. Unknown subtypes never\n * silently disappear from the wire — they round-trip as\n * `{ subtype: 'unsupported', rawSubtypeCode }`.\n */\nexport const ANNOTATION_SUBTYPES = [\n 'text',\n 'link',\n 'free-text',\n 'line',\n 'square',\n 'circle',\n 'polygon',\n 'polyline',\n 'highlight',\n 'underline',\n 'squiggly',\n 'strikeout',\n 'stamp',\n 'caret',\n 'ink',\n 'popup',\n 'file-attachment',\n 'redact',\n 'widget',\n 'unsupported',\n] as const;\n\nexport type AnnotationSubtype = (typeof ANNOTATION_SUBTYPES)[number];\n\n/**\n * PDFium subtype codes from `FPDFAnnot_GetSubtype`. Mirrors\n * `FPDF_ANNOTATION_SUBTYPE` in `public/fpdf_annot.h`. We keep this here\n * (rather than importing from @embedpdf/engine-runtime) so the engine-core\n * package has zero PDFium dependency.\n */\nexport const PdfAnnotationSubtypeCode = {\n UNKNOWN: 0,\n TEXT: 1,\n LINK: 2,\n FREETEXT: 3,\n LINE: 4,\n SQUARE: 5,\n CIRCLE: 6,\n POLYGON: 7,\n POLYLINE: 8,\n HIGHLIGHT: 9,\n UNDERLINE: 10,\n SQUIGGLY: 11,\n STRIKEOUT: 12,\n STAMP: 13,\n CARET: 14,\n INK: 15,\n POPUP: 16,\n FILEATTACHMENT: 17,\n SOUND: 18,\n MOVIE: 19,\n WIDGET: 20,\n SCREEN: 21,\n PRINTERMARK: 22,\n TRAPNET: 23,\n WATERMARK: 24,\n THREED: 25,\n RICHMEDIA: 26,\n XFAWIDGET: 27,\n REDACT: 28,\n} as const;\n\nexport type PdfAnnotationSubtypeCode =\n (typeof PdfAnnotationSubtypeCode)[keyof typeof PdfAnnotationSubtypeCode];\n\n/**\n * Map from PDFium codes to wire-stable string subtypes. Codes the engine\n * recognises as a v3 subtype (or maps to 'unsupported' if not yet wired).\n */\nexport const PDF_CODE_TO_SUBTYPE: Readonly<Record<number, AnnotationSubtype>> = Object.freeze({\n [PdfAnnotationSubtypeCode.TEXT]: 'text',\n [PdfAnnotationSubtypeCode.LINK]: 'link',\n [PdfAnnotationSubtypeCode.FREETEXT]: 'free-text',\n [PdfAnnotationSubtypeCode.LINE]: 'line',\n [PdfAnnotationSubtypeCode.SQUARE]: 'square',\n [PdfAnnotationSubtypeCode.CIRCLE]: 'circle',\n [PdfAnnotationSubtypeCode.POLYGON]: 'polygon',\n [PdfAnnotationSubtypeCode.POLYLINE]: 'polyline',\n [PdfAnnotationSubtypeCode.HIGHLIGHT]: 'highlight',\n [PdfAnnotationSubtypeCode.UNDERLINE]: 'underline',\n [PdfAnnotationSubtypeCode.SQUIGGLY]: 'squiggly',\n [PdfAnnotationSubtypeCode.STRIKEOUT]: 'strikeout',\n [PdfAnnotationSubtypeCode.STAMP]: 'stamp',\n [PdfAnnotationSubtypeCode.CARET]: 'caret',\n [PdfAnnotationSubtypeCode.INK]: 'ink',\n [PdfAnnotationSubtypeCode.POPUP]: 'popup',\n [PdfAnnotationSubtypeCode.FILEATTACHMENT]: 'file-attachment',\n [PdfAnnotationSubtypeCode.WIDGET]: 'widget',\n [PdfAnnotationSubtypeCode.REDACT]: 'redact',\n});\n\n/**\n * Reverse map for write paths that need to turn a wire subtype back into the\n * PDFium integer code without importing the zod-backed annotation registry.\n */\nexport const PDF_SUBTYPE_TO_CODE: Readonly<Record<AnnotationSubtype, PdfAnnotationSubtypeCode>> =\n Object.freeze({\n text: PdfAnnotationSubtypeCode.TEXT,\n link: PdfAnnotationSubtypeCode.LINK,\n 'free-text': PdfAnnotationSubtypeCode.FREETEXT,\n line: PdfAnnotationSubtypeCode.LINE,\n square: PdfAnnotationSubtypeCode.SQUARE,\n circle: PdfAnnotationSubtypeCode.CIRCLE,\n polygon: PdfAnnotationSubtypeCode.POLYGON,\n polyline: PdfAnnotationSubtypeCode.POLYLINE,\n highlight: PdfAnnotationSubtypeCode.HIGHLIGHT,\n underline: PdfAnnotationSubtypeCode.UNDERLINE,\n squiggly: PdfAnnotationSubtypeCode.SQUIGGLY,\n strikeout: PdfAnnotationSubtypeCode.STRIKEOUT,\n stamp: PdfAnnotationSubtypeCode.STAMP,\n caret: PdfAnnotationSubtypeCode.CARET,\n ink: PdfAnnotationSubtypeCode.INK,\n popup: PdfAnnotationSubtypeCode.POPUP,\n 'file-attachment': PdfAnnotationSubtypeCode.FILEATTACHMENT,\n redact: PdfAnnotationSubtypeCode.REDACT,\n widget: PdfAnnotationSubtypeCode.WIDGET,\n unsupported: PdfAnnotationSubtypeCode.UNKNOWN,\n });\n\nexport function subtypeFromCode(code: number): AnnotationSubtype {\n return PDF_CODE_TO_SUBTYPE[code] ?? 'unsupported';\n}\n","import type { PageTextSnapshot } from '../dto/PageTextSnapshot';\n\n/**\n * Character↔text index translation — the ONE place the two text index\n * spaces of a page meet.\n *\n * A page has two index spaces:\n *\n * - CHARACTER space: PDFium's internal character list, the space geometry\n * runs tile (`PageGeometryRun.charStart`), hit-testing addresses, and\n * selection ranges live in. Size: `PageTextSnapshot.charCount`.\n * - TEXT space: UTF-16 code-unit offsets into the extracted string\n * `PageTextSnapshot.text` — what search matching and slicing operate on.\n *\n * They diverge in exactly two ways, both encoded by `charMap` anchors:\n * - a non-printing character occupies a character slot but contributes\n * ZERO text units (PDFium's `char_indices_` skip list);\n * - a supplementary-plane character contributes TWO text units (a\n * surrogate pair) once extraction is UTF-16-faithful.\n *\n * `charMap` semantics (see {@link CharMapAnchor}): an implicit head anchor\n * `[0, 0]`; between anchors every character advances the text by exactly one\n * unit; an anchor sits at `i + 1` whenever character `i` contributed\n * something other than one unit. Absent/empty map = identity\n * (`charCount === text.length`). `charMapViolation` is the validator both\n * the wire schema and engine builders share — malformed maps are rejected,\n * never silently repaired.\n *\n * Boundary model: a \"character boundary\" `c` ∈ [0, charCount] sits BEFORE\n * character `c`; `boundaryTextOffset(s, charCount) === text.length` always.\n * All ranges are half-open in both spaces.\n */\n\n/**\n * One anchor of the character→text mapping: from character index `char`\n * onward (until the next anchor), boundary `c` maps to text offset\n * `text + (c - char)`.\n */\nexport type CharMapAnchor = readonly [char: number, text: number];\n\n/** Direction to resolve a text offset that falls on a zero-width plateau —\n * see {@link charBoundaryAtTextOffset}. */\nexport type CharBoundaryBias = 'start' | 'end';\n\nconst clampInt = (value: number, lo: number, hi: number): number =>\n Math.min(Math.max(Math.floor(value), lo), hi);\n\n/** The greatest anchor with `anchor.char <= boundary` (implicit head [0,0]). */\nfunction anchorAtOrBefore(map: ReadonlyArray<CharMapAnchor>, boundary: number): CharMapAnchor {\n let lo = 0;\n let hi = map.length - 1;\n let best: CharMapAnchor | null = null;\n while (lo <= hi) {\n const mid = (lo + hi) >> 1;\n if (map[mid][0] <= boundary) {\n best = map[mid];\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return best ?? [0, 0];\n}\n\n/**\n * Text offset of character boundary `boundary` (clamped into\n * [0, charCount]). For a dropped character this is the offset of the next\n * real text unit — exactly what half-open slicing needs at either end.\n */\nexport function boundaryTextOffset(snapshot: PageTextSnapshot, boundary: number): number {\n const b = clampInt(boundary, 0, snapshot.charCount);\n const map = snapshot.charMap;\n if (!map || map.length === 0) return Math.min(b, snapshot.text.length);\n const [char, text] = anchorAtOrBefore(map, b);\n return text + (b - char);\n}\n\n/**\n * The character boundary for a text offset, with explicit plateau bias:\n *\n * - `'start'`: the GREATEST boundary whose text offset is <= `offset` —\n * scans forward past zero-width characters, so a range start never\n * includes a dropped character that precedes its first real unit.\n * - `'end'`: the SMALLEST boundary whose text offset is >= `offset` —\n * stops before zero-width characters, so a range end never swallows a\n * dropped character that follows its last real unit.\n *\n * An offset inside a surrogate pair resolves outward under both biases\n * (start → the pair's character, end → past it), so a clipped astral\n * character is always covered whole. Prefer {@link charRangeForTextOffsets}\n * — the two biases exist to be used as a PAIR.\n */\nexport function charBoundaryAtTextOffset(\n snapshot: PageTextSnapshot,\n offset: number,\n bias: CharBoundaryBias,\n): number {\n const target = clampInt(offset, 0, snapshot.text.length);\n let lo = 0;\n let hi = snapshot.charCount;\n if (bias === 'start') {\n // Greatest boundary with B(boundary) <= target. B(0) = 0 <= target always.\n while (lo < hi) {\n const mid = lo + Math.ceil((hi - lo) / 2);\n if (boundaryTextOffset(snapshot, mid) <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n }\n // Smallest boundary with B(boundary) >= target. B(charCount) = text.length\n // >= target always (offset is clamped).\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (boundaryTextOffset(snapshot, mid) >= target) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n}\n\n/**\n * The character range whose text projection is exactly\n * `text.slice(startOffset, endOffset)` — THE inverse used by search-hit\n * conversion and select-by-text. Start is right-biased and end left-biased\n * (see {@link charBoundaryAtTextOffset}), so zero-width characters adjacent\n * to the range fall OUTSIDE it on both sides.\n *\n * A non-empty text range can never invert: `end > start` implies\n * `B(end) >= endOffset > startOffset >= B(start)`, which forces\n * `end > start` by monotonicity. Empty input (`endOffset <= startOffset`)\n * normalizes to the end-rule boundary of `startOffset` on both sides.\n */\nexport function charRangeForTextOffsets(\n snapshot: PageTextSnapshot,\n startOffset: number,\n endOffset: number,\n): { start: number; end: number } {\n if (endOffset <= startOffset) {\n const b = charBoundaryAtTextOffset(snapshot, startOffset, 'end');\n return { start: b, end: b };\n }\n return {\n start: charBoundaryAtTextOffset(snapshot, startOffset, 'start'),\n end: charBoundaryAtTextOffset(snapshot, endOffset, 'end'),\n };\n}\n\n/**\n * THE copy primitive: the text projection of the half-open character range\n * `[charStart, charEnd)`. Dropped characters inside the range contribute\n * nothing; a supplementary character contributes its full surrogate pair.\n * `sliceTextByChars(s, 0, s.charCount) === s.text` always.\n */\nexport function sliceTextByChars(\n snapshot: PageTextSnapshot,\n charStart: number,\n charEnd: number,\n): string {\n if (charEnd <= charStart) return '';\n return snapshot.text.slice(\n boundaryTextOffset(snapshot, charStart),\n boundaryTextOffset(snapshot, charEnd),\n );\n}\n\n/**\n * First invariant violation of a snapshot's char map, or null when valid.\n * Shared by the wire schema (rejects malformed bodies) and engine builders\n * (dev-mode assertion). The invariants:\n *\n * 1. absent/empty map ⇒ identity, so `charCount === text.length`;\n * 2. anchor chars strictly increasing, each in [1, charCount];\n * 3. step rule: against the previous anchor (implicit head [0, 0]), the\n * character just before each anchor contributed exactly 0 units\n * (dropped) or 2 (surrogate pair):\n * `t₂ - (t₁ + (c₂ - c₁ - 1)) ∈ {0, 2}`;\n * 4. tail: `lastText + (charCount - lastChar) === text.length`\n * (equivalently `boundaryTextOffset(s, charCount) === text.length`).\n */\nexport function charMapViolation(\n charCount: number,\n textLength: number,\n charMap: ReadonlyArray<CharMapAnchor> | undefined,\n): string | null {\n if (!charMap || charMap.length === 0) {\n return charCount === textLength\n ? null\n : `identity map requires charCount (${charCount}) === text length (${textLength})`;\n }\n let prevChar = 0;\n let prevText = 0;\n for (let i = 0; i < charMap.length; i++) {\n const [char, text] = charMap[i];\n if (!Number.isInteger(char) || !Number.isInteger(text)) {\n return `anchor ${i} is not integer-valued`;\n }\n if (char <= prevChar) {\n return `anchor ${i} char ${char} not strictly after ${prevChar}`;\n }\n if (char > charCount) {\n return `anchor ${i} char ${char} outside [1, ${charCount}]`;\n }\n const step = text - (prevText + (char - prevChar - 1));\n if (step !== 0 && step !== 2) {\n return `anchor ${i} implies a character contributing ${step} text units (expected 0 or 2)`;\n }\n prevChar = char;\n prevText = text;\n }\n const tail = prevText + (charCount - prevChar);\n if (tail !== textLength) {\n return `map projects charCount to ${tail}, but text length is ${textLength}`;\n }\n return null;\n}\n","import type { PdfSize } from '../geometry/primitives';\n\n/**\n * Upper bound on `count` for a single `pages.insertBlank()` call. A CONTRACT\n * cap, enforced identically by every engine (the cloud input schema carries\n * the same number), so a UI never discovers a lower server limit by surprise.\n * Larger batches are a loop at the call site.\n */\nexport const PAGE_INSERT_BLANK_MAX_COUNT = 100;\n\n/**\n * Spec for `pages.insertBlank()`: create `count` blank pages of `size`.\n *\n * `size` is explicit and in PDF points, un-rotated — the same convention as\n * `PageLayout.size`. The \"match my neighbour\" default developers expect is a\n * registry read, so it lives in the page-edit capability, not here: the\n * engine wire never guesses (the `rotateBy` law).\n */\nexport interface PageInsertBlankSpec {\n /** Page size in PDF points. Both dimensions must be finite and > 0. */\n size: PdfSize;\n /** Pages to create, in `[1, PAGE_INSERT_BLANK_MAX_COUNT]`. Default 1. */\n count?: number;\n}\n"],"mappings":";AAAA,MAAa,kBAAkB;CAC7B,SAAS;CACT,YAAY;CACZ,YAAY;CACZ,eAAe;CACf,qBAAqB;CACrB,sBAAsB;;;;;;;;CAQtB,uBAAuB;CACvB,SAAS;CACT,SAAS;CACT,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,oBAAoB;;;;;;;;;;;;CAYpB,kBAAkB;;;;;;;CAOlB,+BAA+B;;;;;;;;CAQ/B,sBAAsB;;;;;;;CAOtB,gBAAgB;;;;;;;;;;;;;;;;CAgBhB,cAAc;;;;;CAKd,gBAAgB;;CAEhB,gBAAgB;;;;;;;CAOhB,wBAAwB;;;;;;;;CAQxB,kBAAkB;;;;;;;CAOlB,mBAAmB;;;;;;CAMnB,WAAW;AACb;;;;;;;;;;;;;;AChGA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAUA,MAAa,2BAA2B;CACtC,SAAS;CACT,MAAM;CACN,MAAM;CACN,UAAU;CACV,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,UAAU;CACV,WAAW;CACX,WAAW;CACX,UAAU;CACV,WAAW;CACX,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,gBAAgB;CAChB,OAAO;CACP,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,SAAS;CACT,WAAW;CACX,QAAQ;CACR,WAAW;CACX,WAAW;CACX,QAAQ;AACV;;;;;AASA,MAAa,sBAAmE,OAAO,OAAO;EAC3F,yBAAyB,OAAO;EAChC,yBAAyB,OAAO;EAChC,yBAAyB,WAAW;EACpC,yBAAyB,OAAO;EAChC,yBAAyB,SAAS;EAClC,yBAAyB,SAAS;EAClC,yBAAyB,UAAU;EACnC,yBAAyB,WAAW;EACpC,yBAAyB,YAAY;EACrC,yBAAyB,YAAY;EACrC,yBAAyB,WAAW;EACpC,yBAAyB,YAAY;EACrC,yBAAyB,QAAQ;EACjC,yBAAyB,QAAQ;EACjC,yBAAyB,MAAM;EAC/B,yBAAyB,QAAQ;EACjC,yBAAyB,iBAAiB;EAC1C,yBAAyB,SAAS;EAClC,yBAAyB,SAAS;AACrC,CAAC;;;;;AAMD,MAAa,sBACX,OAAO,OAAO;CACZ,MAAM,yBAAyB;CAC/B,MAAM,yBAAyB;CAC/B,aAAa,yBAAyB;CACtC,MAAM,yBAAyB;CAC/B,QAAQ,yBAAyB;CACjC,QAAQ,yBAAyB;CACjC,SAAS,yBAAyB;CAClC,UAAU,yBAAyB;CACnC,WAAW,yBAAyB;CACpC,WAAW,yBAAyB;CACpC,UAAU,yBAAyB;CACnC,WAAW,yBAAyB;CACpC,OAAO,yBAAyB;CAChC,OAAO,yBAAyB;CAChC,KAAK,yBAAyB;CAC9B,OAAO,yBAAyB;CAChC,mBAAmB,yBAAyB;CAC5C,QAAQ,yBAAyB;CACjC,QAAQ,yBAAyB;CACjC,aAAa,yBAAyB;AACxC,CAAC;AAEH,SAAgB,gBAAgB,MAAiC;CAC/D,OAAO,oBAAoB,SAAS;AACtC;;;ACzFA,MAAM,YAAY,OAAe,IAAY,OAC3C,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE;;AAG9C,SAAS,iBAAiB,KAAmC,UAAiC;CAC5F,IAAI,KAAK;CACT,IAAI,KAAK,IAAI,SAAS;CACtB,IAAI,OAA6B;CACjC,OAAO,MAAM,IAAI;EACf,MAAM,MAAO,KAAK,MAAO;EACzB,IAAI,IAAI,IAAI,CAAC,MAAM,UAAU;GAC3B,OAAO,IAAI;GACX,KAAK,MAAM;EACb,OACE,KAAK,MAAM;CAEf;CACA,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB;;;;;;AAOA,SAAgB,mBAAmB,UAA4B,UAA0B;CACvF,MAAM,IAAI,SAAS,UAAU,GAAG,SAAS,SAAS;CAClD,MAAM,MAAM,SAAS;CACrB,IAAI,CAAC,OAAO,IAAI,WAAW,GAAG,OAAO,KAAK,IAAI,GAAG,SAAS,KAAK,MAAM;CACrE,MAAM,CAAC,MAAM,QAAQ,iBAAiB,KAAK,CAAC;CAC5C,OAAO,QAAQ,IAAI;AACrB;;;;;;;;;;;;;;;;AAiBA,SAAgB,yBACd,UACA,QACA,MACQ;CACR,MAAM,SAAS,SAAS,QAAQ,GAAG,SAAS,KAAK,MAAM;CACvD,IAAI,KAAK;CACT,IAAI,KAAK,SAAS;CAClB,IAAI,SAAS,SAAS;EAEpB,OAAO,KAAK,IAAI;GACd,MAAM,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,CAAC;GACxC,IAAI,mBAAmB,UAAU,GAAG,KAAK,QAAQ,KAAK;QACjD,KAAK,MAAM;EAClB;EACA,OAAO;CACT;CAGA,OAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,MAAO;EACzB,IAAI,mBAAmB,UAAU,GAAG,KAAK,QAAQ,KAAK;OACjD,KAAK,MAAM;CAClB;CACA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,wBACd,UACA,aACA,WACgC;CAChC,IAAI,aAAa,aAAa;EAC5B,MAAM,IAAI,yBAAyB,UAAU,aAAa,KAAK;EAC/D,OAAO;GAAE,OAAO;GAAG,KAAK;EAAE;CAC5B;CACA,OAAO;EACL,OAAO,yBAAyB,UAAU,aAAa,OAAO;EAC9D,KAAK,yBAAyB,UAAU,WAAW,KAAK;CAC1D;AACF;;;;;;;AAQA,SAAgB,iBACd,UACA,WACA,SACQ;CACR,IAAI,WAAW,WAAW,OAAO;CACjC,OAAO,SAAS,KAAK,MACnB,mBAAmB,UAAU,SAAS,GACtC,mBAAmB,UAAU,OAAO,CACtC;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,iBACd,WACA,YACA,SACe;CACf,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO,cAAc,aACjB,OACA,oCAAoC,UAAU,qBAAqB,WAAW;CAEpF,IAAI,WAAW;CACf,IAAI,WAAW;CACf,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,CAAC,MAAM,QAAQ,QAAQ;EAC7B,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,CAAC,OAAO,UAAU,IAAI,GACnD,OAAO,UAAU,EAAE;EAErB,IAAI,QAAQ,UACV,OAAO,UAAU,EAAE,QAAQ,KAAK,sBAAsB;EAExD,IAAI,OAAO,WACT,OAAO,UAAU,EAAE,QAAQ,KAAK,eAAe,UAAU;EAE3D,MAAM,OAAO,QAAQ,YAAY,OAAO,WAAW;EACnD,IAAI,SAAS,KAAK,SAAS,GACzB,OAAO,UAAU,EAAE,oCAAoC,KAAK;EAE9D,WAAW;EACX,WAAW;CACb;CACA,MAAM,OAAO,YAAY,YAAY;CACrC,IAAI,SAAS,YACX,OAAO,6BAA6B,KAAK,uBAAuB;CAElE,OAAO;AACT;;;;;;;;;AC7MA,MAAa,8BAA8B"}
@@ -70,7 +70,42 @@ const EngineErrorCode = {
70
70
  * at all) and `NotFound` (a well-formed page/annotation/etc. simply
71
71
  * doesn't exist).
72
72
  */
73
- MalformedPdf: "MalformedPdf"
73
+ MalformedPdf: "MalformedPdf",
74
+ /**
75
+ * A signing candidate is parked on this session: every mutation is
76
+ * refused until `signatures.complete` or `signatures.abort`.
77
+ */
78
+ SigningPending: "SigningPending",
79
+ /** The candidate's TTL elapsed; prepare again. */
80
+ SigningExpired: "SigningExpired",
81
+ /**
82
+ * `complete` presented a version the candidate was not built on (the
83
+ * document or the layer moved since `prepare`). The CMS cannot be
84
+ * reused: it signs a digest of bytes that will never be the head.
85
+ * Prepare again on the current version.
86
+ */
87
+ SigningVersionMismatch: "SigningVersionMismatch",
88
+ /**
89
+ * The engine refused to author the signature: the field is signed,
90
+ * read-only, or locked by an earlier signature; a certification is
91
+ * requested after a signature exists; the seed value requires what the
92
+ * engine does not implement; the request is inconsistent. The message
93
+ * names the reason.
94
+ */
95
+ SignatureRefused: "SignatureRefused",
96
+ /**
97
+ * A signature already in the document forbids this change (a
98
+ * certification's permission, a FieldMDP or `/Lock`, or the approval
99
+ * baseline). The message names the signature and the restriction. The
100
+ * engine option `signedDocumentPolicy: 'permit'` disables the guard.
101
+ */
102
+ ProtectedDocument: "ProtectedDocument",
103
+ /**
104
+ * The layer is built on a base version that is no longer the
105
+ * document's head (another signing completed); it can be read and
106
+ * edited but not signed from.
107
+ */
108
+ StaleBase: "StaleBase"
74
109
  };
75
110
  //#endregion
76
111
  //#region src/annotation/subtype.ts
@@ -340,4 +375,4 @@ const PAGE_INSERT_BLANK_MAX_COUNT = 100;
340
375
  //#endregion
341
376
  export { charRangeForTextOffsets as a, PDF_CODE_TO_SUBTYPE as c, subtypeFromCode as d, EngineErrorCode as f, charMapViolation as i, PDF_SUBTYPE_TO_CODE as l, boundaryTextOffset as n, sliceTextByChars as o, charBoundaryAtTextOffset as r, ANNOTATION_SUBTYPES as s, PAGE_INSERT_BLANK_MAX_COUNT as t, PdfAnnotationSubtypeCode as u };
342
377
 
343
- //# sourceMappingURL=PageInsertBlankInput-WKY0tr6S.js.map
378
+ //# sourceMappingURL=PageInsertBlankInput-cHq9-i-z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageInsertBlankInput-cHq9-i-z.js","names":[],"sources":["../src/errors/EngineErrorCode.ts","../src/annotation/subtype.ts","../src/text/charmap.ts","../src/mutation/PageInsertBlankInput.ts"],"sourcesContent":["export const EngineErrorCode = {\n Unknown: 'Unknown',\n InvalidArg: 'InvalidArg',\n DocNotOpen: 'DocNotOpen',\n DocOpenFailed: 'DocOpenFailed',\n DocPasswordRequired: 'DocPasswordRequired',\n DocPasswordIncorrect: 'DocPasswordIncorrect',\n /**\n * A share-token exchange (`open({ kind: 'share' })`) needs the grant's\n * passphrase: none was supplied, or the supplied one was rejected —\n * indistinguishable by design, mirroring the server. Prompt and retry\n * with `sharePassword`. Distinct from `DocPasswordRequired`, which is\n * about the PDF's own encryption password.\n */\n SharePasswordRequired: 'SharePasswordRequired',\n Aborted: 'Aborted',\n Network: 'Network',\n Unauthenticated: 'Unauthenticated',\n Forbidden: 'Forbidden',\n NotFound: 'NotFound',\n WireFormat: 'WireFormat',\n RuntimeUnavailable: 'RuntimeUnavailable',\n /**\n * Annotation/page reference could not be resolved. Surfaced when:\n * - `AnnotationRef.kind === 'objectNumber'` but the document has no\n * annotation with that indirect object number on the addressed page\n * - `AnnotationRef.kind === 'nm'` but no annotation on the page has\n * that `/NM`\n * - `AnnotationRef.kind === 'index'` but the supplied `RevisionToken`\n * does not match the current per-page generation\n * - `AnnotationRef.kind === 'index'` but `index` is out of range for\n * the page's current annotation count\n */\n InvalidReference: 'InvalidReference',\n /**\n * A weak-annotation structural edit (delete/move) was attempted while the\n * caller does not have sole active edit presence for that page. This is a\n * collaboration guard for index-addressed annotations; stable pages and\n * non-shifting annotation updates do not use it.\n */\n WeakAnnotationSessionConflict: 'WeakAnnotationSessionConflict',\n /**\n * A layer write lost its optimistic-concurrency check: the layer's\n * durable version advanced (another writer — typically another server\n * replica — committed) between the operation's prepare and its commit,\n * and the server exhausted its rebase-and-retry budget. Retryable: the\n * operation was NOT applied; re-issue it against the new state.\n */\n LayerVersionConflict: 'LayerVersionConflict',\n /**\n * The requested operation is typed but not yet wired in this engine\n * version. The wire shape stays valid; clients can detect this with\n * `EngineError.is(err, EngineErrorCode.NotImplemented)` and degrade\n * gracefully.\n */\n NotImplemented: 'NotImplemented',\n /**\n * The PDF was loadable enough for PDFium to parse, but violates a\n * structural invariant the engine relies on. Surfaced for the rare\n * spec-violating PDF that ships a direct (non-indirect) page\n * dictionary in the /Pages tree — see `EPDFPage_GetObjectNumber`\n * returning `0`. PDFium creation paths (`FPDFPage_New`,\n * `EPDFPage_CreateAnnot`) always produce indirect objects, so this\n * error code is intentionally not used for engine-produced state;\n * it specifically signals \"this input file is broken in a way we\n * cannot work around without compromising stable identity\".\n *\n * Distinct from `DocOpenFailed` (PDFium refused to load the bytes\n * at all) and `NotFound` (a well-formed page/annotation/etc. simply\n * doesn't exist).\n */\n MalformedPdf: 'MalformedPdf',\n /**\n * A signing candidate is parked on this session: every mutation is\n * refused until `signatures.complete` or `signatures.abort`.\n */\n SigningPending: 'SigningPending',\n /** The candidate's TTL elapsed; prepare again. */\n SigningExpired: 'SigningExpired',\n /**\n * `complete` presented a version the candidate was not built on (the\n * document or the layer moved since `prepare`). The CMS cannot be\n * reused: it signs a digest of bytes that will never be the head.\n * Prepare again on the current version.\n */\n SigningVersionMismatch: 'SigningVersionMismatch',\n /**\n * The engine refused to author the signature: the field is signed,\n * read-only, or locked by an earlier signature; a certification is\n * requested after a signature exists; the seed value requires what the\n * engine does not implement; the request is inconsistent. The message\n * names the reason.\n */\n SignatureRefused: 'SignatureRefused',\n /**\n * A signature already in the document forbids this change (a\n * certification's permission, a FieldMDP or `/Lock`, or the approval\n * baseline). The message names the signature and the restriction. The\n * engine option `signedDocumentPolicy: 'permit'` disables the guard.\n */\n ProtectedDocument: 'ProtectedDocument',\n /**\n * The layer is built on a base version that is no longer the\n * document's head (another signing completed); it can be read and\n * edited but not signed from.\n */\n StaleBase: 'StaleBase',\n} as const;\n\nexport type EngineErrorCode = (typeof EngineErrorCode)[keyof typeof EngineErrorCode];\n","/**\n * Closed-world string catalog of annotation subtypes the v3 wire format knows\n * about. The TypeScript discriminated union `AnnotationDTO` is keyed on this\n * literal type. PDFium's integer subtype code is mapped onto these strings\n * by the reader registry.\n *\n * `unsupported` is the forward-compat fallback for any PDF subtype that\n * the engine has not implemented a reader for yet. Unknown subtypes never\n * silently disappear from the wire — they round-trip as\n * `{ subtype: 'unsupported', rawSubtypeCode }`.\n */\nexport const ANNOTATION_SUBTYPES = [\n 'text',\n 'link',\n 'free-text',\n 'line',\n 'square',\n 'circle',\n 'polygon',\n 'polyline',\n 'highlight',\n 'underline',\n 'squiggly',\n 'strikeout',\n 'stamp',\n 'caret',\n 'ink',\n 'popup',\n 'file-attachment',\n 'redact',\n 'widget',\n 'unsupported',\n] as const;\n\nexport type AnnotationSubtype = (typeof ANNOTATION_SUBTYPES)[number];\n\n/**\n * PDFium subtype codes from `FPDFAnnot_GetSubtype`. Mirrors\n * `FPDF_ANNOTATION_SUBTYPE` in `public/fpdf_annot.h`. We keep this here\n * (rather than importing from @embedpdf/engine-runtime) so the engine-core\n * package has zero PDFium dependency.\n */\nexport const PdfAnnotationSubtypeCode = {\n UNKNOWN: 0,\n TEXT: 1,\n LINK: 2,\n FREETEXT: 3,\n LINE: 4,\n SQUARE: 5,\n CIRCLE: 6,\n POLYGON: 7,\n POLYLINE: 8,\n HIGHLIGHT: 9,\n UNDERLINE: 10,\n SQUIGGLY: 11,\n STRIKEOUT: 12,\n STAMP: 13,\n CARET: 14,\n INK: 15,\n POPUP: 16,\n FILEATTACHMENT: 17,\n SOUND: 18,\n MOVIE: 19,\n WIDGET: 20,\n SCREEN: 21,\n PRINTERMARK: 22,\n TRAPNET: 23,\n WATERMARK: 24,\n THREED: 25,\n RICHMEDIA: 26,\n XFAWIDGET: 27,\n REDACT: 28,\n} as const;\n\nexport type PdfAnnotationSubtypeCode =\n (typeof PdfAnnotationSubtypeCode)[keyof typeof PdfAnnotationSubtypeCode];\n\n/**\n * Map from PDFium codes to wire-stable string subtypes. Codes the engine\n * recognises as a v3 subtype (or maps to 'unsupported' if not yet wired).\n */\nexport const PDF_CODE_TO_SUBTYPE: Readonly<Record<number, AnnotationSubtype>> = Object.freeze({\n [PdfAnnotationSubtypeCode.TEXT]: 'text',\n [PdfAnnotationSubtypeCode.LINK]: 'link',\n [PdfAnnotationSubtypeCode.FREETEXT]: 'free-text',\n [PdfAnnotationSubtypeCode.LINE]: 'line',\n [PdfAnnotationSubtypeCode.SQUARE]: 'square',\n [PdfAnnotationSubtypeCode.CIRCLE]: 'circle',\n [PdfAnnotationSubtypeCode.POLYGON]: 'polygon',\n [PdfAnnotationSubtypeCode.POLYLINE]: 'polyline',\n [PdfAnnotationSubtypeCode.HIGHLIGHT]: 'highlight',\n [PdfAnnotationSubtypeCode.UNDERLINE]: 'underline',\n [PdfAnnotationSubtypeCode.SQUIGGLY]: 'squiggly',\n [PdfAnnotationSubtypeCode.STRIKEOUT]: 'strikeout',\n [PdfAnnotationSubtypeCode.STAMP]: 'stamp',\n [PdfAnnotationSubtypeCode.CARET]: 'caret',\n [PdfAnnotationSubtypeCode.INK]: 'ink',\n [PdfAnnotationSubtypeCode.POPUP]: 'popup',\n [PdfAnnotationSubtypeCode.FILEATTACHMENT]: 'file-attachment',\n [PdfAnnotationSubtypeCode.WIDGET]: 'widget',\n [PdfAnnotationSubtypeCode.REDACT]: 'redact',\n});\n\n/**\n * Reverse map for write paths that need to turn a wire subtype back into the\n * PDFium integer code without importing the zod-backed annotation registry.\n */\nexport const PDF_SUBTYPE_TO_CODE: Readonly<Record<AnnotationSubtype, PdfAnnotationSubtypeCode>> =\n Object.freeze({\n text: PdfAnnotationSubtypeCode.TEXT,\n link: PdfAnnotationSubtypeCode.LINK,\n 'free-text': PdfAnnotationSubtypeCode.FREETEXT,\n line: PdfAnnotationSubtypeCode.LINE,\n square: PdfAnnotationSubtypeCode.SQUARE,\n circle: PdfAnnotationSubtypeCode.CIRCLE,\n polygon: PdfAnnotationSubtypeCode.POLYGON,\n polyline: PdfAnnotationSubtypeCode.POLYLINE,\n highlight: PdfAnnotationSubtypeCode.HIGHLIGHT,\n underline: PdfAnnotationSubtypeCode.UNDERLINE,\n squiggly: PdfAnnotationSubtypeCode.SQUIGGLY,\n strikeout: PdfAnnotationSubtypeCode.STRIKEOUT,\n stamp: PdfAnnotationSubtypeCode.STAMP,\n caret: PdfAnnotationSubtypeCode.CARET,\n ink: PdfAnnotationSubtypeCode.INK,\n popup: PdfAnnotationSubtypeCode.POPUP,\n 'file-attachment': PdfAnnotationSubtypeCode.FILEATTACHMENT,\n redact: PdfAnnotationSubtypeCode.REDACT,\n widget: PdfAnnotationSubtypeCode.WIDGET,\n unsupported: PdfAnnotationSubtypeCode.UNKNOWN,\n });\n\nexport function subtypeFromCode(code: number): AnnotationSubtype {\n return PDF_CODE_TO_SUBTYPE[code] ?? 'unsupported';\n}\n","import type { PageTextSnapshot } from '../dto/PageTextSnapshot';\n\n/**\n * Character↔text index translation — the ONE place the two text index\n * spaces of a page meet.\n *\n * A page has two index spaces:\n *\n * - CHARACTER space: PDFium's internal character list, the space geometry\n * runs tile (`PageGeometryRun.charStart`), hit-testing addresses, and\n * selection ranges live in. Size: `PageTextSnapshot.charCount`.\n * - TEXT space: UTF-16 code-unit offsets into the extracted string\n * `PageTextSnapshot.text` — what search matching and slicing operate on.\n *\n * They diverge in exactly two ways, both encoded by `charMap` anchors:\n * - a non-printing character occupies a character slot but contributes\n * ZERO text units (PDFium's `char_indices_` skip list);\n * - a supplementary-plane character contributes TWO text units (a\n * surrogate pair) once extraction is UTF-16-faithful.\n *\n * `charMap` semantics (see {@link CharMapAnchor}): an implicit head anchor\n * `[0, 0]`; between anchors every character advances the text by exactly one\n * unit; an anchor sits at `i + 1` whenever character `i` contributed\n * something other than one unit. Absent/empty map = identity\n * (`charCount === text.length`). `charMapViolation` is the validator both\n * the wire schema and engine builders share — malformed maps are rejected,\n * never silently repaired.\n *\n * Boundary model: a \"character boundary\" `c` ∈ [0, charCount] sits BEFORE\n * character `c`; `boundaryTextOffset(s, charCount) === text.length` always.\n * All ranges are half-open in both spaces.\n */\n\n/**\n * One anchor of the character→text mapping: from character index `char`\n * onward (until the next anchor), boundary `c` maps to text offset\n * `text + (c - char)`.\n */\nexport type CharMapAnchor = readonly [char: number, text: number];\n\n/** Direction to resolve a text offset that falls on a zero-width plateau —\n * see {@link charBoundaryAtTextOffset}. */\nexport type CharBoundaryBias = 'start' | 'end';\n\nconst clampInt = (value: number, lo: number, hi: number): number =>\n Math.min(Math.max(Math.floor(value), lo), hi);\n\n/** The greatest anchor with `anchor.char <= boundary` (implicit head [0,0]). */\nfunction anchorAtOrBefore(map: ReadonlyArray<CharMapAnchor>, boundary: number): CharMapAnchor {\n let lo = 0;\n let hi = map.length - 1;\n let best: CharMapAnchor | null = null;\n while (lo <= hi) {\n const mid = (lo + hi) >> 1;\n if (map[mid][0] <= boundary) {\n best = map[mid];\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return best ?? [0, 0];\n}\n\n/**\n * Text offset of character boundary `boundary` (clamped into\n * [0, charCount]). For a dropped character this is the offset of the next\n * real text unit — exactly what half-open slicing needs at either end.\n */\nexport function boundaryTextOffset(snapshot: PageTextSnapshot, boundary: number): number {\n const b = clampInt(boundary, 0, snapshot.charCount);\n const map = snapshot.charMap;\n if (!map || map.length === 0) return Math.min(b, snapshot.text.length);\n const [char, text] = anchorAtOrBefore(map, b);\n return text + (b - char);\n}\n\n/**\n * The character boundary for a text offset, with explicit plateau bias:\n *\n * - `'start'`: the GREATEST boundary whose text offset is <= `offset` —\n * scans forward past zero-width characters, so a range start never\n * includes a dropped character that precedes its first real unit.\n * - `'end'`: the SMALLEST boundary whose text offset is >= `offset` —\n * stops before zero-width characters, so a range end never swallows a\n * dropped character that follows its last real unit.\n *\n * An offset inside a surrogate pair resolves outward under both biases\n * (start → the pair's character, end → past it), so a clipped astral\n * character is always covered whole. Prefer {@link charRangeForTextOffsets}\n * — the two biases exist to be used as a PAIR.\n */\nexport function charBoundaryAtTextOffset(\n snapshot: PageTextSnapshot,\n offset: number,\n bias: CharBoundaryBias,\n): number {\n const target = clampInt(offset, 0, snapshot.text.length);\n let lo = 0;\n let hi = snapshot.charCount;\n if (bias === 'start') {\n // Greatest boundary with B(boundary) <= target. B(0) = 0 <= target always.\n while (lo < hi) {\n const mid = lo + Math.ceil((hi - lo) / 2);\n if (boundaryTextOffset(snapshot, mid) <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n }\n // Smallest boundary with B(boundary) >= target. B(charCount) = text.length\n // >= target always (offset is clamped).\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (boundaryTextOffset(snapshot, mid) >= target) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n}\n\n/**\n * The character range whose text projection is exactly\n * `text.slice(startOffset, endOffset)` — THE inverse used by search-hit\n * conversion and select-by-text. Start is right-biased and end left-biased\n * (see {@link charBoundaryAtTextOffset}), so zero-width characters adjacent\n * to the range fall OUTSIDE it on both sides.\n *\n * A non-empty text range can never invert: `end > start` implies\n * `B(end) >= endOffset > startOffset >= B(start)`, which forces\n * `end > start` by monotonicity. Empty input (`endOffset <= startOffset`)\n * normalizes to the end-rule boundary of `startOffset` on both sides.\n */\nexport function charRangeForTextOffsets(\n snapshot: PageTextSnapshot,\n startOffset: number,\n endOffset: number,\n): { start: number; end: number } {\n if (endOffset <= startOffset) {\n const b = charBoundaryAtTextOffset(snapshot, startOffset, 'end');\n return { start: b, end: b };\n }\n return {\n start: charBoundaryAtTextOffset(snapshot, startOffset, 'start'),\n end: charBoundaryAtTextOffset(snapshot, endOffset, 'end'),\n };\n}\n\n/**\n * THE copy primitive: the text projection of the half-open character range\n * `[charStart, charEnd)`. Dropped characters inside the range contribute\n * nothing; a supplementary character contributes its full surrogate pair.\n * `sliceTextByChars(s, 0, s.charCount) === s.text` always.\n */\nexport function sliceTextByChars(\n snapshot: PageTextSnapshot,\n charStart: number,\n charEnd: number,\n): string {\n if (charEnd <= charStart) return '';\n return snapshot.text.slice(\n boundaryTextOffset(snapshot, charStart),\n boundaryTextOffset(snapshot, charEnd),\n );\n}\n\n/**\n * First invariant violation of a snapshot's char map, or null when valid.\n * Shared by the wire schema (rejects malformed bodies) and engine builders\n * (dev-mode assertion). The invariants:\n *\n * 1. absent/empty map ⇒ identity, so `charCount === text.length`;\n * 2. anchor chars strictly increasing, each in [1, charCount];\n * 3. step rule: against the previous anchor (implicit head [0, 0]), the\n * character just before each anchor contributed exactly 0 units\n * (dropped) or 2 (surrogate pair):\n * `t₂ - (t₁ + (c₂ - c₁ - 1)) ∈ {0, 2}`;\n * 4. tail: `lastText + (charCount - lastChar) === text.length`\n * (equivalently `boundaryTextOffset(s, charCount) === text.length`).\n */\nexport function charMapViolation(\n charCount: number,\n textLength: number,\n charMap: ReadonlyArray<CharMapAnchor> | undefined,\n): string | null {\n if (!charMap || charMap.length === 0) {\n return charCount === textLength\n ? null\n : `identity map requires charCount (${charCount}) === text length (${textLength})`;\n }\n let prevChar = 0;\n let prevText = 0;\n for (let i = 0; i < charMap.length; i++) {\n const [char, text] = charMap[i];\n if (!Number.isInteger(char) || !Number.isInteger(text)) {\n return `anchor ${i} is not integer-valued`;\n }\n if (char <= prevChar) {\n return `anchor ${i} char ${char} not strictly after ${prevChar}`;\n }\n if (char > charCount) {\n return `anchor ${i} char ${char} outside [1, ${charCount}]`;\n }\n const step = text - (prevText + (char - prevChar - 1));\n if (step !== 0 && step !== 2) {\n return `anchor ${i} implies a character contributing ${step} text units (expected 0 or 2)`;\n }\n prevChar = char;\n prevText = text;\n }\n const tail = prevText + (charCount - prevChar);\n if (tail !== textLength) {\n return `map projects charCount to ${tail}, but text length is ${textLength}`;\n }\n return null;\n}\n","import type { PdfSize } from '../geometry/primitives';\n\n/**\n * Upper bound on `count` for a single `pages.insertBlank()` call. A CONTRACT\n * cap, enforced identically by every engine (the cloud input schema carries\n * the same number), so a UI never discovers a lower server limit by surprise.\n * Larger batches are a loop at the call site.\n */\nexport const PAGE_INSERT_BLANK_MAX_COUNT = 100;\n\n/**\n * Spec for `pages.insertBlank()`: create `count` blank pages of `size`.\n *\n * `size` is explicit and in PDF points, un-rotated — the same convention as\n * `PageLayout.size`. The \"match my neighbour\" default developers expect is a\n * registry read, so it lives in the page-edit capability, not here: the\n * engine wire never guesses (the `rotateBy` law).\n */\nexport interface PageInsertBlankSpec {\n /** Page size in PDF points. Both dimensions must be finite and > 0. */\n size: PdfSize;\n /** Pages to create, in `[1, PAGE_INSERT_BLANK_MAX_COUNT]`. Default 1. */\n count?: number;\n}\n"],"mappings":";AAAA,MAAa,kBAAkB;CAC7B,SAAS;CACT,YAAY;CACZ,YAAY;CACZ,eAAe;CACf,qBAAqB;CACrB,sBAAsB;;;;;;;;CAQtB,uBAAuB;CACvB,SAAS;CACT,SAAS;CACT,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,oBAAoB;;;;;;;;;;;;CAYpB,kBAAkB;;;;;;;CAOlB,+BAA+B;;;;;;;;CAQ/B,sBAAsB;;;;;;;CAOtB,gBAAgB;;;;;;;;;;;;;;;;CAgBhB,cAAc;;;;;CAKd,gBAAgB;;CAEhB,gBAAgB;;;;;;;CAOhB,wBAAwB;;;;;;;;CAQxB,kBAAkB;;;;;;;CAOlB,mBAAmB;;;;;;CAMnB,WAAW;AACb;;;;;;;;;;;;;;AChGA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAUA,MAAa,2BAA2B;CACtC,SAAS;CACT,MAAM;CACN,MAAM;CACN,UAAU;CACV,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,UAAU;CACV,WAAW;CACX,WAAW;CACX,UAAU;CACV,WAAW;CACX,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,gBAAgB;CAChB,OAAO;CACP,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,SAAS;CACT,WAAW;CACX,QAAQ;CACR,WAAW;CACX,WAAW;CACX,QAAQ;AACV;;;;;AASA,MAAa,sBAAmE,OAAO,OAAO;EAC3F,yBAAyB,OAAO;EAChC,yBAAyB,OAAO;EAChC,yBAAyB,WAAW;EACpC,yBAAyB,OAAO;EAChC,yBAAyB,SAAS;EAClC,yBAAyB,SAAS;EAClC,yBAAyB,UAAU;EACnC,yBAAyB,WAAW;EACpC,yBAAyB,YAAY;EACrC,yBAAyB,YAAY;EACrC,yBAAyB,WAAW;EACpC,yBAAyB,YAAY;EACrC,yBAAyB,QAAQ;EACjC,yBAAyB,QAAQ;EACjC,yBAAyB,MAAM;EAC/B,yBAAyB,QAAQ;EACjC,yBAAyB,iBAAiB;EAC1C,yBAAyB,SAAS;EAClC,yBAAyB,SAAS;AACrC,CAAC;;;;;AAMD,MAAa,sBACX,OAAO,OAAO;CACZ,MAAM,yBAAyB;CAC/B,MAAM,yBAAyB;CAC/B,aAAa,yBAAyB;CACtC,MAAM,yBAAyB;CAC/B,QAAQ,yBAAyB;CACjC,QAAQ,yBAAyB;CACjC,SAAS,yBAAyB;CAClC,UAAU,yBAAyB;CACnC,WAAW,yBAAyB;CACpC,WAAW,yBAAyB;CACpC,UAAU,yBAAyB;CACnC,WAAW,yBAAyB;CACpC,OAAO,yBAAyB;CAChC,OAAO,yBAAyB;CAChC,KAAK,yBAAyB;CAC9B,OAAO,yBAAyB;CAChC,mBAAmB,yBAAyB;CAC5C,QAAQ,yBAAyB;CACjC,QAAQ,yBAAyB;CACjC,aAAa,yBAAyB;AACxC,CAAC;AAEH,SAAgB,gBAAgB,MAAiC;CAC/D,OAAO,oBAAoB,SAAS;AACtC;;;ACzFA,MAAM,YAAY,OAAe,IAAY,OAC3C,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE;;AAG9C,SAAS,iBAAiB,KAAmC,UAAiC;CAC5F,IAAI,KAAK;CACT,IAAI,KAAK,IAAI,SAAS;CACtB,IAAI,OAA6B;CACjC,OAAO,MAAM,IAAI;EACf,MAAM,MAAO,KAAK,MAAO;EACzB,IAAI,IAAI,IAAI,CAAC,MAAM,UAAU;GAC3B,OAAO,IAAI;GACX,KAAK,MAAM;EACb,OACE,KAAK,MAAM;CAEf;CACA,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB;;;;;;AAOA,SAAgB,mBAAmB,UAA4B,UAA0B;CACvF,MAAM,IAAI,SAAS,UAAU,GAAG,SAAS,SAAS;CAClD,MAAM,MAAM,SAAS;CACrB,IAAI,CAAC,OAAO,IAAI,WAAW,GAAG,OAAO,KAAK,IAAI,GAAG,SAAS,KAAK,MAAM;CACrE,MAAM,CAAC,MAAM,QAAQ,iBAAiB,KAAK,CAAC;CAC5C,OAAO,QAAQ,IAAI;AACrB;;;;;;;;;;;;;;;;AAiBA,SAAgB,yBACd,UACA,QACA,MACQ;CACR,MAAM,SAAS,SAAS,QAAQ,GAAG,SAAS,KAAK,MAAM;CACvD,IAAI,KAAK;CACT,IAAI,KAAK,SAAS;CAClB,IAAI,SAAS,SAAS;EAEpB,OAAO,KAAK,IAAI;GACd,MAAM,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,CAAC;GACxC,IAAI,mBAAmB,UAAU,GAAG,KAAK,QAAQ,KAAK;QACjD,KAAK,MAAM;EAClB;EACA,OAAO;CACT;CAGA,OAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,MAAO;EACzB,IAAI,mBAAmB,UAAU,GAAG,KAAK,QAAQ,KAAK;OACjD,KAAK,MAAM;CAClB;CACA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,wBACd,UACA,aACA,WACgC;CAChC,IAAI,aAAa,aAAa;EAC5B,MAAM,IAAI,yBAAyB,UAAU,aAAa,KAAK;EAC/D,OAAO;GAAE,OAAO;GAAG,KAAK;EAAE;CAC5B;CACA,OAAO;EACL,OAAO,yBAAyB,UAAU,aAAa,OAAO;EAC9D,KAAK,yBAAyB,UAAU,WAAW,KAAK;CAC1D;AACF;;;;;;;AAQA,SAAgB,iBACd,UACA,WACA,SACQ;CACR,IAAI,WAAW,WAAW,OAAO;CACjC,OAAO,SAAS,KAAK,MACnB,mBAAmB,UAAU,SAAS,GACtC,mBAAmB,UAAU,OAAO,CACtC;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,iBACd,WACA,YACA,SACe;CACf,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO,cAAc,aACjB,OACA,oCAAoC,UAAU,qBAAqB,WAAW;CAEpF,IAAI,WAAW;CACf,IAAI,WAAW;CACf,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,CAAC,MAAM,QAAQ,QAAQ;EAC7B,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,CAAC,OAAO,UAAU,IAAI,GACnD,OAAO,UAAU,EAAE;EAErB,IAAI,QAAQ,UACV,OAAO,UAAU,EAAE,QAAQ,KAAK,sBAAsB;EAExD,IAAI,OAAO,WACT,OAAO,UAAU,EAAE,QAAQ,KAAK,eAAe,UAAU;EAE3D,MAAM,OAAO,QAAQ,YAAY,OAAO,WAAW;EACnD,IAAI,SAAS,KAAK,SAAS,GACzB,OAAO,UAAU,EAAE,oCAAoC,KAAK;EAE9D,WAAW;EACX,WAAW;CACb;CACA,MAAM,OAAO,YAAY,YAAY;CACrC,IAAI,SAAS,YACX,OAAO,6BAA6B,KAAK,uBAAuB;CAElE,OAAO;AACT;;;;;;;;;AC7MA,MAAa,8BAA8B"}