js.documents 4.5.0 → 4.6.0

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/README.md CHANGED
@@ -575,7 +575,7 @@ The package is layered from generic primitives outward to the two conversion dir
575
575
  - **`src/markdown/`** — third adapter family, via `markdown-codec`. `readMarkdownContent` passes `readMarkdownContent`'s (markdown-codec's flat reader, so named since that package's 4.0.0; the bare `readMarkdown` name is now its tree-form `DocumentPackage` reader) result through the math-lowering pass (`math.ts` — markdown-codec's preserved `$$` display blocks and `\( \)` inline spans become two-layer formula blocks, with the document's symbol table seeded from its own prose). `buildMarkdownText` wraps `writeMarkdownContent`, reconstructing markdown math syntax from formula blocks carrying a presentation layer. `text.ts` is the byte↔text boundary. `MarkdownEditor` holds a mutable in-memory `ContentDocument`.
576
576
  - **`src/csv/`** — fourth adapter family, sharing the spreadsheet variant with xlsx/ods. `records.ts` is the RFC 4180 record parser/writer (one shared `quoteCsvField`, also used by the `.odb` CSV exporter); `text.ts` is the byte↔text boundary, rejecting malformed UTF-8; `read.ts` turns records into a spreadsheet `ContentDocument` (first record as verbatim string header, data cells through the same cell-typing heuristic `pdfToOds` uses); `write.ts` turns one sheet of a spreadsheet `ContentDocument` back into records via each cell's `displayText`. TSV is the same format with `{ delimiter: '\t' }` on either side.
577
577
  - **`src/svg/`** — fifth adapter family, sharing the drawing variant with odg. `text.ts` is the byte↔text boundary, rejecting malformed UTF-8; `read.ts` maps the six SVG shape primitives (rect/circle/ellipse/line/polyline/polygon/path) onto a one-page drawing `ContentDocument`, with transform lists composed as 2×3 affines and CSS lengths and the viewBox map resolved into page points; `write.ts` writes the six primitives back out, one shape element each; `path.ts` is the full SVG path-data grammar (M/L/H/V/C/S/Z plus Q/T/A and the relative forms — S/Q/T convert exactly, A is the one bounded approximation at ≤90° per cubic); `transform.ts` parses and composes the transform attribute and classifies the result by frame representability; `units.ts` resolves CSS length units and the viewBox; `paint.ts` resolves fill/stroke presentation attributes and dash styles; `diagnostics.ts` is the shared scope-limit vocabulary.
578
- - **`src/layout/`** — the pure conversion algorithms: `engine.ts` (wordprocessing → layout: flow, line-breaking, pagination), `slides.ts` (presentation → layout: direct placement), `sheets.ts` (spreadsheet → layout: grid, print settings, the first algorithm accepting `AbortSignal`), `drawing.ts` (drawing → layout: vector primitives + shape reuse), `reconstruct.ts` (layout → content: baseline clustering for wordprocessing/presentation, near-1:1 mapping for drawing, gridline-lattice-or-text-clustering for spreadsheet; plus the PDF construct surfacing — link reconciliation to `ContentRun.hyperlink` or link constructs, hidden-layer content dropped, anchor/comment and AcroForm contentControl constructs).
578
+ - **`src/layout/`** — the pure conversion algorithms: `engine.ts` (wordprocessing → layout: flow, line-breaking, pagination), `slides.ts` (presentation → layout: direct placement), `sheets.ts` (spreadsheet → layout: grid, print settings, the first algorithm accepting `AbortSignal`), `drawing.ts` (drawing → layout: vector primitives + shape reuse), `reconstruct.ts` (layout → content: baseline clustering for wordprocessing/presentation, near-1:1 mapping for drawing, gridline-lattice-or-text-clustering for spreadsheet; plus the PDF construct surfacing — link reconciliation to `ContentRun.hyperlink` or link constructs, hidden-layer content dropped, anchor/comment and AcroForm contentControl constructs, and tagged-structure semantics where the file states them: heading levels from owning `H1`..`H6` elements over the font-size census, lattice-free table recovery from `/Table`/`/TR`/`/TH`/`/TD` ownership, and `division` constructs around `/Part`/`/Sect`/`/Div` extents).
579
579
  - **`src/hsqldb/`** — `.odb` decoders, four tiers: `script.ts` (TEXT-script DDL/DML parser), `rowformat.ts`/`cache.ts` (CACHED binary row-store), `binary-script.ts` (BINARY/COMPRESSED whole-script). All import only `document-schema.js` — no odf.js knowledge.
580
580
  - **`src/firebird/`** — Tier 3: gbak logical-backup reader. `reader.ts` (attribute framing + RLE decompression + XDR decoding), `schema.ts`/`data.ts` (table/row walking). No ratified spec — built against Firebird's own engine source.
581
581
  - **`src/odb/`** — decoder-selection and pivot-mapping: `read.ts` routes to the right tier, `spreadsheet.ts`/`csv.ts` map to output formats. `odb/sql/` is the bounded SQL engine, `odb/formula/` is the rpt formula engine, `odb/report/` is the renderer, `odb/values.ts` is shared comparison/aggregation semantics.
@@ -67,10 +67,25 @@ function stampPdfPackageTables(pkg, layout) {
67
67
  };
68
68
  });
69
69
  });
70
+ if (layout.structure !== void 0) walkStructureElements(layout.structure, void 0, definitions);
70
71
  if (Object.keys(definitions).length > 0) pkg.definitions = {
71
72
  ...pkg.definitions,
72
73
  ...definitions
73
74
  };
74
75
  }
76
+ function walkStructureElements(elements, parentKey, definitions) {
77
+ for (const element of elements) {
78
+ definitions[element.id] = {
79
+ kind: "structure",
80
+ type: element.type,
81
+ ...element.title !== void 0 ? { title: element.title } : {},
82
+ ...element.language !== void 0 ? { language: element.language } : {},
83
+ ...element.alt !== void 0 ? { alt: element.alt } : {},
84
+ ...element.actualText !== void 0 ? { actualText: element.actualText } : {},
85
+ ...parentKey !== void 0 ? { parent: parentKey } : {}
86
+ };
87
+ walkStructureElements(element.children, element.id, definitions);
88
+ }
89
+ }
75
90
  //#endregion
