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

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);
@@ -1397,15 +1732,16 @@ var PagesReader = class {
1397
1732
  });
1398
1733
  return {
1399
1734
  pageCount: pages.length,
1400
- pages
1735
+ pages,
1736
+ namedPages: readNamedPages(fn, mem, docPtr)
1401
1737
  };
1402
1738
  });
1403
1739
  }
1404
1740
  };
1405
1741
  function readPageActions(fn, mem, docPtr, pageObjectNumber, budget) {
1406
1742
  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);
1743
+ const open = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetPageActionModel(docPtr, pageObjectNumber, 0), budget);
1744
+ const close = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetPageActionModel(docPtr, pageObjectNumber, 1), budget);
1409
1745
  if (open) actions.open = open;
1410
1746
  if (close) actions.close = close;
1411
1747
  return open || close ? actions : void 0;
@@ -1462,6 +1798,41 @@ function readUserUnit(fn, mem, docPtr, index, userUnitPtr) {
1462
1798
  function readLabel(fn, mem, docPtr, index) {
1463
1799
  return readUtf16String(mem, (buf, capacity) => fn.FPDF_GetPageLabel(docPtr, index, buf, capacity), null) || null;
1464
1800
  }
1801
+ const NAMED_PAGE_TREES = [0, 1];
1802
+ const NAMED_PAGE_KIND_PAGE = 0;
1803
+ const NAMED_PAGE_KIND_TEMPLATE = 1;
1804
+ /**
1805
+ * The catalog's `/Names /Pages` and `/Names /Templates` registrations, in
1806
+ * tree order, each value classified by the fork (page / template /
1807
+ * dangling). Page-identity data like `label`, so it ships inside the same
1808
+ * snapshot — a registration is only meaningful against the page set that
1809
+ * contains its target.
1810
+ */
1811
+ function readNamedPages(fn, mem, docPtr) {
1812
+ const entries = [];
1813
+ withScratchN(mem, [4, 4], ([objNumPtr, kindPtr]) => {
1814
+ for (const tree of NAMED_PAGE_TREES) {
1815
+ const count = fn.EPDFDoc_GetNamedPageCount(docPtr, tree);
1816
+ for (let index = 0; index < count; index++) {
1817
+ const name = readUtf16String(mem, (buf, capacity) => fn.EPDFDoc_GetNamedPageAt(docPtr, tree, index, buf, capacity, objNumPtr, kindPtr), "");
1818
+ if (name === null) continue;
1819
+ const objectNumber = Number(mem.peek(objNumPtr, "i32")) >>> 0;
1820
+ const kind = Number(mem.peek(kindPtr, "i32"));
1821
+ entries.push({
1822
+ name,
1823
+ target: kind === NAMED_PAGE_KIND_PAGE ? {
1824
+ kind: "page",
1825
+ pageObjectNumber: objectNumber
1826
+ } : kind === NAMED_PAGE_KIND_TEMPLATE ? {
1827
+ kind: "template",
1828
+ objectNumber
1829
+ } : { kind: "dangling" }
1830
+ });
1831
+ }
1832
+ }
1833
+ });
1834
+ return entries;
1835
+ }
1465
1836
  //#endregion
1466
1837
  //#region src/features/pages/PagesMutator.ts
1467
1838
  /**
@@ -1609,6 +1980,38 @@ var PagesMutator = class {
1609
1980
  cache: null
1610
1981
  };
1611
1982
  }
1983
+ /**
1984
+ * Register `name` → page in `/Names /Pages` (create, or replace what the
1985
+ * key points at); with `replace`, drop that other key first — a rename as
1986
+ * one job. Named pages are LAYOUT: page identity and order are untouched
1987
+ * (no registry refresh, no revision bumps) and the fresh snapshot is
1988
+ * returned like `move()`.
1989
+ */
1990
+ setName(input, signal) {
1991
+ throwIfAborted(signal);
1992
+ if (input.name.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "pages.setName requires a non-empty name");
1993
+ const { fn, mem } = this.runtime;
1994
+ const docPtr = this.session.requireDocPtr();
1995
+ this.session.recordByObjectNumber(input.pageObjectNumber);
1996
+ if (input.replace !== void 0 && input.replace !== input.name && input.replace.length > 0) writeUtf16String(mem, input.replace, (ptr) => fn.EPDFDoc_RemoveNamedPage(docPtr, ptr));
1997
+ if (!writeUtf16String(mem, input.name, (ptr) => fn.EPDFDoc_SetNamedPage(docPtr, ptr, input.pageObjectNumber))) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `EPDFDoc_SetNamedPage rejected '${input.name}' for page ${input.pageObjectNumber}`);
1998
+ return {
1999
+ layout: new PagesReader(this.runtime, this.session).read(signal),
2000
+ cache: null
2001
+ };
2002
+ }
2003
+ /** Remove one `/Names /Pages` registration; the page stays. */
2004
+ removeName(input, signal) {
2005
+ throwIfAborted(signal);
2006
+ if (input.name.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "pages.removeName requires a non-empty name");
2007
+ const { fn, mem } = this.runtime;
2008
+ const docPtr = this.session.requireDocPtr();
2009
+ if (!writeUtf16String(mem, input.name, (ptr) => fn.EPDFDoc_RemoveNamedPage(docPtr, ptr))) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `pages.removeName: no /Names /Pages registration '${input.name}'`);
2010
+ return {
2011
+ layout: new PagesReader(this.runtime, this.session).read(signal),
2012
+ cache: null
2013
+ };
2014
+ }
1612
2015
  /** Shared input check: non-empty, no duplicate PONs. */
