@embedpdf/engine-services 3.0.0-next.10 → 3.0.0-next.11

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 CHANGED
@@ -1212,11 +1212,143 @@ function readI32(mem, ptr, byteOffset = 0) {
1212
1212
  return Number(mem.peek(ptr, "i32", byteOffset));
1213
1213
  }
1214
1214
  //#endregion
1215
+ //#region src/features/destinations/destinationViewCodes.ts
1216
+ /**
1217
+ * `PDFDEST_VIEW_*` fit-type codes (public/fpdf_doc.h) — the shared
1218
+ * vocabulary of `FPDFDest_GetView` (read) and `EPDFDest_CreateView`
1219
+ * (write). XYZ is listed for completeness but never goes through
1220
+ * CreateView (it has its own null-aware constructor).
1221
+ */
1222
+ const DEST_VIEW = {
1223
+ unknown: 0,
1224
+ xyz: 1,
1225
+ fit: 2,
1226
+ fitH: 3,
1227
+ fitV: 4,
1228
+ fitR: 5,
1229
+ fitB: 6,
1230
+ fitBH: 7,
1231
+ fitBV: 8
1232
+ };
1233
+ /** Wire destination kind → PDFDEST_VIEW code, for the non-XYZ kinds. */
1234
+ const VIEW_CODE_BY_KIND = {
1235
+ fit: DEST_VIEW.fit,
1236
+ fitH: DEST_VIEW.fitH,
1237
+ fitV: DEST_VIEW.fitV,
1238
+ fitR: DEST_VIEW.fitR,
1239
+ fitB: DEST_VIEW.fitB,
1240
+ fitBH: DEST_VIEW.fitBH,
1241
+ fitBV: DEST_VIEW.fitBV
1242
+ };
1243
+ //#endregion
1244
+ //#region src/features/destinations/readDestination.ts
1245
+ const MAX_VIEW_PARAMS = 4;
1246
+ /**
1247
+ * Materialise one explicit `PdfDestination` from a live `destPtr`. PDFium
1248
+ * already resolved named destinations before handing us the pointer, so
1249
+ * only explicit arrays arrive here (the rule `PdfDestination` documents).
1250
+ *
1251
+ * Returns `null` when the destination is unreadable: no visible indirect page
1252
+ * in the document, or an unknown fit type. Coordinates stay raw PDF user
1253
+ * space — no conversion at this layer.
1254
+ *
1255
+ * Shared by the link reader, `goto` action-node payloads, and the document
1256
+ * `openDestination`; the outline/bookmark port reuses it too.
1257
+ */
1258
+ function readDestination(fn, mem, docPtr, destPtr) {
1259
+ const pageObjectNumber = fn.EPDFDest_GetPageObjectNumber(docPtr, destPtr);
1260
+ if (pageObjectNumber === 0) return null;
1261
+ const view = withScratchN(mem, [4, MAX_VIEW_PARAMS * 4], ([countPtr, paramsPtr]) => {
1262
+ const code = fn.FPDFDest_GetView(destPtr, countPtr, paramsPtr);
1263
+ const count = Math.min(readI32(mem, countPtr), MAX_VIEW_PARAMS);
1264
+ const params = [];
1265
+ for (let i = 0; i < count; i++) params.push(readF32(mem, paramsPtr, i * 4));
1266
+ return {
1267
+ code,
1268
+ params
1269
+ };
1270
+ });
1271
+ const at = (i) => i < view.params.length ? view.params[i] : null;
1272
+ switch (view.code) {
1273
+ case DEST_VIEW.xyz: return {
1274
+ kind: "xyz",
1275
+ pageObjectNumber,
1276
+ ...readXyzLocation(fn, mem, destPtr)
1277
+ };
1278
+ case DEST_VIEW.fit: return {
1279
+ kind: "fit",
1280
+ pageObjectNumber
1281
+ };
1282
+ case DEST_VIEW.fitH: return {
1283
+ kind: "fitH",
1284
+ pageObjectNumber,
1285
+ top: at(0)
1286
+ };
1287
+ case DEST_VIEW.fitV: return {
1288
+ kind: "fitV",
1289
+ pageObjectNumber,
1290
+ left: at(0)
1291
+ };
1292
+ case DEST_VIEW.fitR:
1293
+ if (view.params.length < 4) return {
1294
+ kind: "fit",
1295
+ pageObjectNumber
1296
+ };
1297
+ return {
1298
+ kind: "fitR",
1299
+ pageObjectNumber,
1300
+ left: view.params[0],
1301
+ bottom: view.params[1],
1302
+ right: view.params[2],
1303
+ top: view.params[3]
1304
+ };
1305
+ case DEST_VIEW.fitB: return {
1306
+ kind: "fitB",
1307
+ pageObjectNumber
1308
+ };
1309
+ case DEST_VIEW.fitBH: return {
1310
+ kind: "fitBH",
1311
+ pageObjectNumber,
1312
+ top: at(0)
1313
+ };
1314
+ case DEST_VIEW.fitBV: return {
1315
+ kind: "fitBV",
1316
+ pageObjectNumber,
1317
+ left: at(0)
1318
+ };
1319
+ default: return null;
1320
+ }
1321
+ }
1322
+ /** `/XYZ` null-aware axes via `FPDFDest_GetLocationInPage` (FPDF_BOOL is 4 bytes). */
1323
+ function readXyzLocation(fn, mem, destPtr) {
1324
+ return withScratchN(mem, [
1325
+ 4,
1326
+ 4,
1327
+ 4,
1328
+ 4,
1329
+ 4,
1330
+ 4
1331
+ ], ([hasLeft, hasTop, hasZoom, left, top, zoom]) => {
1332
+ if (!fn.FPDFDest_GetLocationInPage(destPtr, hasLeft, hasTop, hasZoom, left, top, zoom)) return {
1333
+ left: null,
1334
+ top: null,
1335
+ zoom: null
1336
+ };
1337
+ return {
1338
+ left: readI32(mem, hasLeft) ? readF32(mem, left) : null,
1339
+ top: readI32(mem, hasTop) ? readF32(mem, top) : null,
1340
+ zoom: readI32(mem, hasZoom) ? readF32(mem, zoom) : null
1341
+ };
1342
+ });
1343
+ }
1344
+ //#endregion
1215
1345
  //#region src/features/actions/ActionModelReader.ts
1216
1346
  const DEFAULT_ACTION_READ_BUDGET = {
1217
1347
  maxModels: 512,
1218
1348
  maxNodes: 16384,
1219
- maxScriptCodeUnits: 16e6
1349
+ maxScriptCodeUnits: 16e6,
1350
+ maxTargetEntries: 4096,
1351
+ maxPayloadCodeUnits: 262144
1220
1352
  };