76
91
  exports.stampPdfPackageTables = stampPdfPackageTables;
@@ -66,10 +66,25 @@ function stampPdfPackageTables(pkg, layout) {
66
66
  };
67
67
  });
68
68
  });
69
+ if (layout.structure !== void 0) walkStructureElements(layout.structure, void 0, definitions);
69
70
  if (Object.keys(definitions).length > 0) pkg.definitions = {
70
71
  ...pkg.definitions,
71
72
  ...definitions
72
73
  };
73
74
  }
75
+ function walkStructureElements(elements, parentKey, definitions) {
76
+ for (const element of elements) {
77
+ definitions[element.id] = {
78
+ kind: "structure",
79
+ type: element.type,
80
+ ...element.title !== void 0 ? { title: element.title } : {},
81
+ ...element.language !== void 0 ? { language: element.language } : {},
82
+ ...element.alt !== void 0 ? { alt: element.alt } : {},
83
+ ...element.actualText !== void 0 ? { actualText: element.actualText } : {},
84
+ ...parentKey !== void 0 ? { parent: parentKey } : {}
85
+ };
86
+ walkStructureElements(element.children, element.id, definitions);
87
+ }
88
+ }
74
89
  //#endregion
75
90
  export { stampPdfPackageTables };
@@ -136,19 +136,20 @@ function reconstructWordprocessing(doc, options) {
136
136
  const signal = options?.signal;
137
137
  doc = dropHiddenLayerContent(doc);
138
138
  const headingLevels = headingSizeLevels(doc);
139
+ const structure = indexStructure(doc);
139
140
  const sections = [];
140
141
  let currentGroup = [];
141
142
  let groupStartPageIndex = 0;
142
143
  for (const page of doc.pages) {
143
144
  require_ports_abort.throwIfAborted(signal);
144
145
  if (currentGroup.length > 0 && !samePageSize(currentGroup[0], page)) {
145
- sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
146
+ sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form, structure));
146
147
  groupStartPageIndex += currentGroup.length;
147
148
  currentGroup = [];
148
149
  }
149
150
  currentGroup.push(page);
150
151
  }
151
- if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
152
+ if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form, structure));
152
153
  return {
153
154
  kind: "wordprocessing",
154
155
  metadata: doc.metadata,
@@ -169,6 +170,59 @@ function dropHiddenLayerContent(doc) {
169
170
  }))
170
171
  };
171
172
  }
173
+ const HEADING_LEVELS = /* @__PURE__ */ new Map([
174
+ ["H1", 1],
175
+ ["H2", 2],
176
+ ["H3", 3],
177
+ ["H4", 4],
178
+ ["H5", 5],
179
+ ["H6", 6]
180
+ ]);
181
+ const DIVISION_TYPES = /* @__PURE__ */ new Set([
182
+ "Part",
183
+ "Sect",
184
+ "Div"
185
+ ]);
186
+ const CELL_TYPES = /* @__PURE__ */ new Set(["TD", "TH"]);
187
+ const ROW_TYPES = /* @__PURE__ */ new Set(["TR"]);
188
+ const TABLE_TYPES = /* @__PURE__ */ new Set(["Table"]);
189
+ function indexStructure(doc) {
190
+ if (doc.structure === void 0 || doc.structure.length === 0) return;
191
+ const byId = /* @__PURE__ */ new Map();
192
+ let order = 0;
193
+ const walk = (element, parent) => {
194
+ const node = {
195
+ id: element.id,
196
+ type: element.type,
197
+ parent,
198
+ order: order++
199
+ };
200
+ byId.set(element.id, node);
201
+ for (const child of element.children) walk(child, node);
202
+ };
203
+ for (const root of doc.structure) walk(root, void 0);
204
+ return { nodeOfItem: (item) => item.kind === "link" || item.kind === "internalLink" ? void 0 : byId.get(item.structure ?? "") };
205
+ }
206
+ function nearestOfType(node, types) {
207
+ for (let current = node; current !== void 0; current = current.parent) if (types.has(current.type)) return current;
208
+ }
209
+ function nearestHeadingLevel(node) {
210
+ for (let current = node; current !== void 0; current = current.parent) {
211
+ const level = HEADING_LEVELS.get(current.type);
212
+ if (level !== void 0) return level;
213
+ }
214
+ }
215
+ function structureHeadingLevel(items, structure) {
216
+ if (items.length === 0 || items.some((item) => structure.nodeOfItem(item) === void 0)) return;
217
+ let level;
218
+ for (const item of items) {
219
+ const itemLevel = nearestHeadingLevel(structure.nodeOfItem(item));
220
+ if (itemLevel === void 0) return { level: void 0 };
221
+ if (level === void 0) level = itemLevel;
222
+ else if (level !== itemLevel) return;
223
+ }
224
+ return { level };
225
+ }
172
226
  const HEADING_MIN_SIZE_DELTA_PT = 2;
173
227
  const MAX_HEADING_LEVEL = 6;
174
228
  function headingSizeLevels(doc) {
@@ -193,11 +247,12 @@ function headingLevelOf(paragraph, levels) {
193
247
  function samePageSize(a, b) {
194
248
  return a.widthPt === b.widthPt && a.heightPt === b.heightPt;
195
249
  }
196
- function buildSection(pages, startPageIndex, images, headingLevels, form) {
250
+ function buildSection(pages, startPageIndex, images, headingLevels, form, structure) {
197
251
  const blocks = [];
252
+ const divisionByBlock = /* @__PURE__ */ new WeakMap();
198
253
  pages.forEach((page, i) => {
199
254
  if (i > 0) blocks.push({ kind: "pageBreak" });
200
- blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images, headingLevels, form));
255
+ blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images, headingLevels, form, structure, divisionByBlock));
201
256
  });
202
257
  return {
203
258
  pageSize: {
@@ -205,20 +260,34 @@ function buildSection(pages, startPageIndex, images, headingLevels, form) {
205
260
  heightPt: pages[0].heightPt
206
261
  },
207
262
  margins: ZERO_MARGINS,
208
- blocks
263
+ blocks: wrapDivisionExtents(blocks, divisionByBlock)
209
264
  };
210
265
  }
