js.documents 4.0.20 → 4.4.6

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.
Files changed (116) hide show
  1. package/README.md +43 -29
  2. package/dist/cell-typing-CEWlywIc.d.cts +22 -0
  3. package/dist/cell-typing-CEWlywIc.d.ts +22 -0
  4. package/dist/codecs/read.cjs +66 -0
  5. package/dist/codecs/read.d.cts +15 -0
  6. package/dist/codecs/read.d.ts +15 -0
  7. package/dist/codecs/read.js +64 -0
  8. package/dist/codecs/registry.cjs +13 -59
  9. package/dist/codecs/registry.d.cts +2 -8
  10. package/dist/codecs/registry.d.ts +2 -8
  11. package/dist/codecs/registry.js +20 -64
  12. package/dist/convert/codec.cjs +11 -10
  13. package/dist/convert/codec.js +2 -1
  14. package/dist/convert/composition-to-pdf.cjs +133 -0
  15. package/dist/convert/composition-to-pdf.d.cts +7 -0
  16. package/dist/convert/composition-to-pdf.d.ts +7 -0
  17. package/dist/convert/composition-to-pdf.js +131 -0
  18. package/dist/convert/composition.cjs +31 -129
  19. package/dist/convert/composition.d.cts +14 -4
  20. package/dist/convert/composition.d.ts +14 -4
  21. package/dist/convert/composition.js +25 -128
  22. package/dist/convert/convert.cjs +35 -75
  23. package/dist/convert/convert.d.cts +5 -20
  24. package/dist/convert/convert.d.ts +5 -20
  25. package/dist/convert/convert.js +2 -32
  26. package/dist/convert/from-package.cjs +2 -1
  27. package/dist/convert/from-package.js +2 -1
  28. package/dist/convert/from-pdf.cjs +50 -0
  29. package/dist/convert/from-pdf.d.cts +26 -0
  30. package/dist/convert/from-pdf.d.ts +26 -0
  31. package/dist/convert/from-pdf.js +39 -0
  32. package/dist/convert/local.cjs +2 -1
  33. package/dist/convert/local.js +2 -1
  34. package/dist/convert/pdf-package-tables.cjs +76 -0
  35. package/dist/convert/pdf-package-tables.d.cts +6 -0
  36. package/dist/convert/pdf-package-tables.d.ts +6 -0
  37. package/dist/convert/pdf-package-tables.js +75 -0
  38. package/dist/csv/read.d.cts +1 -1
  39. package/dist/csv/read.d.ts +1 -1
  40. package/dist/diagnostics-DVAXKgcf.d.cts +10 -0
  41. package/dist/diagnostics-DVAXKgcf.d.ts +10 -0
  42. package/dist/edit/docx/content.cjs +1 -0
  43. package/dist/edit/docx/content.js +1 -0
  44. package/dist/edit/docx/paragraph.cjs +18 -1
  45. package/dist/edit/docx/paragraph.d.cts +3 -0
  46. package/dist/edit/docx/paragraph.d.ts +3 -0
  47. package/dist/edit/docx/paragraph.js +18 -1
  48. package/dist/edit/docx/props.cjs +1 -0
  49. package/dist/edit/docx/props.js +1 -0
  50. package/dist/edit/markdown/editor.cjs +1 -1
  51. package/dist/edit/markdown/editor.js +1 -1
  52. package/dist/edit/pdf/item.cjs +42 -1
  53. package/dist/edit/pdf/item.d.cts +2 -2
  54. package/dist/edit/pdf/item.d.ts +2 -2
  55. package/dist/edit/pdf/item.js +42 -2
  56. package/dist/edit/pdf/page.d.cts +1 -1
  57. package/dist/edit/pdf/page.d.ts +1 -1
  58. package/dist/fonts/registry.d.cts +1 -19
  59. package/dist/fonts/registry.d.ts +1 -19
  60. package/dist/hsqldb/binary-script.d.cts +1 -1
  61. package/dist/hsqldb/binary-script.d.ts +1 -1
  62. package/dist/hsqldb/cache.d.cts +1 -1
  63. package/dist/hsqldb/cache.d.ts +1 -1
  64. package/dist/hsqldb/rowformat.d.cts +1 -24
  65. package/dist/hsqldb/rowformat.d.ts +1 -24
  66. package/dist/index.cjs +17 -16
  67. package/dist/index.d.cts +11 -10
  68. package/dist/index.d.ts +11 -10
  69. package/dist/index.js +8 -7
  70. package/dist/{item-D9vQKT0T.d.cts → item-DhXpJNAQ.d.cts} +17 -3
  71. package/dist/{item-D9vQKT0T.d.ts → item-DhXpJNAQ.d.ts} +17 -3
  72. package/dist/layout/cell-typing.d.cts +1 -21
  73. package/dist/layout/cell-typing.d.ts +1 -21
  74. package/dist/layout/reconstruct.cjs +177 -14
  75. package/dist/layout/reconstruct.d.cts +1 -1
  76. package/dist/layout/reconstruct.d.ts +1 -1
  77. package/dist/layout/reconstruct.js +175 -12
  78. package/dist/layout/shared.cjs +2 -2
  79. package/dist/layout/shared.js +1 -1
  80. package/dist/markdown/math.cjs +5 -7
  81. package/dist/markdown/math.js +5 -7
  82. package/dist/markdown/read.cjs +15 -1
  83. package/dist/markdown/read.js +16 -2
  84. package/dist/markdown/write.cjs +7 -10
  85. package/dist/markdown/write.d.cts +3 -6
  86. package/dist/markdown/write.d.ts +3 -6
  87. package/dist/markdown/write.js +8 -11
  88. package/dist/metadata/write.cjs +2 -1
  89. package/dist/metadata/write.js +2 -1
  90. package/dist/model/bytes.cjs +8 -0
  91. package/dist/model/bytes.d.cts +2 -1
  92. package/dist/model/bytes.d.ts +2 -1
  93. package/dist/model/bytes.js +8 -1
  94. package/dist/odb/read.d.cts +1 -1
  95. package/dist/odb/read.d.ts +1 -1
  96. package/dist/odb/report/content.d.cts +1 -1
  97. package/dist/odb/report/content.d.ts +1 -1
  98. package/dist/odb/report/source.d.cts +1 -1
  99. package/dist/odb/report/source.d.ts +1 -1
  100. package/dist/odf/odt/read.cjs +1 -1
  101. package/dist/odf/odt/read.js +1 -1
  102. package/dist/registry-BcBT-bnX.d.cts +20 -0
  103. package/dist/registry-D1Pu5XxB.d.ts +20 -0
  104. package/dist/rowformat-Cl2exlEh.d.cts +25 -0
  105. package/dist/rowformat-Cl2exlEh.d.ts +25 -0
  106. package/dist/svg/diagnostics.d.cts +1 -9
  107. package/dist/svg/diagnostics.d.ts +1 -9
  108. package/dist/svg/read.d.cts +1 -1
  109. package/dist/svg/read.d.ts +1 -1
  110. package/dist/svg/write.d.cts +1 -1
  111. package/dist/svg/write.d.ts +1 -1
  112. package/package.json +27 -10
  113. package/dist/metadata/read.cjs +0 -15
  114. package/dist/metadata/read.d.cts +0 -9
  115. package/dist/metadata/read.d.ts +0 -9
  116. package/dist/metadata/read.js +0 -14