1221
1353
  const ACTION_TYPE_BY_CODE = {
1222
1354
  0: "unknown",
@@ -1244,22 +1376,20 @@ const WARNING_MALFORMED_NEXT = 2;
1244
1376
  const WARNING_INCOMPLETE = 4;
1245
1377
  const INVALID_NODE_U32 = 4294967295;
1246
1378
  /** Decode one `EPDF_ACTION_TYPE_*` code — the shared vocabulary consumers
1247
- * (the link-target projection) use so the two surfaces cannot drift. */
1379
+ * use so action-shaped surfaces cannot drift. */
1248
1380
  function actionTypeFromCode(code) {
1249
1381
  return ACTION_TYPE_BY_CODE[code] ?? "unknown";
1250
1382
  }
1251
- /** Is this `EPDFAction_GetRootNode`/`GetNextAt` result a real node id?
1252
- * (`EPDF_ACTION_NODE_INVALID` may surface as -1 or u32 max via cwrap.) */
1253
- function isValidActionNodeId(raw) {
1254
- return raw !== -1 && raw >>> 0 !== INVALID_NODE_U32;
1255
- }
1256
- /** Mutable aggregate budget shared by every action model in one read job. */
1383
+ /** Mutable aggregate budget shared by every action model in one read job.
1384
+ * Payload budgets are RESERVED before the backing buffer is allocated. */
1257
1385
  var ActionReadBudgetTracker = class {
1258
1386
  constructor(limits = DEFAULT_ACTION_READ_BUDGET) {
1259
1387
  this.limits = limits;
1260
1388
  _defineProperty(this, "models", 0);
1261
1389
  _defineProperty(this, "nodes", 0);
1262
1390
  _defineProperty(this, "scriptCodeUnits", 0);
1391
+ _defineProperty(this, "targetEntries", 0);
1392
+ _defineProperty(this, "payloadCodeUnits", 0);
1263
1393
  }
1264
1394
  addModel(nodeCount) {
1265
1395
  this.models += 1;
@@ -1270,11 +1400,23 @@ var ActionReadBudgetTracker = class {
1270
1400
  this.scriptCodeUnits += script.length;
1271
1401
  this.assertWithinBudget();
1272
1402
  }
1403
+ /** Charge target entries BEFORE the target array is walked. */
1404
+ reserveTargets(count) {
1405
+ this.targetEntries += count;
1406
+ this.assertWithinBudget();
1407
+ }
1408
+ /** Charge payload text BEFORE the scratch buffer for it is allocated. */
1409
+ reservePayloadBytes(length) {
1410
+ this.payloadCodeUnits += length;
1411
+ this.assertWithinBudget();
1412
+ }
1273
1413
  assertWithinBudget() {
1274
- if (this.models > this.limits.maxModels || this.nodes > this.limits.maxNodes || this.scriptCodeUnits > this.limits.maxScriptCodeUnits) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.MalformedPdf, "aggregate PDF action read budget exceeded", { details: {
1414
+ if (this.models > this.limits.maxModels || this.nodes > this.limits.maxNodes || this.scriptCodeUnits > this.limits.maxScriptCodeUnits || this.targetEntries > this.limits.maxTargetEntries || this.payloadCodeUnits > this.limits.maxPayloadCodeUnits) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.MalformedPdf, "aggregate PDF action read budget exceeded", { details: {
1275
1415
  models: this.models,
1276
1416
  nodes: this.nodes,
1277
1417
  scriptCodeUnits: this.scriptCodeUnits,
1418
+ targetEntries: this.targetEntries,
1419
+ payloadCodeUnits: this.payloadCodeUnits,
1278
1420
  limits: this.limits
1279
1421
  } });
1280
1422
  }
@@ -1282,8 +1424,17 @@ var ActionReadBudgetTracker = class {
1282
1424
  /**
1283
1425
  * Convert and close one detached native action model. This is the only
1284
1426
  * native-model walker used by document, page, field, and annotation reads.
1427
+ *
1428
+ * `docPtr` resolves `goto` destinations (page identity + named-destination
1429
+ * lookup); every caller reads within an open document session.
1430
+ *
1431
+ * Payload rule: a node whose payload cannot be materialised (a goto without
1432
+ * a resolvable destination, a javascript node with no `/JS`, a withheld
1433
+ * target list) degrades to `{ type: 'unknown' }` with its original `/S` on
1434
+ * `subtype`, and the tree gains the `'payload-dropped'` warning — impossible
1435
+ * states stay unrepresentable while malformed documents stay readable.
1285
1436
  */
1286
- function readActionModel(fn, mem, modelPtr, budget) {
1437
+ function readActionModel(fn, mem, docPtr, modelPtr, budget) {
1287
1438
  if (modelPtr === _embedpdf_engine_runtime.NULL_PTR) return null;
1288
1439
  try {
1289
1440
  const nodeCount = fn.EPDFAction_GetNodeCount(modelPtr);
@@ -1293,13 +1444,61 @@ function readActionModel(fn, mem, modelPtr, budget) {
1293
1444
  const incomplete = !fn.EPDFAction_IsComplete(modelPtr);
1294
1445
  const rootId = normalizeOptionalNodeId(fn.EPDFAction_GetRootNode(modelPtr), nodeCount);
1295
1446
  const visiting = /* @__PURE__ */ new Set();
1447
+ let payloadDropped = false;
1448
+ /** Probe the two-call length first and charge the budget BEFORE the
1449
+ * scratch buffer for the value is allocated. */
1450
+ const readPayloadString = (call) => {
1451
+ const length = call(_embedpdf_engine_runtime.NULL_PTR, 0);
1452
+ if (length <= 0) return null;
1453
+ budget.reservePayloadBytes(length);
1454
+ return readUtf8String(mem, call);
1455
+ };
1456
+ const readOutBool = (read) => withScratchN(mem, [4], ([outPtr]) => read(outPtr) ? readI32(mem, outPtr) !== 0 : null);
1457
+ const readTargetObjectNumber = (nodeId, index) => withScratchN(mem, [4], ([outPtr]) => {
1458
+ if (!fn.EPDFAction_GetNodeTargetObjectNumber(modelPtr, nodeId, index, outPtr)) return null;
1459
+ const value = readI32(mem, outPtr) >>> 0;
1460
+ return value === 0 ? null : value;
1461
+ });
1462
+ const readTargets = (nodeId, count) => {
1463
+ const targets = [];
1464
+ for (let index = 0; index < count; index++) {
1465
+ const name = readPayloadString((buf, capacity) => fn.EPDFAction_GetNodeTargetName(modelPtr, nodeId, index, buf, capacity));
1466
+ if (name !== null) {
1467
+ targets.push({
1468
+ kind: "name",
1469
+ name
1470
+ });
1471
+ continue;
1472
+ }
1473
+ const objectNumber = readTargetObjectNumber(nodeId, index);
1474
+ if (objectNumber === null) return null;
1475
+ targets.push({
1476
+ kind: "objectNumber",
1477
+ objectNumber
1478
+ });
1479
+ }
1480
+ return targets;
1481
+ };
1482
+ const readResetFormState = (nodeId) => withScratchN(mem, [4, 4], ([hasFieldsPtr, excludePtr]) => {
1483
+ if (!fn.EPDFAction_GetNodeResetForm(modelPtr, nodeId, hasFieldsPtr, excludePtr)) return null;
1484
+ return {
1485
+ hasFields: readI32(mem, hasFieldsPtr) !== 0,
1486
+ exclude: readI32(mem, excludePtr) !== 0
1487
+ };
1488
+ });
1489
+ const readSubmitFormState = (nodeId) => withScratchN(mem, [4, 4], ([hasFieldsPtr, flagsPtr]) => {
1490
+ if (!fn.EPDFAction_GetNodeSubmitForm(modelPtr, nodeId, hasFieldsPtr, flagsPtr)) return null;
1491
+ return {
1492
+ hasFields: readI32(mem, hasFieldsPtr) !== 0,
1493
+ flags: readI32(mem, flagsPtr) >>> 0
1494
+ };
1495
+ });
1296
1496
  const readNode = (nodeId) => {
1297
1497
  if (visiting.has(nodeId)) throw malformedActionModel("native action model contains a cycle", { nodeId });
1298
1498
  visiting.add(nodeId);
1299
1499
  try {
1500
+ const type = actionTypeFromCode(fn.EPDFAction_GetNodeType(modelPtr, nodeId));
1300
1501
  const subtype = readUtf8String(mem, (buf, capacity) => fn.EPDFAction_GetNodeSubtype(modelPtr, nodeId, buf, capacity)) ?? "";
1301
- const script = fn.EPDFAction_NodeHasJavaScript(modelPtr, nodeId) ? readUtf16String(mem, (buf, capacity) => fn.EPDFAction_GetNodeJavaScript(modelPtr, nodeId, buf, capacity)) ?? "" : void 0;
1302
- if (script !== void 0) budget.addScript(script);
1303
1502
  const nextCount = fn.EPDFAction_GetNextCount(modelPtr, nodeId);
1304
1503
  if (!Number.isInteger(nextCount) || nextCount < 0) throw malformedActionModel("invalid action child count", {
1305
1504
  nodeId,
@@ -1310,21 +1509,157 @@ function readActionModel(fn, mem, modelPtr, budget) {
1310
1509
  const childId = normalizeNodeId(fn.EPDFAction_GetNextAt(modelPtr, nodeId, index), nodeCount);
1311
1510
  next.push(readNode(childId));
1312
1511
  }
1313
- return {
1314
- type: ACTION_TYPE_BY_CODE[fn.EPDFAction_GetNodeType(modelPtr, nodeId)] ?? "unknown",
1315
- subtype,
1316
- ...script !== void 0 ? { script } : {},
1317
- next
1512
+ const degraded = () => {
1513
+ payloadDropped = true;
1514
+ return {
1515
+ type: "unknown",
1516
+ subtype,
1517
+ next
1518
+ };
1318
1519
  };
1520
+ switch (type) {
1521
+ case "javascript": {
1522
+ if (!fn.EPDFAction_NodeHasJavaScript(modelPtr, nodeId)) return degraded();
1523
+ const script = readUtf16String(mem, (buf, capacity) => fn.EPDFAction_GetNodeJavaScript(modelPtr, nodeId, buf, capacity)) ?? "";
1524
+ budget.addScript(script);
1525
+ return {
1526
+ type,
1527
+ subtype,
1528
+ script,
1529
+ next
1530
+ };
1531
+ }
1532
+ case "rendition": {
1533
+ if (!fn.EPDFAction_NodeHasJavaScript(modelPtr, nodeId)) return {
1534
+ type,
1535
+ subtype,
1536
+ next
1537
+ };
1538
+ const script = readUtf16String(mem, (buf, capacity) => fn.EPDFAction_GetNodeJavaScript(modelPtr, nodeId, buf, capacity)) ?? "";
1539
+ budget.addScript(script);
1540
+ return {
1541
+ type,
1542
+ subtype,
1543
+ script,
1544
+ next
1545
+ };
1546
+ }
1547
+ case "goto": {
1548
+ const destPtr = fn.EPDFAction_GetNodeDest(docPtr, modelPtr, nodeId);
1549
+ const destination = destPtr !== _embedpdf_engine_runtime.NULL_PTR ? readDestination(fn, mem, docPtr, destPtr) : null;
1550
+ return destination ? {
1551
+ type,
1552
+ subtype,
1553
+ destination,
1554
+ next
1555
+ } : degraded();
1556
+ }
1557
+ case "uri": {
1558
+ const uri = readPayloadString((buf, capacity) => fn.EPDFAction_GetNodeURI(docPtr, modelPtr, nodeId, buf, capacity));
1559
+ if (uri === null) return degraded();
1560
+ return {
1561
+ type,
1562
+ subtype,
1563
+ uri,
1564
+ isMap: readOutBool((outPtr) => fn.EPDFAction_GetNodeURIIsMap(modelPtr, nodeId, outPtr)) ?? false,
1565
+ next
1566
+ };
1567
+ }
1568
+ case "named": {
1569
+ const name = readPayloadString((buf, capacity) => fn.EPDFAction_GetNodeName(modelPtr, nodeId, buf, capacity));
1570
+ return name === null ? degraded() : {
1571
+ type,
1572
+ subtype,
1573
+ name,
1574
+ next
1575
+ };
1576
+ }
1577
+ case "hide": {
1578
+ const count = fn.EPDFAction_GetNodeTargetCount(modelPtr, nodeId);
1579
+ if (!Number.isInteger(count) || count < 0) return degraded();
1580
+ budget.reserveTargets(count);
1581
+ const targets = readTargets(nodeId, count);
1582
+ if (targets === null) return degraded();
1583
+ return {
1584
+ type,
1585
+ subtype,
1586
+ targets,
1587
+ hide: readOutBool((outPtr) => fn.EPDFAction_GetNodeHideFlag(modelPtr, nodeId, outPtr)) ?? true,
1588
+ next
1589
+ };
1590
+ }
1591
+ case "reset-form": {
1592
+ const state = readResetFormState(nodeId);
1593
+ const count = fn.EPDFAction_GetNodeTargetCount(modelPtr, nodeId);
1594
+ if (state === null || !Number.isInteger(count) || count < 0) return degraded();
1595
+ budget.reserveTargets(count);
1596
+ const fields = state.hasFields ? readTargets(nodeId, count) : null;
1597
+ if (state.hasFields && fields === null) return degraded();
1598
+ return {
1599
+ type,
1600
+ subtype,
1601
+ fields,
1602
+ exclude: state.exclude,
1603
+ next
1604
+ };
1605
+ }
1606
+ case "submit-form": {
1607
+ if (typeof fn.EPDFAction_GetNodeSubmitForm !== "function") return {
1608
+ type,
1609
+ subtype,
1610
+ next
1611
+ };
1612
+ const state = readSubmitFormState(nodeId);
1613
+ if (state === null) return degraded();
1614
+ const url = readPayloadString((buf, capacity) => fn.EPDFAction_GetNodeSubmitFormURL(modelPtr, nodeId, buf, capacity));
1615
+ if (url === null) return degraded();
1616
+ const count = fn.EPDFAction_GetNodeTargetCount(modelPtr, nodeId);
1617
+ if (!Number.isInteger(count) || count < 0) return degraded();
1618
+ budget.reserveTargets(count);
1619
+ const fields = state.hasFields ? readTargets(nodeId, count) : null;
1620
+ if (state.hasFields && fields === null) return degraded();
1621
+ const charSet = readPayloadString((buf, capacity) => fn.EPDFAction_GetNodeSubmitFormCharSet(modelPtr, nodeId, buf, capacity));
1622
+ return {
1623
+ type,
1624
+ subtype,
1625
+ payload: {
1626
+ url,
1627
+ fields,
1628
+ flags: (0, _embedpdf_engine_core_runtime.decodeSubmitFormFlags)(state.flags),
1629
+ ...charSet === null ? {} : { charSet }
1630
+ },
1631
+ next
1632
+ };
1633
+ }
1634
+ case "goto-remote":
1635
+ case "goto-embedded":
1636
+ case "launch": {
1637
+ const filePath = readPayloadString((buf, capacity) => fn.EPDFAction_GetNodeFilePath(modelPtr, nodeId, buf, capacity));
1638
+ return filePath === null ? degraded() : {
1639
+ type,
1640
+ subtype,
1641
+ filePath,
1642
+ next
1643
+ };
1644
+ }
1645
+ default: return {
1646
+ type,
1647
+ subtype,
1648
+ next
1649
+ };
1650
+ }
1319
1651
  } finally {
1320
1652
  visiting.delete(nodeId);
1321
1653
  }
1322
1654
  };
1655
+ const root = rootId === null ? null : readNode(rootId);
1656
+ const warnings = warningsFromFlags(warningFlags, incomplete);
1657
+ if (payloadDropped) warnings.push("payload-dropped");
1323
1658
  return {
1324
- root: rootId === null ? null : readNode(rootId),
1659
+ root,
1325
1660
  incomplete,
1326
1661
  warningFlags,
1327
- warnings: warningsFromFlags(warningFlags, incomplete)
1662
+ warnings
1328
1663
  };
1329
1664
  } finally {
1330
1665
  fn.EPDFAction_CloseModel(modelPtr);
@@ -1404,8 +1739,8 @@ var PagesReader = class {
1404
1739
  };
1405
1740
  function readPageActions(fn, mem, docPtr, pageObjectNumber, budget) {
1406
1741
  const actions = {};
1407
- const open = readActionModel(fn, mem, fn.EPDFDoc_GetPageActionModel(docPtr, pageObjectNumber, 0), budget);
1408
- const close = readActionModel(fn, mem, fn.EPDFDoc_GetPageActionModel(docPtr, pageObjectNumber, 1), budget);
1742
+ const open = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetPageActionModel(docPtr, pageObjectNumber, 0), budget);
1743
+ const close = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetPageActionModel(docPtr, pageObjectNumber, 1), budget);
1409
1744
  if (open) actions.open = open;
1410
1745
  if (close) actions.close = close;
1411
1746
  return open || close ? actions : void 0;
@@ -2823,167 +3158,67 @@ function readLine(fn, mem, annotPtr, base) {
2823
3158
  };
2824
3159
  }
2825
3160
  //#endregion
2826
- //#region src/features/annotations/internal/destinationViewCodes.ts
2827
- /**
2828
- * `PDFDEST_VIEW_*` fit-type codes (public/fpdf_doc.h) — the shared
2829
- * vocabulary of `FPDFDest_GetView` (read) and `EPDFDest_CreateView`
2830
- * (write). XYZ is listed for completeness but never goes through
2831
- * CreateView (it has its own null-aware constructor).
2832
- */
2833
- const DEST_VIEW = {
2834
- unknown: 0,
2835
- xyz: 1,
2836
- fit: 2,
2837
- fitH: 3,
2838
- fitV: 4,
2839
- fitR: 5,
2840
- fitB: 6,
2841
- fitBH: 7,
2842
- fitBV: 8
2843
- };
2844
- /** Wire destination kind → PDFDEST_VIEW code, for the non-XYZ kinds. */
2845
- const VIEW_CODE_BY_KIND = {
2846
- fit: DEST_VIEW.fit,
2847
- fitH: DEST_VIEW.fitH,
2848
- fitV: DEST_VIEW.fitV,
2849
- fitR: DEST_VIEW.fitR,
2850
- fitB: DEST_VIEW.fitB,
2851
- fitBH: DEST_VIEW.fitBH,
2852
- fitBV: DEST_VIEW.fitBV
2853
- };
2854
- //#endregion
2855
- //#region src/features/annotations/internal/read/readDestination.ts
2856
- const MAX_VIEW_PARAMS = 4;
2857
- /**
2858
- * Materialise one explicit `PdfDestination` from a live `destPtr`. PDFium
2859
- * already resolved named destinations before handing us the pointer, so
2860
- * only explicit arrays arrive here (the rule `PdfDestination` documents).
2861
- *
2862
- * Returns `null` when the destination is unreadable: no visible indirect page
2863
- * in the document, or an unknown fit type. Coordinates stay raw PDF user
2864
- * space — no conversion at this layer.
2865
- *
2866
- * Shared by the link reader today; the outline/bookmark port reuses it.
2867
- */
2868
- function readDestination(fn, mem, docPtr, destPtr) {
2869
- const pageObjectNumber = fn.EPDFDest_GetPageObjectNumber(docPtr, destPtr);
2870
- if (pageObjectNumber === 0) return null;
2871
- const view = withScratchN(mem, [4, MAX_VIEW_PARAMS * 4], ([countPtr, paramsPtr]) => {
2872
- const code = fn.FPDFDest_GetView(destPtr, countPtr, paramsPtr);
2873
- const count = Math.min(readI32(mem, countPtr), MAX_VIEW_PARAMS);
2874
- const params = [];
2875
- for (let i = 0; i < count; i++) params.push(readF32(mem, paramsPtr, i * 4));
2876
- return {
2877
- code,
2878
- params
2879
- };
2880
- });
2881
- const at = (i) => i < view.params.length ? view.params[i] : null;
2882
- switch (view.code) {
2883
- case DEST_VIEW.xyz: return {
2884
- kind: "xyz",
2885
- pageObjectNumber,
2886
- ...readXyzLocation(fn, mem, destPtr)
2887
- };
2888
- case DEST_VIEW.fit: return {
2889
- kind: "fit",
2890
- pageObjectNumber
2891
- };
2892
- case DEST_VIEW.fitH: return {
2893
- kind: "fitH",
2894
- pageObjectNumber,
2895
- top: at(0)
2896
- };
2897
- case DEST_VIEW.fitV: return {
2898
- kind: "fitV",
2899
- pageObjectNumber,
2900
- left: at(0)
2901
- };
2902
- case DEST_VIEW.fitR:
2903
- if (view.params.length < 4) return {
2904
- kind: "fit",
2905
- pageObjectNumber
2906
- };
2907
- return {
2908
- kind: "fitR",
2909
- pageObjectNumber,
2910
- left: view.params[0],
2911
- bottom: view.params[1],
2912
- right: view.params[2],
2913
- top: view.params[3]
2914
- };
2915
- case DEST_VIEW.fitB: return {
2916
- kind: "fitB",
2917
- pageObjectNumber
2918
- };
2919
- case DEST_VIEW.fitBH: return {
2920
- kind: "fitBH",
2921
- pageObjectNumber,
2922
- top: at(0)
2923
- };
2924
- case DEST_VIEW.fitBV: return {
2925
- kind: "fitBV",
2926
- pageObjectNumber,
2927
- left: at(0)
2928
- };
2929
- default: return null;
2930
- }
2931
- }
2932
- /** `/XYZ` null-aware axes via `FPDFDest_GetLocationInPage` (FPDF_BOOL is 4 bytes). */
2933
- function readXyzLocation(fn, mem, destPtr) {
2934
- return withScratchN(mem, [
2935
- 4,
2936
- 4,
2937
- 4,
2938
- 4,
2939
- 4,
2940
- 4
2941
- ], ([hasLeft, hasTop, hasZoom, left, top, zoom]) => {
2942
- if (!fn.FPDFDest_GetLocationInPage(destPtr, hasLeft, hasTop, hasZoom, left, top, zoom)) return {
2943
- left: null,
2944
- top: null,
2945
- zoom: null
2946
- };
2947
- return {
2948
- left: readI32(mem, hasLeft) ? readF32(mem, left) : null,
2949
- top: readI32(mem, hasTop) ? readF32(mem, top) : null,
2950
- zoom: readI32(mem, hasZoom) ? readF32(mem, zoom) : null
2951
- };
2952
- });
2953
- }
2954
- //#endregion
2955
3161
  //#region src/features/annotations/internal/read/readLinkAnnotation.ts
2956
3162
  /**
2957
3163
  * Link reader: rect/flags/relationship ride the base (a link grouped to an
2958
3164
  * annotation is plain `inReplyTo` + `replyType: 'group'`); this module only
2959
3165
  * materialises the normalized target.
2960
3166
  *
2961
- * ONE truth, two views: the target is projected off the SAME fork action
2962
- * model the scripting plane reads (`base.actions`) — root classification
2963
- * via the full `EPDF_ACTION_TYPE_*` vocabulary, payloads via the on-demand
2964
- * node getters. The classic `FPDFAction_*` surface (5 types, JavaScript-
2965
- * and Named-blind) is deliberately not consulted; `FPDFLink_GetDest`
2966
- * remains only for the data-only direct `/Dest`, which is not an action.
3167
+ * ONE truth, one projection: the target is a pure function of the SAME
3168
+ * payload-carrying action tree the scripting plane reads
3169
+ * (`base.actions.activate`) — no second native read exists any more, so the
3170
+ * two action-shaped surfaces cannot drift by construction.
2967
3171
  *
2968
- * `/A` wins over `/Dest` — the spec forbids carrying both, and when a
2969
- * malformed file has both, Acrobat gives the action precedence. (Our own
2970
- * writes can't produce that state: `EPDFAnnot_SetAction` strips `/Dest`.)
3172
+ * `/A` precedence, pinned: an activate action that exists but cannot be
3173
+ * executed (an `incomplete` tree, a degraded/unreadable root) projects
3174
+ * `unsupported` — a broken action is a dead link, never an invitation to
3175
+ * guess. The direct `/Dest` (which is data, not an action) is consulted
3176
+ * ONLY when no `/A` exists at all.
2971
3177
  */
2972
3178
  function readLink(fn, mem, annotPtr, base, _rawSubtypeCode, ctx) {
2973
3179
  return {
2974
3180
  ...base,
2975
3181
  subtype: "link",
2976
- target: readLinkTarget(fn, mem, annotPtr, ctx)
3182
+ target: readLinkTarget(fn, mem, annotPtr, base, ctx)
2977
3183
  };
2978
3184
  }
2979
- function readLinkTarget(fn, mem, annotPtr, ctx) {
2980
- const modelPtr = fn.EPDFAnnot_GetActionModel(annotPtr, 0);
2981
- if (modelPtr !== _embedpdf_engine_runtime.NULL_PTR) try {
2982
- const root = fn.EPDFAction_GetRootNode(modelPtr);
2983
- if (isValidActionNodeId(root)) return readRootTarget(fn, mem, modelPtr, root, ctx);
2984
- } finally {
2985
- fn.EPDFAction_CloseModel(modelPtr);
3185
+ /**
3186
+ * Project the navigation view from a payload-carrying activate tree. Pure —
3187
+ * shared with any consumer that wants the root-level navigation reading of a
3188
+ * tree (the link plugin's no-actions-plugin fallback uses the same law).
3189
+ */
3190
+ function linkTargetFromActionTree(tree) {
3191
+ if (tree.incomplete) return { kind: "unsupported" };
3192
+ const root = tree.root;
3193
+ if (!root) return { kind: "unsupported" };
3194
+ switch (root.type) {
3195
+ case "goto": return {
3196
+ kind: "goto",
3197
+ destination: root.destination
3198
+ };
3199
+ case "uri": return {
3200
+ kind: "uri",
3201
+ uri: root.uri
3202
+ };
3203
+ case "goto-remote": return {
3204
+ kind: "goto-remote",
3205
+ file: root.filePath
3206
+ };
3207
+ case "launch": return {
3208
+ kind: "launch",
3209
+ path: root.filePath
3210
+ };
3211
+ case "javascript": return { kind: "javascript" };
3212
+ case "named": return {
3213
+ kind: "named",
3214
+ name: root.name
3215
+ };
3216
+ default: return { kind: "unsupported" };
2986
3217
  }
3218
+ }
3219
+ function readLinkTarget(fn, mem, annotPtr, base, ctx) {
3220
+ const activate = base.actions?.activate;
3221
+ if (activate) return linkTargetFromActionTree(activate);
2987
3222
  const linkPtr = fn.FPDFAnnot_GetLink(annotPtr);
2988
3223
  if (!linkPtr) return null;
2989
3224
  const destPtr = fn.FPDFLink_GetDest(ctx.docPtr, linkPtr);
@@ -2994,43 +3229,6 @@ function readLinkTarget(fn, mem, annotPtr, ctx) {
2994
3229
  destination
2995
3230
  } : { kind: "unsupported" };
2996
3231
  }
2997
- /** Classify the root action node and fetch its payload — the projection. */
2998
- function readRootTarget(fn, mem, modelPtr, node, ctx) {
2999
- switch (actionTypeFromCode(fn.EPDFAction_GetNodeType(modelPtr, node))) {
3000
- case "goto": {
3001
- const destPtr = fn.EPDFAction_GetNodeDest(ctx.docPtr, modelPtr, node);
3002
- const destination = destPtr ? readDestination(fn, mem, ctx.docPtr, destPtr) : null;
3003
- return destination ? {
3004
- kind: "goto",
3005
- destination
3006
- } : { kind: "unsupported" };
3007
- }
3008
- case "uri": {
3009
- const uri = readUtf8String(mem, (buf, capacity) => fn.EPDFAction_GetNodeURI(ctx.docPtr, modelPtr, node, buf, capacity));
3010
- return uri == null ? { kind: "unsupported" } : {
3011
- kind: "uri",
3012
- uri
3013
- };
3014
- }
3015
- case "goto-remote": return {
3016
- kind: "goto-remote",
3017
- file: readUtf8String(mem, (buf, capacity) => fn.EPDFAction_GetNodeFilePath(modelPtr, node, buf, capacity)) ?? ""
3018
- };
3019
- case "launch": return {
3020
- kind: "launch",
3021
- path: readUtf8String(mem, (buf, capacity) => fn.EPDFAction_GetNodeFilePath(modelPtr, node, buf, capacity)) ?? ""
3022
- };
3023
- case "javascript": return { kind: "javascript" };
3024
- case "named": {
3025
- const name = readUtf8String(mem, (buf, capacity) => fn.EPDFAction_GetNodeName(modelPtr, node, buf, capacity));
3026
- return name == null ? { kind: "unsupported" } : {
3027
- kind: "named",
3028
- name
3029
- };
3030
- }
3031
- default: return { kind: "unsupported" };
3032
- }
3033
- }
3034
3232
  //#endregion
3035
3233
  //#region src/features/annotations/internal/read/readRedactAnnotation.ts
3036
3234
  /** Default `/C` marking outline (red) — the redaction marking convention and
@@ -3540,7 +3738,7 @@ function readOptions(runtime, model, fieldIndex) {
3540
3738
  return options;
3541
3739
  }
3542
3740
  /** Read one field of the native model into its per-family DTO. */
3543
- function readFieldAt(runtime, model, fieldIndex, actionBudget = new ActionReadBudgetTracker()) {
3741
+ function readFieldAt(runtime, model, fieldIndex, docPtr, actionBudget = new ActionReadBudgetTracker()) {
3544
3742
  const { fn } = runtime;
3545
3743
  const fieldObjectNumber = fn.EPDFForm_GetFieldObjNum(model, fieldIndex);
3546
3744
  const name = readWide(runtime, (buf, cap) => fn.EPDFForm_GetFieldName(model, fieldIndex, buf, cap));
@@ -3548,7 +3746,7 @@ function readFieldAt(runtime, model, fieldIndex, actionBudget = new ActionReadBu
3548
3746
  const family = FAMILY_BY_CODE[fn.EPDFForm_GetFieldFamily(model, fieldIndex)] ?? "unknown";
3549
3747
  const valueEntry = readValueEntry(runtime, model, fieldIndex, false);
3550
3748
  const defaultValueEntry = readValueEntry(runtime, model, fieldIndex, true);
3551
- const actions = readFieldActions(runtime, model, fieldIndex, actionBudget);
3749
+ const actions = readFieldActions(runtime, model, fieldIndex, docPtr, actionBudget);
3552
3750
  const base = {
3553
3751
  ref: fieldObjectNumber > 0 ? {
3554
3752
  kind: "objectNumber",
@@ -3647,12 +3845,12 @@ function readFieldAt(runtime, model, fieldIndex, actionBudget = new ActionReadBu
3647
3845
  }
3648
3846
  }
3649
3847
  /** Read the whole native model into a detached {@link FormSnapshot}. */
3650
- function readFormSnapshot(runtime, model) {
3848
+ function readFormSnapshot(runtime, model, docPtr) {
3651
3849
  const { fn } = runtime;
3652
3850
  const count = fn.EPDFForm_CountFields(model);
3653
3851
  const fields = [];
3654
3852
  const actionBudget = new ActionReadBudgetTracker();
3655
- for (let i = 0; i < count; i++) fields.push(readFieldAt(runtime, model, i, actionBudget));
3853
+ for (let i = 0; i < count; i++) fields.push(readFieldAt(runtime, model, i, docPtr, actionBudget));
3656
3854
  const calculationOrder = [];
3657
3855
  const calculationCount = fn.EPDFForm_CountCalculationOrder(model);
3658
3856
  for (let index = 0; index < calculationCount; index++) {
@@ -3688,7 +3886,7 @@ function entryScalar(entry) {
3688
3886
  if (entry.kind === "array") return entry.values[0] ?? "";
3689
3887
  return "";
3690
3888
  }
3691
- function readFieldActions(runtime, model, fieldIndex, budget) {
3889
+ function readFieldActions(runtime, model, fieldIndex, docPtr, budget) {
3692
3890
  const { fn, mem } = runtime;
3693
3891
  const entries = [
3694
3892
  ["keystroke", 0],
@@ -3698,7 +3896,7 @@ function readFieldActions(runtime, model, fieldIndex, budget) {
3698
3896
  ];
3699
3897
  const actions = {};
3700
3898
  for (const [key, event] of entries) {
3701
- const action = readActionModel(fn, mem, fn.EPDFForm_GetFieldActionModel(model, fieldIndex, event), budget);
3899
+ const action = readActionModel(fn, mem, docPtr, fn.EPDFForm_GetFieldActionModel(model, fieldIndex, event), budget);
3702
3900
  if (action) actions[key] = action;
3703
3901
  }
3704
3902
  return Object.keys(actions).length > 0 ? actions : void 0;
@@ -3944,7 +4142,7 @@ function blendModeFromCode(code) {
3944
4142
  * per-subtype reader builds its DTO by extending this with its own
3945
4143
  * fields and `subtype: '...'` discriminator.
3946
4144
  */
3947
- function readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision, actionBudget = new ActionReadBudgetTracker()) {
4145
+ function readAnnotationBase(fn, mem, docPtr, annotPtr, pageObjectNumber, index, revision, actionBudget = new ActionReadBudgetTracker()) {
3948
4146
  const identity = readAnnotationIdentity(fn, mem, annotPtr, pageObjectNumber, index, revision);
3949
4147
  const rect = readAnnotRect(fn, mem, annotPtr);
3950
4148
  const flags = readAnnotFlags(fn, annotPtr);
@@ -3956,7 +4154,7 @@ function readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision
3956
4154
  const blendMode = blendModeFromCode(fn.EPDFAnnot_GetBlendMode(annotPtr));
3957
4155
  const embd = readEmbedMetadata(fn, mem, annotPtr);
3958
4156
  const relationship = readAnnotationRelationship(fn, mem, annotPtr, pageObjectNumber);
3959
- const actions = readAnnotationActions(fn, mem, annotPtr, actionBudget);
4157
+ const actions = readAnnotationActions(fn, mem, docPtr, annotPtr, actionBudget);
3960
4158
  return {
3961
4159
  ref: identity.ref,
3962
4160
  pageObjectNumber,
@@ -3980,7 +4178,7 @@ function readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision
3980
4178
  ...actions ? { actions } : {}
3981
4179
  };
3982
4180
  }
3983
- function readAnnotationActions(fn, mem, annotPtr, budget) {
4181
+ function readAnnotationActions(fn, mem, docPtr, annotPtr, budget) {
3984
4182
  const events = [
3985
4183
  ["activate", 0],
3986
4184
  ["cursorEnter", 1],
@@ -3996,7 +4194,7 @@ function readAnnotationActions(fn, mem, annotPtr, budget) {
3996
4194
  ];
3997
4195
  const actions = {};
3998
4196
  for (const [key, event] of events) {
3999
- const action = readActionModel(fn, mem, fn.EPDFAnnot_GetActionModel(annotPtr, event), budget);
4197
+ const action = readActionModel(fn, mem, docPtr, fn.EPDFAnnot_GetActionModel(annotPtr, event), budget);
4000
4198
  if (action) actions[key] = action;
4001
4199
  }
4002
4200
  return Object.keys(actions).length > 0 ? actions : void 0;
@@ -4027,7 +4225,7 @@ function collectPageAnnotations(input) {
4027
4225
  const annotPtr = getAnnotPtrAt(i);
4028
4226
  if (!annotPtr) continue;
4029
4227
  try {
4030
- const base = readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, i, revision, actionBudget);
4228
+ const base = readAnnotationBase(fn, mem, readCtx.docPtr, annotPtr, pageObjectNumber, i, revision, actionBudget);
4031
4229
  const subtypeCode = fn.FPDFAnnot_GetSubtype(annotPtr);
4032
4230
  const { reader } = pickReader(subtypeCode);
4033
4231
  const dto = reader(fn, mem, annotPtr, base, subtypeCode, readCtx);
@@ -4965,7 +5163,7 @@ function computeMutationImpact(inputs) {
4965
5163
  * `AnnotationUpdateResult.updated`.
4966
5164
  */
4967
5165
  function readAnnotationFromPtr(fn, mem, annotPtr, pageObjectNumber, index, revision, ctx) {
4968
- const base = readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision);
5166
+ const base = readAnnotationBase(fn, mem, ctx.docPtr, annotPtr, pageObjectNumber, index, revision);
4969
5167
  const subtypeCode = fn.FPDFAnnot_GetSubtype(annotPtr);
4970
5168
  const { reader } = pickReader(subtypeCode);
4971
5169
  return reader(fn, mem, annotPtr, base, subtypeCode, ctx);
@@ -8183,13 +8381,13 @@ var FormReader = class {
8183
8381
  snapshot(signal) {
8184
8382
  throwIfAborted(signal);
8185
8383
  const model = acquireFormModel(this.runtime, this.session);
8186
- return readFormSnapshot(this.runtime, model);
8384
+ return readFormSnapshot(this.runtime, model, this.session.requireDocPtr());
8187
8385
  }
8188
8386
  field(ref, signal) {
8189
8387
  throwIfAborted(signal);
8190
8388
  const model = acquireFormModel(this.runtime, this.session);
8191
8389
  const { fieldIndex } = resolveFieldRef(this.runtime, model, ref);
8192
- return readFieldAt(this.runtime, model, fieldIndex);
8390
+ return readFieldAt(this.runtime, model, fieldIndex, this.session.requireDocPtr());
8193
8391
  }
8194
8392
  /**
8195
8393
  * Serialize form data. Exports read the reconciled view (recovered
@@ -8345,7 +8543,7 @@ var FormMutator = class {
8345
8543
  const model = acquireFormModel(this.runtime, this.session);
8346
8544
  const resolved = resolveFieldRef(this.runtime, model, ref);
8347
8545
  this.assertWritable(resolved.fieldObjectNumber);
8348
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8546
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8349
8547
  if (!FAMILY_BY_VALUE_TYPE[value.type].includes(before.family)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `value type '${value.type}' does not apply to a '${before.family}' field`);
8350
8548
  const changed = this.applyWrite(resolved.fieldObjectNumber, value);
8351
8549
  return this.readBack(resolved.fieldObjectNumber, changed);
@@ -8381,7 +8579,7 @@ var FormMutator = class {
8381
8579
  const fresh = acquireFormModel(this.runtime, this.session);
8382
8580
  return {
8383
8581
  ...counters,
8384
- snapshot: readFormSnapshot(this.runtime, fresh),
8582
+ snapshot: readFormSnapshot(this.runtime, fresh, this.session.requireDocPtr()),
8385
8583
  meta: EMPTY_META$1
8386
8584
  };
8387
8585
  }
@@ -8454,7 +8652,7 @@ var FormMutator = class {
8454
8652
  const model = acquireFormModel(this.runtime, this.session);
8455
8653
  const resolved = resolveFieldRef(this.runtime, model, ref);
8456
8654
  this.assertWritable(resolved.fieldObjectNumber);
8457
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8655
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8458
8656
  if (before.family !== patch.family) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `patch family '${patch.family}' does not match field family '${before.family}'`);
8459
8657
  const fieldObjectNumber = resolved.fieldObjectNumber;
8460
8658
  if (patch.name !== void 0) this.applyWideSetter(fn.EPDFForm_SetFieldName, fieldObjectNumber, patch.name, `cannot rename to "${patch.name}" (sibling conflict or invalid)`);
@@ -8485,7 +8683,7 @@ var FormMutator = class {
8485
8683
  const model = acquireFormModel(this.runtime, this.session);
8486
8684
  const resolved = resolveFieldRef(this.runtime, model, ref);
8487
8685
  this.assertWritable(resolved.fieldObjectNumber);
8488
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8686
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8489
8687
  if (!withScratchN(mem, [256 * 4, 4], ([buf, countPtr]) => {
8490
8688
  mem.poke(countPtr, "i32", 0);
8491
8689
  return fn.EPDFForm_DeleteField(this.session.requireDocPtr(), resolved.fieldObjectNumber, buf, 256, countPtr);
@@ -8505,7 +8703,7 @@ var FormMutator = class {
8505
8703
  const model = acquireFormModel(this.runtime, this.session);
8506
8704
  const resolved = resolveFieldRef(this.runtime, model, ref);
8507
8705
  this.assertWritable(resolved.fieldObjectNumber);
8508
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8706
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8509
8707
  const toggle = before.family === "checkbox" || before.family === "radio";
8510
8708
  const state = toggle ? onState ?? (before.family === "checkbox" ? "Yes" : "") : "";
8511
8709
  if (toggle && (!state || state === "Off")) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "attaching to a radio group needs a non-\"Off\" onState");
@@ -8551,7 +8749,7 @@ var FormMutator = class {
8551
8749
  const fresh = acquireFormModel(this.runtime, this.session);
8552
8750
  const fieldIndex = this.runtime.fn.EPDFForm_GetFieldIndexByObjNum(fresh, fieldObjectNumber);
8553
8751
  if (fieldIndex < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "form field vanished after write");
8554
- return readFieldAt(this.runtime, fresh, fieldIndex);
8752
+ return readFieldAt(this.runtime, fresh, fieldIndex, this.session.requireDocPtr());
8555
8753
  }
8556
8754
  assertWritable(fieldObjectNumber) {
8557
8755
  if (fieldObjectNumber === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "form field is stored as a direct object and cannot be written");
@@ -8597,7 +8795,7 @@ var FormMutator = class {
8597
8795
  const fresh = acquireFormModel(this.runtime, this.session);
8598
8796
  const fieldIndex = this.runtime.fn.EPDFForm_GetFieldIndexByObjNum(fresh, fieldObjectNumber);
8599
8797
  if (fieldIndex < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "form field vanished after write");
8600
- const field = readFieldAt(this.runtime, fresh, fieldIndex);
8798
+ const field = readFieldAt(this.runtime, fresh, fieldIndex, this.session.requireDocPtr());
8601
8799
  const changedSet = new Set(changedObjNums);
8602
8800
  return {
8603
8801
  field,
@@ -8743,7 +8941,7 @@ var FormsEffectsApplier = class {
8743
8941
  if (refs.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "reset effect requires at least one field");
8744
8942
  const model = acquireFormModel(this.runtime, this.session);
8745
8943
  const resolved = refs.map((ref) => this.resolve(model, ref));
8746
- const fields = resolved.map(({ fieldIndex }) => readFieldAt(this.runtime, model, fieldIndex, actionBudget));
8944
+ const fields = resolved.map(({ fieldIndex }) => readFieldAt(this.runtime, model, fieldIndex, this.session.requireDocPtr(), actionBudget));
8747
8945
  validateEffect(effect, fields);
8748
8946
  return {
8749
8947
  effect,
@@ -8768,7 +8966,7 @@ var FormsEffectsApplier = class {
8768
8966
  const model = acquireFormModel(this.runtime, this.session);
8769
8967
  const index = this.runtime.fn.EPDFForm_GetFieldIndexByObjNum(model, fieldObjectNumber);
8770
8968
  if (index < 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidReference, "form field disappeared");
8771
- return readFieldAt(this.runtime, model, index, actionBudget);
8969
+ return readFieldAt(this.runtime, model, index, this.session.requireDocPtr(), actionBudget);
8772
8970
  }
8773
8971
  readFieldsBestEffort(fieldObjectNumbers, actionBudget) {
8774
8972
  const fields = [];
@@ -9135,22 +9333,26 @@ var DocumentActionsReader = class {
9135
9333
  } finally {
9136
9334
  fn.FPDFDoc_CloseJavaScriptAction(namedPtr);
9137
9335
  }
9138
- const action = requireAction(readActionModel(fn, mem, fn.EPDFDoc_GetNamedJavaScriptActionModel(docPtr, index), budget), "name-tree script", index);
9336
+ budget.reservePayloadBytes(name.length);
9337
+ const action = requireAction(readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetNamedJavaScriptActionModel(docPtr, index), budget), "name-tree script", index);
9139
9338
  nameTreeScripts.push({
9140
9339
  name,
9141
9340
  action
9142
9341
  });
9143
9342
  }
9144
- const openAction = readActionModel(fn, mem, fn.EPDFDoc_GetOpenActionModel(docPtr), budget);
9343
+ const openAction = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetOpenActionModel(docPtr), budget);
9344
+ const openDestPtr = openAction ? _embedpdf_engine_runtime.NULL_PTR : fn.EPDFDoc_GetOpenActionDest(docPtr);
9345
+ const openDestination = openDestPtr !== _embedpdf_engine_runtime.NULL_PTR ? readDestination(fn, mem, docPtr, openDestPtr) : null;
9145
9346
  const additional = {};
9146
9347
  for (const [key, event] of DOCUMENT_ACTION_EVENTS) {
9147
9348
  throwIfAborted(signal);
9148
- const action = readActionModel(fn, mem, fn.EPDFDoc_GetAdditionalActionModel(docPtr, event), budget);
9349
+ const action = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetAdditionalActionModel(docPtr, event), budget);
9149
9350
  if (action) additional[key] = action;
9150
9351
  }
9151
9352
  return {
9152
9353
  nameTreeScripts,
9153
9354
  openAction,
9355
+ openDestination,
9154
9356
  ...additional
9155
9357
  };
9156
9358
  }