211
- function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
212
- const recoveredTable = recoverTable(page, pageIndex);
213
- const consumedText = recoveredTable?.consumedText;
214
- const textItems = page.items.filter((i) => i.kind === "text" && consumedText?.has(i) !== true);
266
+ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, structure, divisionByBlock) {
267
+ const recoveredTables = recoverTables(page, pageIndex, structure);
268
+ const consumedText = /* @__PURE__ */ new Set();
269
+ const claimedItems = /* @__PURE__ */ new Set();
270
+ for (const recovered of recoveredTables) {
271
+ for (const item of recovered.consumedText) consumedText.add(item);
272
+ for (const item of recovered.latticeItems) claimedItems.add(item);
273
+ }
274
+ const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
215
275
  const imageItems = page.items.filter((i) => i.kind === "image");
216
276
  const paragraphs = clusterIntoParagraphs(clusterIntoLines(textItems));
277
+ const recordDivisions = (block, items) => {
278
+ if (structure !== void 0) divisionByBlock.set(block, divisionChainOf(items, structure));
279
+ };
217
280
  const positioned = [];
218
- for (const paragraph of paragraphs) positioned.push({
219
- yPt: paragraph.lines[0].baselineY,
220
- block: paragraphToContentParagraph(paragraph, pageIndex, headingLevelOf(paragraph, headingLevels))
221
- });
281
+ for (const paragraph of paragraphs) {
282
+ const paragraphItems = paragraph.lines.flatMap((line) => line.items);
283
+ const structural = structure === void 0 ? void 0 : structureHeadingLevel(paragraphItems, structure);
284
+ const block = paragraphToContentParagraph(paragraph, pageIndex, structural !== void 0 ? structural.level : headingLevelOf(paragraph, headingLevels));
285
+ recordDivisions(block, paragraphItems);
286
+ positioned.push({
287
+ yPt: paragraph.lines[0].baselineY,
288
+ block
289
+ });
290
+ }
222
291
  for (const img of imageItems) {
223
292
  const asset = images[img.imageId];
224
293
  if (asset !== void 0) {
@@ -235,21 +304,29 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
235
304
  widthPt: img.widthPt,
236
305
  heightPt: img.heightPt
237
306
  });
307
+ recordDivisions(block, [img]);
238
308
  positioned.push({
239
309
  yPt: img.yPt,
240
310
  block
241
311
  });
242
312
  }
243
313
  }
244
- if (recoveredTable !== void 0) positioned.push({
245
- yPt: recoveredTable.topYPt,
246
- block: recoveredTable.table
247
- });
248
- const recoveredVectors = recoverPageVectors(page, pageIndex, recoveredTable?.latticeItems ?? NO_ITEMS);
249
- if (recoveredVectors !== void 0) positioned.push({
250
- yPt: recoveredVectors.topYPt,
251
- block: recoveredVectors.block
252
- });
314
+ for (const recovered of recoveredTables) {
315
+ recordDivisions(recovered.table, [...recovered.consumedText]);
316
+ positioned.push({
317
+ yPt: recovered.topYPt,
318
+ block: recovered.table
319
+ });
320
+ }
321
+ const recoveredVectors = recoverPageVectors(page, pageIndex, claimedItems);
322
+ if (recoveredVectors !== void 0) {
323
+ const vectorItems = page.items.filter((item) => !claimedItems.has(item) && layoutItemToVector(item, page.heightPt) !== void 0);
324
+ recordDivisions(recoveredVectors.block, vectorItems);
325
+ positioned.push({
326
+ yPt: recoveredVectors.topYPt,
327
+ block: recoveredVectors.block
328
+ });
329
+ }
253
330
  positioned.sort((a, b) => b.yPt - a.yPt);
254
331
  const blocks = positioned.map((p) => p.block);
255
332
  reconcileLinks(blocks, page, pageIndex);
@@ -257,6 +334,49 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
257
334
  appendFormFieldConstructs(blocks, form, pageIndex);
258
335
  return blocks;
259
336
  }
337
+ function divisionChainOf(items, structure) {
338
+ let prefix;
339
+ for (const item of items) {
340
+ const chain = [];
341
+ for (let current = structure.nodeOfItem(item); current !== void 0; current = current.parent) if (DIVISION_TYPES.has(current.type)) chain.push(current.id);
342
+ chain.reverse();
343
+ if (prefix === void 0) {
344
+ prefix = chain;
345
+ continue;
346
+ }
347
+ let shared = 0;
348
+ while (shared < prefix.length && shared < chain.length && prefix[shared] === chain[shared]) shared += 1;
349
+ prefix = prefix.slice(0, shared);
350
+ }
351
+ return prefix ?? [];
352
+ }
353
+ function wrapDivisionExtents(blocks, divisionByBlock) {
354
+ const out = [];
355
+ const open = [];
356
+ for (const block of blocks) {
357
+ const chain = divisionByBlock.get(block);
358
+ if (chain !== void 0) {
359
+ while (open.length > 0 && !chain.includes(open[open.length - 1])) {
360
+ out.push({ kind: "constructEnd" });
361
+ open.pop();
362
+ }
363
+ for (const id of chain) {
364
+ if (open.includes(id)) continue;
365
+ out.push({
366
+ kind: "constructStart",
367
+ descriptor: { kind: "division" }
368
+ });
369
+ open.push(id);
370
+ }
371
+ }
372
+ out.push(block);
373
+ }
374
+ while (open.length > 0) {
375
+ out.push({ kind: "constructEnd" });
376
+ open.pop();
377
+ }
378
+ return out;
379
+ }
260
380
  function framesIntersect(a, b) {
261
381
  return a.xPt < b.xPt + b.widthPt && b.xPt < a.xPt + a.widthPt && a.yPt < b.yPt + b.heightPt && b.yPt < a.yPt + a.heightPt;
262
382
  }
@@ -420,9 +540,10 @@ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
420
540
  }