@@ -1,22 +1,2 @@
1
- import { ContentCellValue } from "document-schema.js";
2
- //#region src/layout/cell-typing.d.ts
3
- type CellTypeRule = 'boolean-literal' | 'iso-date' | 'named-month-date' | 'plain-number' | 'grouped-number' | 'percentage' | 'currency';
4
- type CellTypeDeclineReason = 'ambiguous-boolean-word' | 'ambiguous-date-order' | 'ambiguous-grouping-separator' | 'leading-zero-digits' | 'precision-loss';
5
- type CellTypeInferenceResult = {
6
- readonly outcome: 'retyped';
7
- readonly value: ContentCellValue;
8
- readonly rule: CellTypeRule;
9
- } | {
10
- readonly outcome: 'declined';
11
- readonly reason: CellTypeDeclineReason;
12
- };
13
- type CellTypeInference = {
14
- readonly sheetIndex: number;
15
- readonly row: number;
16
- readonly column: number;
17
- readonly displayText: string;
18
- } & CellTypeInferenceResult;
19
- type CellTypeInferenceSink = (inference: CellTypeInference) => void;
20
- declare function inferCellValue(displayText: string): CellTypeInferenceResult | undefined;
21
- //#endregion
1
+ import { a as CellTypeRule, i as CellTypeInferenceSink, n as CellTypeInference, o as inferCellValue, r as CellTypeInferenceResult, t as CellTypeDeclineReason } from "../cell-typing-CEWlywIc.js";
22
2
  export { CellTypeDeclineReason, CellTypeInference, CellTypeInferenceResult, CellTypeInferenceSink, CellTypeRule, inferCellValue };
@@ -5,7 +5,8 @@ const require_layout_cell_typing = require("./cell-typing.cjs");
5
5
  const require_layout_shared = require("./shared.cjs");
6
6
  const require_ports_abort = require("../ports/abort.cjs");
7
7
  const require_layout_lattice = require("./lattice.cjs");
8
- let pdf_codec = require("pdf-codec");
8
+ let pdf_codec_fonts = require("pdf-codec/fonts");
9
+ let pdf_codec_afm_widths = require("pdf-codec/afm-widths");
9
10
  //#region src/layout/reconstruct.ts
