@embedpdf/engine-services 3.0.0-next.15 → 3.0.0-next.16
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.cjs +1818 -518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -39
- package/dist/index.d.ts +233 -39
- package/dist/index.js +1818 -520
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1264,7 +1264,7 @@ var DocumentSession = class {
|
|
|
1264
1264
|
* Nothing else creates objects at held numbers, so a held number is free
|
|
1265
1265
|
* otherwise; the fork checks that again as it creates.
|
|
1266
1266
|
*/
|
|
1267
|
-
|
|
1267
|
+
claimObjectNumber(objectNumber) {
|
|
1268
1268
|
if (!Number.isInteger(objectNumber) || objectNumber < 1) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `${objectNumber} is not an object number`, { details: { field: "objectNumber" } });
|
|
1269
1269
|
if (this.objectNumberAuthority === "session" && !this.heldObjectNumbers.has(objectNumber)) throw objectNumberUnavailable(objectNumber, "not-held");
|
|
1270
1270
|
if (this.objectNumbersInUse.includes(objectNumber)) throw objectNumberUnavailable(objectNumber, "taken");
|
|
@@ -2420,6 +2420,10 @@ function readBoxes(fn, mem, docPtr, index, rectPtr) {
|
|
|
2420
2420
|
art: read(BOX_ART)
|
|
2421
2421
|
});
|
|
2422
2422
|
}
|
|
2423
|
+
/** The turn of the page at `index` as it reads: its own `/Rotate`, or the one it inherits. */
|
|
2424
|
+
function rotationAt(runtime, docPtr, index) {
|
|
2425
|
+
return withScratch(runtime.mem, 4, (intPtr) => readRotation(runtime.fn, runtime.mem, docPtr, index, intPtr));
|
|
2426
|
+
}
|
|
2423
2427
|
/** The page's turn: its `/Rotate` as written, read by the rules of `pageRotationOf`. */
|
|
2424
2428
|
function readRotation(fn, mem, docPtr, index, intPtr) {
|
|
2425
2429
|
if (!fn.EPDF_GetPageRotateByIndex(docPtr, index, intPtr)) return 0;
|
|
@@ -2698,12 +2702,18 @@ var PagesMutator = class {
|
|
|
2698
2702
|
//#endregion
|
|
2699
2703
|
//#region src/features/pages/PagesExtractor.ts
|
|
2700
2704
|
const FPDF_NO_INCREMENTAL$3 = 2;
|
|
2705
|
+
const FLAT_NORMALDISPLAY = 0;
|
|
2706
|
+
const FLAT_PRINT = 1;
|
|
2707
|
+
const EXPORT_FLATTENED_ANNOTATIONS = 1;
|
|
2708
|
+
const EXPORT_FLATTENED_FORM_FIELDS = 2;
|
|
2701
2709
|
/**
|
|
2702
2710
|
* Export a subset of pages as a standalone PDF. A read over the session:
|
|
2703
|
-
*
|
|
2704
|
-
*
|
|
2705
|
-
*
|
|
2706
|
-
*
|
|
2711
|
+
* the pages are copied into a scratch document, so the source is untouched —
|
|
2712
|
+
* no registry refresh, no layer artifact. A plain copy is
|
|
2713
|
+
* `FPDF_ImportPagesByIndex`; a flattened one `EPDFDoc_ExportPagesFlattened`,
|
|
2714
|
+
* which copies the pages as they draw. Either keeps the document's title.
|
|
2715
|
+
* Lives next to `PagesReader`/`PagesMutator` (one file per page verb family)
|
|
2716
|
+
* so both worker hosts share the code path.
|
|
2707
2717
|
*/
|
|
2708
2718
|
var PagesExtractor = class {
|
|
2709
2719
|
constructor(runtime, session) {
|
|
@@ -2712,11 +2722,13 @@ var PagesExtractor = class {
|
|
|
2712
2722
|
}
|
|
2713
2723
|
/**
|
|
2714
2724
|
* Copy the given pages, in the supplied caller order, into a new
|
|
2715
|
-
* document and serialize it
|
|
2716
|
-
*
|
|
2717
|
-
*
|
|
2725
|
+
* document and serialize it: as they are, or, with `flatten`, as they draw
|
|
2726
|
+
* (checked against the caller's rights before it gets here). Validation up
|
|
2727
|
+
* front: non-empty, no duplicates, every page object number resolvable
|
|
2728
|
+
* (`NotFound` from the session on a bad one). The scratch document is
|
|
2729
|
+
* always closed, success or throw.
|
|
2718
2730
|
*/
|
|
2719
|
-
extract(pages, signal) {
|
|
2731
|
+
extract(pages, flatten, signal) {
|
|
2720
2732
|
throwIfAborted(signal);
|
|
2721
2733
|
if (pages.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "pages.extract requires at least one page");
|
|
2722
2734
|
const pageObjectNumbers = this.session.resolvePageRefs(pages);
|
|
@@ -2728,14 +2740,20 @@ var PagesExtractor = class {
|
|
|
2728
2740
|
const indices = pageObjectNumbers.map((pageObjectNumber) => this.session.recordByObjectNumber(pageObjectNumber).pageIndex);
|
|
2729
2741
|
const { fn, mem } = this.runtime;
|
|
2730
2742
|
const srcPtr = this.session.requireDocPtr();
|
|
2731
|
-
const destPtr = fn.FPDF_CreateNewDocument();
|
|
2732
|
-
if (!destPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "FPDF_CreateNewDocument failed");
|
|
2733
2743
|
const idxPtr = mem.alloc(4 * indices.length);
|
|
2744
|
+
for (let i = 0; i < indices.length; i++) mem.poke(idxPtr, "i32", indices[i], 4 * i);
|
|
2745
|
+
let destPtr;
|
|
2746
|
+
try {
|
|
2747
|
+
destPtr = flatten ? this.copyFlattened(srcPtr, idxPtr, indices, flatten) : this.copyPlain(srcPtr, idxPtr, indices);
|
|
2748
|
+
} catch (error) {
|
|
2749
|
+
mem.free(idxPtr);
|
|
2750
|
+
throw error;
|
|
2751
|
+
}
|
|
2734
2752
|
const sizePtr = mem.alloc(8);
|
|
2735
2753
|
let pdfPtr = null;
|
|
2736
2754
|
try {
|
|
2737
|
-
|
|
2738
|
-
if (
|
|
2755
|
+
const title = readMetaText(fn, mem, srcPtr, "Title");
|
|
2756
|
+
if (title !== null) writeMetaText(fn, mem, destPtr, "Title", title);
|
|
2739
2757
|
pokeU64(mem, sizePtr, 0);
|
|
2740
2758
|
pdfPtr = fn.EPDF_SaveDocumentToOwnedBuffer(destPtr, FPDF_NO_INCREMENTAL$3, sizePtr);
|
|
2741
2759
|
const size = Number(mem.peek(sizePtr, "i32"));
|
|
@@ -2754,6 +2772,28 @@ var PagesExtractor = class {
|
|
|
2754
2772
|
fn.FPDF_CloseDocument(destPtr);
|
|
2755
2773
|
}
|
|
2756
2774
|
}
|
|
2775
|
+
/** A new document with copies of the pages at `indices`, as they are. */
|
|
2776
|
+
copyPlain(srcPtr, idxPtr, indices) {
|
|
2777
|
+
const { fn } = this.runtime;
|
|
2778
|
+
const destPtr = fn.FPDF_CreateNewDocument();
|
|
2779
|
+
if (!destPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "FPDF_CreateNewDocument failed");
|
|
2780
|
+
if (!fn.FPDF_ImportPagesByIndex(destPtr, srcPtr, idxPtr, indices.length, 0)) {
|
|
2781
|
+
fn.FPDF_CloseDocument(destPtr);
|
|
2782
|
+
throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `FPDF_ImportPagesByIndex rejected the request (indices=[${indices.join(",")}])`);
|
|
2783
|
+
}
|
|
2784
|
+
return destPtr;
|
|
2785
|
+
}
|
|
2786
|
+
/**
|
|
2787
|
+
* A new document with the pages at `indices` as they draw for
|
|
2788
|
+
* `flatten.usage`: what `flatten` includes painted in, nothing interactive
|
|
2789
|
+
* left, layers in their state for the usage.
|
|
2790
|
+
*/
|
|
2791
|
+
copyFlattened(srcPtr, idxPtr, indices, flatten) {
|
|
2792
|
+
const flags = (flatten.includeAnnotations ? EXPORT_FLATTENED_ANNOTATIONS : 0) | (flatten.includeFormFields ? EXPORT_FLATTENED_FORM_FIELDS : 0);
|
|
2793
|
+
const destPtr = this.runtime.fn.EPDFDoc_ExportPagesFlattened(srcPtr, idxPtr, indices.length, flatten.usage === "print" ? FLAT_PRINT : FLAT_NORMALDISPLAY, flags);
|
|
2794
|
+
if (!destPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `EPDFDoc_ExportPagesFlattened rejected the request (indices=[${indices.join(",")}])`);
|
|
2795
|
+
return destPtr;
|
|
2796
|
+
}
|
|
2757
2797
|
};
|
|
2758
2798
|
//#endregion
|
|
2759
2799
|
//#region src/features/annotations/internal/write/promoteInlineAnnotations.ts
|
|
@@ -2852,7 +2892,7 @@ var PagesInserter = class {
|
|
|
2852
2892
|
const destPtr = this.session.requireDocPtr();
|
|
2853
2893
|
const at = pageIndexAt(this.session, position, fn.FPDF_GetPageCount(destPtr));
|
|
2854
2894
|
if (objectNumbers && objectNumbers.length !== count) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `pages.insertBlank needs one object number per page: ${objectNumbers.length} for ${count}`, { details: { field: "objectNumbers" } });
|
|
2855
|
-
for (const objectNumber of objectNumbers ?? []) this.session.
|
|
2895
|
+
for (const objectNumber of objectNumbers ?? []) this.session.claimObjectNumber(objectNumber);
|
|
2856
2896
|
for (let i = 0; i < count; i++) {
|
|
2857
2897
|
const objectNumber = objectNumbers?.[i];
|
|
2858
2898
|
if (!fn.EPDFPage_InsertBlankRaw(destPtr, at + i, size.width, size.height, objectNumber ?? 0)) {
|
|
@@ -3874,6 +3914,11 @@ function toExactArrayBuffer(bytes) {
|
|
|
3874
3914
|
const FPDF_RENDER_ANNOT = 1;
|
|
3875
3915
|
/** `EPDF_RENDER_WIDGETS`: the form fields' widgets. */
|
|
3876
3916
|
const EPDF_RENDER_WIDGETS = 32768;
|
|
3917
|
+
/**
|
|
3918
|
+
* `FPDF_PRINTING`: the print rules. Layers take their print state, and
|
|
3919
|
+
* annotations and widgets draw by their `print` flag (no-view ones too).
|
|
3920
|
+
*/
|
|
3921
|
+
const FPDF_PRINTING = 2048;
|
|
3877
3922
|
const FPDF_RENDER_TOBECONTINUED = 1;
|
|
3878
3923
|
const FPDF_RENDER_DONE = 2;
|
|
3879
3924
|
var PageRenderReader = class {
|
|
@@ -3893,6 +3938,8 @@ var PageRenderReader = class {
|
|
|
3893
3938
|
const pagePtr = await pool.acquireInSlices(pageObjectNumber, signal, slices);
|
|
3894
3939
|
try {
|
|
3895
3940
|
throwIfAborted(signal);
|
|
3941
|
+
(0, _embedpdf_engine_core_runtime.checkRenderUsage)(options, null);
|
|
3942
|
+
const print = options.usage === "print";
|
|
3896
3943
|
const page = readPageBox(this.runtime, pagePtr);
|
|
3897
3944
|
const target = resolveTarget(options.target, page);
|
|
3898
3945
|
const rotation = options.rotation ?? 0;
|
|
@@ -3907,6 +3954,7 @@ var PageRenderReader = class {
|
|
|
3907
3954
|
let flags = 16;
|
|
3908
3955
|
if (layers.includeAnnotations) flags |= FPDF_RENDER_ANNOT;
|
|
3909
3956
|
if (layers.includeFormFields) flags |= EPDF_RENDER_WIDGETS;
|
|
3957
|
+
if (print) flags |= FPDF_PRINTING;
|
|
3910
3958
|
const raster = await rasterizeAsync(this.runtime, {
|
|
3911
3959
|
rect: target,
|
|
3912
3960
|
page,
|
|
@@ -4790,7 +4838,7 @@ function extractAttachmentToBuffer(runtime, attachmentPtr, maxDecodedBytes) {
|
|
|
4790
4838
|
mem.poke(bufPtrSlot, "ptr", 0);
|
|
4791
4839
|
mem.poke(sizePtr, "i32", 0);
|
|
4792
4840
|
mem.poke(statusPtr, "i32", -1);
|
|
4793
|
-
if (!fn.EPDFAttachment_ExtractFileToOwnedBuffer(attachmentPtr, BigInt(maxDecodedBytes ?? 0), bufPtrSlot, sizePtr, statusPtr)) throw extractStatusError(Number(mem.peek(statusPtr, "i32")));
|
|
4841
|
+
if (!fn.EPDFAttachment_ExtractFileToOwnedBuffer(attachmentPtr, BigInt(maxDecodedBytes ?? 0), bufPtrSlot, sizePtr, statusPtr)) throw extractStatusError(Number(mem.peek(statusPtr, "i32")), maxDecodedBytes);
|
|
4794
4842
|
const size = Number(mem.peek(sizePtr, "i32")) >>> 0;
|
|
4795
4843
|
dataPtr = mem.peek(bufPtrSlot, "ptr");
|
|
4796
4844
|
if (size === 0 || !dataPtr) return {
|
|
@@ -4826,7 +4874,7 @@ function extractAttachmentToFile(runtime, attachmentPtr, path, maxDecodedBytes)
|
|
|
4826
4874
|
try {
|
|
4827
4875
|
mem.poke(sizePtr, "i32", 0);
|
|
4828
4876
|
mem.poke(statusPtr, "i32", -1);
|
|
4829
|
-
if (!fn.EPDFAttachment_ExtractFile(attachmentPtr, writer.ptr, BigInt(maxDecodedBytes ?? 0), sizePtr, statusPtr)) throw extractStatusError(Number(mem.peek(statusPtr, "i32")));
|
|
4877
|
+
if (!fn.EPDFAttachment_ExtractFile(attachmentPtr, writer.ptr, BigInt(maxDecodedBytes ?? 0), sizePtr, statusPtr)) throw extractStatusError(Number(mem.peek(statusPtr, "i32")), maxDecodedBytes);
|
|
4830
4878
|
return { size: Number(mem.peek(sizePtr, "i32")) >>> 0 };
|
|
4831
4879
|
} finally {
|
|
4832
4880
|
writer.close();
|
|
@@ -4835,11 +4883,11 @@ function extractAttachmentToFile(runtime, attachmentPtr, path, maxDecodedBytes)
|
|
|
4835
4883
|
}
|
|
4836
4884
|
}
|
|
4837
4885
|
/** `EPDFAttachmentExtractStatus` (public/fpdf_attachment.h) → EngineError. */
|
|
4838
|
-
function extractStatusError(status) {
|
|
4886
|
+
function extractStatusError(status, maxDecodedBytes) {
|
|
4839
4887
|
switch (status) {
|
|
4840
4888
|
case 1: return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "attachment has no embedded file stream (missing /EF)");
|
|
4841
4889
|
case 2: return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.MalformedPdf, "embedded file stream failed to decode");
|
|
4842
|
-
case 3: return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.PayloadTooLarge,
|
|
4890
|
+
case 3: return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.PayloadTooLarge, `the embedded file decodes to more than ${maxDecodedBytes} bytes, the limit`, { details: { limit: maxDecodedBytes } });
|
|
4843
4891
|
case 4: return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "writing the extracted attachment failed");
|
|
4844
4892
|
default: return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `attachment extraction failed (status ${status})`);
|
|
4845
4893
|
}
|
|
@@ -5662,34 +5710,50 @@ function readLine(fn, mem, annotPtr, base) {
|
|
|
5662
5710
|
};
|
|
5663
5711
|
}
|
|
5664
5712
|
//#endregion
|
|
5665
|
-
//#region src/features/
|
|
5713
|
+
//#region src/features/destinations/createDestination.ts
|
|
5666
5714
|
/**
|
|
5667
|
-
*
|
|
5668
|
-
*
|
|
5669
|
-
*
|
|
5670
|
-
*
|
|
5671
|
-
* One truth, one projection: the target is a pure function of the same
|
|
5672
|
-
* payload-carrying action tree the scripting plane reads
|
|
5673
|
-
* (`base.actions.activate`) — no second native read exists any more, so the
|
|
5674
|
-
* two action-shaped surfaces cannot drift by construction.
|
|
5675
|
-
*
|
|
5676
|
-
* `/A` precedence, pinned: an activate action that exists but cannot be
|
|
5677
|
-
* executed (an `incomplete` tree, a degraded/unreadable root) projects
|
|
5678
|
-
* `unsupported` — a broken action is a dead link, never an invitation to
|
|
5679
|
-
* guess. The direct `/Dest` (which is data, not an action) is consulted
|
|
5680
|
-
* only when no `/A` exists at all.
|
|
5715
|
+
* Build an indirect explicit-destination array for `dest`. The target page
|
|
5716
|
+
* is named by its object number and never loaded: a destination only refers
|
|
5717
|
+
* to the page's dictionary.
|
|
5681
5718
|
*/
|
|
5682
|
-
function
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5719
|
+
function createDestination(fn, mem, docPtr, dest) {
|
|
5720
|
+
const page = dest.page.objectNumber;
|
|
5721
|
+
const destPtr = dest.kind === "xyz" ? fn.EPDFDest_CreateXYZByObjectNumber(docPtr, page, dest.left != null, dest.left ?? 0, dest.top != null, dest.top ?? 0, dest.zoom != null, dest.zoom ?? 0) : createViewDestination(fn, mem, docPtr, page, dest);
|
|
5722
|
+
if (!destPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `destination page not found: pageObjectNumber=${page}`);
|
|
5723
|
+
return destPtr;
|
|
5724
|
+
}
|
|
5725
|
+
function createViewDestination(fn, mem, docPtr, page, dest) {
|
|
5726
|
+
const params = (() => {
|
|
5727
|
+
switch (dest.kind) {
|
|
5728
|
+
case "fitH":
|
|
5729
|
+
case "fitBH": return dest.top != null ? [dest.top] : [];
|
|
5730
|
+
case "fitV":
|
|
5731
|
+
case "fitBV": return dest.left != null ? [dest.left] : [];
|
|
5732
|
+
case "fitR": return [
|
|
5733
|
+
dest.left,
|
|
5734
|
+
dest.bottom,
|
|
5735
|
+
dest.right,
|
|
5736
|
+
dest.top
|
|
5737
|
+
];
|
|
5738
|
+
default: return [];
|
|
5739
|
+
}
|
|
5740
|
+
})();
|
|
5741
|
+
const view = VIEW_CODE_BY_KIND[dest.kind];
|
|
5742
|
+
if (!params.length) return fn.EPDFDest_CreateViewByObjectNumber(docPtr, page, view, _embedpdf_engine_runtime.NULL_PTR, 0);
|
|
5743
|
+
return withScratch(mem, params.length * 4, (buf) => {
|
|
5744
|
+
for (let i = 0; i < params.length; i++) mem.poke(buf, "f32", params[i], i * 4);
|
|
5745
|
+
return fn.EPDFDest_CreateViewByObjectNumber(docPtr, page, view, buf, params.length);
|
|
5746
|
+
});
|
|
5688
5747
|
}
|
|
5748
|
+
//#endregion
|
|
5749
|
+
//#region src/features/destinations/linkTarget.ts
|
|
5689
5750
|
/**
|
|
5690
|
-
*
|
|
5691
|
-
*
|
|
5692
|
-
*
|
|
5751
|
+
* A link target read the way every reader reads one (links, outline items):
|
|
5752
|
+
* the navigation view of a payload-carrying activate tree. Pure.
|
|
5753
|
+
*
|
|
5754
|
+
* The law, enforced at the projection too: never execute — not even
|
|
5755
|
+
* navigate the root of — a tree marked incomplete. A broken action is a
|
|
5756
|
+
* dead target, never an invitation to guess.
|
|
5693
5757
|
*/
|
|
5694
5758
|
function linkTargetFromActionTree(tree) {
|
|
5695
5759
|
if (tree.incomplete) return { kind: "unsupported" };
|
|
@@ -5720,6 +5784,41 @@ function linkTargetFromActionTree(tree) {
|
|
|
5720
5784
|
default: return { kind: "unsupported" };
|
|
5721
5785
|
}
|
|
5722
5786
|
}
|
|
5787
|
+
/**
|
|
5788
|
+
* A new indirect action of the document for a writable target: a GoTo to a
|
|
5789
|
+
* new destination, a URI, or a standard named verb. Links and outline items
|
|
5790
|
+
* write their targets with it, so both read back the same way.
|
|
5791
|
+
*/
|
|
5792
|
+
function createLinkTargetAction(fn, mem, docPtr, target) {
|
|
5793
|
+
const actionPtr = target.kind === "uri" ? fn.EPDFAction_CreateURI(docPtr, target.uri) : target.kind === "named" ? fn.EPDFAction_CreateNamed(docPtr, target.name) : fn.EPDFAction_CreateGoTo(docPtr, createDestination(fn, mem, docPtr, target.destination));
|
|
5794
|
+
if (!actionPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `failed to create '${target.kind}' action`);
|
|
5795
|
+
return actionPtr;
|
|
5796
|
+
}
|
|
5797
|
+
//#endregion
|
|
5798
|
+
//#region src/features/annotations/internal/read/readLinkAnnotation.ts
|
|
5799
|
+
/**
|
|
5800
|
+
* Link reader: rect/flags/relationship ride the base (a link grouped to an
|
|
5801
|
+
* annotation is plain `inReplyTo` + `replyType: 'group'`); this module only
|
|
5802
|
+
* materialises the normalized target.
|
|
5803
|
+
*
|
|
5804
|
+
* One truth, one projection: the target is a pure function of the same
|
|
5805
|
+
* payload-carrying action tree the scripting plane reads
|
|
5806
|
+
* (`base.actions.activate`) — no second native read exists any more, so the
|
|
5807
|
+
* two action-shaped surfaces cannot drift by construction.
|
|
5808
|
+
*
|
|
5809
|
+
* `/A` precedence, pinned: an activate action that exists but cannot be
|
|
5810
|
+
* executed (an `incomplete` tree, a degraded/unreadable root) projects
|
|
5811
|
+
* `unsupported` — a broken action is a dead link, never an invitation to
|
|
5812
|
+
* guess. The direct `/Dest` (which is data, not an action) is consulted
|
|
5813
|
+
* only when no `/A` exists at all.
|
|
5814
|
+
*/
|
|
5815
|
+
function readLink(fn, mem, annotPtr, base, _rawSubtypeCode, ctx) {
|
|
5816
|
+
return {
|
|
5817
|
+
...base,
|
|
5818
|
+
subtype: "link",
|
|
5819
|
+
target: readLinkTarget(fn, mem, annotPtr, base, ctx)
|
|
5820
|
+
};
|
|
5821
|
+
}
|
|
5723
5822
|
function readLinkTarget(fn, mem, annotPtr, base, ctx) {
|
|
5724
5823
|
const activate = base.actions?.activate;
|
|
5725
5824
|
if (activate) return linkTargetFromActionTree(activate);
|
|
@@ -8525,42 +8624,6 @@ function isLineSubtype(subtype) {
|
|
|
8525
8624
|
return subtype === "line";
|
|
8526
8625
|
}
|
|
8527
8626
|
//#endregion
|
|
8528
|
-
//#region src/features/destinations/createDestination.ts
|
|
8529
|
-
/**
|
|
8530
|
-
* Build an indirect explicit-destination array for `dest`. The target page
|
|
8531
|
-
* is named by its object number and never loaded: a destination only refers
|
|
8532
|
-
* to the page's dictionary.
|
|
8533
|
-
*/
|
|
8534
|
-
function createDestination(fn, mem, docPtr, dest) {
|
|
8535
|
-
const page = dest.page.objectNumber;
|
|
8536
|
-
const destPtr = dest.kind === "xyz" ? fn.EPDFDest_CreateXYZByObjectNumber(docPtr, page, dest.left != null, dest.left ?? 0, dest.top != null, dest.top ?? 0, dest.zoom != null, dest.zoom ?? 0) : createViewDestination(fn, mem, docPtr, page, dest);
|
|
8537
|
-
if (!destPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `destination page not found: pageObjectNumber=${page}`);
|
|
8538
|
-
return destPtr;
|
|
8539
|
-
}
|
|
8540
|
-
function createViewDestination(fn, mem, docPtr, page, dest) {
|
|
8541
|
-
const params = (() => {
|
|
8542
|
-
switch (dest.kind) {
|
|
8543
|
-
case "fitH":
|
|
8544
|
-
case "fitBH": return dest.top != null ? [dest.top] : [];
|
|
8545
|
-
case "fitV":
|
|
8546
|
-
case "fitBV": return dest.left != null ? [dest.left] : [];
|
|
8547
|
-
case "fitR": return [
|
|
8548
|
-
dest.left,
|
|
8549
|
-
dest.bottom,
|
|
8550
|
-
dest.right,
|
|
8551
|
-
dest.top
|
|
8552
|
-
];
|
|
8553
|
-
default: return [];
|
|
8554
|
-
}
|
|
8555
|
-
})();
|
|
8556
|
-
const view = VIEW_CODE_BY_KIND[dest.kind];
|
|
8557
|
-
if (!params.length) return fn.EPDFDest_CreateViewByObjectNumber(docPtr, page, view, _embedpdf_engine_runtime.NULL_PTR, 0);
|
|
8558
|
-
return withScratch(mem, params.length * 4, (buf) => {
|
|
8559
|
-
for (let i = 0; i < params.length; i++) mem.poke(buf, "f32", params[i], i * 4);
|
|
8560
|
-
return fn.EPDFDest_CreateViewByObjectNumber(docPtr, page, view, buf, params.length);
|
|
8561
|
-
});
|
|
8562
|
-
}
|
|
8563
|
-
//#endregion
|
|
8564
8627
|
//#region src/features/annotations/internal/write/writeLinkAnnotation.ts
|
|
8565
8628
|
/**
|
|
8566
8629
|
* Link writer: rect + the `/A` action. `goto`, `uri` and the standard
|
|
@@ -8603,8 +8666,7 @@ function isLinkSubtype(subtype) {
|
|
|
8603
8666
|
function applyLinkTarget(fn, mem, annotPtr, target, ctx) {
|
|
8604
8667
|
const docPtr = ctx?.docPtr;
|
|
8605
8668
|
if (!docPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "writing a link target requires a document pointer on the write context");
|
|
8606
|
-
const actionPtr =
|
|
8607
|
-
if (!actionPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `failed to create '${target.kind}' action`);
|
|
8669
|
+
const actionPtr = createLinkTargetAction(fn, mem, docPtr, target);
|
|
8608
8670
|
if (!fn.EPDFAnnot_SetAction(annotPtr, actionPtr)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetAction failed for link");
|
|
8609
8671
|
}
|
|
8610
8672
|
//#endregion
|
|
@@ -9800,7 +9862,7 @@ var AnnotationBatchApplier = class {
|
|
|
9800
9862
|
...given,
|
|
9801
9863
|
nm: (0, _embedpdf_engine_core_runtime.generateUuidV7)()
|
|
9802
9864
|
};
|
|
9803
|
-
if (create.objectNumber !== void 0) labelled(create.label, () => this.session.
|
|
9865
|
+
if (create.objectNumber !== void 0) labelled(create.label, () => this.session.claimObjectNumber(create.objectNumber));
|
|
9804
9866
|
const replyTo = create.replyTo && {
|
|
9805
9867
|
type: create.replyTo.type,
|
|
9806
9868
|
to: labelled(create.label, () => this.linkTarget(create.replyTo.to, create.page, creates.length, "reply"))
|
|
@@ -9818,7 +9880,7 @@ var AnnotationBatchApplier = class {
|
|
|
9818
9880
|
throwIfAborted(signal);
|
|
9819
9881
|
if (prepared.length === 0) return {
|
|
9820
9882
|
created: [],
|
|
9821
|
-
meta: metaOf(this.session.writeStamp(), [], [])
|
|
9883
|
+
meta: metaOf$1(this.session.writeStamp(), [], [])
|
|
9822
9884
|
};
|
|
9823
9885
|
const placed = prepared.map(({ create, record, ctx, draft }) => {
|
|
9824
9886
|
throwIfAborted(signal);
|
|
@@ -9864,7 +9926,7 @@ var AnnotationBatchApplier = class {
|
|
|
9864
9926
|
joinWidgetFieldNumbers(this.runtime, this.session, created);
|
|
9865
9927
|
return {
|
|
9866
9928
|
created,
|
|
9867
|
-
meta: metaOf(this.session.writeStamp(), placed, linked)
|
|
9929
|
+
meta: metaOf$1(this.session.writeStamp(), placed, linked)
|
|
9868
9930
|
};
|
|
9869
9931
|
}
|
|
9870
9932
|
/**
|
|
@@ -9935,7 +9997,7 @@ var AnnotationBatchApplier = class {
|
|
|
9935
9997
|
* and each it linked a popup to. Creates only append, so no other annotation
|
|
9936
9998
|
* moves.
|
|
9937
9999
|
*/
|
|
9938
|
-
function metaOf(stamp, placed, linked) {
|
|
10000
|
+
function metaOf$1(stamp, placed, linked) {
|
|
9939
10001
|
return {
|
|
9940
10002
|
affectedPages: [...new Set(placed.map((at) => at.pageObjectNumber))].map((page) => (0, _embedpdf_engine_core_runtime.toPageRef)(page)),
|
|
9941
10003
|
cacheDelta: null,
|
|
@@ -10783,6 +10845,16 @@ function importAnnots(runtime, docPtr, pageIndex, capture) {
|
|
|
10783
10845
|
function exportDict(runtime, docPtr, objectNumber, deepKeys) {
|
|
10784
10846
|
return takeOwned(runtime, `object ${objectNumber}`, (sizePtr) => runtime.fn.EPDFDoc_ExportDictRawToOwnedBuffer(docPtr, objectNumber, deepKeys.join(" "), sizePtr));
|
|
10785
10847
|
}
|
|
10848
|
+
/**
|
|
10849
|
+
* The dictionary `objectNumber` as bytes a guard compares: without
|
|
10850
|
+
* `skipKeys`, and under `deepKeys` the objects the layer holds, each stream
|
|
10851
|
+
* as it reads. They stay the same while it reads the same, also after a save
|
|
10852
|
+
* encoded its streams again or the layer learned its file's hash; never
|
|
10853
|
+
* imported.
|
|
10854
|
+
*/
|
|
10855
|
+
function canonicalDict(runtime, docPtr, objectNumber, deepKeys, skipKeys = []) {
|
|
10856
|
+
return takeOwned(runtime, `object ${objectNumber}`, (sizePtr) => runtime.fn.EPDFDoc_ExportDictCanonicalToOwnedBuffer(docPtr, objectNumber, deepKeys.join(" "), skipKeys.join(" "), sizePtr));
|
|
10857
|
+
}
|
|
10786
10858
|
/** Puts the dictionary of `capture` back as it was: exactly its keys. */
|
|
10787
10859
|
function importDict(runtime, docPtr, capture) {
|
|
10788
10860
|
withBytes(runtime, capture, (ptr) => {
|
|
@@ -10799,6 +10871,49 @@ function importField(runtime, docPtr, capture) {
|
|
|
10799
10871
|
if (!runtime.fn.EPDFForm_ImportFieldRaw(docPtr, ptr, capture.byteLength)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFForm_ImportFieldRaw refused a capture");
|
|
10800
10872
|
});
|
|
10801
10873
|
}
|
|
10874
|
+
/** An outline item as its delete removes it: the item, the items under it, its place. */
|
|
10875
|
+
function exportOutlineItem(runtime, docPtr, objectNumber) {
|
|
10876
|
+
return takeOwned(runtime, `outline item ${objectNumber}`, (sizePtr) => runtime.fn.EPDFOutline_ExportItemRawToOwnedBuffer(docPtr, objectNumber, sizePtr));
|
|
10877
|
+
}
|
|
10878
|
+
/** Puts the outline item of `capture` back, with the items under it, at its place. */
|
|
10879
|
+
function importOutlineItem(runtime, docPtr, capture) {
|
|
10880
|
+
withBytes(runtime, capture, (ptr) => {
|
|
10881
|
+
if (!runtime.fn.EPDFOutline_ImportItemRaw(docPtr, ptr, capture.byteLength)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFOutline_ImportItemRaw refused a capture");
|
|
10882
|
+
});
|
|
10883
|
+
}
|
|
10884
|
+
/** A page as its delete removes it: the page, what it reaches, its names, its place. */
|
|
10885
|
+
function exportPage(runtime, docPtr, pageObjectNumber) {
|
|
10886
|
+
return takeOwned(runtime, `page ${pageObjectNumber}`, (sizePtr) => runtime.fn.EPDFPage_ExportRawToOwnedBuffer(docPtr, pageObjectNumber, sizePtr));
|
|
10887
|
+
}
|
|
10888
|
+
/** Puts the page of `capture` back into the page tree, at its place, with its names. */
|
|
10889
|
+
function importPage(runtime, docPtr, capture) {
|
|
10890
|
+
withBytes(runtime, capture, (ptr) => {
|
|
10891
|
+
if (!runtime.fn.EPDFPage_ImportRaw(docPtr, ptr, capture.byteLength)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFPage_ImportRaw refused a capture");
|
|
10892
|
+
});
|
|
10893
|
+
}
|
|
10894
|
+
/** The embedded file under `key` as its delete removes it: its key, its file specification, its streams. */
|
|
10895
|
+
function exportAttachment(runtime, docPtr, key) {
|
|
10896
|
+
return takeOwned(runtime, `attachment '${key}'`, (sizePtr) => withUtf16String(runtime.mem, key, (keyPtr) => runtime.fn.EPDFAttachment_ExportRawToOwnedBuffer(docPtr, keyPtr, sizePtr)));
|
|
10897
|
+
}
|
|
10898
|
+
/**
|
|
10899
|
+
* The embedded file under `key` as bytes a guard compares: the same while it
|
|
10900
|
+
* is the same file, as it was (its number included); null when no file has
|
|
10901
|
+
* the key. Never imported.
|
|
10902
|
+
*/
|
|
10903
|
+
function canonicalAttachment(runtime, docPtr, key) {
|
|
10904
|
+
try {
|
|
10905
|
+
return takeOwned(runtime, `attachment '${key}'`, (sizePtr) => withUtf16String(runtime.mem, key, (keyPtr) => runtime.fn.EPDFAttachment_ExportCanonicalToOwnedBuffer(docPtr, keyPtr, sizePtr)));
|
|
10906
|
+
} catch (error) {
|
|
10907
|
+
if (_embedpdf_engine_core_runtime.EngineError.is(error)) return null;
|
|
10908
|
+
throw error;
|
|
10909
|
+
}
|
|
10910
|
+
}
|
|
10911
|
+
/** Puts the embedded file of `capture` back under its key. */
|
|
10912
|
+
function importAttachment(runtime, docPtr, capture) {
|
|
10913
|
+
withBytes(runtime, capture, (ptr) => {
|
|
10914
|
+
if (!runtime.fn.EPDFAttachment_ImportRaw(docPtr, ptr, capture.byteLength)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAttachment_ImportRaw refused a capture");
|
|
10915
|
+
});
|
|
10916
|
+
}
|
|
10802
10917
|
/** The bytes of a buffer an export returned, which is then released. */
|
|
10803
10918
|
function takeOwned(runtime, what, call) {
|
|
10804
10919
|
const { fn, mem } = runtime;
|
|
@@ -10877,6 +10992,14 @@ function assertExpected(opIndex, current, expected) {
|
|
|
10877
10992
|
} });
|
|
10878
10993
|
}
|
|
10879
10994
|
//#endregion
|
|
10995
|
+
//#region src/shared/bytesEqual.ts
|
|
10996
|
+
/** Whether two byte arrays hold the same bytes. */
|
|
10997
|
+
function bytesEqual(a, b) {
|
|
10998
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
10999
|
+
for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
|
|
11000
|
+
return true;
|
|
11001
|
+
}
|
|
11002
|
+
//#endregion
|
|
10880
11003
|
//#region src/features/changes/internal/objectCaptures.ts
|
|
10881
11004
|
/**
|
|
10882
11005
|
* Captures each dictionary before an op writes it: every key, and under
|
|
@@ -10895,18 +11018,21 @@ function captureBefore(runtime, session, objects) {
|
|
|
10895
11018
|
}
|
|
10896
11019
|
return [...captured.values()];
|
|
10897
11020
|
}
|
|
10898
|
-
/**
|
|
11021
|
+
/**
|
|
11022
|
+
* Completes captures once the op has written: each dictionary as the op left
|
|
11023
|
+
* it, the guard, in canonical bytes (`canonicalDict`).
|
|
11024
|
+
*/
|
|
10899
11025
|
function captureAfter(runtime, session, pending) {
|
|
10900
11026
|
return pending.map((object) => ({
|
|
10901
11027
|
...object,
|
|
10902
|
-
after:
|
|
11028
|
+
after: canonicalDict(runtime, session.requireDocPtr(), object.objectNumber, [])
|
|
10903
11029
|
}));
|
|
10904
11030
|
}
|
|
10905
11031
|
/** Whether every dictionary still reads exactly as the op left it. */
|
|
10906
11032
|
function unchangedSince(runtime, session, objects) {
|
|
10907
11033
|
return objects.every((object) => {
|
|
10908
|
-
const now =
|
|
10909
|
-
return now !== null &&
|
|
11034
|
+
const now = tryCanonicalDict(runtime, session, object.objectNumber);
|
|
11035
|
+
return now !== null && bytesEqual(now, object.after);
|
|
10910
11036
|
});
|
|
10911
11037
|
}
|
|
10912
11038
|
/**
|
|
@@ -10921,6 +11047,15 @@ function revertObjects(runtime, session, objects) {
|
|
|
10921
11047
|
session.invalidateDerived();
|
|
10922
11048
|
return captureAfter(runtime, session, redo);
|
|
10923
11049
|
}
|
|
11050
|
+
/** A dictionary's canonical bytes, or null when there is none at that number. */
|
|
11051
|
+
function tryCanonicalDict(runtime, session, objectNumber) {
|
|
11052
|
+
try {
|
|
11053
|
+
return canonicalDict(runtime, session.requireDocPtr(), objectNumber, []);
|
|
11054
|
+
} catch (error) {
|
|
11055
|
+
if (_embedpdf_engine_core_runtime.EngineError.is(error)) return null;
|
|
11056
|
+
throw error;
|
|
11057
|
+
}
|
|
11058
|
+
}
|
|
10924
11059
|
/** The capture of a dictionary, or null when there is none at that number. */
|
|
10925
11060
|
function tryExportDict(runtime, session, objectNumber, deep) {
|
|
10926
11061
|
try {
|
|
@@ -10930,11 +11065,6 @@ function tryExportDict(runtime, session, objectNumber, deep) {
|
|
|
10930
11065
|
throw error;
|
|
10931
11066
|
}
|
|
10932
11067
|
}
|
|
10933
|
-
function sameBytes(a, b) {
|
|
10934
|
-
if (a.byteLength !== b.byteLength) return false;
|
|
10935
|
-
for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
|
|
10936
|
-
return true;
|
|
10937
|
-
}
|
|
10938
11068
|
//#endregion
|
|
10939
11069
|
//#region src/features/annotations/internal/stackingOrder.ts
|
|
10940
11070
|
/**
|
|
@@ -11177,7 +11307,7 @@ function updateAnnotation(ctx, op, opIndex) {
|
|
|
11177
11307
|
if (current.subtype === "widget") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "a widget is the form's: change its place and look with doc.forms.updateWidget");
|
|
11178
11308
|
(0, _embedpdf_engine_core_runtime.authorizeUnprotected)(ctx.authority, (0, _embedpdf_engine_core_runtime.annotationWriteCapability)(current.subtype));
|
|
11179
11309
|
assertExpected(opIndex, current, op.expect);
|
|
11180
|
-
return writeUpdate$
|
|
11310
|
+
return writeUpdate$2(ctx, op.ref, op.patch, "annotations.update", op.resources);
|
|
11181
11311
|
}
|
|
11182
11312
|
/** `annotations.delete`; its reverse brings back everything it removed. */
|
|
11183
11313
|
function deleteAnnotation(ctx, op, opIndex) {
|
|
@@ -11185,7 +11315,7 @@ function deleteAnnotation(ctx, op, opIndex) {
|
|
|
11185
11315
|
if (current.subtype === "widget") throw widgetDeleteRefused(current);
|
|
11186
11316
|
(0, _embedpdf_engine_core_runtime.authorizeUnprotected)(ctx.authority, (0, _embedpdf_engine_core_runtime.annotationWriteCapability)(current.subtype));
|
|
11187
11317
|
assertExpected(opIndex, current, op.expect);
|
|
11188
|
-
return writeDelete$
|
|
11318
|
+
return writeDelete$4(ctx, op.ref);
|
|
11189
11319
|
}
|
|
11190
11320
|
/** Why a widget isn't deleted as an annotation, naming the form op that removes it. */
|
|
11191
11321
|
function widgetDeleteRefused(widget) {
|
|
@@ -11252,7 +11382,7 @@ function revertAnnotation(ctx, step) {
|
|
|
11252
11382
|
const kept = keys.filter((key) => valuesEqual(now[key], left[key]));
|
|
11253
11383
|
if (kept.length === 0) return leftAlone(ctx, reports);
|
|
11254
11384
|
const writable = (key, value) => key === "actions" && current.subtype === "widget" ? (0, _embedpdf_engine_core_runtime.widgetActionsOf)(value) : value;
|
|
11255
|
-
const done = writeUpdate$
|
|
11385
|
+
const done = writeUpdate$2(ctx, ref, Object.fromEntries(kept.map((key) => [key, writable(key, restore[key])])), reports);
|
|
11256
11386
|
const skipped = keys.filter((key) => !kept.includes(key));
|
|
11257
11387
|
const item = done.item;
|
|
11258
11388
|
if (skipped.length === 0 || (0, _embedpdf_engine_core_runtime.isSkippedItem)(item) || item.type !== reports) return done;
|
|
@@ -11276,7 +11406,7 @@ function removeAnnotation(ctx, step) {
|
|
|
11276
11406
|
}))) return leftAlone(ctx, "annotations.delete");
|
|
11277
11407
|
for (const capability of (0, _embedpdf_engine_core_runtime.annotationWriteCapabilities)(members)) (0, _embedpdf_engine_core_runtime.authorizeUnprotected)(ctx.authority, capability);
|
|
11278
11408
|
if (step.rights) authorizeImportRights(ctx.authority);
|
|
11279
|
-
return writeDelete$
|
|
11409
|
+
return writeDelete$4(ctx, step.ref, step.rights);
|
|
11280
11410
|
}
|
|
11281
11411
|
/**
|
|
11282
11412
|
* Brings back what a delete removed, at the same numbers and positions. Skipped
|
|
@@ -11397,7 +11527,7 @@ function reorderItem(ctx, family, page, order, moved) {
|
|
|
11397
11527
|
* An update of `ref` with `patch`, recording the dictionaries it writes, and
|
|
11398
11528
|
* reported as `reports`: an annotation's update, or a widget's form op.
|
|
11399
11529
|
*/
|
|
11400
|
-
function writeUpdate$
|
|
11530
|
+
function writeUpdate$2(ctx, ref, patch, reports, resources) {
|
|
11401
11531
|
promoteInlineAnnotations(ctx.runtime, ctx.session, ref.page.objectNumber);
|
|
11402
11532
|
const before = readAnnotation(ctx, ref);
|
|
11403
11533
|
const written = before.subtype === "widget" ? (0, _embedpdf_engine_core_runtime.fitWidgetToFamily)(before.fieldFamily, patch) : patch;
|
|
@@ -11445,7 +11575,7 @@ function updatedItem(ctx, reports, ref, result) {
|
|
|
11445
11575
|
}
|
|
11446
11576
|
/** A delete of `ref` and what goes with it, capturing them first. */
|
|
11447
11577
|
/** Deletes `ref` and what goes with it; `rights` is what a step under an import's rights checked. */
|
|
11448
|
-
function writeDelete$
|
|
11578
|
+
function writeDelete$4(ctx, ref, rights) {
|
|
11449
11579
|
const page = ref.page.objectNumber;
|
|
11450
11580
|
promoteInlineAnnotations(ctx.runtime, ctx.session, page);
|
|
11451
11581
|
const members = (0, _embedpdf_engine_core_runtime.deletedWith)(listPage(ctx, page), ref);
|
|
@@ -11562,66 +11692,314 @@ function objectNumberOf$1(ctx, ref) {
|
|
|
11562
11692
|
}
|
|
11563
11693
|
}
|
|
11564
11694
|
//#endregion
|
|
11565
|
-
//#region src/features/
|
|
11566
|
-
const WIDGET_SUBTYPE_CODE = 20;
|
|
11695
|
+
//#region src/features/attachments/AttachmentReader.ts
|
|
11567
11696
|
/**
|
|
11568
|
-
*
|
|
11569
|
-
*
|
|
11570
|
-
*
|
|
11571
|
-
*
|
|
11572
|
-
*
|
|
11573
|
-
*
|
|
11574
|
-
*
|
|
11575
|
-
*
|
|
11576
|
-
*
|
|
11697
|
+
* Read-only access to embedded files, at both of their homes: the
|
|
11698
|
+
* document catalog's `/EmbeddedFiles` name tree (list / readFile by
|
|
11699
|
+
* index) and a FileAttachment annotation's `/FS` (readAnnotationFile by
|
|
11700
|
+
* ref). Pure reads over the session — no layer artifacts (the
|
|
11701
|
+
* `PagesExtractor` shape).
|
|
11702
|
+
*
|
|
11703
|
+
* Byte delivery mirrors the request's `path?`: absent → a standalone
|
|
11704
|
+
* buffer for the transfer list (browser); present → the decoded file is
|
|
11705
|
+
* streamed to `path` via `FPDF_FILEWRITE` (server), and only metadata
|
|
11706
|
+
* rides the response.
|
|
11577
11707
|
*/
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
if (annotPtr === _embedpdf_engine_runtime.NULL_PTR) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "failed to create widget annotation");
|
|
11583
|
-
try {
|
|
11584
|
-
writeAnnotString(fn, mem, annotPtr, "NM", (0, _embedpdf_engine_core_runtime.generateUuidV7)());
|
|
11585
|
-
const { page: _page, rect, rotation, exportValue: _exportValue, actions, ...style } = placement;
|
|
11586
|
-
setAnnotRect(fn, mem, annotPtr, rect);
|
|
11587
|
-
if (rotation) setWidgetTurn(fn, annotPtr, rotation);
|
|
11588
|
-
if (Object.keys(style).length > 0) applyWidgetStyle(fn, mem, annotPtr, style);
|
|
11589
|
-
if (actions) writeWidgetActions(runtime, docPtr, annotPtr, actions);
|
|
11590
|
-
const widgetObjectNumber = fn.EPDFAnnot_GetObjectNumber(annotPtr);
|
|
11591
|
-
if (widgetObjectNumber <= 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "widget annotation has no object number");
|
|
11592
|
-
return widgetObjectNumber;
|
|
11593
|
-
} finally {
|
|
11594
|
-
fn.FPDFPage_CloseAnnot(annotPtr);
|
|
11708
|
+
var AttachmentReader = class {
|
|
11709
|
+
constructor(runtime, session) {
|
|
11710
|
+
this.runtime = runtime;
|
|
11711
|
+
this.session = session;
|
|
11595
11712
|
}
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11713
|
+
/** Snapshot of the `/EmbeddedFiles` name tree, in tree (key-sorted) order. */
|
|
11714
|
+
list(signal) {
|
|
11715
|
+
throwIfAborted(signal);
|
|
11716
|
+
const { fn, mem } = this.runtime;
|
|
11717
|
+
const docPtr = this.session.requireDocPtr();
|
|
11718
|
+
const count = fn.FPDFDoc_GetAttachmentCount(docPtr);
|
|
11719
|
+
const items = [];
|
|
11720
|
+
for (let index = 0; index < count; index++) {
|
|
11721
|
+
const attachmentPtr = fn.FPDFDoc_GetAttachment(docPtr, index);
|
|
11722
|
+
if (!attachmentPtr) continue;
|
|
11723
|
+
const key = readAttachmentKey(fn, mem, docPtr, index) ?? "";
|
|
11724
|
+
items.push({
|
|
11725
|
+
...readAttachmentFileInfo(fn, mem, attachmentPtr),
|
|
11726
|
+
ref: (0, _embedpdf_engine_core_runtime.toAttachmentRef)(key),
|
|
11727
|
+
index
|
|
11728
|
+
});
|
|
11729
|
+
}
|
|
11730
|
+
return items;
|
|
11731
|
+
}
|
|
11732
|
+
/** Decode one document-level embedded file, addressed by key. */
|
|
11733
|
+
readFile(ref, path, maxDecodedBytes, signal) {
|
|
11734
|
+
throwIfAborted(signal);
|
|
11735
|
+
const { fn, mem } = this.runtime;
|
|
11736
|
+
const docPtr = this.session.requireDocPtr();
|
|
11737
|
+
const index = resolveAttachmentIndex(fn, mem, docPtr, ref);
|
|
11738
|
+
if (index < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no embedded file with key '${ref.key}' in the document`);
|
|
11739
|
+
const attachmentPtr = fn.FPDFDoc_GetAttachment(docPtr, index);
|
|
11740
|
+
if (!attachmentPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `embedded file with key '${ref.key}' resolved to index ${index} but could not be opened`);
|
|
11741
|
+
const info = readAttachmentFileInfo(fn, mem, attachmentPtr);
|
|
11742
|
+
return this.extract(attachmentPtr, info.name, info.mimeType ?? void 0, path, maxDecodedBytes);
|
|
11743
|
+
}
|
|
11744
|
+
/** Decode the file embedded in a FileAttachment annotation's `/FS`. */
|
|
11745
|
+
/** A file attachment annotation's file; a page not parsed yet loads in slices. */
|
|
11746
|
+
async readAnnotationFile(pageObjectNumber, ref, path, maxDecodedBytes, signal, slices) {
|
|
11747
|
+
throwIfAborted(signal);
|
|
11748
|
+
const { fn, mem } = this.runtime;
|
|
11749
|
+
const pool = this.session.pagePool();
|
|
11750
|
+
const pagePtr = await pool.acquireInSlices(pageObjectNumber, signal, slices);
|
|
11751
|
+
let annotPtr = null;
|
|
11752
|
+
try {
|
|
11753
|
+
annotPtr = resolveAnnotPtr(this.runtime, this.session, pagePtr, ref);
|
|
11754
|
+
const attachmentPtr = fn.FPDFAnnot_GetFileAttachment(annotPtr);
|
|
11755
|
+
if (!attachmentPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "annotation is not a file attachment or carries no embedded file (/FS)");
|
|
11756
|
+
const info = readAttachmentFileInfo(fn, mem, attachmentPtr);
|
|
11757
|
+
return this.extract(attachmentPtr, info.name, info.mimeType ?? void 0, path, maxDecodedBytes);
|
|
11758
|
+
} finally {
|
|
11759
|
+
if (annotPtr !== null) fn.FPDFPage_CloseAnnot(annotPtr);
|
|
11760
|
+
pool.release(pageObjectNumber);
|
|
11761
|
+
}
|
|
11762
|
+
}
|
|
11763
|
+
extract(attachmentPtr, name, mimeType, path, maxDecodedBytes) {
|
|
11764
|
+
if (path !== void 0) {
|
|
11765
|
+
const { size } = extractAttachmentToFile(this.runtime, attachmentPtr, path, maxDecodedBytes);
|
|
11766
|
+
return {
|
|
11767
|
+
name,
|
|
11768
|
+
...mimeType ? { mimeType } : {},
|
|
11769
|
+
size,
|
|
11770
|
+
path
|
|
11771
|
+
};
|
|
11772
|
+
}
|
|
11773
|
+
const { bytes, size } = extractAttachmentToBuffer(this.runtime, attachmentPtr, maxDecodedBytes);
|
|
11774
|
+
return {
|
|
11775
|
+
name,
|
|
11776
|
+
...mimeType ? { mimeType } : {},
|
|
11777
|
+
size,
|
|
11778
|
+
bytes
|
|
11779
|
+
};
|
|
11780
|
+
}
|
|
11781
|
+
};
|
|
11782
|
+
//#endregion
|
|
11783
|
+
//#region src/features/attachments/AttachmentMutator.ts
|
|
11784
|
+
/**
|
|
11785
|
+
* Create/delete writes over the document catalog's `/EmbeddedFiles` name
|
|
11786
|
+
* tree, sharing the write path (`writeAttachmentFilePayload`) with the
|
|
11787
|
+
* file-attachment annotation writer. The change ops run them
|
|
11788
|
+
* (`attachmentChanges.ts`), which decide what an undo needs.
|
|
11789
|
+
*
|
|
11790
|
+
* Identity is the name-tree key (unique by construction). Note the
|
|
11791
|
+
* tree is key-sorted: both writes shift other entries' indices; keys
|
|
11792
|
+
* never move.
|
|
11793
|
+
*/
|
|
11794
|
+
var AttachmentMutator = class {
|
|
11795
|
+
constructor(runtime, session) {
|
|
11796
|
+
this.runtime = runtime;
|
|
11797
|
+
this.session = session;
|
|
11798
|
+
}
|
|
11799
|
+
/**
|
|
11800
|
+
* Create an embedded file from `bytes`. `file.name` becomes the tree key
|
|
11801
|
+
* (PDFium writes key = /UF = /F); a duplicate key rejects with
|
|
11802
|
+
* `InvalidArg`. The result carries the read-back entry,
|
|
11803
|
+
* `checksum`/`size`/dates included.
|
|
11804
|
+
*/
|
|
11805
|
+
create(file, bytes, signal) {
|
|
11806
|
+
throwIfAborted(signal);
|
|
11807
|
+
const { fn, mem } = this.runtime;
|
|
11808
|
+
const docPtr = this.session.requireDocPtr();
|
|
11809
|
+
if (!bytes) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "an attachments.create op needs its bytes as its file resource", { details: { field: "resources.file" } });
|
|
11810
|
+
if (file.name.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "attachment file requires a name");
|
|
11811
|
+
if (resolveAttachmentIndex(fn, mem, docPtr, {
|
|
11812
|
+
kind: "key",
|
|
11813
|
+
key: file.name
|
|
11814
|
+
}) !== -1) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `an embedded file with key '${file.name}' already exists — keys are unique; rename and retry`);
|
|
11815
|
+
throwIfAborted(signal);
|
|
11816
|
+
const namePtr = mem.writeU16String(file.name);
|
|
11817
|
+
let attachmentPtr;
|
|
11818
|
+
try {
|
|
11819
|
+
attachmentPtr = fn.FPDFDoc_AddAttachment(docPtr, namePtr);
|
|
11820
|
+
} finally {
|
|
11821
|
+
mem.free(namePtr);
|
|
11822
|
+
}
|
|
11823
|
+
if (!attachmentPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "FPDFDoc_AddAttachment returned NULL");
|
|
11824
|
+
writeAttachmentFilePayload(fn, mem, attachmentPtr, docPtr, file, {
|
|
11825
|
+
bytes,
|
|
11826
|
+
...file.mimeType !== void 0 ? { mimeType: file.mimeType } : {},
|
|
11827
|
+
name: file.name
|
|
11828
|
+
});
|
|
11829
|
+
const ref = (0, _embedpdf_engine_core_runtime.toAttachmentRef)(file.name);
|
|
11830
|
+
const attachment = this.find(ref);
|
|
11831
|
+
if (!attachment) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "created attachment did not resolve by key after the write");
|
|
11832
|
+
return {
|
|
11833
|
+
attachment,
|
|
11834
|
+
meta: this.meta(ref)
|
|
11835
|
+
};
|
|
11836
|
+
}
|
|
11837
|
+
/** The entry under `ref`'s key as it reads now; `null` when no file has the key. */
|
|
11838
|
+
find(ref) {
|
|
11839
|
+
const { fn, mem } = this.runtime;
|
|
11840
|
+
const docPtr = this.session.requireDocPtr();
|
|
11841
|
+
const index = resolveAttachmentIndex(fn, mem, docPtr, ref);
|
|
11842
|
+
const attachmentPtr = index < 0 ? null : fn.FPDFDoc_GetAttachment(docPtr, index);
|
|
11843
|
+
if (!attachmentPtr) return null;
|
|
11844
|
+
return {
|
|
11845
|
+
...readAttachmentFileInfo(fn, mem, attachmentPtr),
|
|
11846
|
+
ref,
|
|
11847
|
+
index
|
|
11848
|
+
};
|
|
11849
|
+
}
|
|
11850
|
+
/** A write's meta, naming the file it changed. Cloud coherence pins are a server concern. */
|
|
11851
|
+
meta(ref) {
|
|
11852
|
+
return {
|
|
11853
|
+
affectedPages: [],
|
|
11854
|
+
cacheDelta: null,
|
|
11855
|
+
...this.session.writeStamp(),
|
|
11856
|
+
changed: [ref]
|
|
11857
|
+
};
|
|
11858
|
+
}
|
|
11859
|
+
/** Delete an embedded file by key. Unlinks the tree entry only. */
|
|
11860
|
+
delete(ref, signal) {
|
|
11861
|
+
throwIfAborted(signal);
|
|
11862
|
+
const { fn, mem } = this.runtime;
|
|
11863
|
+
const docPtr = this.session.requireDocPtr();
|
|
11864
|
+
const index = resolveAttachmentIndex(fn, mem, docPtr, ref);
|
|
11865
|
+
if (index < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no embedded file with key '${ref.key}' in the document`);
|
|
11866
|
+
if (!fn.FPDFDoc_DeleteAttachment(docPtr, index)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "FPDFDoc_DeleteAttachment returned false");
|
|
11867
|
+
return { meta: this.meta(ref) };
|
|
11868
|
+
}
|
|
11869
|
+
};
|
|
11870
|
+
//#endregion
|
|
11871
|
+
//#region src/features/changes/internal/attachmentChanges.ts
|
|
11872
|
+
const mutatorOf$2 = (ctx) => new AttachmentMutator(ctx.runtime, ctx.session);
|
|
11873
|
+
const authorize$2 = (ctx) => (0, _embedpdf_engine_core_runtime.authorizeCapability)(ctx.authority, "doc.attachments.modify");
|
|
11874
|
+
/** `attachments.create`; its reverse deletes the file while it is still the file this made. */
|
|
11875
|
+
function createAttachment(ctx, op) {
|
|
11876
|
+
authorize$2(ctx);
|
|
11877
|
+
const result = mutatorOf$2(ctx).create(op.file, op.resources.file, ctx.signal);
|
|
11878
|
+
return {
|
|
11879
|
+
item: {
|
|
11880
|
+
type: "attachments.create",
|
|
11881
|
+
...result
|
|
11882
|
+
},
|
|
11883
|
+
reverse: [removeStepOf$1(ctx, result.attachment.ref)]
|
|
11884
|
+
};
|
|
11885
|
+
}
|
|
11886
|
+
/** `attachments.delete`, captured in its transaction so an undo brings back the same file. */
|
|
11887
|
+
function deleteAttachment(ctx, op) {
|
|
11888
|
+
authorize$2(ctx);
|
|
11889
|
+
return writeDelete$3(ctx, op.ref);
|
|
11890
|
+
}
|
|
11891
|
+
/** Deletes a file a create or a restore brought, while it reads as the change left it. */
|
|
11892
|
+
function removeAttachment(ctx, step) {
|
|
11893
|
+
const now = canonicalAttachment(ctx.runtime, ctx.session.requireDocPtr(), step.ref.key);
|
|
11894
|
+
if (!now || !bytesEqual(now, step.left)) return leftAlone(ctx, "attachments.delete");
|
|
11895
|
+
authorize$2(ctx);
|
|
11896
|
+
return writeDelete$3(ctx, step.ref);
|
|
11897
|
+
}
|
|
11898
|
+
/** Brings back a file a delete removed, while no file has its key. */
|
|
11899
|
+
function restoreAttachment(ctx, step) {
|
|
11900
|
+
const mutator = mutatorOf$2(ctx);
|
|
11901
|
+
if (mutator.find(step.ref)) return leftAlone(ctx, "attachments.restore");
|
|
11902
|
+
authorize$2(ctx);
|
|
11903
|
+
importAttachment(ctx.runtime, ctx.session.requireDocPtr(), step.capture);
|
|
11904
|
+
const attachment = mutator.find(step.ref);
|
|
11905
|
+
if (!attachment) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `attachment '${step.ref.key}' did not come back`);
|
|
11906
|
+
return {
|
|
11907
|
+
item: {
|
|
11908
|
+
type: "attachments.restore",
|
|
11909
|
+
attachment,
|
|
11910
|
+
meta: mutator.meta(step.ref)
|
|
11911
|
+
},
|
|
11912
|
+
reverse: [removeStepOf$1(ctx, step.ref)]
|
|
11913
|
+
};
|
|
11914
|
+
}
|
|
11915
|
+
/** Deletes the file under `ref`, captured first so its restore brings it back. */
|
|
11916
|
+
function writeDelete$3(ctx, ref) {
|
|
11917
|
+
const mutator = mutatorOf$2(ctx);
|
|
11918
|
+
if (!mutator.find(ref)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no embedded file with key '${ref.key}' in the document`, { details: { field: "ref" } });
|
|
11919
|
+
const capture = exportAttachment(ctx.runtime, ctx.session.requireDocPtr(), ref.key);
|
|
11920
|
+
return {
|
|
11921
|
+
item: {
|
|
11922
|
+
type: "attachments.delete",
|
|
11923
|
+
...mutator.delete(ref, ctx.signal)
|
|
11924
|
+
},
|
|
11925
|
+
reverse: [{
|
|
11926
|
+
kind: "attachments.restore",
|
|
11927
|
+
ref,
|
|
11928
|
+
capture
|
|
11929
|
+
}]
|
|
11930
|
+
};
|
|
11931
|
+
}
|
|
11932
|
+
/** The step that deletes the file under `ref` again while it reads as it does now. */
|
|
11933
|
+
function removeStepOf$1(ctx, ref) {
|
|
11934
|
+
const left = canonicalAttachment(ctx.runtime, ctx.session.requireDocPtr(), ref.key);
|
|
11935
|
+
if (!left) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `attachment '${ref.key}' is not there to guard`);
|
|
11936
|
+
return {
|
|
11937
|
+
kind: "attachments.remove",
|
|
11938
|
+
ref,
|
|
11939
|
+
left
|
|
11940
|
+
};
|
|
11941
|
+
}
|
|
11942
|
+
//#endregion
|
|
11943
|
+
//#region src/features/forms/internal/authorWidget.ts
|
|
11944
|
+
const WIDGET_SUBTYPE_CODE = 20;
|
|
11945
|
+
/**
|
|
11946
|
+
* Birth a widget through the annotation plane (EPDFPage_CreateAnnotRaw -
|
|
11947
|
+
* indirect, durable object number, no page load), place it and turn it
|
|
11948
|
+
* (`/MK /R`), style it with the placement's style fields through the
|
|
11949
|
+
* widget-plane writer (`applyWidgetStyle` - the same code the widget
|
|
11950
|
+
* annotation kind uses for create/patch), and give it the placement's
|
|
11951
|
+
* actions. Like every annotation the engine makes, it gets a fresh UUIDv7
|
|
11952
|
+
* `/NM`. Returns the widget's object number (`objectNumber`, which the
|
|
11953
|
+
* session checked, or the next free one), ready for EPDFForm_AttachWidget
|
|
11954
|
+
* adoption, which draws its appearance turned.
|
|
11955
|
+
*/
|
|
11956
|
+
function createUnattachedWidget(runtime, docPtr, pageIndex, placement, objectNumber) {
|
|
11957
|
+
const { fn, mem } = runtime;
|
|
11958
|
+
const annotPtr = fn.EPDFPage_CreateAnnotRaw(docPtr, pageIndex, WIDGET_SUBTYPE_CODE, objectNumber ?? 0);
|
|
11959
|
+
if (annotPtr === _embedpdf_engine_runtime.NULL_PTR && objectNumber !== void 0) throw objectNumberUnavailable(objectNumber, "taken");
|
|
11960
|
+
if (annotPtr === _embedpdf_engine_runtime.NULL_PTR) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "failed to create widget annotation");
|
|
11961
|
+
try {
|
|
11962
|
+
writeAnnotString(fn, mem, annotPtr, "NM", (0, _embedpdf_engine_core_runtime.generateUuidV7)());
|
|
11963
|
+
const { page: _page, rect, rotation, exportValue: _exportValue, actions, ...style } = placement;
|
|
11964
|
+
setAnnotRect(fn, mem, annotPtr, rect);
|
|
11965
|
+
if (rotation) setWidgetTurn(fn, annotPtr, rotation);
|
|
11966
|
+
if (Object.keys(style).length > 0) applyWidgetStyle(fn, mem, annotPtr, style);
|
|
11967
|
+
if (actions) writeWidgetActions(runtime, docPtr, annotPtr, actions);
|
|
11968
|
+
const widgetObjectNumber = fn.EPDFAnnot_GetObjectNumber(annotPtr);
|
|
11969
|
+
if (widgetObjectNumber <= 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "widget annotation has no object number");
|
|
11970
|
+
return widgetObjectNumber;
|
|
11971
|
+
} finally {
|
|
11972
|
+
fn.FPDFPage_CloseAnnot(annotPtr);
|
|
11973
|
+
}
|
|
11974
|
+
}
|
|
11975
|
+
//#endregion
|
|
11976
|
+
//#region src/features/forms/internal/fieldFlagBits.ts
|
|
11977
|
+
/**
|
|
11978
|
+
* /Ff bits writable through drafts and patches (1-based spec bit numbers).
|
|
11979
|
+
* Family-defining bits (Radio 1<<15, Pushbutton 1<<16, Combo 1<<17) are set
|
|
11980
|
+
* once by EPDFForm_CreateField and immutable afterwards - the native
|
|
11981
|
+
* setter rejects them.
|
|
11982
|
+
*/
|
|
11983
|
+
const FIELD_FLAG_BITS = {
|
|
11984
|
+
readOnly: 1,
|
|
11985
|
+
required: 2,
|
|
11986
|
+
noExport: 4,
|
|
11987
|
+
multiline: 4096,
|
|
11988
|
+
password: 8192,
|
|
11989
|
+
noToggleToOff: 16384,
|
|
11990
|
+
edit: 1 << 18,
|
|
11991
|
+
multiSelect: 1 << 21,
|
|
11992
|
+
comb: 1 << 24,
|
|
11993
|
+
radiosInUnison: 1 << 25
|
|
11994
|
+
};
|
|
11995
|
+
/**
|
|
11996
|
+
* Fold optional booleans into set/clear masks: `true` sets the bit,
|
|
11997
|
+
* `false` clears it, `undefined` leaves it alone.
|
|
11998
|
+
*/
|
|
11999
|
+
function flagMasks(flags) {
|
|
12000
|
+
let setBits = 0;
|
|
12001
|
+
let clearBits = 0;
|
|
12002
|
+
for (const [name, bit] of Object.entries(FIELD_FLAG_BITS)) {
|
|
11625
12003
|
const value = flags[name];
|
|
11626
12004
|
if (value === true) setBits |= bit;
|
|
11627
12005
|
else if (value === false) clearBits |= bit;
|
|
@@ -12527,8 +12905,8 @@ var FormMutator = class {
|
|
|
12527
12905
|
}
|
|
12528
12906
|
const { objectNumber, widgetObjectNumbers } = numbers;
|
|
12529
12907
|
if (widgetObjectNumbers && widgetObjectNumbers.length !== placements.length) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `forms.create needs one widget object number per widget: ${widgetObjectNumbers.length} for ${placements.length}`, { details: { field: "widgetObjectNumbers" } });
|
|
12530
|
-
if (objectNumber !== void 0) this.session.
|
|
12531
|
-
for (const number of widgetObjectNumbers ?? []) this.session.
|
|
12908
|
+
if (objectNumber !== void 0) this.session.claimObjectNumber(objectNumber);
|
|
12909
|
+
for (const number of widgetObjectNumbers ?? []) this.session.claimObjectNumber(number);
|
|
12532
12910
|
throwIfAborted(signal);
|
|
12533
12911
|
const fieldObjectNumber = this.createFieldNode(draft, objectNumber);
|
|
12534
12912
|
this.configureNewField(draft, fieldObjectNumber);
|
|
@@ -12748,9 +13126,9 @@ var FormMutator = class {
|
|
|
12748
13126
|
const pageIndex = this.preflightPlacement(fitted);
|
|
12749
13127
|
const onState = onStateOf(before.family, fitted);
|
|
12750
13128
|
const mergedPage = before.widgets.find((w) => w.objectNumber === resolved.fieldObjectNumber)?.page ?? null;
|
|
12751
|
-
if (numbers.objectNumber !== void 0) this.session.
|
|
13129
|
+
if (numbers.objectNumber !== void 0) this.session.claimObjectNumber(numbers.objectNumber);
|
|
12752
13130
|
const splitObjectNumber = mergedPage ? numbers.splitObjectNumber : void 0;
|
|
12753
|
-
if (splitObjectNumber !== void 0) this.session.
|
|
13131
|
+
if (splitObjectNumber !== void 0) this.session.claimObjectNumber(splitObjectNumber);
|
|
12754
13132
|
throwIfAborted(signal);
|
|
12755
13133
|
if (mergedPage) promoteInlineAnnotations(this.runtime, this.session, mergedPage.objectNumber);
|
|
12756
13134
|
const widgetObjectNumber = createUnattachedWidget(this.runtime, docPtr, pageIndex, fitted, numbers.objectNumber);
|
|
@@ -13315,14 +13693,14 @@ function updateField(ctx, op, opIndex) {
|
|
|
13315
13693
|
const before = readField(ctx, op.field);
|
|
13316
13694
|
if (op.patch.groupId !== void 0 && op.patch.groupId !== before.field.groupId) (0, _embedpdf_engine_core_runtime.authorizeFieldGroup)(ctx.authority, op.patch.groupId);
|
|
13317
13695
|
assertExpected(opIndex, propertiesOf(before.field), op.expect);
|
|
13318
|
-
return writeUpdate(ctx, before, op.patch);
|
|
13696
|
+
return writeUpdate$1(ctx, before, op.patch);
|
|
13319
13697
|
}
|
|
13320
13698
|
/** `forms.delete`; its reverse brings back the field, its widgets and the parents it pruned. */
|
|
13321
13699
|
function deleteField(ctx, op, opIndex) {
|
|
13322
13700
|
(0, _embedpdf_engine_core_runtime.authorizeCapability)(ctx.authority, "doc.forms.modify");
|
|
13323
13701
|
const before = readField(ctx, op.field);
|
|
13324
13702
|
assertExpected(opIndex, before.field, op.expect);
|
|
13325
|
-
return writeDelete(ctx, before);
|
|
13703
|
+
return writeDelete$2(ctx, before);
|
|
13326
13704
|
}
|
|
13327
13705
|
/**
|
|
13328
13706
|
* `forms.addWidget`. Its reverse puts back the field and the pages the widget
|
|
@@ -13451,7 +13829,7 @@ function updateWidget(ctx, op, opIndex) {
|
|
|
13451
13829
|
const current = readAnnotation(ctx, op.widget);
|
|
13452
13830
|
if (current.subtype !== "widget") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `${current.subtype} is not a widget: change it with doc.annotations.update`, { details: { field: "widget" } });
|
|
13453
13831
|
assertExpected(opIndex, current, op.expect);
|
|
13454
|
-
return writeUpdate$
|
|
13832
|
+
return writeUpdate$2(ctx, op.widget, {
|
|
13455
13833
|
...op.patch,
|
|
13456
13834
|
subtype: "widget"
|
|
13457
13835
|
}, "forms.updateWidget");
|
|
@@ -13727,7 +14105,7 @@ function revertProperties(ctx, fallback) {
|
|
|
13727
14105
|
const keys = Object.keys(left);
|
|
13728
14106
|
const kept = keys.filter((key) => valuesEqual(now[key], left[key]) && (key !== "name" || nameIsFree(ctx, current.field.name, String(restore.name))));
|
|
13729
14107
|
if (kept.length === 0) return leftAlone(ctx, "forms.update");
|
|
13730
|
-
const done = writeUpdate(ctx, current, pick$1(restore, kept));
|
|
14108
|
+
const done = writeUpdate$1(ctx, current, pick$1(restore, kept));
|
|
13731
14109
|
const skipped = keys.filter((key) => !kept.includes(key));
|
|
13732
14110
|
const item = done.item;
|
|
13733
14111
|
if (skipped.length === 0 || (0, _embedpdf_engine_core_runtime.isSkippedItem)(item) || item.type !== "forms.update") return done;
|
|
@@ -13752,7 +14130,7 @@ function removeField(ctx, step) {
|
|
|
13752
14130
|
if (step.rights) (0, _embedpdf_engine_core_runtime.authorizeCapability)(ctx.authority, "doc.forms.import");
|
|
13753
14131
|
const current = tryReadFieldByNumber(ctx, step.objectNumber);
|
|
13754
14132
|
if (!current || !valuesEqual(current.field, step.left)) return leftAlone(ctx, "forms.delete");
|
|
13755
|
-
return writeDelete(ctx, current, step.rights);
|
|
14133
|
+
return writeDelete$2(ctx, current, step.rights);
|
|
13756
14134
|
}
|
|
13757
14135
|
/**
|
|
13758
14136
|
* Brings back a field a delete removed. Left alone when another field took its
|
|
@@ -13887,7 +14265,7 @@ function writeEffect(ctx, type, write, before) {
|
|
|
13887
14265
|
* An update of the field `before` reads, recording the dictionaries it writes
|
|
13888
14266
|
* and the properties it changes: their old values, and the values it left.
|
|
13889
14267
|
*/
|
|
13890
|
-
function writeUpdate(ctx, before, patch) {
|
|
14268
|
+
function writeUpdate$1(ctx, before, patch) {
|
|
13891
14269
|
const ref = {
|
|
13892
14270
|
kind: "objectNumber",
|
|
13893
14271
|
objectNumber: before.objectNumber
|
|
@@ -13962,7 +14340,7 @@ function recordRevert(ctx, before, type, write, fallbackOf) {
|
|
|
13962
14340
|
* A delete of the field `before` reads, capturing it first. Its restore
|
|
13963
14341
|
* takes `rights` too.
|
|
13964
14342
|
*/
|
|
13965
|
-
function writeDelete(ctx, before, rights) {
|
|
14343
|
+
function writeDelete$2(ctx, before, rights) {
|
|
13966
14344
|
const capture = exportField(ctx.runtime, ctx.session.requireDocPtr(), before.objectNumber);
|
|
13967
14345
|
const ref = {
|
|
13968
14346
|
kind: "objectNumber",
|
|
@@ -14295,57 +14673,1153 @@ function entryOf(values, key) {
|
|
|
14295
14673
|
function pick(values, keys) {
|
|
14296
14674
|
return Object.fromEntries(keys.map((key) => [key, entryOf(values, key)]));
|
|
14297
14675
|
}
|
|
14298
|
-
//#endregion
|
|
14299
|
-
//#region src/features/changes/ChangeApplier.ts
|
|
14300
14676
|
/**
|
|
14301
|
-
*
|
|
14302
|
-
*
|
|
14303
|
-
* order
|
|
14304
|
-
*
|
|
14305
|
-
*
|
|
14306
|
-
*
|
|
14307
|
-
|
|
14308
|
-
|
|
14309
|
-
|
|
14310
|
-
*/
|
|
14311
|
-
|
|
14312
|
-
|
|
14313
|
-
|
|
14314
|
-
|
|
14315
|
-
|
|
14677
|
+
* The outline's shape as the document links it now, by object number: each
|
|
14678
|
+
* item's parent ({@link TOP} for a top-level item) and each parent's
|
|
14679
|
+
* children in order. Only what a write needs: no titles, no targets. An item
|
|
14680
|
+
* that isn't an indirect object is left out with the items under it, a loop
|
|
14681
|
+
* ends its list, and items deeper than a read goes aren't in it, as every
|
|
14682
|
+
* read of the outline does.
|
|
14683
|
+
*/
|
|
14684
|
+
var OutlineTree = class {
|
|
14685
|
+
constructor(fn, docPtr) {
|
|
14686
|
+
_defineProperty(this, "childrenByParent", /* @__PURE__ */ new Map());
|
|
14687
|
+
_defineProperty(this, "parentByItem", /* @__PURE__ */ new Map());
|
|
14688
|
+
const visit = (parentHandle, parent, depth) => {
|
|
14689
|
+
if (depth >= _embedpdf_engine_core_runtime.OUTLINE_READ_LIMITS.depth) return;
|
|
14690
|
+
const children = [];
|
|
14691
|
+
this.childrenByParent.set(parent, children);
|
|
14692
|
+
for (let handle = fn.FPDFBookmark_GetFirstChild(docPtr, parentHandle); handle; handle = fn.FPDFBookmark_GetNextSibling(docPtr, handle)) {
|
|
14693
|
+
const item = fn.EPDFOutline_GetObjectNumber(handle);
|
|
14694
|
+
if (!item) continue;
|
|
14695
|
+
if (this.parentByItem.has(item)) break;
|
|
14696
|
+
this.parentByItem.set(item, parent);
|
|
14697
|
+
children.push(item);
|
|
14698
|
+
visit(handle, item, depth + 1);
|
|
14699
|
+
}
|
|
14700
|
+
};
|
|
14701
|
+
visit(_embedpdf_engine_runtime.NULL_PTR, 0, 0);
|
|
14316
14702
|
}
|
|
14317
|
-
|
|
14318
|
-
|
|
14319
|
-
return collect(authority, ops.map((op, at) => () => runOp(ctx, op, at)), signal);
|
|
14703
|
+
has(item) {
|
|
14704
|
+
return this.parentByItem.has(item);
|
|
14320
14705
|
}
|
|
14321
|
-
|
|
14322
|
-
|
|
14323
|
-
return
|
|
14706
|
+
/** The item's parent ({@link TOP} at the top level); `undefined` when it isn't in the outline. */
|
|
14707
|
+
parentOf(item) {
|
|
14708
|
+
return this.parentByItem.get(item);
|
|
14324
14709
|
}
|
|
14325
|
-
|
|
14326
|
-
|
|
14327
|
-
|
|
14328
|
-
runtime: this.runtime,
|
|
14329
|
-
session: this.session,
|
|
14330
|
-
...this.fonts ? { fonts: this.fonts } : {},
|
|
14331
|
-
authority,
|
|
14332
|
-
signal,
|
|
14333
|
-
fillsOwnField: () => answer ?? (answer = ownFill(ctx))
|
|
14334
|
-
};
|
|
14335
|
-
return ctx;
|
|
14710
|
+
/** A parent's children in order: none for a leaf, or an item the outline doesn't have. */
|
|
14711
|
+
childrenOf(parent) {
|
|
14712
|
+
return this.childrenByParent.get(parent) ?? [];
|
|
14336
14713
|
}
|
|
14337
|
-
|
|
14338
|
-
|
|
14339
|
-
|
|
14340
|
-
|
|
14341
|
-
|
|
14342
|
-
for (const run of runs) {
|
|
14343
|
-
throwIfAborted(signal);
|
|
14344
|
-
const done = run();
|
|
14345
|
-
items.push(done.item);
|
|
14346
|
-
reverses.push(done.reverse);
|
|
14714
|
+
/** The item and the items under it, depth first: the item first. */
|
|
14715
|
+
subtreeOf(item) {
|
|
14716
|
+
const items = [item];
|
|
14717
|
+
for (const child of this.childrenOf(item)) items.push(...this.subtreeOf(child));
|
|
14718
|
+
return items;
|
|
14347
14719
|
}
|
|
14348
|
-
|
|
14720
|
+
/** How deep the item sits: 0 at the top level; `undefined` when it isn't in the outline. */
|
|
14721
|
+
levelOf(item) {
|
|
14722
|
+
let level = -1;
|
|
14723
|
+
for (let current = item; current; current = this.parentOf(current)) level += 1;
|
|
14724
|
+
return this.has(item) ? level : void 0;
|
|
14725
|
+
}
|
|
14726
|
+
/** Whether `node` is `item` or one of the items under it. */
|
|
14727
|
+
isAtOrUnder(node, item) {
|
|
14728
|
+
for (let current = node; current; current = this.parentOf(current)) if (current === item) return true;
|
|
14729
|
+
return false;
|
|
14730
|
+
}
|
|
14731
|
+
/** Every item, depth first: what an order of the whole outline is. */
|
|
14732
|
+
items() {
|
|
14733
|
+
return this.childrenOf(0).flatMap((item) => this.subtreeOf(item));
|
|
14734
|
+
}
|
|
14735
|
+
};
|
|
14736
|
+
//#endregion
|
|
14737
|
+
//#region src/features/outline/internal/readOutlineItem.ts
|
|
14738
|
+
/**
|
|
14739
|
+
* What one read of an outline may spend on its items' actions: one action
|
|
14740
|
+
* model per item at most, and room for a URI or a script on each. Past it,
|
|
14741
|
+
* an item's action reads as missing and the outline as incomplete.
|
|
14742
|
+
*/
|
|
14743
|
+
const OUTLINE_ACTION_READ_BUDGET = {
|
|
14744
|
+
maxModels: 5e4,
|
|
14745
|
+
maxNodes: 2e5,
|
|
14746
|
+
maxScriptCodeUnits: 16e6,
|
|
14747
|
+
maxTargetEntries: 65536,
|
|
14748
|
+
maxPayloadCodeUnits: 16e6
|
|
14749
|
+
};
|
|
14750
|
+
/** `/F` bit 1. */
|
|
14751
|
+
const ITALIC$1 = 1;
|
|
14752
|
+
/** `/F` bit 2. */
|
|
14753
|
+
const BOLD$1 = 2;
|
|
14754
|
+
/** A budget for one read of the outline's actions. */
|
|
14755
|
+
function outlineActionBudget() {
|
|
14756
|
+
return new ActionReadBudgetTracker(OUTLINE_ACTION_READ_BUDGET);
|
|
14757
|
+
}
|
|
14758
|
+
/**
|
|
14759
|
+
* The item `handle` reads, at `objectNumber` under `parent` ({@link TOP} for
|
|
14760
|
+
* the top level). Its target is read the way a link's is: the `/A` tree's
|
|
14761
|
+
* navigation view, else the `/Dest`; an action that can't be read makes the
|
|
14762
|
+
* target `unsupported`.
|
|
14763
|
+
*/
|
|
14764
|
+
function readOutlineItem(runtime, docPtr, handle, objectNumber, parent, budget) {
|
|
14765
|
+
const { fn, mem } = runtime;
|
|
14766
|
+
const title = readUtf16String(mem, (buf, capacity) => fn.FPDFBookmark_GetTitle(handle, buf, capacity)) ?? "";
|
|
14767
|
+
const hasChildren = fn.FPDFBookmark_GetFirstChild(docPtr, handle) !== _embedpdf_engine_runtime.NULL_PTR;
|
|
14768
|
+
const style = fn.FPDFBookmark_GetStyle(handle);
|
|
14769
|
+
const { action, target, complete } = readTarget(runtime, docPtr, handle, budget);
|
|
14770
|
+
return {
|
|
14771
|
+
item: {
|
|
14772
|
+
ref: (0, _embedpdf_engine_core_runtime.toOutlineItemRef)(objectNumber),
|
|
14773
|
+
parent: parent === 0 ? null : (0, _embedpdf_engine_core_runtime.toOutlineItemRef)(parent),
|
|
14774
|
+
title,
|
|
14775
|
+
target,
|
|
14776
|
+
action,
|
|
14777
|
+
open: hasChildren && fn.FPDFBookmark_GetCount(handle) > 0,
|
|
14778
|
+
color: readColor(runtime, handle),
|
|
14779
|
+
bold: (style & BOLD$1) !== 0,
|
|
14780
|
+
italic: (style & ITALIC$1) !== 0
|
|
14781
|
+
},
|
|
14782
|
+
complete
|
|
14783
|
+
};
|
|
14784
|
+
}
|
|
14785
|
+
function readTarget(runtime, docPtr, handle, budget) {
|
|
14786
|
+
const { fn, mem } = runtime;
|
|
14787
|
+
const actionPtr = fn.FPDFBookmark_GetAction(handle);
|
|
14788
|
+
if (actionPtr) try {
|
|
14789
|
+
const action = readActionModel(fn, mem, docPtr, fn.EPDFAction_LoadModel(actionPtr), budget);
|
|
14790
|
+
return {
|
|
14791
|
+
action,
|
|
14792
|
+
target: action ? linkTargetFromActionTree(action) : { kind: "unsupported" },
|
|
14793
|
+
complete: action !== null && !action.incomplete
|
|
14794
|
+
};
|
|
14795
|
+
} catch (error) {
|
|
14796
|
+
if (!_embedpdf_engine_core_runtime.EngineError.is(error)) throw error;
|
|
14797
|
+
return {
|
|
14798
|
+
action: null,
|
|
14799
|
+
target: { kind: "unsupported" },
|
|
14800
|
+
complete: false
|
|
14801
|
+
};
|
|
14802
|
+
}
|
|
14803
|
+
const destPtr = fn.FPDFBookmark_GetDest(docPtr, handle);
|
|
14804
|
+
if (!destPtr) return {
|
|
14805
|
+
action: null,
|
|
14806
|
+
target: null,
|
|
14807
|
+
complete: true
|
|
14808
|
+
};
|
|
14809
|
+
const destination = readDestination(fn, mem, docPtr, destPtr);
|
|
14810
|
+
return {
|
|
14811
|
+
action: null,
|
|
14812
|
+
target: destination ? {
|
|
14813
|
+
kind: "goto",
|
|
14814
|
+
destination
|
|
14815
|
+
} : { kind: "unsupported" },
|
|
14816
|
+
complete: true
|
|
14817
|
+
};
|
|
14818
|
+
}
|
|
14819
|
+
/** `/C` as `#rrggbb`, or `null` when the item has none. */
|
|
14820
|
+
function readColor(runtime, handle) {
|
|
14821
|
+
const { fn, mem } = runtime;
|
|
14822
|
+
return withScratchN(mem, [
|
|
14823
|
+
4,
|
|
14824
|
+
4,
|
|
14825
|
+
4
|
|
14826
|
+
], ([r, g, b]) => {
|
|
14827
|
+
if (!fn.FPDFBookmark_GetColor(handle, r, g, b)) return null;
|
|
14828
|
+
const channel = (ptr) => Math.min(255, Math.max(0, Math.round(readF32(mem, ptr) * 255)));
|
|
14829
|
+
return (0, _embedpdf_engine_core_runtime.colorOf)(channel(r), channel(g), channel(b));
|
|
14830
|
+
});
|
|
14831
|
+
}
|
|
14832
|
+
//#endregion
|
|
14833
|
+
//#region src/features/outline/OutlineReader.ts
|
|
14834
|
+
/**
|
|
14835
|
+
* Synchronous read of the document's outline, shared by the local worker and
|
|
14836
|
+
* the server. Dictionary reads only: no page is loaded. It never throws for
|
|
14837
|
+
* a bad outline: what it can't read (a loop, an item that isn't an indirect
|
|
14838
|
+
* object, a broken action, past `OUTLINE_READ_LIMITS`) it leaves out or
|
|
14839
|
+
* marks, and says `incomplete`.
|
|
14840
|
+
*/
|
|
14841
|
+
var OutlineReader = class {
|
|
14842
|
+
constructor(runtime, session) {
|
|
14843
|
+
this.runtime = runtime;
|
|
14844
|
+
this.session = session;
|
|
14845
|
+
}
|
|
14846
|
+
read(signal) {
|
|
14847
|
+
const { fn } = this.runtime;
|
|
14848
|
+
const docPtr = this.session.requireDocPtr();
|
|
14849
|
+
const budget = outlineActionBudget();
|
|
14850
|
+
const items = [];
|
|
14851
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14852
|
+
let incomplete = false;
|
|
14853
|
+
const visit = (parentHandle, parent, depth) => {
|
|
14854
|
+
for (let handle = fn.FPDFBookmark_GetFirstChild(docPtr, parentHandle); handle; handle = fn.FPDFBookmark_GetNextSibling(docPtr, handle)) {
|
|
14855
|
+
throwIfAborted(signal);
|
|
14856
|
+
const objectNumber = fn.EPDFOutline_GetObjectNumber(handle);
|
|
14857
|
+
if (!objectNumber) {
|
|
14858
|
+
incomplete = true;
|
|
14859
|
+
continue;
|
|
14860
|
+
}
|
|
14861
|
+
if (seen.has(objectNumber)) {
|
|
14862
|
+
incomplete = true;
|
|
14863
|
+
break;
|
|
14864
|
+
}
|
|
14865
|
+
if (items.length >= _embedpdf_engine_core_runtime.OUTLINE_READ_LIMITS.items) {
|
|
14866
|
+
incomplete = true;
|
|
14867
|
+
return false;
|
|
14868
|
+
}
|
|
14869
|
+
seen.add(objectNumber);
|
|
14870
|
+
const read = readOutlineItem(this.runtime, docPtr, handle, objectNumber, parent, budget);
|
|
14871
|
+
items.push(read.item);
|
|
14872
|
+
if (!read.complete) incomplete = true;
|
|
14873
|
+
if (fn.FPDFBookmark_GetFirstChild(docPtr, handle) === _embedpdf_engine_runtime.NULL_PTR) continue;
|
|
14874
|
+
if (depth + 1 >= _embedpdf_engine_core_runtime.OUTLINE_READ_LIMITS.depth) {
|
|
14875
|
+
incomplete = true;
|
|
14876
|
+
continue;
|
|
14877
|
+
}
|
|
14878
|
+
if (!visit(handle, objectNumber, depth + 1)) return false;
|
|
14879
|
+
}
|
|
14880
|
+
return true;
|
|
14881
|
+
};
|
|
14882
|
+
visit(_embedpdf_engine_runtime.NULL_PTR, 0, 0);
|
|
14883
|
+
return {
|
|
14884
|
+
items,
|
|
14885
|
+
incomplete
|
|
14886
|
+
};
|
|
14887
|
+
}
|
|
14888
|
+
/** The item `objectNumber`, under `parent` ({@link TOP} for the top level), as it reads now. */
|
|
14889
|
+
readItem(objectNumber, parent) {
|
|
14890
|
+
const docPtr = this.session.requireDocPtr();
|
|
14891
|
+
const handle = this.runtime.fn.EPDFOutline_GetItem(docPtr, objectNumber);
|
|
14892
|
+
if (!handle) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no outline item ${objectNumber}`, { details: { field: "ref" } });
|
|
14893
|
+
return readOutlineItem(this.runtime, docPtr, handle, objectNumber, parent, outlineActionBudget()).item;
|
|
14894
|
+
}
|
|
14895
|
+
};
|
|
14896
|
+
//#endregion
|
|
14897
|
+
//#region src/features/outline/internal/writeOutlineFields.ts
|
|
14898
|
+
/** `/F` bit 1. */
|
|
14899
|
+
const ITALIC = 1;
|
|
14900
|
+
/** `/F` bit 2. */
|
|
14901
|
+
const BOLD = 2;
|
|
14902
|
+
/** The most a PDF text string holds in every PDF app (ISO 32000-2 Annex C). */
|
|
14903
|
+
const MAX_TITLE_LENGTH = 32767;
|
|
14904
|
+
const HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
|
|
14905
|
+
/**
|
|
14906
|
+
* Refuses, before anything is written, fields no write can take: a title
|
|
14907
|
+
* too long, a colour that isn't `#rrggbb`, a target a write can't make.
|
|
14908
|
+
*/
|
|
14909
|
+
function assertWritableFields(fields) {
|
|
14910
|
+
const refuse = (field, message) => {
|
|
14911
|
+
throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, message, { details: { field } });
|
|
14912
|
+
};
|
|
14913
|
+
if (fields.title !== void 0) {
|
|
14914
|
+
if (typeof fields.title !== "string") refuse("title", "a title is a string");
|
|
14915
|
+
if (fields.title.length > MAX_TITLE_LENGTH) refuse("title", `a title holds at most ${MAX_TITLE_LENGTH} characters`);
|
|
14916
|
+
}
|
|
14917
|
+
if (fields.color != null && !HEX_COLOR.test(fields.color)) refuse("color", "expected a color as '#rrggbb'");
|
|
14918
|
+
const target = fields.target;
|
|
14919
|
+
if (target != null) {
|
|
14920
|
+
if (!(target.kind === "goto" || target.kind === "uri" || target.kind === "named" && [
|
|
14921
|
+
"NextPage",
|
|
14922
|
+
"PrevPage",
|
|
14923
|
+
"FirstPage",
|
|
14924
|
+
"LastPage"
|
|
14925
|
+
].includes(target.name ?? ""))) refuse("target", `a '${target.kind}' target can't be written`);
|
|
14926
|
+
}
|
|
14927
|
+
}
|
|
14928
|
+
/**
|
|
14929
|
+
* Writes the fields `fields` names on the item `objectNumber`; the others
|
|
14930
|
+
* stay as they are. A target is always written as `/A`, as a link's is.
|
|
14931
|
+
*/
|
|
14932
|
+
function writeOutlineFields(runtime, docPtr, objectNumber, fields) {
|
|
14933
|
+
const { fn, mem } = runtime;
|
|
14934
|
+
const check = (ok, what) => {
|
|
14935
|
+
if (!ok) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `the ${what} of outline item ${objectNumber} could not be written`);
|
|
14936
|
+
};
|
|
14937
|
+
if (fields.title !== void 0) {
|
|
14938
|
+
const title = fields.title;
|
|
14939
|
+
check(withUtf16String(mem, title, (ptr) => fn.EPDFOutline_SetTitle(docPtr, objectNumber, ptr)), "title");
|
|
14940
|
+
}
|
|
14941
|
+
if (fields.target !== void 0) {
|
|
14942
|
+
const action = fields.target === null ? _embedpdf_engine_runtime.NULL_PTR : createLinkTargetAction(fn, mem, docPtr, fields.target);
|
|
14943
|
+
check(fn.EPDFOutline_SetAction(docPtr, objectNumber, action), "target");
|
|
14944
|
+
}
|
|
14945
|
+
if (fields.color !== void 0) if (fields.color === null) check(fn.EPDFOutline_SetColor(docPtr, objectNumber, false, 0, 0, 0), "colour");
|
|
14946
|
+
else {
|
|
14947
|
+
const { r, g, b } = (0, _embedpdf_engine_core_runtime.rgbOf)(fields.color);
|
|
14948
|
+
check(fn.EPDFOutline_SetColor(docPtr, objectNumber, true, r / 255, g / 255, b / 255), "colour");
|
|
14949
|
+
}
|
|
14950
|
+
if (fields.bold !== void 0 || fields.italic !== void 0) {
|
|
14951
|
+
let flags = fn.FPDFBookmark_GetStyle(fn.EPDFOutline_GetItem(docPtr, objectNumber));
|
|
14952
|
+
if (fields.bold !== void 0) flags = fields.bold ? flags | BOLD : flags & -3;
|
|
14953
|
+
if (fields.italic !== void 0) flags = fields.italic ? flags | ITALIC : flags & -2;
|
|
14954
|
+
check(fn.EPDFOutline_SetStyle(docPtr, objectNumber, flags), "style");
|
|
14955
|
+
}
|
|
14956
|
+
if (fields.open !== void 0) check(fn.EPDFOutline_SetOpen(docPtr, objectNumber, fields.open), "open state");
|
|
14957
|
+
}
|
|
14958
|
+
//#endregion
|
|
14959
|
+
//#region src/features/outline/OutlineMutator.ts
|
|
14960
|
+
/**
|
|
14961
|
+
* Synchronous outline writes, shared by the local worker and the server.
|
|
14962
|
+
* Each checks everything it can before the first native write, and answers
|
|
14963
|
+
* with what it wrote, re-read. The fork keeps the links and `/Count` right
|
|
14964
|
+
* (public/epdf_outline.h); what goes where is decided here.
|
|
14965
|
+
*/
|
|
14966
|
+
var OutlineMutator = class {
|
|
14967
|
+
constructor(runtime, session) {
|
|
14968
|
+
this.runtime = runtime;
|
|
14969
|
+
this.session = session;
|
|
14970
|
+
_defineProperty(this, "reader", void 0);
|
|
14971
|
+
this.reader = new OutlineReader(runtime, session);
|
|
14972
|
+
}
|
|
14973
|
+
/** The outline's shape as it is now. */
|
|
14974
|
+
tree() {
|
|
14975
|
+
return new OutlineTree(this.runtime.fn, this.session.requireDocPtr());
|
|
14976
|
+
}
|
|
14977
|
+
create(parent, position, data, objectNumber) {
|
|
14978
|
+
assertWritableFields(data);
|
|
14979
|
+
const tree = this.tree();
|
|
14980
|
+
const parentNumber = this.parentNumberOf(tree, parent);
|
|
14981
|
+
const children = tree.childrenOf(parentNumber);
|
|
14982
|
+
const at = (0, _embedpdf_engine_core_runtime.positionIndex)(children.map(_embedpdf_engine_core_runtime.toOutlineItemRef), position, keyOf);
|
|
14983
|
+
const after = at === 0 ? 0 : children[at - 1];
|
|
14984
|
+
if (objectNumber !== void 0) this.session.claimObjectNumber(objectNumber);
|
|
14985
|
+
const { fn, mem } = this.runtime;
|
|
14986
|
+
const docPtr = this.session.requireDocPtr();
|
|
14987
|
+
const created = withUtf16String(mem, data.title, (title) => fn.EPDFOutline_CreateItem(docPtr, objectNumber ?? 0, parentNumber, after, title));
|
|
14988
|
+
if (!created) throw outlineRefused("create the item");
|
|
14989
|
+
const { title: _title, ...fields } = data;
|
|
14990
|
+
writeOutlineFields(this.runtime, docPtr, created, fields);
|
|
14991
|
+
const item = this.reader.readItem(created, parentNumber);
|
|
14992
|
+
return {
|
|
14993
|
+
item,
|
|
14994
|
+
order: this.orderOf(this.tree(), parentNumber),
|
|
14995
|
+
meta: this.meta([item.ref])
|
|
14996
|
+
};
|
|
14997
|
+
}
|
|
14998
|
+
update(ref, patch) {
|
|
14999
|
+
assertWritableFields(patch);
|
|
15000
|
+
const tree = this.tree();
|
|
15001
|
+
const parent = this.requireItem(tree, ref, "ref");
|
|
15002
|
+
writeOutlineFields(this.runtime, this.session.requireDocPtr(), ref.objectNumber, patch);
|
|
15003
|
+
return {
|
|
15004
|
+
item: this.reader.readItem(ref.objectNumber, parent),
|
|
15005
|
+
meta: this.meta([ref])
|
|
15006
|
+
};
|
|
15007
|
+
}
|
|
15008
|
+
/**
|
|
15009
|
+
* The items go together, in the order given, under `parent` at `position`
|
|
15010
|
+
* among its children; the rules of a reorder (`reorderedList`) hold.
|
|
15011
|
+
*/
|
|
15012
|
+
move(refs, parent, position) {
|
|
15013
|
+
const tree = this.tree();
|
|
15014
|
+
const items = refs.map((ref) => {
|
|
15015
|
+
this.requireItem(tree, ref, "refs");
|
|
15016
|
+
return ref.objectNumber;
|
|
15017
|
+
});
|
|
15018
|
+
const moving = new Set(items);
|
|
15019
|
+
if (moving.size !== items.length) invalid("refs", "an item is named twice");
|
|
15020
|
+
for (const item of items) if (items.some((other) => other !== item && tree.isAtOrUnder(item, other))) invalid("refs", `item ${item} is under another item that moves`);
|
|
15021
|
+
const parentNumber = this.parentNumberOf(tree, parent);
|
|
15022
|
+
if (items.some((item) => tree.isAtOrUnder(parentNumber, item))) invalid("parent", "an item would go under itself");
|
|
15023
|
+
const staying = tree.childrenOf(parentNumber).filter((child) => !moving.has(child));
|
|
15024
|
+
const anchor = position === "start" || position === "end" ? null : "before" in position ? position.before : position.after;
|
|
15025
|
+
if (anchor && moving.has(anchor.objectNumber)) invalid("position", `the neighbour ${anchor.objectNumber} is one of the items that move`);
|
|
15026
|
+
const at = (0, _embedpdf_engine_core_runtime.positionIndex)(staying.map(_embedpdf_engine_core_runtime.toOutlineItemRef), position, keyOf);
|
|
15027
|
+
let after = at === 0 ? 0 : staying[at - 1];
|
|
15028
|
+
return this.place(items.map((item) => {
|
|
15029
|
+
const placement = {
|
|
15030
|
+
item,
|
|
15031
|
+
parent: parentNumber,
|
|
15032
|
+
after
|
|
15033
|
+
};
|
|
15034
|
+
after = item;
|
|
15035
|
+
return placement;
|
|
15036
|
+
}), tree);
|
|
15037
|
+
}
|
|
15038
|
+
/**
|
|
15039
|
+
* Puts items where `placements` say, in order: each right after its
|
|
15040
|
+
* `after` while that is a child of its parent, else first. What an undo of
|
|
15041
|
+
* a move does; the caller has checked that each item and parent is there.
|
|
15042
|
+
*/
|
|
15043
|
+
place(placements, before = this.tree()) {
|
|
15044
|
+
const { fn } = this.runtime;
|
|
15045
|
+
const docPtr = this.session.requireDocPtr();
|
|
15046
|
+
const parents = /* @__PURE__ */ new Set();
|
|
15047
|
+
for (const { item, parent, after } of placements) {
|
|
15048
|
+
parents.add(before.parentOf(item) ?? 0);
|
|
15049
|
+
parents.add(parent);
|
|
15050
|
+
const now = this.tree();
|
|
15051
|
+
const anchor = after && now.parentOf(after) === parent ? after : 0;
|
|
15052
|
+
if (!fn.EPDFOutline_MoveItem(docPtr, item, parent, anchor)) throw outlineRefused(`move item ${item}`);
|
|
15053
|
+
}
|
|
15054
|
+
const after = this.tree();
|
|
15055
|
+
return {
|
|
15056
|
+
orders: [...parents].map((parent) => this.orderOf(after, parent)),
|
|
15057
|
+
meta: this.meta(placements.map(({ item }) => (0, _embedpdf_engine_core_runtime.toOutlineItemRef)(item)))
|
|
15058
|
+
};
|
|
15059
|
+
}
|
|
15060
|
+
/** Where `items` are now, in document order: an anchor always comes before what follows it. */
|
|
15061
|
+
placementsOf(items, tree = this.tree()) {
|
|
15062
|
+
const wanted = new Set(items);
|
|
15063
|
+
return tree.items().filter((item) => wanted.has(item)).map((item) => {
|
|
15064
|
+
const parent = tree.parentOf(item);
|
|
15065
|
+
const siblings = tree.childrenOf(parent);
|
|
15066
|
+
const index = siblings.indexOf(item);
|
|
15067
|
+
return {
|
|
15068
|
+
item,
|
|
15069
|
+
parent,
|
|
15070
|
+
after: index > 0 ? siblings[index - 1] : 0
|
|
15071
|
+
};
|
|
15072
|
+
});
|
|
15073
|
+
}
|
|
15074
|
+
/** The item goes, with the items under it. */
|
|
15075
|
+
delete(ref) {
|
|
15076
|
+
const tree = this.tree();
|
|
15077
|
+
this.requireItem(tree, ref, "ref");
|
|
15078
|
+
const going = tree.subtreeOf(ref.objectNumber);
|
|
15079
|
+
if (!this.runtime.fn.EPDFOutline_DeleteItem(this.session.requireDocPtr(), ref.objectNumber)) throw outlineRefused(`delete item ${ref.objectNumber}`);
|
|
15080
|
+
return { meta: this.meta(going.map(_embedpdf_engine_core_runtime.toOutlineItemRef)) };
|
|
15081
|
+
}
|
|
15082
|
+
/**
|
|
15083
|
+
* The items a capture brought back (`importOutlineItem`, run by the
|
|
15084
|
+
* caller): the item first, then the items under it, and its parent's
|
|
15085
|
+
* children.
|
|
15086
|
+
*/
|
|
15087
|
+
restored(objectNumber) {
|
|
15088
|
+
const tree = this.tree();
|
|
15089
|
+
const parent = tree.parentOf(objectNumber);
|
|
15090
|
+
if (parent === void 0) throw outlineRefused(`restore item ${objectNumber}`);
|
|
15091
|
+
const items = this.subtree(objectNumber, tree);
|
|
15092
|
+
return {
|
|
15093
|
+
items,
|
|
15094
|
+
order: this.orderOf(tree, parent),
|
|
15095
|
+
meta: this.meta(items.map((item) => item.ref))
|
|
15096
|
+
};
|
|
15097
|
+
}
|
|
15098
|
+
/** The item as it reads now; `null` when the outline has no such item. */
|
|
15099
|
+
find(ref, tree = this.tree()) {
|
|
15100
|
+
const parent = tree.parentOf(ref.objectNumber);
|
|
15101
|
+
return parent === void 0 ? null : this.reader.readItem(ref.objectNumber, parent);
|
|
15102
|
+
}
|
|
15103
|
+
/** The item and the items under it, depth first, as they read now. */
|
|
15104
|
+
subtree(objectNumber, tree = this.tree()) {
|
|
15105
|
+
return tree.subtreeOf(objectNumber).map((item) => this.reader.readItem(item, tree.parentOf(item)));
|
|
15106
|
+
}
|
|
15107
|
+
/** An update's answer for an item written some other way: a revert put it back. */
|
|
15108
|
+
updated(ref) {
|
|
15109
|
+
const item = this.find(ref);
|
|
15110
|
+
if (!item) throw outlineRefused(`read item ${ref.objectNumber}`);
|
|
15111
|
+
return {
|
|
15112
|
+
item,
|
|
15113
|
+
meta: this.meta([ref])
|
|
15114
|
+
};
|
|
15115
|
+
}
|
|
15116
|
+
/**
|
|
15117
|
+
* Counts the items above `ref` again from the tree, as every outline write
|
|
15118
|
+
* does: a revert puts back the item's own dictionary, its `/Count` with
|
|
15119
|
+
* it, and nothing above it. Setting the open state it reads changes
|
|
15120
|
+
* nothing else.
|
|
15121
|
+
*/
|
|
15122
|
+
countAgain(ref) {
|
|
15123
|
+
const item = this.find(ref);
|
|
15124
|
+
if (!item) return;
|
|
15125
|
+
writeOutlineFields(this.runtime, this.session.requireDocPtr(), ref.objectNumber, { open: item.open });
|
|
15126
|
+
}
|
|
15127
|
+
/**
|
|
15128
|
+
* The number a parent ref names: {@link TOP} for `null`; `NotFound` when it
|
|
15129
|
+
* isn't an item; `InvalidArg` for an item at the deepest level a read goes
|
|
15130
|
+
* to, whose children no read shows.
|
|
15131
|
+
*/
|
|
15132
|
+
parentNumberOf(tree, parent) {
|
|
15133
|
+
if (parent === null) return 0;
|
|
15134
|
+
this.requireItem(tree, parent, "parent");
|
|
15135
|
+
if ((tree.levelOf(parent.objectNumber) ?? 0) >= _embedpdf_engine_core_runtime.OUTLINE_READ_LIMITS.depth - 1) invalid("parent", `an item under ${parent.objectNumber} would be deeper than reads go`);
|
|
15136
|
+
return parent.objectNumber;
|
|
15137
|
+
}
|
|
15138
|
+
/** The item's parent; `NotFound` when the outline has no such item. */
|
|
15139
|
+
requireItem(tree, ref, field) {
|
|
15140
|
+
const parent = tree.parentOf(ref.objectNumber);
|
|
15141
|
+
if (parent === void 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no outline item ${ref.objectNumber}`, { details: { field } });
|
|
15142
|
+
return parent;
|
|
15143
|
+
}
|
|
15144
|
+
orderOf(tree, parent) {
|
|
15145
|
+
return {
|
|
15146
|
+
parent: parent === 0 ? null : (0, _embedpdf_engine_core_runtime.toOutlineItemRef)(parent),
|
|
15147
|
+
children: tree.childrenOf(parent).map(_embedpdf_engine_core_runtime.toOutlineItemRef)
|
|
15148
|
+
};
|
|
15149
|
+
}
|
|
15150
|
+
meta(changed) {
|
|
15151
|
+
return {
|
|
15152
|
+
affectedPages: [],
|
|
15153
|
+
cacheDelta: null,
|
|
15154
|
+
...this.session.writeStamp(),
|
|
15155
|
+
changed
|
|
15156
|
+
};
|
|
15157
|
+
}
|
|
15158
|
+
};
|
|
15159
|
+
const keyOf = (ref) => String(ref.objectNumber);
|
|
15160
|
+
function invalid(field, message) {
|
|
15161
|
+
throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, message, { details: { field } });
|
|
15162
|
+
}
|
|
15163
|
+
/**
|
|
15164
|
+
* The fork refused a write the checks above let through: a list it would
|
|
15165
|
+
* change holds an item that isn't an indirect object, or `/Root /Outlines`
|
|
15166
|
+
* is a direct dictionary.
|
|
15167
|
+
*/
|
|
15168
|
+
function outlineRefused(what) {
|
|
15169
|
+
return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.MalformedPdf, `could not ${what}: the outline holds an item that isn't an indirect object`);
|
|
15170
|
+
}
|
|
15171
|
+
//#endregion
|
|
15172
|
+
//#region src/features/changes/internal/outlineChanges.ts
|
|
15173
|
+
/** The fields an update can change, each with a value a revert can put back. */
|
|
15174
|
+
const FIELDS = [
|
|
15175
|
+
"title",
|
|
15176
|
+
"target",
|
|
15177
|
+
"color",
|
|
15178
|
+
"bold",
|
|
15179
|
+
"italic",
|
|
15180
|
+
"open"
|
|
15181
|
+
];
|
|
15182
|
+
/** What a capture of an item follows: its action, and its destination. */
|
|
15183
|
+
const TARGET_KEYS = ["A", "Dest"];
|
|
15184
|
+
const mutatorOf$1 = (ctx) => new OutlineMutator(ctx.runtime, ctx.session);
|
|
15185
|
+
const authorize$1 = (ctx) => (0, _embedpdf_engine_core_runtime.authorizeCapability)(ctx.authority, "doc.outline.modify");
|
|
15186
|
+
/** `outline.create`; its reverse deletes the item while nobody changed it since. */
|
|
15187
|
+
function createOutlineItem(ctx, op) {
|
|
15188
|
+
authorize$1(ctx);
|
|
15189
|
+
const result = mutatorOf$1(ctx).create(op.parent, op.position ?? "end", op.data, op.objectNumber);
|
|
15190
|
+
return {
|
|
15191
|
+
item: {
|
|
15192
|
+
type: "outline.create",
|
|
15193
|
+
...result
|
|
15194
|
+
},
|
|
15195
|
+
reverse: [{
|
|
15196
|
+
kind: "outline.remove",
|
|
15197
|
+
ref: result.item.ref,
|
|
15198
|
+
left: guardOf(ctx, [result.item.ref])
|
|
15199
|
+
}]
|
|
15200
|
+
};
|
|
15201
|
+
}
|
|
15202
|
+
/**
|
|
15203
|
+
* `outline.update`; its reverse puts the item's dictionary back as it was,
|
|
15204
|
+
* or, when someone changed it since, the fields still holding what it set.
|
|
15205
|
+
*/
|
|
15206
|
+
function updateOutlineItem(ctx, op, opIndex) {
|
|
15207
|
+
authorize$1(ctx);
|
|
15208
|
+
const before = requireItem(ctx, op.ref);
|
|
15209
|
+
assertExpected(opIndex, before, op.expect);
|
|
15210
|
+
return writeUpdate(ctx, op.ref, op.patch, before);
|
|
15211
|
+
}
|
|
15212
|
+
/**
|
|
15213
|
+
* `outline.move`; its reverse puts each item back beside the sibling it
|
|
15214
|
+
* followed, and opens again a parent it emptied that was open.
|
|
15215
|
+
*/
|
|
15216
|
+
function moveOutlineItems(ctx, op) {
|
|
15217
|
+
authorize$1(ctx);
|
|
15218
|
+
const mutator = mutatorOf$1(ctx);
|
|
15219
|
+
const tree = mutator.tree();
|
|
15220
|
+
const items = op.refs.map((ref) => ref.objectNumber);
|
|
15221
|
+
const back = mutator.placementsOf(items, tree);
|
|
15222
|
+
const reopens = reopensOf(ctx, tree, items);
|
|
15223
|
+
const result = mutator.move(op.refs, op.parent, op.position);
|
|
15224
|
+
const parent = op.parent?.objectNumber ?? 0;
|
|
15225
|
+
return {
|
|
15226
|
+
item: {
|
|
15227
|
+
type: "outline.move",
|
|
15228
|
+
...result
|
|
15229
|
+
},
|
|
15230
|
+
reverse: [{
|
|
15231
|
+
kind: "outline.move",
|
|
15232
|
+
placements: back,
|
|
15233
|
+
left: items.map((item) => ({
|
|
15234
|
+
item,
|
|
15235
|
+
parent
|
|
15236
|
+
}))
|
|
15237
|
+
}, ...reopens]
|
|
15238
|
+
};
|
|
15239
|
+
}
|
|
15240
|
+
/** `outline.delete`; its reverse brings the item and the items under it back. */
|
|
15241
|
+
function deleteOutlineItem(ctx, op, opIndex) {
|
|
15242
|
+
authorize$1(ctx);
|
|
15243
|
+
assertExpected(opIndex, requireItem(ctx, op.ref), op.expect);
|
|
15244
|
+
return writeDelete$1(ctx, op.ref);
|
|
15245
|
+
}
|
|
15246
|
+
/** Puts an item's dictionary back, or the fields that still show the update (see `updateOutlineItem`). */
|
|
15247
|
+
function revertOutlineItem(ctx, step) {
|
|
15248
|
+
const ref = step.subject.outlineItem;
|
|
15249
|
+
const mutator = mutatorOf$1(ctx);
|
|
15250
|
+
const current = mutator.find(ref);
|
|
15251
|
+
if (!current) return leftAlone(ctx, "outline.update");
|
|
15252
|
+
authorize$1(ctx);
|
|
15253
|
+
if (unchangedSince(ctx.runtime, ctx.session, step.objects)) {
|
|
15254
|
+
const reverted = revertObjects(ctx.runtime, ctx.session, step.objects);
|
|
15255
|
+
mutator.countAgain(ref);
|
|
15256
|
+
const redo = captureAfter(ctx.runtime, ctx.session, reverted);
|
|
15257
|
+
return {
|
|
15258
|
+
item: {
|
|
15259
|
+
type: "outline.update",
|
|
15260
|
+
...mutator.updated(ref)
|
|
15261
|
+
},
|
|
15262
|
+
reverse: [{
|
|
15263
|
+
...step,
|
|
15264
|
+
objects: redo,
|
|
15265
|
+
...swapped(step.fallback)
|
|
15266
|
+
}]
|
|
15267
|
+
};
|
|
15268
|
+
}
|
|
15269
|
+
const fallback = step.fallback;
|
|
15270
|
+
if (fallback?.kind !== "outline") return leftAlone(ctx, "outline.update");
|
|
15271
|
+
const now = current;
|
|
15272
|
+
const left = fallback.left;
|
|
15273
|
+
const restore = fallback.restore;
|
|
15274
|
+
const keys = Object.keys(left);
|
|
15275
|
+
const kept = keys.filter((key) => valuesEqual(now[key], left[key]) && writable(ctx, key, restore[key]));
|
|
15276
|
+
if (kept.length === 0) return leftAlone(ctx, "outline.update");
|
|
15277
|
+
const done = writeUpdate(ctx, ref, Object.fromEntries(kept.map((key) => [key, restore[key]])), current);
|
|
15278
|
+
const skipped = keys.filter((key) => !kept.includes(key));
|
|
15279
|
+
if (skipped.length === 0 || done.item.type !== "outline.update") return done;
|
|
15280
|
+
return {
|
|
15281
|
+
...done,
|
|
15282
|
+
item: {
|
|
15283
|
+
...done.item,
|
|
15284
|
+
skipped
|
|
15285
|
+
}
|
|
15286
|
+
};
|
|
15287
|
+
}
|
|
15288
|
+
/**
|
|
15289
|
+
* Deletes what a create or a restore brought back, while the item and the
|
|
15290
|
+
* items under it are the same ones and read exactly as the change left them.
|
|
15291
|
+
*/
|
|
15292
|
+
function removeOutlineItem(ctx, step) {
|
|
15293
|
+
const tree = mutatorOf$1(ctx).tree();
|
|
15294
|
+
if (!tree.has(step.ref.objectNumber)) return leftAlone(ctx, "outline.delete");
|
|
15295
|
+
authorize$1(ctx);
|
|
15296
|
+
if (!valuesEqual(tree.subtreeOf(step.ref.objectNumber), step.left.map((object) => object.objectNumber)) || !unchangedSince(ctx.runtime, ctx.session, step.left)) return leftAlone(ctx, "outline.delete");
|
|
15297
|
+
return writeDelete$1(ctx, step.ref);
|
|
15298
|
+
}
|
|
15299
|
+
/** Brings back what a delete removed, while its parent is there and it isn't back already. */
|
|
15300
|
+
function restoreOutlineItem(ctx, step) {
|
|
15301
|
+
const mutator = mutatorOf$1(ctx);
|
|
15302
|
+
const tree = mutator.tree();
|
|
15303
|
+
if (tree.has(step.ref.objectNumber)) return leftAlone(ctx, "outline.restore");
|
|
15304
|
+
if (step.parent && !tree.has(step.parent.objectNumber)) return leftAlone(ctx, "outline.restore");
|
|
15305
|
+
authorize$1(ctx);
|
|
15306
|
+
importOutlineItem(ctx.runtime, ctx.session.requireDocPtr(), step.capture);
|
|
15307
|
+
const result = mutator.restored(step.ref.objectNumber);
|
|
15308
|
+
return {
|
|
15309
|
+
item: {
|
|
15310
|
+
type: "outline.restore",
|
|
15311
|
+
...result
|
|
15312
|
+
},
|
|
15313
|
+
reverse: [{
|
|
15314
|
+
kind: "outline.remove",
|
|
15315
|
+
ref: step.ref,
|
|
15316
|
+
left: guardOf(ctx, result.items.map((item) => item.ref))
|
|
15317
|
+
}]
|
|
15318
|
+
};
|
|
15319
|
+
}
|
|
15320
|
+
/**
|
|
15321
|
+
* Puts items back where a move found them: each still under the parent the
|
|
15322
|
+
* move left it under, its old parent still there, and not under itself.
|
|
15323
|
+
*/
|
|
15324
|
+
function moveOutlineItemsBack(ctx, step) {
|
|
15325
|
+
const mutator = mutatorOf$1(ctx);
|
|
15326
|
+
const tree = mutator.tree();
|
|
15327
|
+
const leftUnder = new Map(step.left.map(({ item, parent }) => [item, parent]));
|
|
15328
|
+
const placements = step.placements.filter(({ item, parent }) => tree.parentOf(item) === leftUnder.get(item) && (parent === 0 || tree.has(parent)) && !tree.isAtOrUnder(parent, item));
|
|
15329
|
+
if (placements.length === 0) return leftAlone(ctx, "outline.move");
|
|
15330
|
+
authorize$1(ctx);
|
|
15331
|
+
const items = placements.map(({ item }) => item);
|
|
15332
|
+
const back = mutator.placementsOf(items, tree);
|
|
15333
|
+
const reopens = reopensOf(ctx, tree, items);
|
|
15334
|
+
return {
|
|
15335
|
+
item: {
|
|
15336
|
+
type: "outline.move",
|
|
15337
|
+
...mutator.place(placements, tree)
|
|
15338
|
+
},
|
|
15339
|
+
reverse: [{
|
|
15340
|
+
kind: "outline.move",
|
|
15341
|
+
placements: back,
|
|
15342
|
+
left: placements.map(({ item, parent }) => ({
|
|
15343
|
+
item,
|
|
15344
|
+
parent
|
|
15345
|
+
}))
|
|
15346
|
+
}, ...reopens]
|
|
15347
|
+
};
|
|
15348
|
+
}
|
|
15349
|
+
/** Opens a parent again, as its step says (see `OutlineReopenStep`). */
|
|
15350
|
+
function reopenOutlineItem(ctx, step) {
|
|
15351
|
+
const mutator = mutatorOf$1(ctx);
|
|
15352
|
+
const tree = mutator.tree();
|
|
15353
|
+
const item = mutator.find(step.ref, tree);
|
|
15354
|
+
const children = tree.childrenOf(step.ref.objectNumber);
|
|
15355
|
+
const same = children.length === step.children.length && children.every((child) => step.children.includes(child));
|
|
15356
|
+
if (!item || item.open || !same) return leftAlone(ctx, "outline.update");
|
|
15357
|
+
authorize$1(ctx);
|
|
15358
|
+
return {
|
|
15359
|
+
item: {
|
|
15360
|
+
type: "outline.update",
|
|
15361
|
+
...mutator.update(step.ref, { open: true })
|
|
15362
|
+
},
|
|
15363
|
+
reverse: []
|
|
15364
|
+
};
|
|
15365
|
+
}
|
|
15366
|
+
/** An update, captured so it can be put back exactly. */
|
|
15367
|
+
function writeUpdate(ctx, ref, patch, before) {
|
|
15368
|
+
const pending = captureBefore(ctx.runtime, ctx.session, [{
|
|
15369
|
+
objectNumber: ref.objectNumber,
|
|
15370
|
+
deep: TARGET_KEYS
|
|
15371
|
+
}]);
|
|
15372
|
+
const result = mutatorOf$1(ctx).update(ref, patch);
|
|
15373
|
+
const objects = captureAfter(ctx.runtime, ctx.session, pending);
|
|
15374
|
+
return {
|
|
15375
|
+
item: {
|
|
15376
|
+
type: "outline.update",
|
|
15377
|
+
...result
|
|
15378
|
+
},
|
|
15379
|
+
reverse: [{
|
|
15380
|
+
kind: "objects.revert",
|
|
15381
|
+
reports: "outline.update",
|
|
15382
|
+
objects,
|
|
15383
|
+
subject: { outlineItem: ref },
|
|
15384
|
+
fallback: fallbackOf(before, result.item, patch)
|
|
15385
|
+
}]
|
|
15386
|
+
};
|
|
15387
|
+
}
|
|
15388
|
+
/**
|
|
15389
|
+
* A delete, captured in its transaction so an undo brings back the same
|
|
15390
|
+
* objects, and opens again the parent it emptied when it was open.
|
|
15391
|
+
*/
|
|
15392
|
+
function writeDelete$1(ctx, ref) {
|
|
15393
|
+
const mutator = mutatorOf$1(ctx);
|
|
15394
|
+
const tree = mutator.tree();
|
|
15395
|
+
const parent = tree.parentOf(ref.objectNumber);
|
|
15396
|
+
const reopens = reopensOf(ctx, tree, [ref.objectNumber]);
|
|
15397
|
+
const capture = exportOutlineItem(ctx.runtime, ctx.session.requireDocPtr(), ref.objectNumber);
|
|
15398
|
+
return {
|
|
15399
|
+
item: {
|
|
15400
|
+
type: "outline.delete",
|
|
15401
|
+
...mutator.delete(ref)
|
|
15402
|
+
},
|
|
15403
|
+
reverse: [{
|
|
15404
|
+
kind: "outline.restore",
|
|
15405
|
+
ref,
|
|
15406
|
+
parent: parent === void 0 || parent === 0 ? null : (0, _embedpdf_engine_core_runtime.toOutlineItemRef)(parent),
|
|
15407
|
+
capture
|
|
15408
|
+
}, ...reopens]
|
|
15409
|
+
};
|
|
15410
|
+
}
|
|
15411
|
+
/**
|
|
15412
|
+
* The parents that lose every child when `items` leave them, and are open:
|
|
15413
|
+
* each comes back closed with its children, so the undo opens it again.
|
|
15414
|
+
*/
|
|
15415
|
+
function reopensOf(ctx, tree, items) {
|
|
15416
|
+
const leaving = new Set(items);
|
|
15417
|
+
const parents = new Set(items.map((item) => tree.parentOf(item) ?? 0));
|
|
15418
|
+
parents.delete(0);
|
|
15419
|
+
return [...parents].flatMap((parent) => {
|
|
15420
|
+
const children = tree.childrenOf(parent);
|
|
15421
|
+
if (!children.every((child) => leaving.has(child))) return [];
|
|
15422
|
+
if (!mutatorOf$1(ctx).find((0, _embedpdf_engine_core_runtime.toOutlineItemRef)(parent), tree)?.open) return [];
|
|
15423
|
+
return [{
|
|
15424
|
+
kind: "outline.reopen",
|
|
15425
|
+
ref: (0, _embedpdf_engine_core_runtime.toOutlineItemRef)(parent),
|
|
15426
|
+
children
|
|
15427
|
+
}];
|
|
15428
|
+
});
|
|
15429
|
+
}
|
|
15430
|
+
/** The guard of items a change leaves: each dictionary as it reads now. */
|
|
15431
|
+
function guardOf(ctx, refs) {
|
|
15432
|
+
return captureAfter(ctx.runtime, ctx.session, refs.map((ref) => ({
|
|
15433
|
+
objectNumber: ref.objectNumber,
|
|
15434
|
+
deep: [],
|
|
15435
|
+
before: null
|
|
15436
|
+
})));
|
|
15437
|
+
}
|
|
15438
|
+
function requireItem(ctx, ref) {
|
|
15439
|
+
const item = mutatorOf$1(ctx).find(ref);
|
|
15440
|
+
if (!item) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no outline item ${ref.objectNumber}`, { details: { field: "ref" } });
|
|
15441
|
+
return item;
|
|
15442
|
+
}
|
|
15443
|
+
/** The fields an update changed: their values before, and the values it left. */
|
|
15444
|
+
function fallbackOf(before, after, patch) {
|
|
15445
|
+
const changed = FIELDS.filter((field) => patch[field] !== void 0 && !valuesEqual(before[field], after[field]));
|
|
15446
|
+
return {
|
|
15447
|
+
kind: "outline",
|
|
15448
|
+
restore: Object.fromEntries(changed.map((field) => [field, before[field]])),
|
|
15449
|
+
left: Object.fromEntries(changed.map((field) => [field, after[field]]))
|
|
15450
|
+
};
|
|
15451
|
+
}
|
|
15452
|
+
/**
|
|
15453
|
+
* Whether a value read back can be written again: a target another app made
|
|
15454
|
+
* may not be, nor one that goes to a page the document no longer has.
|
|
15455
|
+
*/
|
|
15456
|
+
function writable(ctx, field, value) {
|
|
15457
|
+
if (field !== "target" || value === null) return true;
|
|
15458
|
+
const target = value;
|
|
15459
|
+
if (target.kind === "goto") {
|
|
15460
|
+
const page = target.destination?.page.objectNumber;
|
|
15461
|
+
return ctx.session.allRecords().some((record) => record.pageObjectNumber === page);
|
|
15462
|
+
}
|
|
15463
|
+
if (target.kind === "uri") return true;
|
|
15464
|
+
return target.kind === "named" && [
|
|
15465
|
+
"NextPage",
|
|
15466
|
+
"PrevPage",
|
|
15467
|
+
"FirstPage",
|
|
15468
|
+
"LastPage"
|
|
15469
|
+
].includes(target.name ?? "");
|
|
15470
|
+
}
|
|
15471
|
+
function swapped(fallback) {
|
|
15472
|
+
if (fallback?.kind !== "outline") return fallback ? { fallback } : {};
|
|
15473
|
+
return { fallback: {
|
|
15474
|
+
kind: "outline",
|
|
15475
|
+
restore: fallback.left,
|
|
15476
|
+
left: fallback.restore
|
|
15477
|
+
} };
|
|
15478
|
+
}
|
|
15479
|
+
//#endregion
|
|
15480
|
+
//#region src/features/changes/internal/pageChanges.ts
|
|
15481
|
+
/** What a page's guard digests: the page, and what the layer holds of these. */
|
|
15482
|
+
const PAGE_DEEP_KEYS = [
|
|
15483
|
+
"Annots",
|
|
15484
|
+
"Contents",
|
|
15485
|
+
"Resources"
|
|
15486
|
+
];
|
|
15487
|
+
/**
|
|
15488
|
+
* What it leaves out of the page's bytes: its place (/Parent), so moving
|
|
15489
|
+
* pages around it changes nothing, and its /Rotate, which counts by value
|
|
15490
|
+
* instead: turning a page and turning it back reads the same.
|
|
15491
|
+
*/
|
|
15492
|
+
const PAGE_SKIPPED_KEYS = ["Parent", "Rotate"];
|
|
15493
|
+
/**
|
|
15494
|
+
* Runs a page op or step. It changes the page tree in place, so the
|
|
15495
|
+
* document's idle parsed pages close before and after it, as a
|
|
15496
|
+
* `contentWrite` job's do: no page that went, or came back, stays parsed.
|
|
15497
|
+
*/
|
|
15498
|
+
function changingPageTree(ctx, write) {
|
|
15499
|
+
const pool = ctx.session.pagePool();
|
|
15500
|
+
pool.closeIdle();
|
|
15501
|
+
try {
|
|
15502
|
+
return write();
|
|
15503
|
+
} finally {
|
|
15504
|
+
pool.closeIdle();
|
|
15505
|
+
}
|
|
15506
|
+
}
|
|
15507
|
+
const mutatorOf = (ctx) => new PagesMutator(ctx.runtime, ctx.session);
|
|
15508
|
+
const inserterOf = (ctx) => new PagesInserter(ctx.runtime, ctx.session);
|
|
15509
|
+
const authorize = (ctx) => (0, _embedpdf_engine_core_runtime.authorizeCapability)(ctx.authority, "doc.pages.assemble");
|
|
15510
|
+
/** `pages.rotate`; its reverse turns each page back while it still has this rotation. */
|
|
15511
|
+
function rotatePages(ctx, op) {
|
|
15512
|
+
authorize(ctx);
|
|
15513
|
+
const before = rotationsOf(layoutOf(ctx));
|
|
15514
|
+
const result = mutatorOf(ctx).rotate([...op.pages], op.rotation, ctx.signal);
|
|
15515
|
+
const pages = op.pages.map((page) => page.objectNumber);
|
|
15516
|
+
return {
|
|
15517
|
+
item: {
|
|
15518
|
+
type: "pages.rotate",
|
|
15519
|
+
pages: [...op.pages],
|
|
15520
|
+
rotation: op.rotation,
|
|
15521
|
+
...result
|
|
15522
|
+
},
|
|
15523
|
+
reverse: rotateSteps(pages, before, op.rotation)
|
|
15524
|
+
};
|
|
15525
|
+
}
|
|
15526
|
+
/** Turns pages to a step's rotation, those that still have the rotation it left. */
|
|
15527
|
+
function rotatePagesBack(ctx, step) {
|
|
15528
|
+
const now = rotationsOf(layoutOf(ctx));
|
|
15529
|
+
const pages = step.pages.filter((page) => now.get(page) === step.left);
|
|
15530
|
+
if (pages.length === 0) return leftAlone(ctx, "pages.rotate");
|
|
15531
|
+
authorize(ctx);
|
|
15532
|
+
const refs = pages.map(_embedpdf_engine_core_runtime.toPageRef);
|
|
15533
|
+
const result = mutatorOf(ctx).rotate(refs, step.rotation, ctx.signal);
|
|
15534
|
+
return {
|
|
15535
|
+
item: {
|
|
15536
|
+
type: "pages.rotate",
|
|
15537
|
+
pages: refs,
|
|
15538
|
+
rotation: step.rotation,
|
|
15539
|
+
...result
|
|
15540
|
+
},
|
|
15541
|
+
reverse: [{
|
|
15542
|
+
kind: "pages.rotate",
|
|
15543
|
+
pages,
|
|
15544
|
+
rotation: step.left,
|
|
15545
|
+
left: step.rotation
|
|
15546
|
+
}]
|
|
15547
|
+
};
|
|
15548
|
+
}
|
|
15549
|
+
/** `pages.reorder`; its reverse puts each page back after the page it followed. */
|
|
15550
|
+
function reorderPages(ctx, op) {
|
|
15551
|
+
authorize(ctx);
|
|
15552
|
+
const pages = op.pages.map((page) => page.objectNumber);
|
|
15553
|
+
const back = placementsOf(orderOf(ctx), pages);
|
|
15554
|
+
return {
|
|
15555
|
+
item: {
|
|
15556
|
+
type: "pages.reorder",
|
|
15557
|
+
...mutatorOf(ctx).reorder([...op.pages], op.position, ctx.signal)
|
|
15558
|
+
},
|
|
15559
|
+
reverse: [{
|
|
15560
|
+
kind: "pages.reorder",
|
|
15561
|
+
placements: back,
|
|
15562
|
+
left: placementsOf(orderOf(ctx), pages)
|
|
15563
|
+
}]
|
|
15564
|
+
};
|
|
15565
|
+
}
|
|
15566
|
+
/**
|
|
15567
|
+
* Puts pages back where a move found them: each still after the page the move
|
|
15568
|
+
* left it after, the page it goes after still there.
|
|
15569
|
+
*/
|
|
15570
|
+
function reorderPagesBack(ctx, step) {
|
|
15571
|
+
const order = orderOf(ctx);
|
|
15572
|
+
const leftAfter = new Map(step.left.map(({ page, after }) => [page, after]));
|
|
15573
|
+
const placements = step.placements.filter(({ page, after }) => {
|
|
15574
|
+
const at = order.indexOf(page);
|
|
15575
|
+
return at >= 0 && (order[at - 1] ?? 0) === leftAfter.get(page) && (after === 0 || order.includes(after));
|
|
15576
|
+
});
|
|
15577
|
+
if (placements.length === 0) return leftAlone(ctx, "pages.reorder");
|
|
15578
|
+
authorize(ctx);
|
|
15579
|
+
const pages = placements.map(({ page }) => page);
|
|
15580
|
+
const back = placementsOf(order, pages);
|
|
15581
|
+
const mutator = mutatorOf(ctx);
|
|
15582
|
+
let layout = null;
|
|
15583
|
+
for (const { page, after } of placements) {
|
|
15584
|
+
const position = after === 0 ? "start" : { after: (0, _embedpdf_engine_core_runtime.toPageRef)(after) };
|
|
15585
|
+
({layout} = mutator.reorder([(0, _embedpdf_engine_core_runtime.toPageRef)(page)], position, ctx.signal));
|
|
15586
|
+
}
|
|
15587
|
+
return {
|
|
15588
|
+
item: {
|
|
15589
|
+
type: "pages.reorder",
|
|
15590
|
+
pages: pages.map(_embedpdf_engine_core_runtime.toPageRef),
|
|
15591
|
+
layout,
|
|
15592
|
+
meta: metaOf(ctx)
|
|
15593
|
+
},
|
|
15594
|
+
reverse: [{
|
|
15595
|
+
kind: "pages.reorder",
|
|
15596
|
+
placements: back,
|
|
15597
|
+
left: placementsOf(orderOf(ctx), pages)
|
|
15598
|
+
}]
|
|
15599
|
+
};
|
|
15600
|
+
}
|
|
15601
|
+
/** `pages.delete`, captured in its transaction so an undo brings back the same pages. */
|
|
15602
|
+
function deletePages(ctx, op) {
|
|
15603
|
+
authorize(ctx);
|
|
15604
|
+
return writeDelete(ctx, inDisplayOrder(orderOf(ctx), ctx.session.resolvePageRefs(op.pages)));
|
|
15605
|
+
}
|
|
15606
|
+
/** Brings back pages a delete removed, those that aren't back already. */
|
|
15607
|
+
function restorePages(ctx, step) {
|
|
15608
|
+
const order = orderOf(ctx);
|
|
15609
|
+
const gone = step.pages.filter(({ page }) => !order.includes(page));
|
|
15610
|
+
if (gone.length === 0) return leftAlone(ctx, "pages.restore");
|
|
15611
|
+
authorize(ctx);
|
|
15612
|
+
const docPtr = ctx.session.requireDocPtr();
|
|
15613
|
+
for (const { capture } of gone) importPage(ctx.runtime, docPtr, capture);
|
|
15614
|
+
ctx.session.refreshPageRegistry();
|
|
15615
|
+
ctx.session.invalidateDerived();
|
|
15616
|
+
const pages = inDisplayOrder(orderOf(ctx), gone.map(({ page }) => page));
|
|
15617
|
+
return {
|
|
15618
|
+
item: {
|
|
15619
|
+
type: "pages.restore",
|
|
15620
|
+
pages: pages.map(_embedpdf_engine_core_runtime.toPageRef),
|
|
15621
|
+
layout: layoutOf(ctx),
|
|
15622
|
+
meta: metaOf(ctx)
|
|
15623
|
+
},
|
|
15624
|
+
reverse: [removeStepOf(ctx, pages)]
|
|
15625
|
+
};
|
|
15626
|
+
}
|
|
15627
|
+
/**
|
|
15628
|
+
* Deletes pages an insert or a restore brought, those that read as the change
|
|
15629
|
+
* left them; never every page of the document.
|
|
15630
|
+
*/
|
|
15631
|
+
function removePages(ctx, step) {
|
|
15632
|
+
const order = orderOf(ctx);
|
|
15633
|
+
const pages = step.pages.filter(({ page, left }) => order.includes(page) && digestOf(ctx, page) === left).map(({ page }) => page);
|
|
15634
|
+
if (pages.length === 0 || pages.length >= order.length) return leftAlone(ctx, "pages.delete");
|
|
15635
|
+
authorize(ctx);
|
|
15636
|
+
return writeDelete(ctx, inDisplayOrder(order, pages));
|
|
15637
|
+
}
|
|
15638
|
+
/** `pages.insertBlank`; its reverse deletes the new pages while nobody changed them. */
|
|
15639
|
+
function insertBlankPages(ctx, op) {
|
|
15640
|
+
authorize(ctx);
|
|
15641
|
+
const result = inserterOf(ctx).insertBlank(op.spec, op.position ?? "end", ctx.signal, op.objectNumbers);
|
|
15642
|
+
const pages = result.insertedPages.map((page) => page.objectNumber);
|
|
15643
|
+
return {
|
|
15644
|
+
item: {
|
|
15645
|
+
type: "pages.insertBlank",
|
|
15646
|
+
...result
|
|
15647
|
+
},
|
|
15648
|
+
reverse: [removeStepOf(ctx, pages)]
|
|
15649
|
+
};
|
|
15650
|
+
}
|
|
15651
|
+
/** `pages.insert`; its reverse deletes the pages it copied in while nobody changed them. */
|
|
15652
|
+
function insertPages(ctx, op) {
|
|
15653
|
+
authorize(ctx);
|
|
15654
|
+
const result = inserterOf(ctx).insert(bufferOf(op.source), op.position ?? "end", ctx.signal);
|
|
15655
|
+
ctx.session.invalidateDerived();
|
|
15656
|
+
const pages = result.insertedPages.map((page) => page.objectNumber);
|
|
15657
|
+
return {
|
|
15658
|
+
item: {
|
|
15659
|
+
type: "pages.insert",
|
|
15660
|
+
...result
|
|
15661
|
+
},
|
|
15662
|
+
reverse: [removeStepOf(ctx, pages)]
|
|
15663
|
+
};
|
|
15664
|
+
}
|
|
15665
|
+
/** Deletes `pages`, in display order, each captured first so its restore brings it back. */
|
|
15666
|
+
function writeDelete(ctx, pages) {
|
|
15667
|
+
const docPtr = ctx.session.requireDocPtr();
|
|
15668
|
+
const captures = pages.map((page) => ({
|
|
15669
|
+
page,
|
|
15670
|
+
capture: exportPage(ctx.runtime, docPtr, page)
|
|
15671
|
+
}));
|
|
15672
|
+
const refs = pages.map(_embedpdf_engine_core_runtime.toPageRef);
|
|
15673
|
+
const result = mutatorOf(ctx).delete(refs, ctx.signal);
|
|
15674
|
+
ctx.session.invalidateDerived();
|
|
15675
|
+
return {
|
|
15676
|
+
item: {
|
|
15677
|
+
type: "pages.delete",
|
|
15678
|
+
pages: refs,
|
|
15679
|
+
...result
|
|
15680
|
+
},
|
|
15681
|
+
reverse: [{
|
|
15682
|
+
kind: "pages.restore",
|
|
15683
|
+
pages: captures
|
|
15684
|
+
}]
|
|
15685
|
+
};
|
|
15686
|
+
}
|
|
15687
|
+
/** The step that deletes `pages` again while each reads as it does now. */
|
|
15688
|
+
function removeStepOf(ctx, pages) {
|
|
15689
|
+
return {
|
|
15690
|
+
kind: "pages.remove",
|
|
15691
|
+
pages: pages.map((page) => ({
|
|
15692
|
+
page,
|
|
15693
|
+
left: digestOf(ctx, page)
|
|
15694
|
+
}))
|
|
15695
|
+
};
|
|
15696
|
+
}
|
|
15697
|
+
/**
|
|
15698
|
+
* How a page reads now: the digest of its dictionary and of what the layer
|
|
15699
|
+
* holds of its annotations, contents and resources, as they read (canonical
|
|
15700
|
+
* bytes: a save that only encodes a stream again changes nothing), without
|
|
15701
|
+
* its place, and with its turn as a value.
|
|
15702
|
+
*/
|
|
15703
|
+
function digestOf(ctx, page) {
|
|
15704
|
+
const { fn, mem } = ctx.runtime;
|
|
15705
|
+
const docPtr = ctx.session.requireDocPtr();
|
|
15706
|
+
const bytes = canonicalDict(ctx.runtime, docPtr, page, PAGE_DEEP_KEYS, PAGE_SKIPPED_KEYS);
|
|
15707
|
+
const index = ctx.session.allRecords().find((record) => record.pageObjectNumber === page);
|
|
15708
|
+
const turn = index ? rotationAt(ctx.runtime, docPtr, index.pageIndex) : 0;
|
|
15709
|
+
const turned = new TextEncoder().encode(`\nturn ${turn}`);
|
|
15710
|
+
const digested = new Uint8Array(bytes.byteLength + turned.byteLength);
|
|
15711
|
+
digested.set(bytes);
|
|
15712
|
+
digested.set(turned, bytes.byteLength);
|
|
15713
|
+
return sha256HexOf(fn, mem, digested);
|
|
15714
|
+
}
|
|
15715
|
+
/** The steps that turn each page back, one for each rotation the pages had. */
|
|
15716
|
+
function rotateSteps(pages, before, left) {
|
|
15717
|
+
const byRotation = /* @__PURE__ */ new Map();
|
|
15718
|
+
for (const page of pages) {
|
|
15719
|
+
const rotation = before.get(page);
|
|
15720
|
+
byRotation.set(rotation, [...byRotation.get(rotation) ?? [], page]);
|
|
15721
|
+
}
|
|
15722
|
+
return [...byRotation].map(([rotation, group]) => ({
|
|
15723
|
+
kind: "pages.rotate",
|
|
15724
|
+
pages: group,
|
|
15725
|
+
rotation,
|
|
15726
|
+
left
|
|
15727
|
+
}));
|
|
15728
|
+
}
|
|
15729
|
+
/** Where each of `pages` is in `order`: the page it follows (0: first), in display order. */
|
|
15730
|
+
function placementsOf(order, pages) {
|
|
15731
|
+
return inDisplayOrder(order, pages).map((page) => ({
|
|
15732
|
+
page,
|
|
15733
|
+
after: order[order.indexOf(page) - 1] ?? 0
|
|
15734
|
+
}));
|
|
15735
|
+
}
|
|
15736
|
+
/** `pages` sorted as `order` has them. */
|
|
15737
|
+
function inDisplayOrder(order, pages) {
|
|
15738
|
+
return [...pages].sort((left, right) => order.indexOf(left) - order.indexOf(right));
|
|
15739
|
+
}
|
|
15740
|
+
/** The document's pages by object number, in display order. */
|
|
15741
|
+
function orderOf(ctx) {
|
|
15742
|
+
return ctx.session.allRecords().map((record) => record.pageObjectNumber);
|
|
15743
|
+
}
|
|
15744
|
+
function layoutOf(ctx) {
|
|
15745
|
+
return new PagesReader(ctx.runtime, ctx.session).read(ctx.signal);
|
|
15746
|
+
}
|
|
15747
|
+
function rotationsOf(layout) {
|
|
15748
|
+
return new Map(layout.pages.map((page) => [page.ref.objectNumber, page.rotation]));
|
|
15749
|
+
}
|
|
15750
|
+
function metaOf(ctx) {
|
|
15751
|
+
return {
|
|
15752
|
+
affectedPages: [],
|
|
15753
|
+
cacheDelta: null,
|
|
15754
|
+
...ctx.session.writeStamp()
|
|
15755
|
+
};
|
|
15756
|
+
}
|
|
15757
|
+
/** The bytes of `source` as an ArrayBuffer of their own. */
|
|
15758
|
+
function bufferOf(source) {
|
|
15759
|
+
return source.byteOffset === 0 && source.byteLength === source.buffer.byteLength ? source.buffer : source.slice().buffer;
|
|
15760
|
+
}
|
|
15761
|
+
//#endregion
|
|
15762
|
+
//#region src/features/changes/ChangeApplier.ts
|
|
15763
|
+
/**
|
|
15764
|
+
* The file's own coordinates, in and out: for a single verb's one-op change,
|
|
15765
|
+
* whose job converts at the worker's boundary.
|
|
15766
|
+
*/
|
|
15767
|
+
const FILE_SPACE = {
|
|
15768
|
+
opInFileSpace: (op) => op,
|
|
15769
|
+
itemOut: (item) => item
|
|
15770
|
+
};
|
|
15771
|
+
/**
|
|
15772
|
+
* Runs changes inside the job's layer transaction, which its caller opens and
|
|
15773
|
+
* ends, so a failure anywhere leaves the document as it was: a change's ops in
|
|
15774
|
+
* order (`apply`), or the steps of a recorded reverse (`undo`). Each op and
|
|
15775
|
+
* step is checked against the change's authority as it runs, as its single
|
|
15776
|
+
* verb's job checks it.
|
|
15777
|
+
*
|
|
15778
|
+
* Every op records the steps that reverse it as it runs, from what it found
|
|
15779
|
+
* and wrote; the record lists them in reverse op order, so each step meets the
|
|
15780
|
+
* document as the op after it left it.
|
|
15781
|
+
*/
|
|
15782
|
+
var ChangeApplier = class {
|
|
15783
|
+
constructor(runtime, session, fonts) {
|
|
15784
|
+
this.runtime = runtime;
|
|
15785
|
+
this.session = session;
|
|
15786
|
+
this.fonts = fonts;
|
|
15787
|
+
}
|
|
15788
|
+
apply(ops, authority, signal, space) {
|
|
15789
|
+
const ctx = this.context(authority, signal, (self) => opsFillOwnField(self, ops));
|
|
15790
|
+
return collect(authority, ops.map((op, at) => () => runOp(ctx, space.opInFileSpace(op), at)), space, signal);
|
|
15791
|
+
}
|
|
15792
|
+
undo(record, authority, signal, space) {
|
|
15793
|
+
const ctx = this.context(authority, signal, (self) => stepsFillOwnField(self, record.steps));
|
|
15794
|
+
return collect(authority, record.steps.map((step) => () => runStep(ctx, step)), space, signal);
|
|
15795
|
+
}
|
|
15796
|
+
context(authority, signal, ownFill) {
|
|
15797
|
+
let answer;
|
|
15798
|
+
const ctx = {
|
|
15799
|
+
runtime: this.runtime,
|
|
15800
|
+
session: this.session,
|
|
15801
|
+
...this.fonts ? { fonts: this.fonts } : {},
|
|
15802
|
+
authority,
|
|
15803
|
+
signal,
|
|
15804
|
+
fillsOwnField: () => answer ?? (answer = ownFill(ctx))
|
|
15805
|
+
};
|
|
15806
|
+
return ctx;
|
|
15807
|
+
}
|
|
15808
|
+
};
|
|
15809
|
+
/**
|
|
15810
|
+
* Runs each in order, its item measured as soon as it ran; the record
|
|
15811
|
+
* reverses them in the opposite order.
|
|
15812
|
+
*/
|
|
15813
|
+
function collect(authority, runs, space, signal) {
|
|
15814
|
+
const items = [];
|
|
15815
|
+
const reverses = [];
|
|
15816
|
+
for (const run of runs) {
|
|
15817
|
+
throwIfAborted(signal);
|
|
15818
|
+
const done = run();
|
|
15819
|
+
items.push(space.itemOut(done.item));
|
|
15820
|
+
reverses.push(done.reverse);
|
|
15821
|
+
}
|
|
15822
|
+
return {
|
|
14349
15823
|
items,
|
|
14350
15824
|
record: {
|
|
14351
15825
|
userId: authority.identity.userId ?? null,
|
|
@@ -14378,17 +15852,34 @@ function runOp(ctx, op, at) {
|
|
|
14378
15852
|
case "forms.importValues": return importFormValues(ctx, op);
|
|
14379
15853
|
case "metadata.update": return updateMetadata(ctx, op, at);
|
|
14380
15854
|
case "metadata.updateCustom": return updateCustomMetadata(ctx, op);
|
|
15855
|
+
case "outline.create": return createOutlineItem(ctx, op);
|
|
15856
|
+
case "outline.update": return updateOutlineItem(ctx, op, at);
|
|
15857
|
+
case "outline.move": return moveOutlineItems(ctx, op);
|
|
15858
|
+
case "outline.delete": return deleteOutlineItem(ctx, op, at);
|
|
15859
|
+
case "pages.rotate": return changingPageTree(ctx, () => rotatePages(ctx, op));
|
|
15860
|
+
case "pages.reorder": return changingPageTree(ctx, () => reorderPages(ctx, op));
|
|
15861
|
+
case "pages.delete": return changingPageTree(ctx, () => deletePages(ctx, op));
|
|
15862
|
+
case "pages.insertBlank": return changingPageTree(ctx, () => insertBlankPages(ctx, op));
|
|
15863
|
+
case "pages.insert": return changingPageTree(ctx, () => insertPages(ctx, op));
|
|
15864
|
+
case "attachments.create": return createAttachment(ctx, op);
|
|
15865
|
+
case "attachments.delete": return deleteAttachment(ctx, op);
|
|
14381
15866
|
}
|
|
14382
15867
|
}
|
|
14383
15868
|
function runStep(ctx, step) {
|
|
14384
15869
|
switch (step.kind) {
|
|
14385
|
-
case "objects.revert":
|
|
14386
|
-
|
|
14387
|
-
|
|
14388
|
-
|
|
14389
|
-
|
|
14390
|
-
|
|
14391
|
-
|
|
15870
|
+
case "objects.revert":
|
|
15871
|
+
if ("annotation" in step.subject) return revertAnnotation(ctx, {
|
|
15872
|
+
...step,
|
|
15873
|
+
subject: step.subject
|
|
15874
|
+
});
|
|
15875
|
+
if ("outlineItem" in step.subject) return revertOutlineItem(ctx, {
|
|
15876
|
+
...step,
|
|
15877
|
+
subject: step.subject
|
|
15878
|
+
});
|
|
15879
|
+
return revertForm(ctx, {
|
|
15880
|
+
...step,
|
|
15881
|
+
subject: step.subject
|
|
15882
|
+
});
|
|
14392
15883
|
case "annotation.remove": return removeAnnotation(ctx, step);
|
|
14393
15884
|
case "annotation.restore": return restoreAnnotations(ctx, step);
|
|
14394
15885
|
case "annotation.reorder": return reorderBack(ctx, step);
|
|
@@ -14398,6 +15889,16 @@ function runStep(ctx, step) {
|
|
|
14398
15889
|
case "widget.delete": return deleteRestoredWidget(ctx, step);
|
|
14399
15890
|
case "calculations.restore": return restoreCalculationOrder(ctx, step);
|
|
14400
15891
|
case "metadata.revert": return revertMetadata(ctx, step);
|
|
15892
|
+
case "outline.remove": return removeOutlineItem(ctx, step);
|
|
15893
|
+
case "outline.restore": return restoreOutlineItem(ctx, step);
|
|
15894
|
+
case "outline.move": return moveOutlineItemsBack(ctx, step);
|
|
15895
|
+
case "outline.reopen": return reopenOutlineItem(ctx, step);
|
|
15896
|
+
case "pages.rotate": return changingPageTree(ctx, () => rotatePagesBack(ctx, step));
|
|
15897
|
+
case "pages.reorder": return changingPageTree(ctx, () => reorderPagesBack(ctx, step));
|
|
15898
|
+
case "pages.restore": return changingPageTree(ctx, () => restorePages(ctx, step));
|
|
15899
|
+
case "pages.remove": return changingPageTree(ctx, () => removePages(ctx, step));
|
|
15900
|
+
case "attachments.remove": return removeAttachment(ctx, step);
|
|
15901
|
+
case "attachments.restore": return restoreAttachment(ctx, step);
|
|
14401
15902
|
}
|
|
14402
15903
|
}
|
|
14403
15904
|
//#endregion
|
|
@@ -14417,6 +15918,19 @@ function recordBytes(record) {
|
|
|
14417
15918
|
objects(step.unlinked);
|
|
14418
15919
|
break;
|
|
14419
15920
|
case "field.restore":
|
|
15921
|
+
case "outline.restore":
|
|
15922
|
+
bytes += step.capture.byteLength;
|
|
15923
|
+
break;
|
|
15924
|
+
case "outline.remove":
|
|
15925
|
+
objects(step.left);
|
|
15926
|
+
break;
|
|
15927
|
+
case "pages.restore":
|
|
15928
|
+
for (const { capture } of step.pages) bytes += capture.byteLength;
|
|
15929
|
+
break;
|
|
15930
|
+
case "attachments.remove":
|
|
15931
|
+
bytes += step.left.byteLength;
|
|
15932
|
+
break;
|
|
15933
|
+
case "attachments.restore":
|
|
14420
15934
|
bytes += step.capture.byteLength;
|
|
14421
15935
|
break;
|
|
14422
15936
|
default: break;
|
|
@@ -14562,172 +16076,6 @@ function unpackChangeRecord(reverse, capture) {
|
|
|
14562
16076
|
});
|
|
14563
16077
|
}
|
|
14564
16078
|
//#endregion
|
|
14565
|
-
//#region src/features/attachments/AttachmentReader.ts
|
|
14566
|
-
/**
|
|
14567
|
-
* Read-only access to embedded files, at both of their homes: the
|
|
14568
|
-
* document catalog's `/EmbeddedFiles` name tree (list / readFile by
|
|
14569
|
-
* index) and a FileAttachment annotation's `/FS` (readAnnotationFile by
|
|
14570
|
-
* ref). Pure reads over the session — no layer artifacts (the
|
|
14571
|
-
* `PagesExtractor` shape).
|
|
14572
|
-
*
|
|
14573
|
-
* Byte delivery mirrors the request's `path?`: absent → a standalone
|
|
14574
|
-
* buffer for the transfer list (browser); present → the decoded file is
|
|
14575
|
-
* streamed to `path` via `FPDF_FILEWRITE` (server), and only metadata
|
|
14576
|
-
* rides the response.
|
|
14577
|
-
*/
|
|
14578
|
-
var AttachmentReader = class {
|
|
14579
|
-
constructor(runtime, session) {
|
|
14580
|
-
this.runtime = runtime;
|
|
14581
|
-
this.session = session;
|
|
14582
|
-
}
|
|
14583
|
-
/** Snapshot of the `/EmbeddedFiles` name tree, in tree (key-sorted) order. */
|
|
14584
|
-
list(signal) {
|
|
14585
|
-
throwIfAborted(signal);
|
|
14586
|
-
const { fn, mem } = this.runtime;
|
|
14587
|
-
const docPtr = this.session.requireDocPtr();
|
|
14588
|
-
const count = fn.FPDFDoc_GetAttachmentCount(docPtr);
|
|
14589
|
-
const items = [];
|
|
14590
|
-
for (let index = 0; index < count; index++) {
|
|
14591
|
-
const attachmentPtr = fn.FPDFDoc_GetAttachment(docPtr, index);
|
|
14592
|
-
if (!attachmentPtr) continue;
|
|
14593
|
-
const key = readAttachmentKey(fn, mem, docPtr, index) ?? "";
|
|
14594
|
-
items.push({
|
|
14595
|
-
...readAttachmentFileInfo(fn, mem, attachmentPtr),
|
|
14596
|
-
ref: (0, _embedpdf_engine_core_runtime.toAttachmentRef)(key),
|
|
14597
|
-
index
|
|
14598
|
-
});
|
|
14599
|
-
}
|
|
14600
|
-
return items;
|
|
14601
|
-
}
|
|
14602
|
-
/** Decode one document-level embedded file, addressed by key. */
|
|
14603
|
-
readFile(ref, path, maxDecodedBytes, signal) {
|
|
14604
|
-
throwIfAborted(signal);
|
|
14605
|
-
const { fn, mem } = this.runtime;
|
|
14606
|
-
const docPtr = this.session.requireDocPtr();
|
|
14607
|
-
const index = resolveAttachmentIndex(fn, mem, docPtr, ref);
|
|
14608
|
-
if (index < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no embedded file with key '${ref.key}' in the document`);
|
|
14609
|
-
const attachmentPtr = fn.FPDFDoc_GetAttachment(docPtr, index);
|
|
14610
|
-
if (!attachmentPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `embedded file with key '${ref.key}' resolved to index ${index} but could not be opened`);
|
|
14611
|
-
const info = readAttachmentFileInfo(fn, mem, attachmentPtr);
|
|
14612
|
-
return this.extract(attachmentPtr, info.name, info.mimeType ?? void 0, path, maxDecodedBytes);
|
|
14613
|
-
}
|
|
14614
|
-
/** Decode the file embedded in a FileAttachment annotation's `/FS`. */
|
|
14615
|
-
/** A file attachment annotation's file; a page not parsed yet loads in slices. */
|
|
14616
|
-
async readAnnotationFile(pageObjectNumber, ref, path, maxDecodedBytes, signal, slices) {
|
|
14617
|
-
throwIfAborted(signal);
|
|
14618
|
-
const { fn, mem } = this.runtime;
|
|
14619
|
-
const pool = this.session.pagePool();
|
|
14620
|
-
const pagePtr = await pool.acquireInSlices(pageObjectNumber, signal, slices);
|
|
14621
|
-
let annotPtr = null;
|
|
14622
|
-
try {
|
|
14623
|
-
annotPtr = resolveAnnotPtr(this.runtime, this.session, pagePtr, ref);
|
|
14624
|
-
const attachmentPtr = fn.FPDFAnnot_GetFileAttachment(annotPtr);
|
|
14625
|
-
if (!attachmentPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "annotation is not a file attachment or carries no embedded file (/FS)");
|
|
14626
|
-
const info = readAttachmentFileInfo(fn, mem, attachmentPtr);
|
|
14627
|
-
return this.extract(attachmentPtr, info.name, info.mimeType ?? void 0, path, maxDecodedBytes);
|
|
14628
|
-
} finally {
|
|
14629
|
-
if (annotPtr !== null) fn.FPDFPage_CloseAnnot(annotPtr);
|
|
14630
|
-
pool.release(pageObjectNumber);
|
|
14631
|
-
}
|
|
14632
|
-
}
|
|
14633
|
-
extract(attachmentPtr, name, mimeType, path, maxDecodedBytes) {
|
|
14634
|
-
if (path !== void 0) {
|
|
14635
|
-
const { size } = extractAttachmentToFile(this.runtime, attachmentPtr, path, maxDecodedBytes);
|
|
14636
|
-
return {
|
|
14637
|
-
name,
|
|
14638
|
-
...mimeType ? { mimeType } : {},
|
|
14639
|
-
size,
|
|
14640
|
-
path
|
|
14641
|
-
};
|
|
14642
|
-
}
|
|
14643
|
-
const { bytes, size } = extractAttachmentToBuffer(this.runtime, attachmentPtr, maxDecodedBytes);
|
|
14644
|
-
return {
|
|
14645
|
-
name,
|
|
14646
|
-
...mimeType ? { mimeType } : {},
|
|
14647
|
-
size,
|
|
14648
|
-
bytes
|
|
14649
|
-
};
|
|
14650
|
-
}
|
|
14651
|
-
};
|
|
14652
|
-
//#endregion
|
|
14653
|
-
//#region src/features/attachments/AttachmentMutator.ts
|
|
14654
|
-
/**
|
|
14655
|
-
* Create/delete mutations over the document catalog's `/EmbeddedFiles`
|
|
14656
|
-
* name tree — the doc-level sibling of `AnnotationMutator`, sharing the
|
|
14657
|
-
* write path (`writeAttachmentFilePayload`) with the file-attachment
|
|
14658
|
-
* annotation writer.
|
|
14659
|
-
*
|
|
14660
|
-
* Identity is the name-tree key (unique by construction). Note the
|
|
14661
|
-
* tree is key-sorted: both mutations shift other entries' indices; keys
|
|
14662
|
-
* never move. Layer persistence (`finishMutation`) and event publication
|
|
14663
|
-
* happen in the callers, exactly like every other mutation family.
|
|
14664
|
-
*/
|
|
14665
|
-
var AttachmentMutator = class {
|
|
14666
|
-
constructor(runtime, session) {
|
|
14667
|
-
this.runtime = runtime;
|
|
14668
|
-
this.session = session;
|
|
14669
|
-
}
|
|
14670
|
-
/**
|
|
14671
|
-
* Create an embedded file. `file.name` becomes the tree key (PDFium
|
|
14672
|
-
* writes key = /UF = /F); a duplicate key rejects with `InvalidArg`.
|
|
14673
|
-
* The result carries the read-back entry, `checksum`/`size`/dates
|
|
14674
|
-
* included.
|
|
14675
|
-
*/
|
|
14676
|
-
create(file, resources, signal) {
|
|
14677
|
-
throwIfAborted(signal);
|
|
14678
|
-
const { fn, mem } = this.runtime;
|
|
14679
|
-
const docPtr = this.session.requireDocPtr();
|
|
14680
|
-
const resource = resources?.[file.resources.file];
|
|
14681
|
-
if (!resource) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `attachment file references resource '${file.resources.file}' but no such binary payload accompanied the mutation`);
|
|
14682
|
-
if (file.name.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "attachment file requires a name");
|
|
14683
|
-
if (resolveAttachmentIndex(fn, mem, docPtr, {
|
|
14684
|
-
kind: "key",
|
|
14685
|
-
key: file.name
|
|
14686
|
-
}) !== -1) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `an embedded file with key '${file.name}' already exists — keys are unique; rename and retry`);
|
|
14687
|
-
throwIfAborted(signal);
|
|
14688
|
-
const namePtr = mem.writeU16String(file.name);
|
|
14689
|
-
let attachmentPtr;
|
|
14690
|
-
try {
|
|
14691
|
-
attachmentPtr = fn.FPDFDoc_AddAttachment(docPtr, namePtr);
|
|
14692
|
-
} finally {
|
|
14693
|
-
mem.free(namePtr);
|
|
14694
|
-
}
|
|
14695
|
-
if (!attachmentPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "FPDFDoc_AddAttachment returned NULL");
|
|
14696
|
-
writeAttachmentFilePayload(fn, mem, attachmentPtr, docPtr, file, resource);
|
|
14697
|
-
const ref = (0, _embedpdf_engine_core_runtime.toAttachmentRef)(file.name);
|
|
14698
|
-
const index = resolveAttachmentIndex(fn, mem, docPtr, ref);
|
|
14699
|
-
if (index < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "created attachment did not resolve by key after the write");
|
|
14700
|
-
return {
|
|
14701
|
-
attachment: {
|
|
14702
|
-
...readAttachmentFileInfo(fn, mem, attachmentPtr),
|
|
14703
|
-
ref,
|
|
14704
|
-
index
|
|
14705
|
-
},
|
|
14706
|
-
meta: {
|
|
14707
|
-
affectedPages: [],
|
|
14708
|
-
cacheDelta: null,
|
|
14709
|
-
...this.session.writeStamp(),
|
|
14710
|
-
changed: [ref]
|
|
14711
|
-
}
|
|
14712
|
-
};
|
|
14713
|
-
}
|
|
14714
|
-
/** Delete an embedded file by key. Unlinks the tree entry only. */
|
|
14715
|
-
delete(ref, signal) {
|
|
14716
|
-
throwIfAborted(signal);
|
|
14717
|
-
const { fn, mem } = this.runtime;
|
|
14718
|
-
const docPtr = this.session.requireDocPtr();
|
|
14719
|
-
const index = resolveAttachmentIndex(fn, mem, docPtr, ref);
|
|
14720
|
-
if (index < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `no embedded file with key '${ref.key}' in the document`);
|
|
14721
|
-
if (!fn.FPDFDoc_DeleteAttachment(docPtr, index)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "FPDFDoc_DeleteAttachment returned false");
|
|
14722
|
-
return { meta: {
|
|
14723
|
-
affectedPages: [],
|
|
14724
|
-
cacheDelta: null,
|
|
14725
|
-
...this.session.writeStamp(),
|
|
14726
|
-
changed: [ref]
|
|
14727
|
-
} };
|
|
14728
|
-
}
|
|
14729
|
-
};
|
|
14730
|
-
//#endregion
|
|
14731
16079
|
//#region src/features/security/internal/buildSecurityInfo.ts
|
|
14732
16080
|
/**
|
|
14733
16081
|
* Single constructor for the "successfully inspected" shape of
|
|
@@ -15562,11 +16910,6 @@ function assertSealedSignature(runtime, docPtr, model, expected) {
|
|
|
15562
16910
|
function sameRange(a, b) {
|
|
15563
16911
|
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
|
|
15564
16912
|
}
|
|
15565
|
-
function bytesEqual(a, b) {
|
|
15566
|
-
if (a.byteLength !== b.byteLength) return false;
|
|
15567
|
-
for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
|
|
15568
|
-
return true;
|
|
15569
|
-
}
|
|
15570
16913
|
//#endregion
|
|
15571
16914
|
//#region src/features/forms/internal/fieldGroups.ts
|
|
15572
16915
|
/** The group of the field `fieldObjectNumber`; `null` in none, or when the form has no such field. */
|
|
@@ -17096,13 +18439,8 @@ function resultInPageSpace(payload, boxOf) {
|
|
|
17096
18439
|
...payload,
|
|
17097
18440
|
snapshot: (0, _embedpdf_engine_core_runtime.pageListOf)(payload.snapshot)
|
|
17098
18441
|
};
|
|
17099
|
-
case "pages.reorder":
|
|
17100
|
-
case "pages.rotate":
|
|
17101
|
-
case "pages.delete":
|
|
17102
18442
|
case "pages.setName":
|
|
17103
|
-
case "pages.removeName":
|
|
17104
|
-
case "pages.insert":
|
|
17105
|
-
case "pages.insertBlank": return {
|
|
18443
|
+
case "pages.removeName": return {
|
|
17106
18444
|
...payload,
|
|
17107
18445
|
result: {
|
|
17108
18446
|
...payload.result,
|
|
@@ -17145,6 +18483,10 @@ function resultInPageSpace(payload, boxOf) {
|
|
|
17145
18483
|
...payload,
|
|
17146
18484
|
snapshot: (0, _embedpdf_engine_core_runtime.mapDocumentActions)(payload.snapshot, toPage)
|
|
17147
18485
|
};
|
|
18486
|
+
case "outline.read": return {
|
|
18487
|
+
...payload,
|
|
18488
|
+
outline: (0, _embedpdf_engine_core_runtime.pageOutlineOf)(payload.outline, boxOf)
|
|
18489
|
+
};
|
|
17148
18490
|
case "annotations.list": return {
|
|
17149
18491
|
...payload,
|
|
17150
18492
|
list: {
|
|
@@ -17166,17 +18508,8 @@ function resultInPageSpace(payload, boxOf) {
|
|
|
17166
18508
|
annotation: annotation(payload.result.annotation)
|
|
17167
18509
|
}
|
|
17168
18510
|
};
|
|
17169
|
-
case "document.apply":
|
|
17170
|
-
|
|
17171
|
-
result: changeResultInPageSpace(payload.result, boxOf)
|
|
17172
|
-
};
|
|
17173
|
-
case "document.applyChanges": return {
|
|
17174
|
-
...payload,
|
|
17175
|
-
outcomes: payload.outcomes.map((outcome) => outcome.status === "applied" ? {
|
|
17176
|
-
...outcome,
|
|
17177
|
-
result: changeResultInPageSpace(outcome.result, boxOf)
|
|
17178
|
-
} : outcome)
|
|
17179
|
-
};
|
|
18511
|
+
case "document.apply":
|
|
18512
|
+
case "document.applyChanges": return payload;
|
|
17180
18513
|
case "annotations.import": return {
|
|
17181
18514
|
...payload,
|
|
17182
18515
|
result: {
|
|
@@ -17271,17 +18604,8 @@ function requestInFileSpace(job, boxOf) {
|
|
|
17271
18604
|
...job,
|
|
17272
18605
|
draft: (0, _embedpdf_engine_core_runtime.pdfFormFieldDraftOf)(job.draft, boxOf)
|
|
17273
18606
|
};
|
|
17274
|
-
case "document.apply":
|
|
17275
|
-
|
|
17276
|
-
change: changeInFileSpace(job.change, boxOf)
|
|
17277
|
-
};
|
|
17278
|
-
case "document.applyChanges": return {
|
|
17279
|
-
...job,
|
|
17280
|
-
changes: job.changes.map((entry) => ({
|
|
17281
|
-
...entry,
|
|
17282
|
-
change: changeInFileSpace(entry.change, boxOf)
|
|
17283
|
-
}))
|
|
17284
|
-
};
|
|
18607
|
+
case "document.apply":
|
|
18608
|
+
case "document.applyChanges": return job;
|
|
17285
18609
|
case "forms.addWidget": return {
|
|
17286
18610
|
...job,
|
|
17287
18611
|
placement: (0, _embedpdf_engine_core_runtime.pdfWidgetPlacementOf)(job.placement, boxOf)
|
|
@@ -17314,10 +18638,7 @@ function renderOptionsInFileSpace(options, visible) {
|
|
|
17314
18638
|
target: target.kind === "rect" ? (0, _embedpdf_engine_core_runtime.pdfRenderTargetOf)(target, visible()) : target
|
|
17315
18639
|
};
|
|
17316
18640
|
}
|
|
17317
|
-
|
|
17318
|
-
if ("undoOf" in change) return change;
|
|
17319
|
-
return { ops: change.ops.map((op) => opInFileSpace(op, boxOf)) };
|
|
17320
|
-
}
|
|
18641
|
+
/** An op's places in the file's coordinates, as its single verb's job's are. */
|
|
17321
18642
|
function opInFileSpace(op, boxOf) {
|
|
17322
18643
|
switch (op.type) {
|
|
17323
18644
|
case "annotations.import":
|
|
@@ -17367,16 +18688,29 @@ function opInFileSpace(op, boxOf) {
|
|
|
17367
18688
|
expect: pdfWidgetPatchOf(expect, boxOf(op.widget.page), boxOf)
|
|
17368
18689
|
} : rest;
|
|
17369
18690
|
}
|
|
18691
|
+
case "outline.create": return {
|
|
18692
|
+
...op,
|
|
18693
|
+
data: (0, _embedpdf_engine_core_runtime.pdfOutlineDraftOf)(op.data, boxOf)
|
|
18694
|
+
};
|
|
18695
|
+
case "outline.update": {
|
|
18696
|
+
const { expect, ...rest } = op;
|
|
18697
|
+
return {
|
|
18698
|
+
...rest,
|
|
18699
|
+
patch: (0, _embedpdf_engine_core_runtime.pdfOutlinePatchOf)(op.patch, boxOf),
|
|
18700
|
+
...expect ? { expect: (0, _embedpdf_engine_core_runtime.pdfOutlinePatchOf)(expect, boxOf) } : {}
|
|
18701
|
+
};
|
|
18702
|
+
}
|
|
18703
|
+
case "outline.delete": {
|
|
18704
|
+
const { expect, ...rest } = op;
|
|
18705
|
+
return expect ? {
|
|
18706
|
+
...rest,
|
|
18707
|
+
expect: (0, _embedpdf_engine_core_runtime.pdfOutlinePatchOf)(expect, boxOf)
|
|
18708
|
+
} : rest;
|
|
18709
|
+
}
|
|
17370
18710
|
default: return op;
|
|
17371
18711
|
}
|
|
17372
18712
|
}
|
|
17373
|
-
/** What
|
|
17374
|
-
function changeResultInPageSpace(result, boxOf) {
|
|
17375
|
-
return {
|
|
17376
|
-
...result,
|
|
17377
|
-
items: result.items.map((item) => itemInPageSpace(item, boxOf))
|
|
17378
|
-
};
|
|
17379
|
-
}
|
|
18713
|
+
/** What an op did, each place in page space, as its single verb's result is. */
|
|
17380
18714
|
function itemInPageSpace(item, boxOf) {
|
|
17381
18715
|
if ((0, _embedpdf_engine_core_runtime.isSkippedItem)(item)) return item;
|
|
17382
18716
|
const annotation = (read) => (0, _embedpdf_engine_core_runtime.pageAnnotationOf)(read, boxOf(read.page), boxOf);
|
|
@@ -17428,6 +18762,24 @@ function itemInPageSpace(item, boxOf) {
|
|
|
17428
18762
|
field: item.field && (0, _embedpdf_engine_core_runtime.pageFormFieldOf)(item.field, boxOf),
|
|
17429
18763
|
widgets: widgets(item.widgets)
|
|
17430
18764
|
};
|
|
18765
|
+
case "outline.create":
|
|
18766
|
+
case "outline.update": return {
|
|
18767
|
+
...item,
|
|
18768
|
+
item: (0, _embedpdf_engine_core_runtime.pageOutlineItemOf)(item.item, boxOf)
|
|
18769
|
+
};
|
|
18770
|
+
case "outline.restore": return {
|
|
18771
|
+
...item,
|
|
18772
|
+
items: item.items.map((read) => (0, _embedpdf_engine_core_runtime.pageOutlineItemOf)(read, boxOf))
|
|
18773
|
+
};
|
|
18774
|
+
case "pages.rotate":
|
|
18775
|
+
case "pages.reorder":
|
|
18776
|
+
case "pages.delete":
|
|
18777
|
+
case "pages.insertBlank":
|
|
18778
|
+
case "pages.insert":
|
|
18779
|
+
case "pages.restore": return {
|
|
18780
|
+
...item,
|
|
18781
|
+
layout: (0, _embedpdf_engine_core_runtime.pageListOf)(item.layout)
|
|
18782
|
+
};
|
|
17431
18783
|
default: return item;
|
|
17432
18784
|
}
|
|
17433
18785
|
}
|
|
@@ -17965,6 +19317,9 @@ var WorkerHost = class {
|
|
|
17965
19317
|
case "actions.read":
|
|
17966
19318
|
resultPack = this.handleActionsRead(msg, ctrl.signal);
|
|
17967
19319
|
break;
|
|
19320
|
+
case "outline.read":
|
|
19321
|
+
resultPack = this.handleOutlineRead(msg, ctrl.signal);
|
|
19322
|
+
break;
|
|
17968
19323
|
case "annotations.list":
|
|
17969
19324
|
resultPack = this.handleAnnotationsList(msg, ctrl.signal);
|
|
17970
19325
|
break;
|
|
@@ -18070,15 +19425,6 @@ var WorkerHost = class {
|
|
|
18070
19425
|
case "pages.list":
|
|
18071
19426
|
resultPack = this.handlePagesList(msg, ctrl.signal);
|
|
18072
19427
|
break;
|
|
18073
|
-
case "pages.reorder":
|
|
18074
|
-
resultPack = this.handlePagesReorder(msg, ctrl.signal);
|
|
18075
|
-
break;
|
|
18076
|
-
case "pages.rotate":
|
|
18077
|
-
resultPack = this.handlePagesRotate(msg, ctrl.signal);
|
|
18078
|
-
break;
|
|
18079
|
-
case "pages.delete":
|
|
18080
|
-
resultPack = this.handlePagesDelete(msg, ctrl.signal);
|
|
18081
|
-
break;
|
|
18082
19428
|
case "pages.setName":
|
|
18083
19429
|
resultPack = this.handlePagesSetName(msg, ctrl.signal);
|
|
18084
19430
|
break;
|
|
@@ -18109,24 +19455,12 @@ var WorkerHost = class {
|
|
|
18109
19455
|
case "pages.extract":
|
|
18110
19456
|
resultPack = this.handlePagesExtract(msg, ctrl.signal);
|
|
18111
19457
|
break;
|
|
18112
|
-
case "pages.insert":
|
|
18113
|
-
resultPack = this.handlePagesInsert(msg, ctrl.signal);
|
|
18114
|
-
break;
|
|
18115
|
-
case "pages.insertBlank":
|
|
18116
|
-
resultPack = this.handlePagesInsertBlank(msg, ctrl.signal);
|
|
18117
|
-
break;
|
|
18118
19458
|
case "attachments.list":
|
|
18119
19459
|
resultPack = this.handleAttachmentsList(msg, ctrl.signal);
|
|
18120
19460
|
break;
|
|
18121
19461
|
case "attachments.readFile":
|
|
18122
19462
|
resultPack = this.handleAttachmentsReadFile(msg, ctrl.signal);
|
|
18123
19463
|
break;
|
|
18124
|
-
case "attachments.create":
|
|
18125
|
-
resultPack = this.handleAttachmentsCreate(msg, ctrl.signal);
|
|
18126
|
-
break;
|
|
18127
|
-
case "attachments.delete":
|
|
18128
|
-
resultPack = this.handleAttachmentsDelete(msg, ctrl.signal);
|
|
18129
|
-
break;
|
|
18130
19464
|
case "measure.setScale":
|
|
18131
19465
|
resultPack = this.handleMeasureSetScale(msg, ctrl.signal);
|
|
18132
19466
|
break;
|
|
@@ -18484,6 +19818,13 @@ var WorkerHost = class {
|
|
|
18484
19818
|
snapshot: new DocumentActionsReader(this.runtime, session).read(signal)
|
|
18485
19819
|
});
|
|
18486
19820
|
}
|
|
19821
|
+
handleOutlineRead(req, signal) {
|
|
19822
|
+
const session = this.requireSession(req);
|
|
19823
|
+
return (0, _embedpdf_engine_core_runtime.wirePack)({
|
|
19824
|
+
tag: "outline.read",
|
|
19825
|
+
outline: new OutlineReader(this.runtime, session).read(signal)
|
|
19826
|
+
});
|
|
19827
|
+
}
|
|
18487
19828
|
handleAnnotationsList(req, signal) {
|
|
18488
19829
|
const session = this.requireSession(req);
|
|
18489
19830
|
const pages = req.pages?.map((page) => session.resolvePageRef(page).pageObjectNumber);
|
|
@@ -18669,12 +20010,15 @@ var WorkerHost = class {
|
|
|
18669
20010
|
const prior = ledger.outcome(opId);
|
|
18670
20011
|
let result;
|
|
18671
20012
|
if (prior) result = this.replayed(prior, fingerprint, opId);
|
|
18672
|
-
else result = this.recorded(session, opId, fingerprint, () => new ChangeApplier(this.runtime, session, this.fonts).apply(change.ops, authority, signal));
|
|
20013
|
+
else result = this.recorded(session, opId, fingerprint, () => new ChangeApplier(this.runtime, session, this.fonts).apply(change.ops, authority, signal, FILE_SPACE));
|
|
18673
20014
|
const item = result.items[0];
|
|
18674
20015
|
if (!item || (0, _embedpdf_engine_core_runtime.isSkippedItem)(item)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `${op.type} wrote nothing`);
|
|
18675
20016
|
return item;
|
|
18676
20017
|
}
|
|
18677
|
-
/**
|
|
20018
|
+
/**
|
|
20019
|
+
* A change's answer the first time, kept under its `opId`; a retry's
|
|
20020
|
+
* checks. The answer is in the space its job answered in (`ChangeOutcome`).
|
|
20021
|
+
*/
|
|
18678
20022
|
replayed(prior, fingerprint, opId) {
|
|
18679
20023
|
if (prior.fingerprint !== fingerprint) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.IdempotencyKeyReused, `opId ${opId} already answered a different change`);
|
|
18680
20024
|
if (prior.kind === "refused") throw (0, _embedpdf_engine_core_runtime.deserializeError)(prior.error);
|
|
@@ -18707,12 +20051,13 @@ var WorkerHost = class {
|
|
|
18707
20051
|
session.assertUsable();
|
|
18708
20052
|
if (req.objectNumberFloor !== void 0) session.raiseLastObjectNumber(req.objectNumberFloor - 1);
|
|
18709
20053
|
const applier = new ChangeApplier(this.runtime, session, this.fonts);
|
|
20054
|
+
const space = this.pageSpaceOf(session);
|
|
18710
20055
|
const records = /* @__PURE__ */ new Map();
|
|
18711
20056
|
const outcomes = [];
|
|
18712
20057
|
for (const entry of req.changes) {
|
|
18713
20058
|
session.beginTransaction(entry.opId);
|
|
18714
20059
|
try {
|
|
18715
|
-
const applied = runServerChange(applier, entry, records, signal);
|
|
20060
|
+
const applied = runServerChange(applier, entry, records, signal, space);
|
|
18716
20061
|
const result = resultOf(session, applied);
|
|
18717
20062
|
session.commitTransaction();
|
|
18718
20063
|
const record = result.meta.undoable ? applied.record : null;
|
|
@@ -18741,11 +20086,23 @@ var WorkerHost = class {
|
|
|
18741
20086
|
if (!outcomes.some((outcome) => outcome.status === "applied" && outcome.result.items.some(_embedpdf_engine_core_runtime.itemWrote))) return (0, _embedpdf_engine_core_runtime.wirePack)(payload);
|
|
18742
20087
|
return this.finishMutation(session, payload, req.artifactPath);
|
|
18743
20088
|
}
|
|
20089
|
+
/**
|
|
20090
|
+
* Page space for a change, measured as each op runs: the page boxes are read
|
|
20091
|
+
* anew each time, as an op can add, remove or put back the page a later
|
|
20092
|
+
* one names.
|
|
20093
|
+
*/
|
|
20094
|
+
pageSpaceOf(session) {
|
|
20095
|
+
return {
|
|
20096
|
+
opInFileSpace: (op) => opInFileSpace(op, visibleBoxReader(this.runtime, session)),
|
|
20097
|
+
itemOut: (item) => itemInPageSpace(item, visibleBoxReader(this.runtime, session))
|
|
20098
|
+
};
|
|
20099
|
+
}
|
|
18744
20100
|
/** A change's ops, or the steps of the record an undo names. */
|
|
18745
20101
|
runChange(session, req, signal) {
|
|
18746
20102
|
const applier = new ChangeApplier(this.runtime, session, this.fonts);
|
|
20103
|
+
const space = this.pageSpaceOf(session);
|
|
18747
20104
|
const userId = req.authority.identity.userId ?? null;
|
|
18748
|
-
if (!(0, _embedpdf_engine_core_runtime.isUndoChange)(req.change)) return applier.apply(req.change.ops, req.authority, signal);
|
|
20105
|
+
if (!(0, _embedpdf_engine_core_runtime.isUndoChange)(req.change)) return applier.apply(req.change.ops, req.authority, signal, space);
|
|
18749
20106
|
const kept = changeLedgerOf(session).record(req.change.undoOf);
|
|
18750
20107
|
if (!kept || "unavailable" in kept) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.UndoUnavailable, `change ${req.change.undoOf} can no longer be undone`, { details: { reason: kept ? kept.unavailable : "expired" } });
|
|
18751
20108
|
if (!kept.record) return {
|
|
@@ -18756,7 +20113,7 @@ var WorkerHost = class {
|
|
|
18756
20113
|
}
|
|
18757
20114
|
};
|
|
18758
20115
|
if (kept.record.userId !== null && kept.record.userId !== userId) throw new _embedpdf_engine_core_runtime.PermissionDenied("changes:undo", "target");
|
|
18759
|
-
return applier.undo(kept.record, req.authority, signal);
|
|
20116
|
+
return applier.undo(kept.record, req.authority, signal, space);
|
|
18760
20117
|
}
|
|
18761
20118
|
handlePagesList(req, signal) {
|
|
18762
20119
|
const session = this.requireSession(req);
|
|
@@ -18765,30 +20122,6 @@ var WorkerHost = class {
|
|
|
18765
20122
|
snapshot: new PagesReader(this.runtime, session).read(signal)
|
|
18766
20123
|
});
|
|
18767
20124
|
}
|
|
18768
|
-
handlePagesReorder(req, signal) {
|
|
18769
|
-
const session = this.requireSession(req);
|
|
18770
|
-
const result = new PagesMutator(this.runtime, session).reorder(req.pages, req.position, signal);
|
|
18771
|
-
return this.finishMutation(session, {
|
|
18772
|
-
tag: "pages.reorder",
|
|
18773
|
-
result
|
|
18774
|
-
}, req.artifactPath);
|
|
18775
|
-
}
|
|
18776
|
-
handlePagesRotate(req, signal) {
|
|
18777
|
-
const session = this.requireSession(req);
|
|
18778
|
-
const result = new PagesMutator(this.runtime, session).rotate(req.pages, req.rotation, signal);
|
|
18779
|
-
return this.finishMutation(session, {
|
|
18780
|
-
tag: "pages.rotate",
|
|
18781
|
-
result
|
|
18782
|
-
}, req.artifactPath);
|
|
18783
|
-
}
|
|
18784
|
-
handlePagesDelete(req, signal) {
|
|
18785
|
-
const session = this.requireSession(req);
|
|
18786
|
-
const result = new PagesMutator(this.runtime, session).delete(req.pages, signal);
|
|
18787
|
-
return this.finishMutation(session, {
|
|
18788
|
-
tag: "pages.delete",
|
|
18789
|
-
result
|
|
18790
|
-
}, req.artifactPath);
|
|
18791
|
-
}
|
|
18792
20125
|
handlePagesSetName(req, signal) {
|
|
18793
20126
|
const session = this.requireSession(req);
|
|
18794
20127
|
const result = new PagesMutator(this.runtime, session).setName({
|
|
@@ -18841,32 +20174,13 @@ var WorkerHost = class {
|
|
|
18841
20174
|
}
|
|
18842
20175
|
handlePagesExtract(req, signal) {
|
|
18843
20176
|
const session = this.requireSession(req);
|
|
18844
|
-
const extracted = new PagesExtractor(this.runtime, session).extract(req.pages, signal);
|
|
20177
|
+
const extracted = new PagesExtractor(this.runtime, session).extract(req.pages, req.flatten, signal);
|
|
18845
20178
|
return (0, _embedpdf_engine_core_runtime.wirePack)({
|
|
18846
20179
|
tag: "pages.extract",
|
|
18847
20180
|
bytes: extracted.bytes,
|
|
18848
20181
|
size: extracted.size
|
|
18849
20182
|
}, [extracted.bytes]);
|
|
18850
20183
|
}
|
|
18851
|
-
handlePagesInsert(req, signal) {
|
|
18852
|
-
const session = this.requireSession(req);
|
|
18853
|
-
const result = new PagesInserter(this.runtime, session).insert(req.bytes, req.position ?? "end", signal);
|
|
18854
|
-
return this.finishMutation(session, {
|
|
18855
|
-
tag: "pages.insert",
|
|
18856
|
-
result
|
|
18857
|
-
}, req.artifactPath);
|
|
18858
|
-
}
|
|
18859
|
-
handlePagesInsertBlank(req, signal) {
|
|
18860
|
-
const session = this.requireSession(req);
|
|
18861
|
-
const result = new PagesInserter(this.runtime, session).insertBlank({
|
|
18862
|
-
size: req.size,
|
|
18863
|
-
count: req.count
|
|
18864
|
-
}, req.position ?? "end", signal, req.objectNumbers);
|
|
18865
|
-
return this.finishMutation(session, {
|
|
18866
|
-
tag: "pages.insertBlank",
|
|
18867
|
-
result
|
|
18868
|
-
}, req.artifactPath);
|
|
18869
|
-
}
|
|
18870
20184
|
handleAttachmentsList(req, signal) {
|
|
18871
20185
|
const session = this.requireSession(req);
|
|
18872
20186
|
return (0, _embedpdf_engine_core_runtime.wirePack)({
|
|
@@ -18882,22 +20196,6 @@ var WorkerHost = class {
|
|
|
18882
20196
|
content
|
|
18883
20197
|
}, content.bytes !== void 0 ? [content.bytes] : []);
|
|
18884
20198
|
}
|
|
18885
|
-
handleAttachmentsCreate(req, signal) {
|
|
18886
|
-
const session = this.requireSession(req);
|
|
18887
|
-
const result = new AttachmentMutator(this.runtime, session).create(req.file, req.resources, signal);
|
|
18888
|
-
return this.finishMutation(session, {
|
|
18889
|
-
tag: "attachments.create",
|
|
18890
|
-
result
|
|
18891
|
-
}, req.artifactPath);
|
|
18892
|
-
}
|
|
18893
|
-
handleAttachmentsDelete(req, signal) {
|
|
18894
|
-
const session = this.requireSession(req);
|
|
18895
|
-
const result = new AttachmentMutator(this.runtime, session).delete(req.ref, signal);
|
|
18896
|
-
return this.finishMutation(session, {
|
|
18897
|
-
tag: "attachments.delete",
|
|
18898
|
-
result
|
|
18899
|
-
}, req.artifactPath);
|
|
18900
|
-
}
|
|
18901
20199
|
async handleAnnotationsReadFile(req, signal) {
|
|
18902
20200
|
const session = this.requireSession(req);
|
|
18903
20201
|
const content = await new AttachmentReader(this.runtime, session).readAnnotationFile(session.resolvePageRef(req.page).pageObjectNumber, req.ref, req.path, req.maxDecodedBytes, signal, this.slices);
|
|
@@ -19662,9 +20960,9 @@ function resultOf(session, applied) {
|
|
|
19662
20960
|
* this job kept for an earlier change of the request, or the one the server
|
|
19663
20961
|
* sent. A change that left no record leaves nothing to undo.
|
|
19664
20962
|
*/
|
|
19665
|
-
function runServerChange(applier, entry, records, signal) {
|
|
20963
|
+
function runServerChange(applier, entry, records, signal, space) {
|
|
19666
20964
|
const { change, authority } = entry;
|
|
19667
|
-
if (!(0, _embedpdf_engine_core_runtime.isUndoChange)(change)) return applier.apply(change.ops, authority, signal);
|
|
20965
|
+
if (!(0, _embedpdf_engine_core_runtime.isUndoChange)(change)) return applier.apply(change.ops, authority, signal, space);
|
|
19668
20966
|
const userId = authority.identity.userId ?? null;
|
|
19669
20967
|
const record = records.has(change.undoOf) ? records.get(change.undoOf) : entry.record;
|
|
19670
20968
|
if (record === void 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.UndoUnavailable, `change ${change.undoOf} can no longer be undone`, { details: { reason: "expired" } });
|
|
@@ -19675,7 +20973,7 @@ function runServerChange(applier, entry, records, signal) {
|
|
|
19675
20973
|
steps: []
|
|
19676
20974
|
}
|
|
19677
20975
|
};
|
|
19678
|
-
return applier.undo(record, authority, signal);
|
|
20976
|
+
return applier.undo(record, authority, signal, space);
|
|
19679
20977
|
}
|
|
19680
20978
|
/** Every page a change's items touched, once each, in order. */
|
|
19681
20979
|
function pagesOf(items) {
|
|
@@ -19769,6 +21067,7 @@ exports.DocumentPagesMutator = PagesMutator;
|
|
|
19769
21067
|
exports.DocumentSaver = DocumentSaver;
|
|
19770
21068
|
exports.DocumentSession = DocumentSession;
|
|
19771
21069
|
exports.EPDF_CHAR_GEOMETRY_LAYOUT = EPDF_CHAR_GEOMETRY_LAYOUT;
|
|
21070
|
+
exports.FILE_SPACE = FILE_SPACE;
|
|
19772
21071
|
exports.FontRegistrar = FontRegistrar;
|
|
19773
21072
|
exports.FormExporter = FormExporter;
|
|
19774
21073
|
exports.FormImporter = FormImporter;
|
|
@@ -19821,6 +21120,7 @@ exports.readAttachmentFileInfo = readAttachmentFileInfo;
|
|
|
19821
21120
|
exports.readRevisions = readRevisions;
|
|
19822
21121
|
exports.readSignaturesFromModel = readSignaturesFromModel;
|
|
19823
21122
|
exports.readStructure = readStructure;
|
|
21123
|
+
exports.rotationAt = rotationAt;
|
|
19824
21124
|
exports.throwIfAborted = throwIfAborted;
|
|
19825
21125
|
exports.unpackChangeRecord = unpackChangeRecord;
|
|
19826
21126
|
exports.visibleBoxReader = visibleBoxReader;
|