421
541
  function reconstructPresentation(doc, options) {
422
542
  const signal = options?.signal;
543
+ const structure = indexStructure(doc);
423
544
  const slides = doc.pages.map((page, pageIndex) => {
424
545
  require_ports_abort.throwIfAborted(signal);
425
- return reconstructSlide(page, pageIndex, doc.images);
546
+ return reconstructSlide(page, pageIndex, doc.images, structure);
426
547
  });
427
548
  return {
428
549
  kind: "presentation",
@@ -430,10 +551,15 @@ function reconstructPresentation(doc, options) {
430
551
  slides
431
552
  };
432
553
  }
433
- function reconstructSlide(page, pageIndex, images) {
434
- const recoveredTable = recoverTable(page, pageIndex);
435
- const consumedText = recoveredTable?.consumedText;
436
- const textItems = page.items.filter((i) => i.kind === "text" && consumedText?.has(i) !== true);
554
+ function reconstructSlide(page, pageIndex, images, structure) {
555
+ const recoveredTables = recoverTables(page, pageIndex, structure);
556
+ const consumedText = /* @__PURE__ */ new Set();
557
+ const claimedItems = /* @__PURE__ */ new Set();
558
+ for (const recovered of recoveredTables) {
559
+ for (const item of recovered.consumedText) consumedText.add(item);
560
+ for (const item of recovered.latticeItems) claimedItems.add(item);
561
+ }
562
+ const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
437
563
  const imageItems = page.items.filter((i) => i.kind === "image");
438
564
  const textShapes = clusterIntoBlocks(clusterIntoLines(textItems)).map((block) => blockToShape(block, page.heightPt, pageIndex));
439
565
  const imageShapes = [];
@@ -441,14 +567,14 @@ function reconstructSlide(page, pageIndex, images) {
441
567
  const shape = imageToShape(img, page.heightPt, pageIndex, images);
442
568
  if (shape !== void 0) imageShapes.push(shape);
443
569
  }
444
- const recoveredVectors = recoverPageVectors(page, pageIndex, recoveredTable?.latticeItems ?? NO_ITEMS);
570
+ const recoveredVectors = recoverPageVectors(page, pageIndex, claimedItems);
445
571
  const vectorShapes = recoveredVectors === void 0 ? [] : [wrapBlockInShape(recoveredVectors.block, {
446
572
  xPt: 0,
447
573
  yPt: 0,
448
574
  widthPt: page.widthPt,
449
575
  heightPt: page.heightPt
450
576
  }, page.heightPt, pageIndex)];
451
- const tableShapes = recoveredTable === void 0 ? [] : [wrapBlockInShape(recoveredTable.table, recoveredTable.frame, page.heightPt, pageIndex)];
577
+ const tableShapes = recoveredTables.map((recovered) => wrapBlockInShape(recovered.table, recovered.frame, page.heightPt, pageIndex));
452
578
  return {
453
579
  size: {
454
580
  widthPt: page.widthPt,
@@ -894,6 +1020,112 @@ function recoverPageVectors(page, pageIndex, excluded) {
894
1020
  topYPt: page.heightPt - topYDownPt
895
1021
  };
896
1022
  }
1023
+ function recoverTables(page, pageIndex, structure) {
1024
+ if (structure !== void 0) {
1025
+ const tagged = recoverTaggedTables(page, pageIndex, structure);
1026
+ if (tagged.length > 0) return tagged;
1027
+ }
1028
+ const lattice = recoverTable(page, pageIndex);
1029
+ return lattice === void 0 ? [] : [lattice];
1030
+ }
1031
+ function recoverTaggedTables(page, pageIndex, structure) {
1032
+ const cellsByTable = /* @__PURE__ */ new Map();
1033
+ for (const item of page.items) {
1034
+ if (item.kind !== "text") continue;
1035
+ const cell = nearestOfType(structure.nodeOfItem(item), CELL_TYPES);
1036
+ const row = nearestOfType(cell?.parent, ROW_TYPES);
1037
+ const table = nearestOfType(row?.parent, TABLE_TYPES);
1038
+ if (cell === void 0 || row === void 0 || table === void 0) continue;
1039
+ const entry = cellsByTable.get(table.id) ?? {
1040
+ table,
1041
+ cells: []
1042
+ };
1043
+ entry.cells.push({
1044
+ table,
1045
+ row,
1046
+ cell,
1047
+ items: [item]
1048
+ });
1049
+ cellsByTable.set(table.id, entry);
1050
+ }
1051
+ if (cellsByTable.size === 0) return [];
1052
+ const latticeItems = require_layout_lattice.detectGridLattice(page.items)?.sourceItems ?? NO_ITEMS;
1053
+ const recovered = [];
1054
+ for (const { cells } of cellsByTable.values()) {
1055
+ const rowsById = /* @__PURE__ */ new Map();
1056
+ for (const cellEntry of cells) {
1057
+ const rowEntry = rowsById.get(cellEntry.row.id) ?? {
1058
+ row: cellEntry.row,
1059
+ cells: []
1060
+ };
1061
+ rowEntry.cells.push(cellEntry);
1062
+ rowsById.set(cellEntry.row.id, rowEntry);
1063
+ }
1064
+ const rows = [...rowsById.values()].sort((a, b) => a.row.order - b.row.order);
1065
+ for (const row of rows) row.cells.sort((a, b) => a.cell.order - b.cell.order);
1066
+ const columnCount = Math.max(...rows.map((row) => row.cells.length));
1067
+ const columnWidthsPt = [];
1068
+ for (let j = 0; j < columnCount; j++) {
1069
+ let minX = Number.POSITIVE_INFINITY;
1070
+ let maxX = Number.NEGATIVE_INFINITY;
1071
+ for (const row of rows) {
1072
+ const cellEntry = row.cells[j];
1073
+ if (cellEntry === void 0) continue;
1074
+ for (const item of cellEntry.items) {
1075
+ minX = Math.min(minX, item.xPt);
1076
+ maxX = Math.max(maxX, item.xPt + (item.widthPt ?? 0));
1077
+ }
1078
+ }
1079
+ const width = maxX - minX;
1080
+ columnWidthsPt.push(width > 0 ? width : DEFAULT_COLUMN_WIDTH_FALLBACK_PT);
1081
+ }
1082
+ const allItems = cells.flatMap((cellEntry) => cellEntry.items);
1083
+ const contentRows = rows.map((row) => {
1084
+ const contentCells = [];
1085
+ for (let j = 0; j < columnCount; j++) {
1086
+ const cellEntry = row.cells[j];
1087
+ const cell = { blocks: cellBlocksFromItems(cellEntry?.items ?? [], pageIndex) };
1088
+ if (cellEntry !== void 0) require_layout_shared.stampFrame(cell, pageIndex, textItemsPdfBox(cellEntry.items));
1089
+ contentCells.push(cell);
1090
+ }
1091
+ return { cells: contentCells };
1092
+ });
1093
+ const box = textItemsPdfBox(allItems);
1094
+ const table = {
1095
+ kind: "table",
1096
+ rows: contentRows,
1097
+ columnWidthsPt
1098
+ };
1099
+ require_layout_shared.stampFrame(table, pageIndex, box);
1100
+ recovered.push({
1101
+ table,
1102
+ frame: require_model_geometry.flipY(box, page.heightPt),
1103
+ topYPt: box.yPt + box.heightPt,
1104
+ consumedText: new Set(allItems),
1105
+ latticeItems
1106
+ });
1107
+ }
1108
+ return recovered;
1109
+ }
1110
+ function textItemsPdfBox(items) {
1111
+ let minX = Number.POSITIVE_INFINITY;
1112
+ let maxX = Number.NEGATIVE_INFINITY;
1113
+ let minY = Number.POSITIVE_INFINITY;
1114
+ let maxY = Number.NEGATIVE_INFINITY;
1115
+ for (const item of items) {
1116
+ const { ascentPt, descentPt } = textItemVerticalExtent(item);
1117
+ minX = Math.min(minX, item.xPt);
1118
+ maxX = Math.max(maxX, item.xPt + (item.widthPt ?? 0));
1119
+ minY = Math.min(minY, item.yPt - descentPt);
1120
+ maxY = Math.max(maxY, item.yPt + ascentPt);
1121
+ }
1122
+ return {
1123
+ xPt: minX,
1124
+ yPt: minY,
1125
+ widthPt: maxX - minX,
1126
+ heightPt: maxY - minY
1127
+ };
1128
+ }
897
1129
  function cellBlocksFromItems(items, pageIndex) {
898
1130
  return clusterIntoLines(items).map((line) => lineToParagraph(line, pageIndex));
899
1131
  }
@@ -998,23 +1230,7 @@ function buildCellsFromGroups(groups, pageIndex, context) {
998
1230
  value: inferredCellValue(displayText, row, column, context),
999
1231
  displayText
1000
1232
  };
1001
- let minX = Number.POSITIVE_INFINITY;
1002
- let maxX = Number.NEGATIVE_INFINITY;
1003
- let minY = Number.POSITIVE_INFINITY;
1004
- let maxY = Number.NEGATIVE_INFINITY;
1005
- for (const item of items) {
1006
- const { ascentPt, descentPt } = textItemVerticalExtent(item);
1007
- minX = Math.min(minX, item.xPt);
1008
- maxX = Math.max(maxX, item.xPt + (item.widthPt ?? 0));
1009
- minY = Math.min(minY, item.yPt - descentPt);
1010
- maxY = Math.max(maxY, item.yPt + ascentPt);
1011
- }
1012
- require_layout_shared.stampFrame(cell, pageIndex, {
1013
- xPt: minX,
1014
- yPt: minY,
1015
- widthPt: maxX - minX,
1016
- heightPt: maxY - minY
1017
- });
1233
+ require_layout_shared.stampFrame(cell, pageIndex, textItemsPdfBox(items));
1018
1234
  cells.push(cell);
1019
1235
  }
1020
1236
  cells.sort((a, b) => a.row - b.row || a.column - b.column);
@@ -135,19 +135,20 @@ function reconstructWordprocessing(doc, options) {
135
135
  const signal = options?.signal;
136
136
  doc = dropHiddenLayerContent(doc);
137
137
  const headingLevels = headingSizeLevels(doc);
138
+ const structure = indexStructure(doc);
138
139
  const sections = [];
139
140
  let currentGroup = [];
140
141
  let groupStartPageIndex = 0;
141
142
  for (const page of doc.pages) {
142
143
  throwIfAborted(signal);
143
144
  if (currentGroup.length > 0 && !samePageSize(currentGroup[0], page)) {
144
- sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
145
+ sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form, structure));
145
146
  groupStartPageIndex += currentGroup.length;
146
147
  currentGroup = [];
147
148
  }
148
149
  currentGroup.push(page);
149
150
  }
150
- if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
151
+ if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form, structure));
151
152
  return {
152
153
  kind: "wordprocessing",
153
154
  metadata: doc.metadata,
@@ -168,6 +169,59 @@ function dropHiddenLayerContent(doc) {
168
169
  }))