10
11
  const ZERO_MARGINS = {
11
12
  topPt: 0,
@@ -31,8 +32,8 @@ function clusterIntoLines(items) {
31
32
  return working;
32
33
  }
33
34
  function textItemVerticalExtent(item) {
34
- const { standardName } = (0, pdf_codec.resolveStandardFont)(item.font.family, item.font.weight === "bold", item.font.style === "italic");
35
- const metrics = pdf_codec.STANDARD_METRICS[standardName];
35
+ const { standardName } = (0, pdf_codec_fonts.resolveStandardFont)(item.font.family, item.font.weight === "bold", item.font.style === "italic");
36
+ const metrics = pdf_codec_afm_widths.STANDARD_METRICS[standardName];
36
37
  return {
37
38
  ascentPt: metrics.ascender / 1e3 * item.sizePt,
38
39
  descentPt: Math.abs(metrics.descender) / 1e3 * item.sizePt
@@ -41,8 +42,8 @@ function textItemVerticalExtent(item) {
41
42
  function textItemToContentRun(item) {
42
43
  return {
43
44
  text: item.text,
44
- bold: item.font.weight === "bold" ? true : void 0,
45
- italic: item.font.style === "italic" ? true : void 0,
45
+ ...item.font.weight === "bold" ? { bold: true } : {},
46
+ ...item.font.style === "italic" ? { italic: true } : {},
46
47
  fontFamily: item.font.family,
47
48
  sizePt: item.sizePt,
48
49
  color: item.color
@@ -133,33 +134,70 @@ function estimateModalLineSpacing(lines) {
133
134
  }
134
135
  function reconstructWordprocessing(doc, options) {
135
136
  const signal = options?.signal;
137
+ doc = dropHiddenLayerContent(doc);
138
+ const headingLevels = headingSizeLevels(doc);
136
139
  const sections = [];
137
140
  let currentGroup = [];
138
141
  let groupStartPageIndex = 0;
139
142
  for (const page of doc.pages) {
140
143
  require_ports_abort.throwIfAborted(signal);
141
144
  if (currentGroup.length > 0 && !samePageSize(currentGroup[0], page)) {
142
- sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images));
145
+ sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
143
146
  groupStartPageIndex += currentGroup.length;
144
147
  currentGroup = [];
145
148
  }
146
149
  currentGroup.push(page);
147
150
  }
148
- if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images));
151
+ if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
149
152
  return {
150
153
  kind: "wordprocessing",
151
154
  metadata: doc.metadata,
152
155
  sections
153
156
  };
154
157
  }
158
+ function layerOf(item) {
159
+ return item.kind === "link" || item.kind === "internalLink" ? void 0 : item.layer;
160
+ }
161
+ function dropHiddenLayerContent(doc) {
162
+ const hidden = new Set((doc.layers ?? []).filter((layer) => layer.visible !== true).map((layer) => layer.name));
163
+ if (hidden.size === 0) return doc;
164
+ return {
165
+ ...doc,
166
+ pages: doc.pages.map((page) => ({
167
+ ...page,
168
+ items: page.items.filter((item) => layerOf(item) === void 0 || !hidden.has(layerOf(item)))
169
+ }))
170
+ };
171
+ }
172
+ const HEADING_MIN_SIZE_DELTA_PT = 2;
173
+ const MAX_HEADING_LEVEL = 6;
174
+ function headingSizeLevels(doc) {
175
+ const sizes = [];
176
+ for (const page of doc.pages) for (const item of page.items) if (item.kind === "text") sizes.push(item.sizePt);
177
+ if (sizes.length === 0) return /* @__PURE__ */ new Map();
178
+ const bodySizePt = modeOf(sizes, .5);
179
+ const headingBuckets = /* @__PURE__ */ new Set();
180
+ for (const size of sizes) {
181
+ const bucket = Math.round(size / .5) * .5;
182
+ if (bucket - bodySizePt >= HEADING_MIN_SIZE_DELTA_PT) headingBuckets.add(bucket);
183
+ }
184
+ const levels = /* @__PURE__ */ new Map();
185
+ [...headingBuckets].sort((a, b) => b - a).forEach((bucket, index) => {
186
+ levels.set(bucket, Math.min(index + 1, MAX_HEADING_LEVEL));
187
+ });
188
+ return levels;
189
+ }
190
+ function headingLevelOf(paragraph, levels) {
191
+ return levels.get(modeOf(paragraph.lines.flatMap((line) => line.items.map((item) => item.sizePt)), .5));
192
+ }
155
193
  function samePageSize(a, b) {
156
194
  return a.widthPt === b.widthPt && a.heightPt === b.heightPt;
157
195
  }
158
- function buildSection(pages, startPageIndex, images) {
196
+ function buildSection(pages, startPageIndex, images, headingLevels, form) {
159
197
  const blocks = [];
160
198
  pages.forEach((page, i) => {
161
199
  if (i > 0) blocks.push({ kind: "pageBreak" });
162
- blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images));
200
+ blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images, headingLevels, form));
163
201
  });
