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