169
170
  };
170
171
  }
172
+ const HEADING_LEVELS = /* @__PURE__ */ new Map([
173
+ ["H1", 1],
174
+ ["H2", 2],
175
+ ["H3", 3],
176
+ ["H4", 4],
177
+ ["H5", 5],
178
+ ["H6", 6]
179
+ ]);
180
+ const DIVISION_TYPES = /* @__PURE__ */ new Set([
181
+ "Part",
182
+ "Sect",
183
+ "Div"
184
+ ]);
185
+ const CELL_TYPES = /* @__PURE__ */ new Set(["TD", "TH"]);
186
+ const ROW_TYPES = /* @__PURE__ */ new Set(["TR"]);
187
+ const TABLE_TYPES = /* @__PURE__ */ new Set(["Table"]);
188
+ function indexStructure(doc) {
189
+ if (doc.structure === void 0 || doc.structure.length === 0) return;
190
+ const byId = /* @__PURE__ */ new Map();
191
+ let order = 0;
192
+ const walk = (element, parent) => {
193
+ const node = {
194
+ id: element.id,
195
+ type: element.type,
196
+ parent,
197
+ order: order++
198
+ };
199
+ byId.set(element.id, node);
200
+ for (const child of element.children) walk(child, node);
201
+ };
202
+ for (const root of doc.structure) walk(root, void 0);
203
+ return { nodeOfItem: (item) => item.kind === "link" || item.kind === "internalLink" ? void 0 : byId.get(item.structure ?? "") };
204
+ }
205
+ function nearestOfType(node, types) {
206
+ for (let current = node; current !== void 0; current = current.parent) if (types.has(current.type)) return current;
207
+ }
208
+ function nearestHeadingLevel(node) {
209
+ for (let current = node; current !== void 0; current = current.parent) {
210
+ const level = HEADING_LEVELS.get(current.type);
211
+ if (level !== void 0) return level;
212
+ }
213
+ }
214
+ function structureHeadingLevel(items, structure) {
215
+ if (items.length === 0 || items.some((item) => structure.nodeOfItem(item) === void 0)) return;
216
+ let level;
217
+ for (const item of items) {
218
+ const itemLevel = nearestHeadingLevel(structure.nodeOfItem(item));
219
+ if (itemLevel === void 0) return { level: void 0 };
220
+ if (level === void 0) level = itemLevel;
221
+ else if (level !== itemLevel) return;
222
+ }
223
+ return { level };
224
+ }
171
225
  const HEADING_MIN_SIZE_DELTA_PT = 2;