164
202
  return {
165
203
  pageSize: {
@@ -170,7 +208,7 @@ function buildSection(pages, startPageIndex, images) {
170
208
  blocks
171
209
  };
172
210
  }
173
- function reconstructPageBlocks(page, pageIndex, images) {
211
+ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
174
212
  const recoveredTable = recoverTable(page, pageIndex);
175
213
  const consumedText = recoveredTable?.consumedText;
176
214
  const textItems = page.items.filter((i) => i.kind === "text" && consumedText?.has(i) !== true);
@@ -179,7 +217,7 @@ function reconstructPageBlocks(page, pageIndex, images) {
179
217
  const positioned = [];
180
218
  for (const paragraph of paragraphs) positioned.push({
181
219
  yPt: paragraph.lines[0].baselineY,
182
- block: paragraphToContentParagraph(paragraph, pageIndex)
220
+ block: paragraphToContentParagraph(paragraph, pageIndex, headingLevelOf(paragraph, headingLevels))
183
221
  });
184
222
  for (const img of imageItems) {
185
223
  const asset = images[img.imageId];
@@ -213,7 +251,121 @@ function reconstructPageBlocks(page, pageIndex, images) {
213
251
  block: recoveredVectors.block
214
252
  });
215
253
  positioned.sort((a, b) => b.yPt - a.yPt);
216
- return positioned.map((p) => p.block);
254
+ const blocks = positioned.map((p) => p.block);
255
+ reconcileLinks(blocks, page, pageIndex);
256
+ appendAnnotationConstructs(blocks, page, pageIndex);
257
+ appendFormFieldConstructs(blocks, form, pageIndex);
258
+ return blocks;
259
+ }
260
+ function framesIntersect(a, b) {
261
+ 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
+ }
263
+ function intersectionArea(a, b) {
264
+ const width = Math.min(a.xPt + a.widthPt, b.xPt + b.widthPt) - Math.max(a.xPt, b.xPt);
265
+ const height = Math.min(a.yPt + a.heightPt, b.yPt + b.heightPt) - Math.max(a.yPt, b.yPt);
266
+ return width > 0 && height > 0 ? width * height : 0;
267
+ }
268
+ function reconcileLinks(blocks, page, pageIndex) {
269
+ for (const item of page.items) {
270
+ if (item.kind !== "link" && item.kind !== "internalLink") continue;
271
+ if (item.kind === "link") {
272
+ let matchedRun = false;
273
+ for (const block of blocks) {
274
+ if (block.kind !== "paragraph") continue;
275
+ for (const run of block.runs) if ((run.frames ?? []).some((frame) => frame.pageIndex === pageIndex && framesIntersect(item, frame))) {
276
+ run.hyperlink = item.uri;
277
+ matchedRun = true;
278
+ }
279
+ }
280
+ if (matchedRun) continue;
281
+ }
282
+ wrapBestBlock(blocks, item, pageIndex, linkDescriptor(item));
283
+ }
284
+ }
285
+ function linkDescriptor(item) {
286
+ if (item.kind === "link") return {
287
+ kind: "link",
288
+ target: {
289
+ kind: "external",
290
+ uri: item.uri
291
+ },
292
+ ...item.title !== void 0 ? { title: item.title } : {}
293
+ };
294
+ return {
295
+ kind: "link",
296
+ target: {
297
+ kind: "internal",
298
+ anchor: item.destination
299
+ },
300
+ ...item.title !== void 0 ? { title: item.title } : {}
301
+ };
302
+ }
303
+ function blockFramesOn(block, pageIndex) {
304
+ return block.kind === "constructStart" || block.kind === "constructEnd" ? [] : (block.frames ?? []).filter((frame) => frame.pageIndex === pageIndex);
305
+ }
306
+ function wrapBestBlock(blocks, rect, pageIndex, descriptor) {
307
+ let bestIndex = -1;
308
+ let bestArea = 0;
309
+ blocks.forEach((block, index) => {
310
+ const area = blockFramesOn(block, pageIndex).reduce((sum, frame) => sum + intersectionArea(rect, frame), 0);
311
+ if (area > bestArea) {
312
+ bestArea = area;
313
+ bestIndex = index;
314
+ }
315
+ });
316
+ const start = {
317
+ kind: "constructStart",
318
+ descriptor
319
+ };
320
+ const end = { kind: "constructEnd" };
321
+ if (bestIndex >= 0) {
322
+ blocks.splice(bestIndex, 0, start);
323
+ blocks.splice(bestIndex + 2, 0, end);
324
+ } else blocks.push(start, end);
325
+ }
326
+ function appendAnnotationConstructs(blocks, page, pageIndex) {
327
+ (page.annotations ?? []).forEach((annotation, annotIndex) => {
328
+ const key = `pdf-annot-${String(pageIndex)}-${String(annotIndex)}`;
329
+ blocks.push({
330
+ kind: "constructStart",
331
+ descriptor: {
332
+ kind: "anchor",
333
+ anchorType: "comment",
334
+ name: key,
335
+ definition: key,
336
+ ...annotation.source !== void 0 ? { source: annotation.source } : {}
337
+ }
338
+ }, { kind: "constructEnd" });
339
+ });
340
+ }
341
+ function controlTypeOf(field) {
342
+ if (field.fieldType === "text") return "plainText";
343
+ if (field.fieldType === "button") return "button";
344
+ if (field.fieldType === "combobox") return "comboBox";
345
+ if (field.fieldType === "listbox") return "dropDown";
346
+ return "checkbox";
347
+ }
348
+ function appendFormFieldConstructs(blocks, form, pageIndex) {
349
+ const visit = (fields) => {
350
+ for (const field of fields) {
351
+ visit(field.children);
352
+ if (field.fieldType === "group" || field.fieldType === "signature") continue;
353
+ for (const widget of field.widgets) {
354
+ if (widget.pageIndex !== pageIndex) continue;
355
+ wrapBestBlock(blocks, widget, pageIndex, {
356
+ kind: "contentControl",
357
+ controlType: controlTypeOf(field),
358
+ tag: field.name,
359
+ ...field.alias !== void 0 ? { alias: field.alias } : {},
360
+ ...field.readOnly === true ? { lock: "content" } : {},
361
+ ...field.value !== void 0 ? { value: field.value } : {},
362
+ ...field.checked !== void 0 ? { checked: field.checked } : {},
363
+ ...field.options !== void 0 ? { options: [...field.options] } : {}
364
+ });
365
+ }
366
+ }
367
+ };
368
+ visit(form ?? []);
217
369
  }
218
370
  function clusterIntoParagraphs(lines) {
219
371
  if (lines.length === 0) return [];
@@ -230,6 +382,7 @@ function clusterIntoParagraphs(lines) {
230
382
  }
231
383
  function startsNewParagraph(prev, next, modalSpacing, dominantLeftX) {
232
384
  if (prev.baselineY - next.baselineY > PARAGRAPH_GAP_MULTIPLIER * modalSpacing) return true;
385
+ if (!fontSizesClose(prev.items[0].sizePt, next.items[0].sizePt)) return true;
233
386
  const nextLeft = next.items[0].xPt;
234
387
  const prevLeft = prev.items[0].xPt;
235
388
  const emPt = next.items[0].sizePt;
@@ -238,12 +391,16 @@ function startsNewParagraph(prev, next, modalSpacing, dominantLeftX) {
238
391
  return nextIndented && prevAtMargin;
239
392
  }
240
393
  const LEFT_ALIGN_TOLERANCE_PT = 2;
241
- function paragraphToContentParagraph(paragraph, pageIndex) {
394
+ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
242
395
  const dominantLeftX = modeOf(paragraph.lines.map((l) => l.items[0].xPt), 1);
243
396
  const result = {
244
397
  kind: "paragraph",
245
398
  runs: [],
246
- alignment: paragraph.lines.every((l) => Math.abs(l.items[0].xPt - dominantLeftX) <= LEFT_ALIGN_TOLERANCE_PT) ? "left" : void 0
399
+ alignment: paragraph.lines.every((l) => Math.abs(l.items[0].xPt - dominantLeftX) <= LEFT_ALIGN_TOLERANCE_PT) ? "left" : void 0,
400
+ ...headingLevel !== void 0 ? {
401
+ styleId: `Heading${String(headingLevel)}`,
402
+ headingLevel
403
+ } : {}
247
404
  };
248
405
  paragraph.lines.forEach((line, lineIndex) => {
249
406
  require_layout_shared.stampFrame(result, pageIndex, lineBox(line, pageIndex));
@@ -253,6 +410,12 @@ function paragraphToContentParagraph(paragraph, pageIndex) {
253
410
  }
254
411
  pushRunsForLine(result.runs, line, pageIndex);
255
412
  });
413
+ if (headingLevel !== void 0) result.runs = result.runs.map((run) => {
414
+ if (run.bold !== true) return run;
415
+ const withoutBold = { ...run };
416
+ delete withoutBold.bold;
417
+ return withoutBold;
418
+ });
256
419
  return result;
257
420
  }
258
421
  function reconstructPresentation(doc, options) {
@@ -1,4 +1,4 @@
1
- import { CellTypeInferenceSink } from "./cell-typing.cjs";
1
+ import { i as CellTypeInferenceSink } from "../cell-typing-CEWlywIc.cjs";
2
2
  import { ContentDocument } from "document-schema.js";
3
3
  import { LayoutDocument } from "pdf-codec";
4
4
  //#region src/layout/reconstruct.d.ts
@@ -1,4 +1,4 @@
1
- import { CellTypeInferenceSink } from "./cell-typing.js";
1
+ import { i as CellTypeInferenceSink } from "../cell-typing-CEWlywIc.js";
2
2
  import { ContentDocument } from "document-schema.js";
3
3
  import { LayoutDocument } from "pdf-codec";
4
4
  //#region src/layout/reconstruct.d.ts
@@ -4,7 +4,8 @@ import { inferCellValue } from "./cell-typing.js";
4
4
  import { stampFrame } from "./shared.js";
5
5
  import { throwIfAborted } from "../ports/abort.js";
6
6
  import { detectGridLattice, findColumnIndex, findRowIndex } from "./lattice.js";
7
- import { STANDARD_METRICS, resolveStandardFont } from "pdf-codec";
7
+ import { resolveStandardFont } from "pdf-codec/fonts";
8
+ import { STANDARD_METRICS } from "pdf-codec/afm-widths";
8
9
  //#region src/layout/reconstruct.ts
9
10
  const ZERO_MARGINS = {
10
11
  topPt: 0,
@@ -40,8 +41,8 @@ function textItemVerticalExtent(item) {
40
41
  function textItemToContentRun(item) {
41
42
  return {
42
43
  text: item.text,
43
- bold: item.font.weight === "bold" ? true : void 0,
44
- italic: item.font.style === "italic" ? true : void 0,
44
+ ...item.font.weight === "bold" ? { bold: true } : {},
45
+ ...item.font.style === "italic" ? { italic: true } : {},
45
46
  fontFamily: item.font.family,
46
47
  sizePt: item.sizePt,
47
48
  color: item.color
@@ -132,33 +133,70 @@ function estimateModalLineSpacing(lines) {
132
133
  }
133
134
  function reconstructWordprocessing(doc, options) {
134
135
  const signal = options?.signal;
136
+ doc = dropHiddenLayerContent(doc);
137
+ const headingLevels = headingSizeLevels(doc);
135
138
  const sections = [];
136
139
  let currentGroup = [];
137
140
  let groupStartPageIndex = 0;
138
141
  for (const page of doc.pages) {
139
142
  throwIfAborted(signal);
140
143
  if (currentGroup.length > 0 && !samePageSize(currentGroup[0], page)) {
141
- sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images));
144
+ sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
142
145
  groupStartPageIndex += currentGroup.length;
143
146
  currentGroup = [];
144
147
  }
145
148
  currentGroup.push(page);
146
149
  }
147
- if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images));
150
+ if (currentGroup.length > 0) sections.push(buildSection(currentGroup, groupStartPageIndex, doc.images, headingLevels, doc.form));
148
151
  return {
149
152
  kind: "wordprocessing",
150
153
  metadata: doc.metadata,
151
154
  sections
152
155
  };
153
156
  }
157
+ function layerOf(item) {
158
+ return item.kind === "link" || item.kind === "internalLink" ? void 0 : item.layer;
159
+ }
160
+ function dropHiddenLayerContent(doc) {
161
+ const hidden = new Set((doc.layers ?? []).filter((layer) => layer.visible !== true).map((layer) => layer.name));
162
+ if (hidden.size === 0) return doc;
163
+ return {
164
+ ...doc,
165
+ pages: doc.pages.map((page) => ({
166
+ ...page,
167
+ items: page.items.filter((item) => layerOf(item) === void 0 || !hidden.has(layerOf(item)))
168
+ }))
169
+ };
170
+ }
171
+ const HEADING_MIN_SIZE_DELTA_PT = 2;
172
+ const MAX_HEADING_LEVEL = 6;
173
+ function headingSizeLevels(doc) {
174
+ const sizes = [];
175
+ for (const page of doc.pages) for (const item of page.items) if (item.kind === "text") sizes.push(item.sizePt);
176
+ if (sizes.length === 0) return /* @__PURE__ */ new Map();
177
+ const bodySizePt = modeOf(sizes, .5);
178
+ const headingBuckets = /* @__PURE__ */ new Set();
179
+ for (const size of sizes) {
180
+ const bucket = Math.round(size / .5) * .5;
181
+ if (bucket - bodySizePt >= HEADING_MIN_SIZE_DELTA_PT) headingBuckets.add(bucket);
182
+ }
183
+ const levels = /* @__PURE__ */ new Map();
184
+ [...headingBuckets].sort((a, b) => b - a).forEach((bucket, index) => {
185
+ levels.set(bucket, Math.min(index + 1, MAX_HEADING_LEVEL));
186
+ });
187
+ return levels;
188
+ }
189
+ function headingLevelOf(paragraph, levels) {
190
+ return levels.get(modeOf(paragraph.lines.flatMap((line) => line.items.map((item) => item.sizePt)), .5));
191
+ }
154
192
  function samePageSize(a, b) {
155
193
  return a.widthPt === b.widthPt && a.heightPt === b.heightPt;
156
194
  }
157
- function buildSection(pages, startPageIndex, images) {
195
+ function buildSection(pages, startPageIndex, images, headingLevels, form) {
158
196
  const blocks = [];
159
197
  pages.forEach((page, i) => {
160
198
  if (i > 0) blocks.push({ kind: "pageBreak" });
161
- blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images));
199
+ blocks.push(...reconstructPageBlocks(page, startPageIndex + i, images, headingLevels, form));
162
200
  });
163
201
  return {
164
202
  pageSize: {
@@ -169,7 +207,7 @@ function buildSection(pages, startPageIndex, images) {
169
207
  blocks
170
208
  };
171
209
  }
172
- function reconstructPageBlocks(page, pageIndex, images) {
210
+ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form) {
173
211
  const recoveredTable = recoverTable(page, pageIndex);
174
212
  const consumedText = recoveredTable?.consumedText;
175
213
  const textItems = page.items.filter((i) => i.kind === "text" && consumedText?.has(i) !== true);
@@ -178,7 +216,7 @@ function reconstructPageBlocks(page, pageIndex, images) {
178
216
  const positioned = [];
179
217
  for (const paragraph of paragraphs) positioned.push({
180
218
  yPt: paragraph.lines[0].baselineY,
181
- block: paragraphToContentParagraph(paragraph, pageIndex)
219
+ block: paragraphToContentParagraph(paragraph, pageIndex, headingLevelOf(paragraph, headingLevels))
182
220
  });
183
221
  for (const img of imageItems) {
184
222
  const asset = images[img.imageId];
@@ -212,7 +250,121 @@ function reconstructPageBlocks(page, pageIndex, images) {
212
250
  block: recoveredVectors.block
213
251
  });
214
252
  positioned.sort((a, b) => b.yPt - a.yPt);
215
- return positioned.map((p) => p.block);
253
+ const blocks = positioned.map((p) => p.block);
254
+ reconcileLinks(blocks, page, pageIndex);
255
+ appendAnnotationConstructs(blocks, page, pageIndex);
256
+ appendFormFieldConstructs(blocks, form, pageIndex);
257
+ return blocks;
258
+ }
259
+ function framesIntersect(a, b) {
260
+ 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
+ }
262
+ function intersectionArea(a, b) {
263
+ const width = Math.min(a.xPt + a.widthPt, b.xPt + b.widthPt) - Math.max(a.xPt, b.xPt);
264
+ const height = Math.min(a.yPt + a.heightPt, b.yPt + b.heightPt) - Math.max(a.yPt, b.yPt);
265
+ return width > 0 && height > 0 ? width * height : 0;
266
+ }
267
+ function reconcileLinks(blocks, page, pageIndex) {
268
+ for (const item of page.items) {
269
+ if (item.kind !== "link" && item.kind !== "internalLink") continue;
270
+ if (item.kind === "link") {
271
+ let matchedRun = false;
272
+ for (const block of blocks) {
273
+ if (block.kind !== "paragraph") continue;
274
+ for (const run of block.runs) if ((run.frames ?? []).some((frame) => frame.pageIndex === pageIndex && framesIntersect(item, frame))) {
275
+ run.hyperlink = item.uri;
276
+ matchedRun = true;
277
+ }
278
+ }
279
+ if (matchedRun) continue;
280
+ }
281
+ wrapBestBlock(blocks, item, pageIndex, linkDescriptor(item));
282
+ }
283
+ }
284
+ function linkDescriptor(item) {
285
+ if (item.kind === "link") return {
286
+ kind: "link",
287
+ target: {
288
+ kind: "external",
289
+ uri: item.uri
290
+ },
291
+ ...item.title !== void 0 ? { title: item.title } : {}
292
+ };
293
+ return {
294
+ kind: "link",
295
+ target: {
296
+ kind: "internal",
297
+ anchor: item.destination
298
+ },
299
+ ...item.title !== void 0 ? { title: item.title } : {}
300
+ };
301
+ }
302
+ function blockFramesOn(block, pageIndex) {
303
+ return block.kind === "constructStart" || block.kind === "constructEnd" ? [] : (block.frames ?? []).filter((frame) => frame.pageIndex === pageIndex);
304
+ }
305
+ function wrapBestBlock(blocks, rect, pageIndex, descriptor) {
306
+ let bestIndex = -1;
307
+ let bestArea = 0;
308
+ blocks.forEach((block, index) => {
309
+ const area = blockFramesOn(block, pageIndex).reduce((sum, frame) => sum + intersectionArea(rect, frame), 0);
310
+ if (area > bestArea) {
311
+ bestArea = area;
312
+ bestIndex = index;
313
+ }
314
+ });
315
+ const start = {
316
+ kind: "constructStart",
317
+ descriptor
318
+ };
319
+ const end = { kind: "constructEnd" };
320
+ if (bestIndex >= 0) {
321
+ blocks.splice(bestIndex, 0, start);
322
+ blocks.splice(bestIndex + 2, 0, end);
323
+ } else blocks.push(start, end);
324
+ }
325
+ function appendAnnotationConstructs(blocks, page, pageIndex) {
326
+ (page.annotations ?? []).forEach((annotation, annotIndex) => {
327
+ const key = `pdf-annot-${String(pageIndex)}-${String(annotIndex)}`;
328
+ blocks.push({
329
+ kind: "constructStart",
330
+ descriptor: {
331
+ kind: "anchor",
332
+ anchorType: "comment",
333
+ name: key,
334
+ definition: key,
335
+ ...annotation.source !== void 0 ? { source: annotation.source } : {}
336
+ }
337
+ }, { kind: "constructEnd" });
338
+ });
339
+ }
340
+ function controlTypeOf(field) {
341
+ if (field.fieldType === "text") return "plainText";
342
+ if (field.fieldType === "button") return "button";
343
+ if (field.fieldType === "combobox") return "comboBox";
344
+ if (field.fieldType === "listbox") return "dropDown";
345
+ return "checkbox";
346
+ }
347
+ function appendFormFieldConstructs(blocks, form, pageIndex) {
348
+ const visit = (fields) => {
349
+ for (const field of fields) {
350
+ visit(field.children);
351
+ if (field.fieldType === "group" || field.fieldType === "signature") continue;
352
+ for (const widget of field.widgets) {
353
+ if (widget.pageIndex !== pageIndex) continue;
354
+ wrapBestBlock(blocks, widget, pageIndex, {
355
+ kind: "contentControl",
356
+ controlType: controlTypeOf(field),
357
+ tag: field.name,
358
+ ...field.alias !== void 0 ? { alias: field.alias } : {},
359
+ ...field.readOnly === true ? { lock: "content" } : {},
360
+ ...field.value !== void 0 ? { value: field.value } : {},
361
+ ...field.checked !== void 0 ? { checked: field.checked } : {},
362
+ ...field.options !== void 0 ? { options: [...field.options] } : {}
363
+ });
364
+ }
365
+ }
366
+ };
367
+ visit(form ?? []);
216
368
  }
217
369
  function clusterIntoParagraphs(lines) {
218
370
  if (lines.length === 0) return [];
@@ -229,6 +381,7 @@ function clusterIntoParagraphs(lines) {
229
381
  }
230
382
  function startsNewParagraph(prev, next, modalSpacing, dominantLeftX) {
231
383
  if (prev.baselineY - next.baselineY > PARAGRAPH_GAP_MULTIPLIER * modalSpacing) return true;
384
+ if (!fontSizesClose(prev.items[0].sizePt, next.items[0].sizePt)) return true;
232
385
  const nextLeft = next.items[0].xPt;
233
386
  const prevLeft = prev.items[0].xPt;
234
387
  const emPt = next.items[0].sizePt;
@@ -237,12 +390,16 @@ function startsNewParagraph(prev, next, modalSpacing, dominantLeftX) {
237
390
  return nextIndented && prevAtMargin;
238
391
  }
239
392
  const LEFT_ALIGN_TOLERANCE_PT = 2;
240
- function paragraphToContentParagraph(paragraph, pageIndex) {
393
+ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
241
394
  const dominantLeftX = modeOf(paragraph.lines.map((l) => l.items[0].xPt), 1);
242
395
  const result = {
243
396
  kind: "paragraph",
244
397
  runs: [],
245
- alignment: paragraph.lines.every((l) => Math.abs(l.items[0].xPt - dominantLeftX) <= LEFT_ALIGN_TOLERANCE_PT) ? "left" : void 0
398
+ alignment: paragraph.lines.every((l) => Math.abs(l.items[0].xPt - dominantLeftX) <= LEFT_ALIGN_TOLERANCE_PT) ? "left" : void 0,
399
+ ...headingLevel !== void 0 ? {
400
+ styleId: `Heading${String(headingLevel)}`,
401
+ headingLevel
402
+ } : {}
246
403
  };
247
404
  paragraph.lines.forEach((line, lineIndex) => {
248
405
  stampFrame(result, pageIndex, lineBox(line, pageIndex));
@@ -252,6 +409,12 @@ function paragraphToContentParagraph(paragraph, pageIndex) {
252
409
  }
253
410
  pushRunsForLine(result.runs, line, pageIndex);
254
411
  });
412
+ if (headingLevel !== void 0) result.runs = result.runs.map((run) => {
413
+ if (run.bold !== true) return run;
414
+ const withoutBold = { ...run };
415
+ delete withoutBold.bold;
416
+ return withoutBold;
417
+ });
255
418
  return result;
256
419
  }
257
420
  function reconstructPresentation(doc, options) {
@@ -3,8 +3,8 @@ const require_mathml_layout = require("../mathml/layout.cjs");
3
3
  const require_layout_text_layout = require("./text-layout.cjs");
4
4
  let ooxml_js = require("ooxml.js");
5
5
  let document_schema_js = require("document-schema.js");
6
- let pdf_codec = require("pdf-codec");
7
6
  let byte_codec = require("byte-codec");
7
+ let pdf_codec_layout = require("pdf-codec/layout");
8
8
  //#region src/layout/shared.ts
9
9
  function stampFrame(node, pageIndex, box) {
10
10
  const frame = {
@@ -39,7 +39,7 @@ function packagePagesOf(pages) {
39
39
  }
40
40
  function layoutDocumentOf(metadata, pages, images) {
41
41
  return {
42
- formatVersion: pdf_codec.LAYOUT_FORMAT_VERSION,
42
+ formatVersion: pdf_codec_layout.LAYOUT_FORMAT_VERSION,
43
43
  metadata,
44
44
  pages,
45
45
  images
@@ -2,8 +2,8 @@ import { layoutFormula } from "../mathml/layout.js";
2
2
  import { wrapRunsToWidth } from "./text-layout.js";
3
3
  import { base64ToBytes } from "ooxml.js";
4
4
  import { COLOR_BLACK, DEFAULT_LAYOUT_FONT } from "document-schema.js";
5
- import { LAYOUT_FORMAT_VERSION } from "pdf-codec";
6
5
  import { crc32, decodePng, readJpegInfo } from "byte-codec";
6
+ import { LAYOUT_FORMAT_VERSION } from "pdf-codec/layout";
7
7
  //#region src/layout/shared.ts
8
8
  function stampFrame(node, pageIndex, box) {
9
9
  const frame = {
@@ -3,7 +3,6 @@ const require_model_formula = require("../model/formula.cjs");
3
3
  const require_latex_symbols = require("../latex/symbols.cjs");
4
4
  const require_latex_lower = require("../latex/lower.cjs");
5
5
  //#region src/markdown/math.ts
6
- const MATH_BLOCK_STYLE_ID = "MathBlock";
7
6
  const MATH_INLINE_FONT_MARKER = "Cambria Math";
8
7
  const STAND_IN_FRAME = {
9
8
  xPt: 0,
@@ -62,14 +61,13 @@ function lowerMarkdownMath(document, options) {
62
61
  });
63
62
  continue;
64
63
  }
65
- if (block.kind !== "paragraph") {
66
- out.push(block);
64
+ if (block.kind === "embeddedObject" && block.objectKind === "formula" && block.document.kind === "formula" && block.document.formula.presentation !== void 0) {
65
+ const formulaBlock = lowerOne(block.document.formula.presentation.latex, MATH_BLOCK_SOURCE);
66
+ out.push(formulaBlock ?? block);
67
67
  continue;
68
68
  }
69
- if (block.styleId === MATH_BLOCK_STYLE_ID) {
70
- const latex = block.runs.map((run) => run.text).join("");
71
- const formulaBlock = lowerOne(latex, MATH_BLOCK_SOURCE);
72
- out.push(formulaBlock ?? block);
69
+ if (block.kind !== "paragraph") {
70
+ out.push(block);
73
71
  continue;
74
72
  }
75
73
  const extraction = extractInlineMath(block.runs);