1613
2016
  requireUniquePons(op, pageObjectNumbers) {
1614
2017
  if (pageObjectNumbers.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `${op} requires at least one page`);
@@ -1621,7 +2024,7 @@ var PagesMutator = class {
1621
2024
  };
1622
2025
  //#endregion
1623
2026
  //#region src/features/pages/PagesExtractor.ts
1624
- const FPDF_NO_INCREMENTAL$1 = 2;
2027
+ const FPDF_NO_INCREMENTAL$2 = 2;
1625
2028
  /**
1626
2029
  * Export a subset of pages as a standalone PDF. A READ over the session:
1627
2030
  * `FPDF_ImportPagesByIndex` copies pages into a scratch document, so the
@@ -1660,7 +2063,7 @@ var PagesExtractor = class {
1660
2063
  for (let i = 0; i < indices.length; i++) mem.poke(idxPtr, "i32", indices[i], 4 * i);
1661
2064
  if (!fn.FPDF_ImportPagesByIndex(destPtr, srcPtr, idxPtr, indices.length, 0)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `FPDF_ImportPagesByIndex rejected the request (indices=[${indices.join(",")}])`);
1662
2065
  mem.poke(sizePtr, "i32", 0);
1663
- pdfPtr = fn.EPDF_SaveDocumentToOwnedBuffer(destPtr, FPDF_NO_INCREMENTAL$1, sizePtr);
2066
+ pdfPtr = fn.EPDF_SaveDocumentToOwnedBuffer(destPtr, FPDF_NO_INCREMENTAL$2, sizePtr);
1664
2067
  const size = Number(mem.peek(sizePtr, "i32"));
1665
2068
  if (!pdfPtr || size <= 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocOpenFailed, "failed to save extracted pages");
1666
2069
  const bytes = mem.readBytes(pdfPtr, size);
@@ -2212,6 +2615,13 @@ function readQuadPoints(fn, mem, annotPtr) {
2212
2615
  return out;
2213
2616
  });
2214
2617
  }
2618
+ /**
2619
+ * `/Name` as text, whatever its value (`EPDFAnnot_GetName`): a standard
2620
+ * icon/stamp name or a custom identifier. `null` when absent.
2621
+ */
2622
+ function readAnnotName(fn, mem, annotPtr) {
2623
+ return readUtf8String(mem, (buf, capacity) => fn.EPDFAnnot_GetName(annotPtr, buf, capacity));
2624
+ }
2215
2625
  //#endregion
2216
2626
  //#region src/features/annotations/internal/read/readAnnotationTransformMetadata.ts
2217
2627
  /**
@@ -2461,29 +2871,29 @@ function normalizeChecksum(raw) {
2461
2871
  //#endregion
2462
2872
  //#region src/features/annotations/internal/annotationIcon.ts
2463
2873
  /**
2464
- * `/Name` icon ↔ `FPDF_ANNOT_NAME` enum code mapping for text and
2465
- * file-attachment annotations (`EPDFAnnot_SetName` / `EPDFAnnot_GetName`).
2466
- * Codes mirror the `FPDF_ANNOT_NAME` enum in `public/fpdf_annot.h` —
2467
- * same pattern as `stampName.ts` (whose stamp block starts at 13, right
2468
- * after these) keeping engine-core PDFium-free.
2469
- */
2470
- const NOTE_ICON_TO_CODE = Object.freeze({
2471
- comment: 0,
2472
- key: 1,
2473
- note: 2,
2474
- help: 3,
2475
- "new-paragraph": 4,
2476
- paragraph: 5,
2477
- insert: 6
2874
+ * `/Name` icon vocabulary ↔ PDF name mapping for text and file-attachment
2875
+ * annotations (ISO 32000-2 tables 175 and 184). The wire speaks the
2876
+ * kebab-case ids; `EPDFAnnot_SetName` / `EPDFAnnot_GetName` speak the PDF
2877
+ * names. An unknown PDF name reads as the subtype's default (the reader
2878
+ * draws the default glyph for names it does not know — spec behavior).
2879
+ */
2880
+ const NOTE_ICON_TO_NAME = Object.freeze({
2881
+ comment: "Comment",
2882
+ key: "Key",
2883
+ note: "Note",
2884
+ help: "Help",
2885
+ "new-paragraph": "NewParagraph",
2886
+ paragraph: "Paragraph",
2887
+ insert: "Insert"
2478
2888
  });
2479
- const NOTE_CODE_TO_ICON = Object.freeze(Object.fromEntries(Object.entries(NOTE_ICON_TO_CODE).map(([icon, code]) => [code, icon])));
2480
- const FILE_ICON_TO_CODE = Object.freeze({
2481
- graph: 7,
2482
- "push-pin": 8,
2483
- paperclip: 9,
2484
- tag: 10
2889
+ const NOTE_NAME_TO_ICON = Object.freeze(Object.fromEntries(Object.entries(NOTE_ICON_TO_NAME).map(([icon, name]) => [name, icon])));
2890
+ const FILE_ICON_TO_NAME = Object.freeze({
2891
+ graph: "Graph",
2892
+ "push-pin": "PushPin",
2893
+ paperclip: "Paperclip",
2894
+ tag: "Tag"
2485
2895
  });
2486
- const FILE_CODE_TO_ICON = Object.freeze(Object.fromEntries(Object.entries(FILE_ICON_TO_CODE).map(([icon, code]) => [code, icon])));
2896
+ const FILE_NAME_TO_ICON = Object.freeze(Object.fromEntries(Object.entries(FILE_ICON_TO_NAME).map(([icon, name]) => [name, icon])));
2487
2897
  //#endregion
2488
2898
  //#region src/features/annotations/internal/read/readFileAttachmentAnnotation.ts
2489
2899
  /** Default `/C` — matches the generator's default icon fill and the writer default. */
@@ -2505,7 +2915,7 @@ function readFileAttachment(fn, mem, annotPtr, base) {
2505
2915
  const color = readAnnotColor(fn, mem, annotPtr) ?? { ...DEFAULT_FILE_ATTACHMENT_COLOR$1 };
2506
2916
  const ca = readAnnotOpacity(fn, mem, annotPtr);
2507
2917
  const opacity = ca == null ? 1 : Math.max(0, Math.min(1, ca));
2508
- const icon = FILE_CODE_TO_ICON[fn.EPDFAnnot_GetName(annotPtr)] ?? DEFAULT_FILE_ATTACHMENT_ICON;
2918
+ const icon = FILE_NAME_TO_ICON[readAnnotName(fn, mem, annotPtr) ?? ""] ?? DEFAULT_FILE_ATTACHMENT_ICON;
2509
2919
  const attachmentPtr = fn.FPDFAnnot_GetFileAttachment(annotPtr);
2510
2920
  const file = attachmentPtr ? readAttachmentFileInfo(fn, mem, attachmentPtr) : { name: "" };
2511
2921
  return {
@@ -2823,167 +3233,67 @@ function readLine(fn, mem, annotPtr, base) {
2823
3233
  };
2824
3234
  }
2825
3235
  //#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
3236
  //#region src/features/annotations/internal/read/readLinkAnnotation.ts
2956
3237
  /**
2957
3238
  * Link reader: rect/flags/relationship ride the base (a link grouped to an
2958
3239
  * annotation is plain `inReplyTo` + `replyType: 'group'`); this module only
2959
3240
  * materialises the normalized target.
2960
3241
  *
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.
3242
+ * ONE truth, one projection: the target is a pure function of the SAME
3243
+ * payload-carrying action tree the scripting plane reads
3244
+ * (`base.actions.activate`) — no second native read exists any more, so the
3245
+ * two action-shaped surfaces cannot drift by construction.
2967
3246
  *
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`.)
3247
+ * `/A` precedence, pinned: an activate action that exists but cannot be
3248
+ * executed (an `incomplete` tree, a degraded/unreadable root) projects
3249
+ * `unsupported` — a broken action is a dead link, never an invitation to
3250
+ * guess. The direct `/Dest` (which is data, not an action) is consulted
3251
+ * ONLY when no `/A` exists at all.
2971
3252
  */
2972
3253
  function readLink(fn, mem, annotPtr, base, _rawSubtypeCode, ctx) {
2973
3254
  return {
2974
3255
  ...base,
2975
3256
  subtype: "link",
2976
- target: readLinkTarget(fn, mem, annotPtr, ctx)
3257
+ target: readLinkTarget(fn, mem, annotPtr, base, ctx)
2977
3258
  };
2978
3259
  }
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);
3260
+ /**
3261
+ * Project the navigation view from a payload-carrying activate tree. Pure —
3262
+ * shared with any consumer that wants the root-level navigation reading of a
3263
+ * tree (the link plugin's no-actions-plugin fallback uses the same law).
3264
+ */
3265
+ function linkTargetFromActionTree(tree) {
3266
+ if (tree.incomplete) return { kind: "unsupported" };
3267
+ const root = tree.root;
3268
+ if (!root) return { kind: "unsupported" };
3269
+ switch (root.type) {
3270
+ case "goto": return {
3271
+ kind: "goto",
3272
+ destination: root.destination
3273
+ };
3274
+ case "uri": return {
3275
+ kind: "uri",
3276
+ uri: root.uri
3277
+ };
3278
+ case "goto-remote": return {
3279
+ kind: "goto-remote",
3280
+ file: root.filePath
3281
+ };
3282
+ case "launch": return {
3283
+ kind: "launch",
3284
+ path: root.filePath
3285
+ };
3286
+ case "javascript": return { kind: "javascript" };
3287
+ case "named": return {
3288
+ kind: "named",
3289
+ name: root.name
3290
+ };
3291
+ default: return { kind: "unsupported" };
2986
3292
  }
3293
+ }
3294
+ function readLinkTarget(fn, mem, annotPtr, base, ctx) {
3295
+ const activate = base.actions?.activate;
3296
+ if (activate) return linkTargetFromActionTree(activate);
2987
3297
  const linkPtr = fn.FPDFAnnot_GetLink(annotPtr);
2988
3298
  if (!linkPtr) return null;
2989
3299
  const destPtr = fn.FPDFLink_GetDest(ctx.docPtr, linkPtr);
@@ -2994,43 +3304,6 @@ function readLinkTarget(fn, mem, annotPtr, ctx) {
2994
3304
  destination
2995
3305
  } : { kind: "unsupported" };
2996
3306
  }
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
3307
  //#endregion
3035
3308
  //#region src/features/annotations/internal/read/readRedactAnnotation.ts
3036
3309
  /** Default `/C` marking outline (red) — the redaction marking convention and
@@ -3111,45 +3384,10 @@ function readSquare(fn, mem, annotPtr, base) {
3111
3384
  };
3112
3385
  }
3113
3386
  //#endregion
3114
- //#region src/features/annotations/internal/stampName.ts
3115
- /**
3116
- * Stamp `/Name` label ↔ `FPDF_ANNOT_NAME` enum code mapping for the
3117
- * `EPDFAnnot_SetName` / `EPDFAnnot_GetName` fork helpers. Labels are the
3118
- * PDF spec / Adobe literal name values (ISO 32000 §12.5.6.12 table 181
3119
- * plus the Adobe SB/SH extended set the fork supports). Codes mirror the
3120
- * `FPDF_ANNOT_NAME` enum in `public/fpdf_annot.h` — same pattern as
3121
- * `lineEnding.ts` keeping engine-core PDFium-free.
3122
- */
3123
- const STAMP_NAME_TO_CODE = Object.freeze({
3124
- Approved: 13,
3125
- Experimental: 14,
3126
- NotApproved: 15,
3127
- AsIs: 16,
3128
- Expired: 17,
3129
- NotForPublicRelease: 18,
3130
- Confidential: 19,
3131
- Final: 20,
3132
- Sold: 21,
3133
- Departmental: 22,
3134
- ForComment: 23,
3135
- TopSecret: 24,
3136
- Draft: 25,
3137
- ForPublicRelease: 26,
3138
- Completed: 27,
3139
- Void: 28,
3140
- PreliminaryResults: 29,
3141
- InformationOnly: 30,
3142
- Rejected: 31,
3143
- Witness: 32,
3144
- InitialHere: 33,
3145
- SignHere: 34,
3146
- Accepted: 35
3147
- });
3148
- const STAMP_CODE_TO_NAME = Object.freeze(Object.fromEntries(Object.entries(STAMP_NAME_TO_CODE).map(([name, code]) => [code, name])));
3149
- //#endregion
3150
3387
  //#region src/features/annotations/internal/read/readStampAnnotation.ts
3151
3388
  /**
3152
- * Stamp DTO: base + `/Name` label + transform metadata. The visual content
3389
+ * Stamp DTO: base + `/Name` (standard or custom identifier, verbatim) +
3390
+ * transform metadata. The visual content
3153
3391
  * stays in the `/AP` stream — rendered via `renderAppearanceImages()`,
3154
3392
  * never surfaced as DTO data.
3155
3393
  */
@@ -3159,16 +3397,11 @@ function readStamp(fn, mem, annotPtr, base) {
3159
3397
  return {
3160
3398
  ...base,
3161
3399
  subtype: "stamp",
3162
- name: readStampName(fn, mem, annotPtr),
3400
+ name: readAnnotName(fn, mem, annotPtr),
3163
3401
  ...rotation != null ? { rotation } : {},
3164
3402
  ...unrotatedRect != null ? { unrotatedRect } : {}
3165
3403
  };
3166
3404
  }
3167
- function readStampName(fn, mem, annotPtr) {
3168
- const known = STAMP_CODE_TO_NAME[fn.EPDFAnnot_GetName(annotPtr)];
3169
- if (known !== void 0) return known;
3170
- return readAnnotString(fn, mem, annotPtr, "Name");
3171
- }
3172
3405
  //#endregion
3173
3406
  //#region src/features/annotations/internal/annotationState.ts
3174
3407
  /**
@@ -3224,7 +3457,7 @@ function readText(fn, mem, annotPtr, base) {
3224
3457
  const color = readAnnotColor(fn, mem, annotPtr) ?? { ...DEFAULT_NOTE_COLOR$1 };
3225
3458
  const ca = readAnnotOpacity(fn, mem, annotPtr);
3226
3459
  const opacity = ca == null ? 1 : Math.max(0, Math.min(1, ca));
3227
- const icon = NOTE_CODE_TO_ICON[fn.EPDFAnnot_GetName(annotPtr)] ?? DEFAULT_NOTE_ICON;
3460
+ const icon = NOTE_NAME_TO_ICON[readAnnotName(fn, mem, annotPtr) ?? ""] ?? DEFAULT_NOTE_ICON;
3228
3461
  const stateRaw = readAnnotString(fn, mem, annotPtr, "State");
3229
3462
  const stateModelRaw = readAnnotString(fn, mem, annotPtr, "StateModel");
3230
3463
  return {
@@ -3540,7 +3773,7 @@ function readOptions(runtime, model, fieldIndex) {
3540
3773
  return options;
3541
3774
  }
3542
3775
  /** Read one field of the native model into its per-family DTO. */
3543
- function readFieldAt(runtime, model, fieldIndex, actionBudget = new ActionReadBudgetTracker()) {
3776
+ function readFieldAt(runtime, model, fieldIndex, docPtr, actionBudget = new ActionReadBudgetTracker()) {
3544
3777
  const { fn } = runtime;
3545
3778
  const fieldObjectNumber = fn.EPDFForm_GetFieldObjNum(model, fieldIndex);
3546
3779
  const name = readWide(runtime, (buf, cap) => fn.EPDFForm_GetFieldName(model, fieldIndex, buf, cap));
@@ -3548,7 +3781,7 @@ function readFieldAt(runtime, model, fieldIndex, actionBudget = new ActionReadBu
3548
3781
  const family = FAMILY_BY_CODE[fn.EPDFForm_GetFieldFamily(model, fieldIndex)] ?? "unknown";
3549
3782
  const valueEntry = readValueEntry(runtime, model, fieldIndex, false);
3550
3783
  const defaultValueEntry = readValueEntry(runtime, model, fieldIndex, true);
3551
- const actions = readFieldActions(runtime, model, fieldIndex, actionBudget);
3784
+ const actions = readFieldActions(runtime, model, fieldIndex, docPtr, actionBudget);
3552
3785
  const base = {
3553
3786
  ref: fieldObjectNumber > 0 ? {
3554
3787
  kind: "objectNumber",
@@ -3647,12 +3880,12 @@ function readFieldAt(runtime, model, fieldIndex, actionBudget = new ActionReadBu
3647
3880
  }
3648
3881
  }
3649
3882
  /** Read the whole native model into a detached {@link FormSnapshot}. */
3650
- function readFormSnapshot(runtime, model) {
3883
+ function readFormSnapshot(runtime, model, docPtr) {
3651
3884
  const { fn } = runtime;
3652
3885
  const count = fn.EPDFForm_CountFields(model);
3653
3886
  const fields = [];
3654
3887
  const actionBudget = new ActionReadBudgetTracker();
3655
- for (let i = 0; i < count; i++) fields.push(readFieldAt(runtime, model, i, actionBudget));
3888
+ for (let i = 0; i < count; i++) fields.push(readFieldAt(runtime, model, i, docPtr, actionBudget));
3656
3889
  const calculationOrder = [];
3657
3890
  const calculationCount = fn.EPDFForm_CountCalculationOrder(model);
3658
3891
  for (let index = 0; index < calculationCount; index++) {
@@ -3688,7 +3921,7 @@ function entryScalar(entry) {
3688
3921
  if (entry.kind === "array") return entry.values[0] ?? "";
3689
3922
  return "";
3690
3923
  }
3691
- function readFieldActions(runtime, model, fieldIndex, budget) {
3924
+ function readFieldActions(runtime, model, fieldIndex, docPtr, budget) {
3692
3925
  const { fn, mem } = runtime;
3693
3926
  const entries = [
3694
3927
  ["keystroke", 0],
@@ -3698,7 +3931,7 @@ function readFieldActions(runtime, model, fieldIndex, budget) {
3698
3931
  ];
3699
3932
  const actions = {};
3700
3933
  for (const [key, event] of entries) {
3701
- const action = readActionModel(fn, mem, fn.EPDFForm_GetFieldActionModel(model, fieldIndex, event), budget);
3934
+ const action = readActionModel(fn, mem, docPtr, fn.EPDFForm_GetFieldActionModel(model, fieldIndex, event), budget);
3702
3935
  if (action) actions[key] = action;
3703
3936
  }
3704
3937
  return Object.keys(actions).length > 0 ? actions : void 0;
@@ -3944,7 +4177,7 @@ function blendModeFromCode(code) {
3944
4177
  * per-subtype reader builds its DTO by extending this with its own
3945
4178
  * fields and `subtype: '...'` discriminator.
3946
4179
  */
3947
- function readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision, actionBudget = new ActionReadBudgetTracker()) {
4180
+ function readAnnotationBase(fn, mem, docPtr, annotPtr, pageObjectNumber, index, revision, actionBudget = new ActionReadBudgetTracker()) {
3948
4181
  const identity = readAnnotationIdentity(fn, mem, annotPtr, pageObjectNumber, index, revision);
3949
4182
  const rect = readAnnotRect(fn, mem, annotPtr);
3950
4183
  const flags = readAnnotFlags(fn, annotPtr);
@@ -3956,7 +4189,7 @@ function readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision
3956
4189
  const blendMode = blendModeFromCode(fn.EPDFAnnot_GetBlendMode(annotPtr));
3957
4190
  const embd = readEmbedMetadata(fn, mem, annotPtr);
3958
4191
  const relationship = readAnnotationRelationship(fn, mem, annotPtr, pageObjectNumber);
3959
- const actions = readAnnotationActions(fn, mem, annotPtr, actionBudget);
4192
+ const actions = readAnnotationActions(fn, mem, docPtr, annotPtr, actionBudget);
3960
4193
  return {
3961
4194
  ref: identity.ref,
3962
4195
  pageObjectNumber,
@@ -3980,7 +4213,7 @@ function readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision
3980
4213
  ...actions ? { actions } : {}
3981
4214
  };
3982
4215
  }
3983
- function readAnnotationActions(fn, mem, annotPtr, budget) {
4216
+ function readAnnotationActions(fn, mem, docPtr, annotPtr, budget) {
3984
4217
  const events = [
3985
4218
  ["activate", 0],
3986
4219
  ["cursorEnter", 1],
@@ -3996,7 +4229,7 @@ function readAnnotationActions(fn, mem, annotPtr, budget) {
3996
4229
  ];
3997
4230
  const actions = {};
3998
4231
  for (const [key, event] of events) {
3999
- const action = readActionModel(fn, mem, fn.EPDFAnnot_GetActionModel(annotPtr, event), budget);
4232
+ const action = readActionModel(fn, mem, docPtr, fn.EPDFAnnot_GetActionModel(annotPtr, event), budget);
4000
4233
  if (action) actions[key] = action;
4001
4234
  }
4002
4235
  return Object.keys(actions).length > 0 ? actions : void 0;
@@ -4027,7 +4260,7 @@ function collectPageAnnotations(input) {
4027
4260
  const annotPtr = getAnnotPtrAt(i);
4028
4261
  if (!annotPtr) continue;
4029
4262
  try {
4030
- const base = readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, i, revision, actionBudget);
4263
+ const base = readAnnotationBase(fn, mem, readCtx.docPtr, annotPtr, pageObjectNumber, i, revision, actionBudget);
4031
4264
  const subtypeCode = fn.FPDFAnnot_GetSubtype(annotPtr);
4032
4265
  const { reader } = pickReader(subtypeCode);
4033
4266
  const dto = reader(fn, mem, annotPtr, base, subtypeCode, readCtx);
@@ -4965,7 +5198,7 @@ function computeMutationImpact(inputs) {
4965
5198
  * `AnnotationUpdateResult.updated`.
4966
5199
  */
4967
5200
  function readAnnotationFromPtr(fn, mem, annotPtr, pageObjectNumber, index, revision, ctx) {
4968
- const base = readAnnotationBase(fn, mem, annotPtr, pageObjectNumber, index, revision);
5201
+ const base = readAnnotationBase(fn, mem, ctx.docPtr, annotPtr, pageObjectNumber, index, revision);
4969
5202
  const subtypeCode = fn.FPDFAnnot_GetSubtype(annotPtr);
4970
5203
  const { reader } = pickReader(subtypeCode);
4971
5204
  return reader(fn, mem, annotPtr, base, subtypeCode, ctx);
@@ -5246,7 +5479,7 @@ function isFileAttachmentSubtype(subtype) {
5246
5479
  return subtype === "file-attachment";
5247
5480
  }
5248
5481
  function setFileAttachmentIcon(fn, annotPtr, icon) {
5249
- if (!fn.EPDFAnnot_SetName(annotPtr, FILE_ICON_TO_CODE[icon])) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetName returned false");
5482
+ if (!fn.EPDFAnnot_SetName(annotPtr, FILE_ICON_TO_NAME[icon])) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetName returned false");
5250
5483
  }
5251
5484
  function requireFileResource(file, ctx) {
5252
5485
  const resource = ctx?.resources?.[file.resource];
@@ -5908,12 +6141,12 @@ const STAMP_FIT_TO_CODE = {
5908
6141
  * creating an annotation or strengthening a weak annotation id.
5909
6142
  */
5910
6143
  function preflightStampDraft(draft, ctx) {
5911
- if (draft.name !== void 0) requireStampNameCode(draft.name);
6144
+ if (draft.name !== void 0) requireStampName(draft.name);
5912
6145
  requireStampFit(draft.fit ?? "contain");
5913
6146
  requireStampContent(draft.source, ctx);
5914
6147
  }
5915
6148
  function preflightStampPatch(patch, ctx) {
5916
- if (patch.name !== void 0) requireStampNameCode(patch.name);
6149
+ if (patch.name !== void 0 && patch.name !== null) requireStampName(patch.name);
5917
6150
  requireStampFit(patch.fit ?? "contain");
5918
6151
  if (patch.source !== void 0) requireStampContent(patch.source, ctx);
5919
6152
  }
@@ -5939,7 +6172,8 @@ function applyStampDraft(fn, mem, annotPtr, draft, ctx) {
5939
6172
  }
5940
6173
  function applyStampPatch(fn, mem, annotPtr, patch, ctx) {
5941
6174
  applyAnnotationBasePatch(fn, mem, annotPtr, patch);
5942
- if (patch.name !== void 0) setStampName(fn, annotPtr, patch.name);
6175
+ if (patch.name === null) fn.EPDFAnnot_RemoveKey(annotPtr, "Name");
6176
+ else if (patch.name !== void 0) setStampName(fn, annotPtr, patch.name);
5943
6177
  if (patch.source !== void 0) {
5944
6178
  const rect = patch.rect ?? readAnnotRect(fn, mem, annotPtr);
5945
6179
  authorStampAppearance(fn, mem, annotPtr, patch.source, patch.fit ?? "contain", {
@@ -5959,15 +6193,14 @@ function applyStampPatch(fn, mem, annotPtr, patch, ctx) {
5959
6193
  function isStampSubtype(subtype) {
5960
6194
  return subtype === "stamp";
5961
6195
  }
6196
+ /** Any non-empty name: standard ('Approved') or custom ('#LBGiYhk8V…') —
6197
+ * the fork writes a name object and escapes it; predefined sets are a
6198
+ * reader concern (ISO 32000-2 table 187 allows additional names). */
5962
6199
  function setStampName(fn, annotPtr, name) {
5963
- const code = STAMP_NAME_TO_CODE[name];
5964
- if (code === void 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "stamp name preflight invariant broken");
5965
- if (!fn.EPDFAnnot_SetName(annotPtr, code)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetName returned false");
6200
+ if (!fn.EPDFAnnot_SetName(annotPtr, name)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetName returned false");
5966
6201
  }
5967
- function requireStampNameCode(name) {
5968
- const code = STAMP_NAME_TO_CODE[name];
5969
- if (code === void 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `unknown stamp name '${name}'; supported names: ${Object.keys(STAMP_NAME_TO_CODE).join(", ")}`);
5970
- return code;
6202
+ function requireStampName(name) {
6203
+ if (name.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "stamp name must be non-empty");
5971
6204
  }
5972
6205
  function requireStampFit(fit) {
5973
6206
  if (STAMP_FIT_TO_CODE[fit] === void 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `unknown stamp fit '${String(fit)}'; expected contain, cover, or fill`);
@@ -6155,7 +6388,7 @@ function isTextSubtype(subtype) {
6155
6388
  return subtype === "text";
6156
6389
  }
6157
6390
  function setNoteIcon(fn, annotPtr, icon) {
6158
- if (!fn.EPDFAnnot_SetName(annotPtr, NOTE_ICON_TO_CODE[icon])) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetName returned false");
6391
+ if (!fn.EPDFAnnot_SetName(annotPtr, NOTE_ICON_TO_NAME[icon])) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "EPDFAnnot_SetName returned false");
6159
6392
  }
6160
6393
  //#endregion
6161
6394
  //#region src/features/annotations/internal/write/writeVertexAnnotation.ts
@@ -6882,6 +7115,141 @@ var AnnotationMutator = class {
6882
7115
  }
6883
7116
  };
6884
7117
  //#endregion
7118
+ //#region src/features/annotations/AnnotationFlattener.ts
7119
+ const FLATTEN_FAIL$1 = 0;
7120
+ const FLATTEN_SUCCESS$1 = 1;
7121
+ const FLATTEN_NOTHING_TO_DO$1 = 2;
7122
+ const STATUS_APPLIED = 0;
7123
+ const STATUS_NOT_ON_PAGE = 2;
7124
+ const FPDF_NO_INCREMENTAL$1 = 2;
7125
+ /**
7126
+ * The annotation-plane flatten verbs — `pages.flatten` for a chosen set:
7127
+ * in place (`flatten`) or into a new single-page document
7128
+ * (`exportAppearance`). Both resolve refs to native handles on the page,
7129
+ * hand the SET to the fork (one candidate plan, one placement writer — the
7130
+ * same code whole-page flatten runs), and release every handle afterwards.
7131
+ */
7132
+ var AnnotationFlattener = class {
7133
+ constructor(runtime, session) {
7134
+ this.runtime = runtime;
7135
+ this.session = session;
7136
+ }
7137
+ flatten(pageObjectNumber, refs, usage, signal) {
7138
+ throwIfAborted(signal);
7139
+ this.requireRefs("annotations.flatten", pageObjectNumber, refs);
7140
+ const { fn, mem } = this.runtime;
7141
+ const pool = this.session.pagePool();
7142
+ const pagePtr = pool.acquire(pageObjectNumber);
7143
+ try {
7144
+ const annotPtrs = this.resolveAll(pagePtr, refs);
7145
+ let code;
7146
+ let statuses;
7147
+ try {
7148
+ [code, statuses] = this.withHandleArray(annotPtrs, (arrayPtr) => withScratch(mem, 4 * refs.length, (statusPtr) => {
7149
+ const rc = fn.EPDFPage_FlattenAnnotations(pagePtr, arrayPtr, refs.length, usage === "print" ? 1 : 0, statusPtr);
7150
+ const out = [];
7151
+ for (let i = 0; i < refs.length; i++) out.push(Number(mem.peek(statusPtr, "i32", 4 * i)));
7152
+ return [rc, out];
7153
+ }));
7154
+ } finally {
7155
+ for (const annotPtr of annotPtrs) fn.FPDFPage_CloseAnnot(annotPtr);
7156
+ }
7157
+ if (code === FLATTEN_FAIL$1) {
7158
+ const foreign = statuses.findIndex((status) => status === STATUS_NOT_ON_PAGE);
7159
+ throw new _embedpdf_engine_core_runtime.EngineError(foreign >= 0 ? _embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg : _embedpdf_engine_core_runtime.EngineErrorCode.Unknown, foreign >= 0 ? `annotations.flatten: ref ${foreign} is not an annotation of page ${pageObjectNumber}` : "native annotation flatten failed after preflight");
7160
+ }
7161
+ const results = refs.map((ref, i) => ({
7162
+ ref,
7163
+ status: statuses[i] === STATUS_APPLIED ? "applied" : "skipped"
7164
+ }));
7165
+ if (code === FLATTEN_NOTHING_TO_DO$1 || code !== FLATTEN_SUCCESS$1) return {
7166
+ pageObjectNumber,
7167
+ usage,
7168
+ results,
7169
+ meta: null
7170
+ };
7171
+ this.session.noteMutation();
7172
+ this.session.bumpRevision(pageObjectNumber);
7173
+ try {
7174
+ new AnnotationReader(this.runtime, this.session).list(pageObjectNumber, signal);
7175
+ } catch {}
7176
+ return {
7177
+ pageObjectNumber,
7178
+ usage,
7179
+ results,
7180
+ meta: {
7181
+ affectedPages: [this.session.pageState(pageObjectNumber)],
7182
+ cacheDelta: null
7183
+ }
7184
+ };
7185
+ } finally {
7186
+ pool.release(pageObjectNumber);
7187
+ }
7188
+ }
7189
+ exportAppearance(pageObjectNumber, refs, signal) {
7190
+ throwIfAborted(signal);
7191
+ this.requireRefs("annotations.exportAppearance", pageObjectNumber, refs);
7192
+ const { fn, mem } = this.runtime;
7193
+ const pool = this.session.pagePool();
7194
+ const pagePtr = pool.acquire(pageObjectNumber);
7195
+ let exportedPtr = null;
7196
+ let pdfPtr = null;
7197
+ try {
7198
+ const annotPtrs = this.resolveAll(pagePtr, refs);
7199
+ try {
7200
+ exportedPtr = this.withHandleArray(annotPtrs, (arrayPtr) => fn.EPDFPage_ExportAnnotationsAsDocument(pagePtr, arrayPtr, refs.length));
7201
+ } finally {
7202
+ for (const annotPtr of annotPtrs) fn.FPDFPage_CloseAnnot(annotPtr);
7203
+ }
7204
+ if (!exportedPtr) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "annotations.exportAppearance: every ref must be a visible annotation of this page with a normal appearance");
7205
+ return withScratch(mem, 4, (sizePtr) => {
7206
+ mem.poke(sizePtr, "i32", 0);
7207
+ pdfPtr = fn.EPDF_SaveDocumentToOwnedBuffer(exportedPtr, FPDF_NO_INCREMENTAL$1, sizePtr);
7208
+ const size = Number(mem.peek(sizePtr, "i32"));
7209
+ if (!pdfPtr || size <= 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.DocOpenFailed, "failed to save exported appearances");
7210
+ const bytes = mem.readBytes(pdfPtr, size);
7211
+ const buffer = new ArrayBuffer(bytes.byteLength);
7212
+ new Uint8Array(buffer).set(bytes);
7213
+ return {
7214
+ bytes: buffer,
7215
+ size
7216
+ };
7217
+ });
7218
+ } finally {
7219
+ if (pdfPtr) fn.EPDF_FreeBuffer(pdfPtr);
7220
+ if (exportedPtr) fn.FPDF_CloseDocument(exportedPtr);
7221
+ pool.release(pageObjectNumber);
7222
+ }
7223
+ }
7224
+ /** Resolve every ref on `pagePtr` (NotFound / InvalidReference from the
7225
+ * resolver on caller error); on any failure, close what was opened. */
7226
+ resolveAll(pagePtr, refs) {
7227
+ const { fn } = this.runtime;
7228
+ const annotPtrs = [];
7229
+ try {
7230
+ for (const ref of refs) annotPtrs.push(resolveAnnotPtr(this.runtime, this.session, pagePtr, ref));
7231
+ return annotPtrs;
7232
+ } catch (error) {
7233
+ for (const annotPtr of annotPtrs) fn.FPDFPage_CloseAnnot(annotPtr);
7234
+ throw error;
7235
+ }
7236
+ }
7237
+ /** Marshal an `FPDF_ANNOTATION*` array for the call's duration. */
7238
+ withHandleArray(annotPtrs, body) {
7239
+ const { mem } = this.runtime;
7240
+ const isWasm = this.runtime.kind === "wasm";
7241
+ const ptrSize = isWasm ? 4 : 8;
7242
+ return withScratch(mem, Math.max(1, annotPtrs.length * ptrSize), (arrayPtr) => {
7243
+ annotPtrs.forEach((ptr, i) => isWasm ? mem.poke(arrayPtr, "i32", Number(ptr), i * 4) : mem.poke(arrayPtr, "i64", ptr, i * 8));
7244
+ return body(arrayPtr);
7245
+ });
7246
+ }
7247
+ requireRefs(op, pageObjectNumber, refs) {
7248
+ if (refs.length === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `${op} requires at least one ref`);
7249
+ for (const ref of refs) if (ref.pageObjectNumber !== pageObjectNumber) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `${op} refs must all target page ${pageObjectNumber}; got ref on page ${ref.pageObjectNumber}`);
7250
+ }
7251
+ };
7252
+ //#endregion
6885
7253
  //#region src/features/pages/PagesFlattener.ts
6886
7254
  const FLATTEN_FAIL = 0;
6887
7255
  const FLATTEN_SUCCESS = 1;
@@ -8183,13 +8551,13 @@ var FormReader = class {
8183
8551
  snapshot(signal) {
8184
8552
  throwIfAborted(signal);
8185
8553
  const model = acquireFormModel(this.runtime, this.session);
8186
- return readFormSnapshot(this.runtime, model);
8554
+ return readFormSnapshot(this.runtime, model, this.session.requireDocPtr());
8187
8555
  }
8188
8556
  field(ref, signal) {
8189
8557
  throwIfAborted(signal);
8190
8558
  const model = acquireFormModel(this.runtime, this.session);
8191
8559
  const { fieldIndex } = resolveFieldRef(this.runtime, model, ref);
8192
- return readFieldAt(this.runtime, model, fieldIndex);
8560
+ return readFieldAt(this.runtime, model, fieldIndex, this.session.requireDocPtr());
8193
8561
  }
8194
8562
  /**
8195
8563
  * Serialize form data. Exports read the reconciled view (recovered
@@ -8345,7 +8713,7 @@ var FormMutator = class {
8345
8713
  const model = acquireFormModel(this.runtime, this.session);
8346
8714
  const resolved = resolveFieldRef(this.runtime, model, ref);
8347
8715
  this.assertWritable(resolved.fieldObjectNumber);
8348
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8716
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8349
8717
  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
8718
  const changed = this.applyWrite(resolved.fieldObjectNumber, value);
8351
8719
  return this.readBack(resolved.fieldObjectNumber, changed);
@@ -8381,7 +8749,7 @@ var FormMutator = class {
8381
8749
  const fresh = acquireFormModel(this.runtime, this.session);
8382
8750
  return {
8383
8751
  ...counters,
8384
- snapshot: readFormSnapshot(this.runtime, fresh),
8752
+ snapshot: readFormSnapshot(this.runtime, fresh, this.session.requireDocPtr()),
8385
8753
  meta: EMPTY_META$1
8386
8754
  };
8387
8755
  }
@@ -8454,7 +8822,7 @@ var FormMutator = class {
8454
8822
  const model = acquireFormModel(this.runtime, this.session);
8455
8823
  const resolved = resolveFieldRef(this.runtime, model, ref);
8456
8824
  this.assertWritable(resolved.fieldObjectNumber);
8457
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8825
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8458
8826
  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
8827
  const fieldObjectNumber = resolved.fieldObjectNumber;
8460
8828
  if (patch.name !== void 0) this.applyWideSetter(fn.EPDFForm_SetFieldName, fieldObjectNumber, patch.name, `cannot rename to "${patch.name}" (sibling conflict or invalid)`);
@@ -8485,7 +8853,7 @@ var FormMutator = class {
8485
8853
  const model = acquireFormModel(this.runtime, this.session);
8486
8854
  const resolved = resolveFieldRef(this.runtime, model, ref);
8487
8855
  this.assertWritable(resolved.fieldObjectNumber);
8488
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8856
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8489
8857
  if (!withScratchN(mem, [256 * 4, 4], ([buf, countPtr]) => {
8490
8858
  mem.poke(countPtr, "i32", 0);
8491
8859
  return fn.EPDFForm_DeleteField(this.session.requireDocPtr(), resolved.fieldObjectNumber, buf, 256, countPtr);
@@ -8505,7 +8873,7 @@ var FormMutator = class {
8505
8873
  const model = acquireFormModel(this.runtime, this.session);
8506
8874
  const resolved = resolveFieldRef(this.runtime, model, ref);
8507
8875
  this.assertWritable(resolved.fieldObjectNumber);
8508
- const before = readFieldAt(this.runtime, model, resolved.fieldIndex);
8876
+ const before = readFieldAt(this.runtime, model, resolved.fieldIndex, this.session.requireDocPtr());
8509
8877
  const toggle = before.family === "checkbox" || before.family === "radio";
8510
8878
  const state = toggle ? onState ?? (before.family === "checkbox" ? "Yes" : "") : "";
8511
8879
  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 +8919,7 @@ var FormMutator = class {
8551
8919
  const fresh = acquireFormModel(this.runtime, this.session);
8552
8920
  const fieldIndex = this.runtime.fn.EPDFForm_GetFieldIndexByObjNum(fresh, fieldObjectNumber);
8553
8921
  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);
8922
+ return readFieldAt(this.runtime, fresh, fieldIndex, this.session.requireDocPtr());
8555
8923
  }
8556
8924
  assertWritable(fieldObjectNumber) {
8557
8925
  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 +8965,7 @@ var FormMutator = class {
8597
8965
  const fresh = acquireFormModel(this.runtime, this.session);
8598
8966
  const fieldIndex = this.runtime.fn.EPDFForm_GetFieldIndexByObjNum(fresh, fieldObjectNumber);
8599
8967
  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);
8968
+ const field = readFieldAt(this.runtime, fresh, fieldIndex, this.session.requireDocPtr());
8601
8969
  const changedSet = new Set(changedObjNums);
8602
8970
  return {
8603
8971
  field,
@@ -8743,7 +9111,7 @@ var FormsEffectsApplier = class {
8743
9111
  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
9112
  const model = acquireFormModel(this.runtime, this.session);
8745
9113
  const resolved = refs.map((ref) => this.resolve(model, ref));
8746
- const fields = resolved.map(({ fieldIndex }) => readFieldAt(this.runtime, model, fieldIndex, actionBudget));
9114
+ const fields = resolved.map(({ fieldIndex }) => readFieldAt(this.runtime, model, fieldIndex, this.session.requireDocPtr(), actionBudget));
8747
9115
  validateEffect(effect, fields);
8748
9116
  return {
8749
9117
  effect,
@@ -8768,7 +9136,7 @@ var FormsEffectsApplier = class {
8768
9136
  const model = acquireFormModel(this.runtime, this.session);
8769
9137
  const index = this.runtime.fn.EPDFForm_GetFieldIndexByObjNum(model, fieldObjectNumber);
8770
9138
  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);
9139
+ return readFieldAt(this.runtime, model, index, this.session.requireDocPtr(), actionBudget);
8772
9140
  }
8773
9141
  readFieldsBestEffort(fieldObjectNumbers, actionBudget) {
8774
9142
  const fields = [];
@@ -9135,22 +9503,26 @@ var DocumentActionsReader = class {
9135
9503
  } finally {
9136
9504
  fn.FPDFDoc_CloseJavaScriptAction(namedPtr);
9137
9505
  }
9138
- const action = requireAction(readActionModel(fn, mem, fn.EPDFDoc_GetNamedJavaScriptActionModel(docPtr, index), budget), "name-tree script", index);
9506
+ budget.reservePayloadBytes(name.length);
9507
+ const action = requireAction(readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetNamedJavaScriptActionModel(docPtr, index), budget), "name-tree script", index);
9139
9508
  nameTreeScripts.push({
9140
9509
  name,
9141
9510
  action
9142
9511
  });
9143
9512
  }
9144
- const openAction = readActionModel(fn, mem, fn.EPDFDoc_GetOpenActionModel(docPtr), budget);
9513
+ const openAction = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetOpenActionModel(docPtr), budget);
9514
+ const openDestPtr = openAction ? _embedpdf_engine_runtime.NULL_PTR : fn.EPDFDoc_GetOpenActionDest(docPtr);
9515
+ const openDestination = openDestPtr !== _embedpdf_engine_runtime.NULL_PTR ? readDestination(fn, mem, docPtr, openDestPtr) : null;
9145
9516
  const additional = {};
9146
9517
  for (const [key, event] of DOCUMENT_ACTION_EVENTS) {
9147
9518
  throwIfAborted(signal);
9148
- const action = readActionModel(fn, mem, fn.EPDFDoc_GetAdditionalActionModel(docPtr, event), budget);
9519
+ const action = readActionModel(fn, mem, docPtr, fn.EPDFDoc_GetAdditionalActionModel(docPtr, event), budget);
9149
9520
  if (action) additional[key] = action;
9150
9521
  }
9151
9522
  return {
9152
9523
  nameTreeScripts,
9153
9524
  openAction,
9525
+ openDestination,
9154
9526
  ...additional
9155
9527
  };
9156
9528
  }
@@ -9484,6 +9856,18 @@ var WorkerHost = class {
9484
9856
  case "pages.delete":
9485
9857
  resultPack = this.handlePagesDelete(msg, ctrl.signal);
9486
9858
  break;
9859
+ case "pages.setName":
9860
+ resultPack = this.handlePagesSetName(msg, ctrl.signal);
9861
+ break;
9862
+ case "annotations.flatten":
9863
+ resultPack = this.handleAnnotationsFlatten(msg, ctrl.signal);
9864
+ break;
9865
+ case "annotations.exportAppearance":
9866
+ resultPack = this.handleAnnotationsExportAppearance(msg, ctrl.signal);
9867
+ break;
9868
+ case "pages.removeName":
9869
+ resultPack = this.handlePagesRemoveName(msg, ctrl.signal);
9870
+ break;
9487
9871
  case "pages.flatten":
9488
9872
  resultPack = this.handlePagesFlatten(msg, ctrl.signal);
9489
9873
  break;
@@ -9711,6 +10095,27 @@ var WorkerHost = class {
9711
10095
  result
9712
10096
  }, req.artifactPath);
9713
10097
  }
10098
+ handleAnnotationsFlatten(req, signal) {
10099
+ const session = this.requireSession(req);
10100
+ const result = new AnnotationFlattener(this.runtime, session).flatten(req.pageObjectNumber, req.refs, req.usage, signal);
10101
+ if (result.meta === null) return (0, _embedpdf_engine_core_runtime.wirePack)({
10102
+ tag: "annotations.flatten",
10103
+ result
10104
+ });
10105
+ return this.finishMutation(session, {
10106
+ tag: "annotations.flatten",
10107
+ result
10108
+ }, req.artifactPath);
10109
+ }
10110
+ handleAnnotationsExportAppearance(req, signal) {
10111
+ const session = this.requireSession(req);
10112
+ const exported = new AnnotationFlattener(this.runtime, session).exportAppearance(req.pageObjectNumber, req.refs, signal);
10113
+ return (0, _embedpdf_engine_core_runtime.wirePack)({
10114
+ tag: "annotations.exportAppearance",
10115
+ bytes: exported.bytes,
10116
+ size: exported.size
10117
+ }, [exported.bytes]);
10118
+ }
9714
10119
  handleAnnotationsMove(req, signal) {
9715
10120
  const session = this.requireSession(req);
9716
10121
  const result = new AnnotationMutator(this.runtime, session).move(req.pageObjectNumber, req.refs, req.toIndex, signal);
@@ -9750,6 +10155,26 @@ var WorkerHost = class {
9750
10155
  result
9751
10156
  }, req.artifactPath);
9752
10157
  }
10158
+ handlePagesSetName(req, signal) {
10159
+ const session = this.requireSession(req);
10160
+ const result = new PagesMutator(this.runtime, session).setName({
10161
+ name: req.name,
10162
+ pageObjectNumber: req.pageObjectNumber,
10163
+ ...req.replace !== void 0 ? { replace: req.replace } : {}
10164
+ }, signal);
10165
+ return this.finishMutation(session, {
10166
+ tag: "pages.setName",
10167
+ result
10168
+ }, req.artifactPath);
10169
+ }
10170
+ handlePagesRemoveName(req, signal) {
10171
+ const session = this.requireSession(req);
10172
+ const result = new PagesMutator(this.runtime, session).removeName({ name: req.name }, signal);
10173
+ return this.finishMutation(session, {
10174
+ tag: "pages.removeName",
10175
+ result
10176
+ }, req.artifactPath);
10177
+ }
9753
10178
  handlePagesFlatten(req, signal) {
9754
10179
  const session = this.requireSession(req);
9755
10180
  const result = new PagesFlattener(this.runtime, session).flatten(req.pageObjectNumbers, req.usage, signal);
@@ -10327,6 +10752,7 @@ function passwordRequiredProbe() {
10327
10752
  exports.ALL_STANDARD_SECURITY_PERMISSIONS = ALL_STANDARD_SECURITY_PERMISSIONS;
10328
10753
  exports.ActionReadBudgetTracker = ActionReadBudgetTracker;
10329
10754
  exports.AnnotationAppearanceReader = AnnotationAppearanceReader;
10755
+ exports.AnnotationFlattener = AnnotationFlattener;
10330
10756
  exports.AnnotationMutator = AnnotationMutator;
10331
10757
  exports.AnnotationReader = AnnotationReader;
10332
10758
  exports.AttachmentMutator = AttachmentMutator;