172
226
  const MAX_HEADING_LEVEL = 6;
173
227
  function headingSizeLevels(doc) {
@@ -192,11 +246,12 @@ function headingLevelOf(paragraph, levels) {
192
246
  function samePageSize(a, b) {
193
247
  return a.widthPt === b.widthPt && a.heightPt === b.heightPt;
194
248
  }
195
- function buildSection(pages, startPageIndex, images, headingLevels, form) {
249
+ function buildSection(pages, startPageIndex, images, headingLevels, form, structure) {
196
250
  const blocks = [];
251
+ const divisionByBlock = /* @__PURE__ */ new WeakMap();
197
252
  pages.forEach((page, i) => {
198
253
  if (i > 0) blocks.push({ kind: "pageBreak" });
199
- blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images, headingLevels, form));
254
+ blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images, headingLevels, form, structure, divisionByBlock));
200
255
  });
201
256
  return {
202
257
  pageSize: {
@@ -204,20 +259,34 @@ function buildSection(pages, startPageIndex, images, headingLevels, form) {
204
259
  heightPt: pages[0].heightPt
205
260
  },
206
261
  margins: ZERO_MARGINS,
207
- blocks
262
+ blocks: wrapDivisionExtents(blocks, divisionByBlock)
208
263
  };
209
264
  }
210
- function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
211
- const recoveredTable = recoverTable(page, pageIndex);
212
- const consumedText = recoveredTable?.consumedText;
213
- const textItems = page.items.filter((i) => i.kind === "text" && consumedText?.has(i) !== true);
265
+ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, structure, divisionByBlock) {
266
+ const recoveredTables = recoverTables(page, pageIndex, structure);
267
+ const consumedText = /* @__PURE__ */ new Set();
268
+ const claimedItems = /* @__PURE__ */ new Set();
269
+ for (const recovered of recoveredTables) {
270
+ for (const item of recovered.consumedText) consumedText.add(item);
271
+ for (const item of recovered.latticeItems) claimedItems.add(item);
272
+ }
273
+ const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
214
274
  const imageItems = page.items.filter((i) => i.kind === "image");
215
275
  const paragraphs = clusterIntoParagraphs(clusterIntoLines(textItems));
276
+ const recordDivisions = (block, items) => {
277
+ if (structure !== void 0) divisionByBlock.set(block, divisionChainOf(items, structure));
278
+ };
216
279
  const positioned = [];
217
- for (const paragraph of paragraphs) positioned.push({
218
- yPt: paragraph.lines[0].baselineY,
219
- block: paragraphToContentParagraph(paragraph, pageIndex, headingLevelOf(paragraph, headingLevels))
220
- });
280
+ for (const paragraph of paragraphs) {
281
+ const paragraphItems = paragraph.lines.flatMap((line) => line.items);
282
+ const structural = structure === void 0 ? void 0 : structureHeadingLevel(paragraphItems, structure);
283
+ const block = paragraphToContentParagraph(paragraph, pageIndex, structural !== void 0 ? structural.level : headingLevelOf(paragraph, headingLevels));
284
+ recordDivisions(block, paragraphItems);
285
+ positioned.push({
286
+ yPt: paragraph.lines[0].baselineY,
287
+ block
288
+ });
289
+ }
221
290
  for (const img of imageItems) {
222
291
  const asset = images[img.imageId];
223
292
  if (asset !== void 0) {
@@ -234,21 +303,29 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
234
303
  widthPt: img.widthPt,
235
304
  heightPt: img.heightPt
236
305
  });
306
+ recordDivisions(block, [img]);
237
307
  positioned.push({
238
308
  yPt: img.yPt,
239
309
  block
240
310
  });
241
311
  }
242
312
  }
243
- if (recoveredTable !== void 0) positioned.push({
244
- yPt: recoveredTable.topYPt,
245
- block: recoveredTable.table
246
- });
247
- const recoveredVectors = recoverPageVectors(page, pageIndex, recoveredTable?.latticeItems ?? NO_ITEMS);
248
- if (recoveredVectors !== void 0) positioned.push({
249
- yPt: recoveredVectors.topYPt,
250
- block: recoveredVectors.block
251
- });
313
+ for (const recovered of recoveredTables) {
314
+ recordDivisions(recovered.table, [...recovered.consumedText]);
315
+ positioned.push({
316
+ yPt: recovered.topYPt,
317
+ block: recovered.table
318
+ });
319
+ }
320
+ const recoveredVectors = recoverPageVectors(page, pageIndex, claimedItems);
321
+ if (recoveredVectors !== void 0) {
322
+ const vectorItems = page.items.filter((item) => !claimedItems.has(item) && layoutItemToVector(item, page.heightPt) !== void 0);
323
+ recordDivisions(recoveredVectors.block, vectorItems);
324
+ positioned.push({
325
+ yPt: recoveredVectors.topYPt,
326
+ block: recoveredVectors.block
327
+ });
328
+ }
252
329
  positioned.sort((a, b) => b.yPt - a.yPt);
253
330
  const blocks = positioned.map((p) => p.block);
254
331
  reconcileLinks(blocks, page, pageIndex);
@@ -256,6 +333,49 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
256
333
  appendFormFieldConstructs(blocks, form, pageIndex);
257
334
  return blocks;
258
335
  }
336
+ function divisionChainOf(items, structure) {
337
+ let prefix;
338
+ for (const item of items) {
339
+ const chain = [];
340
+ for (let current = structure.nodeOfItem(item); current !== void 0; current = current.parent) if (DIVISION_TYPES.has(current.type)) chain.push(current.id);
341
+ chain.reverse();
342
+ if (prefix === void 0) {
343
+ prefix = chain;
344
+ continue;
345
+ }
346
+ let shared = 0;
347
+ while (shared < prefix.length && shared < chain.length && prefix[shared] === chain[shared]) shared += 1;
348
+ prefix = prefix.slice(0, shared);
349
+ }
350
+ return prefix ?? [];
351
+ }
352
+ function wrapDivisionExtents(blocks, divisionByBlock) {
353
+ const out = [];
354
+ const open = [];
355
+ for (const block of blocks) {
356
+ const chain = divisionByBlock.get(block);
357
+ if (chain !== void 0) {
358
+ while (open.length > 0 && !chain.includes(open[open.length - 1])) {
359
+ out.push({ kind: "constructEnd" });
360
+ open.pop();
361
+ }
362
+ for (const id of chain) {
363
+ if (open.includes(id)) continue;
364
+ out.push({
365
+ kind: "constructStart",
366
+ descriptor: { kind: "division" }
367
+ });
368
+ open.push(id);
369
+ }
370
+ }
371
+ out.push(block);
372
+ }
373
+ while (open.length > 0) {
374
+ out.push({ kind: "constructEnd" });
375
+ open.pop();
376
+ }
377
+ return out;
378
+ }
259
379
  function framesIntersect(a, b) {
260
380
  return a.xPt < b.xPt + b.widthPt && b.xPt < a.xPt + a.widthPt && a.yPt < b.yPt + b.heightPt && b.yPt < a.yPt + a.heightPt;
261
381
  }
@@ -419,9 +539,10 @@ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
419
539
  }
420
540
  function reconstructPresentation(doc, options) {
421
541
  const signal = options?.signal;
542
+ const structure = indexStructure(doc);
422
543
  const slides = doc.pages.map((page, pageIndex) => {
423
544
  throwIfAborted(signal);
424
- return reconstructSlide(page, pageIndex, doc.images);
545
+ return reconstructSlide(page, pageIndex, doc.images, structure);
425
546
  });
426
547
  return {
427
548
  kind: "presentation",
@@ -429,10 +550,15 @@ function reconstructPresentation(doc, options) {
429
550
  slides
430
551
  };
431
552
  }
432
- function reconstructSlide(page, pageIndex, images) {
433
- const recoveredTable = recoverTable(page, pageIndex);
434
- const consumedText = recoveredTable?.consumedText;
435
- const textItems = page.items.filter((i) => i.kind === "text" && consumedText?.has(i) !== true);
553
+ function reconstructSlide(page, pageIndex, images, structure) {
554
+ const recoveredTables = recoverTables(page, pageIndex, structure);
555
+ const consumedText = /* @__PURE__ */ new Set();
556
+ const claimedItems = /* @__PURE__ */ new Set();
557
+ for (const recovered of recoveredTables) {
558
+ for (const item of recovered.consumedText) consumedText.add(item);
559
+ for (const item of recovered.latticeItems) claimedItems.add(item);
560
+ }
561
+ const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
436
562
  const imageItems = page.items.filter((i) => i.kind === "image");
437
563
  const textShapes = clusterIntoBlocks(clusterIntoLines(textItems)).map((block) => blockToShape(block, page.heightPt, pageIndex));
438
564
  const imageShapes = [];
@@ -440,14 +566,14 @@ function reconstructSlide(page, pageIndex, images) {
440
566
  const shape = imageToShape(img, page.heightPt, pageIndex, images);
441
567
  if (shape !== void 0) imageShapes.push(shape);
442
568
  }
443
- const recoveredVectors = recoverPageVectors(page, pageIndex, recoveredTable?.latticeItems ?? NO_ITEMS);
569
+ const recoveredVectors = recoverPageVectors(page, pageIndex, claimedItems);
444
570
  const vectorShapes = recoveredVectors === void 0 ? [] : [wrapBlockInShape(recoveredVectors.block, {
445
571
  xPt: 0,
446
572
  yPt: 0,
447
573
  widthPt: page.widthPt,
448
574
  heightPt: page.heightPt
449
575
  }, page.heightPt, pageIndex)];
450
- const tableShapes = recoveredTable === void 0 ? [] : [wrapBlockInShape(recoveredTable.table, recoveredTable.frame, page.heightPt, pageIndex)];
576
+ const tableShapes = recoveredTables.map((recovered) => wrapBlockInShape(recovered.table, recovered.frame, page.heightPt, pageIndex));
451
577
  return {
452
578
  size: {
453
579
  widthPt: page.widthPt,
@@ -893,6 +1019,112 @@ function recoverPageVectors(page, pageIndex, excluded) {
893
1019
  topYPt: page.heightPt - topYDownPt
894
1020
  };
895
1021
  }
1022
+ function recoverTables(page, pageIndex, structure) {
1023
+ if (structure !== void 0) {
1024
+ const tagged = recoverTaggedTables(page, pageIndex, structure);
1025
+ if (tagged.length > 0) return tagged;
1026
+ }
1027
+ const lattice = recoverTable(page, pageIndex);
1028
+ return lattice === void 0 ? [] : [lattice];
1029
+ }
1030
+ function recoverTaggedTables(page, pageIndex, structure) {
1031
+ const cellsByTable = /* @__PURE__ */ new Map();
1032
+ for (const item of page.items) {
1033
+ if (item.kind !== "text") continue;
1034
+ const cell = nearestOfType(structure.nodeOfItem(item), CELL_TYPES);
1035
+ const row = nearestOfType(cell?.parent, ROW_TYPES);
1036
+ const table = nearestOfType(row?.parent, TABLE_TYPES);
1037
+ if (cell === void 0 || row === void 0 || table === void 0) continue;
1038
+ const entry = cellsByTable.get(table.id) ?? {
1039
+ table,
1040
+ cells: []
1041
+ };
1042
+ entry.cells.push({
1043
+ table,
1044
+ row,
1045
+ cell,
1046
+ items: [item]
1047
+ });
1048
+ cellsByTable.set(table.id, entry);
1049
+ }
1050
+ if (cellsByTable.size === 0) return [];
1051
+ const latticeItems = detectGridLattice(page.items)?.sourceItems ?? NO_ITEMS;
1052
+ const recovered = [];
1053
+ for (const { cells } of cellsByTable.values()) {
1054
+ const rowsById = /* @__PURE__ */ new Map();
1055
+ for (const cellEntry of cells) {
1056
+ const rowEntry = rowsById.get(cellEntry.row.id) ?? {
1057
+ row: cellEntry.row,
1058
+ cells: []
1059
+ };
1060
+ rowEntry.cells.push(cellEntry);
1061
+ rowsById.set(cellEntry.row.id, rowEntry);
1062
+ }
1063
+ const rows = [...rowsById.values()].sort((a, b) => a.row.order - b.row.order);
1064
+ for (const row of rows) row.cells.sort((a, b) => a.cell.order - b.cell.order);
1065
+ const columnCount = Math.max(...rows.map((row) => row.cells.length));
1066
+ const columnWidthsPt = [];
1067
+ for (let j = 0; j < columnCount; j++) {
1068
+ let minX = Number.POSITIVE_INFINITY;
1069
+ let maxX = Number.NEGATIVE_INFINITY;
1070
+ for (const row of rows) {
1071
+ const cellEntry = row.cells[j];
1072
+ if (cellEntry === void 0) continue;
1073
+ for (const item of cellEntry.items) {
1074
+ minX = Math.min(minX, item.xPt);
1075
+ maxX = Math.max(maxX, item.xPt + (item.widthPt ?? 0));
1076
+ }
1077
+ }
1078
+ const width = maxX - minX;
1079
+ columnWidthsPt.push(width > 0 ? width : DEFAULT_COLUMN_WIDTH_FALLBACK_PT);
1080
+ }
1081
+ const allItems = cells.flatMap((cellEntry) => cellEntry.items);
1082
+ const contentRows = rows.map((row) => {
1083
+ const contentCells = [];
1084
+ for (let j = 0; j < columnCount; j++) {
1085
+ const cellEntry = row.cells[j];
1086
+ const cell = { blocks: cellBlocksFromItems(cellEntry?.items ?? [], pageIndex) };
1087
+ if (cellEntry !== void 0) stampFrame(cell, pageIndex, textItemsPdfBox(cellEntry.items));
1088
+ contentCells.push(cell);
1089
+ }
1090
+ return { cells: contentCells };
1091
+ });
1092
+ const box = textItemsPdfBox(allItems);
1093
+ const table = {
1094
+ kind: "table",
1095
+ rows: contentRows,
1096
+ columnWidthsPt
1097
+ };
1098
+ stampFrame(table, pageIndex, box);
1099
+ recovered.push({
1100
+ table,
1101
+ frame: flipY(box, page.heightPt),
1102
+ topYPt: box.yPt + box.heightPt,
1103
+ consumedText: new Set(allItems),
1104
+ latticeItems
1105
+ });
1106
+ }
1107
+ return recovered;
1108
+ }
1109
+ function textItemsPdfBox(items) {
1110
+ let minX = Number.POSITIVE_INFINITY;
1111
+ let maxX = Number.NEGATIVE_INFINITY;
1112
+ let minY = Number.POSITIVE_INFINITY;
1113
+ let maxY = Number.NEGATIVE_INFINITY;
1114
+ for (const item of items) {
1115
+ const { ascentPt, descentPt } = textItemVerticalExtent(item);
1116
+ minX = Math.min(minX, item.xPt);
1117
+ maxX = Math.max(maxX, item.xPt + (item.widthPt ?? 0));
1118
+ minY = Math.min(minY, item.yPt - descentPt);
1119
+ maxY = Math.max(maxY, item.yPt + ascentPt);
1120
+ }
1121
+ return {
1122
+ xPt: minX,
1123
+ yPt: minY,
1124
+ widthPt: maxX - minX,
1125
+ heightPt: maxY - minY
1126
+ };
1127
+ }
896
1128
  function cellBlocksFromItems(items, pageIndex) {
897
1129
  return clusterIntoLines(items).map((line) => lineToParagraph(line, pageIndex));
898
1130
  }
@@ -997,23 +1229,7 @@ function buildCellsFromGroups(groups, pageIndex, context) {
997
1229
  value: inferredCellValue(displayText, row, column, context),
998
1230
  displayText
999
1231
  };
1000
- let minX = Number.POSITIVE_INFINITY;
1001
- let maxX = Number.NEGATIVE_INFINITY;
1002
- let minY = Number.POSITIVE_INFINITY;
1003
- let maxY = Number.NEGATIVE_INFINITY;
1004
- for (const item of items) {
1005
- const { ascentPt, descentPt } = textItemVerticalExtent(item);
1006
- minX = Math.min(minX, item.xPt);
1007
- maxX = Math.max(maxX, item.xPt + (item.widthPt ?? 0));
1008
- minY = Math.min(minY, item.yPt - descentPt);
1009
- maxY = Math.max(maxY, item.yPt + ascentPt);
1010
- }
1011
- stampFrame(cell, pageIndex, {
1012
- xPt: minX,
1013
- yPt: minY,
1014
- widthPt: maxX - minX,
1015
- heightPt: maxY - minY
1016
- });
1232
+ stampFrame(cell, pageIndex, textItemsPdfBox(items));
1017
1233
  cells.push(cell);
1018
1234
  }
1019
1235
  cells.sort((a, b) => a.row - b.row || a.column - b.column);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js.documents",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "description": "Bidirectional docx/pptx <-> PDF conversion and a read+write editable OOXML document model, built on ooxml.js and Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {