js.documents 14.6.0 → 14.6.2

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
@@ -1011,7 +1011,7 @@ Read as **row → column**. `✓` lossless, `~` bounded, `✗` lossy, `✗✗` s
1011
1011
 
1012
1012
  **odf → PDF and embedded formulas** render faithful mathematical typesetting through STIX Two Math: real box-model layout, per-glyph metrics, font-wide constants from the `MATH` table, stretchy fences and braces via `MathVariants`. `pdfToOdf` is not attempted — recovering a semantic operator tree from glyphs is OCR-adjacent.
1013
1013
 
1014
- **PDF → docx/pptx/odt/odp** is best-effort reconstruction from geometry. Reading order, font properties, page count survive; paragraph boundaries are inferred from baseline spacing. Tables recover only from a real gridline lattice. Vector primitives recover into a nested drawing document.
1014
+ **PDF → docx/pptx/odt/odp** is best-effort reconstruction from geometry. Reading order, font properties, page count survive; paragraph boundaries are inferred from baseline spacing. Text set in columns is read one column at a time: a vertical strip that no text touches over a run of lines, with running prose on both sides (a measure of at least twenty characters, lines that wrap, word gaps narrower than the strip), splits the page there before lines are clustered, so two columns whose baselines line up no longer interleave line by line. Lines crossing the strip (a full-width heading, a boxed passage) are read in page order between the column bands above and below them, and images and recovered tables are placed by the column they sit in. A table's rows are still read across, since its columns are too narrow or too ragged to pass as prose; a page with no such strip, or with text whose horizontal extent the reader did not report, reads exactly as clustering the whole page always did (`src/layout/reading-order.ts`). Tables recover only from a real gridline lattice. Vector primitives recover into a nested drawing document.
1015
1015
 
1016
1016
  **PDF → odg** is near-1:1 mapping (no clustering needed). Kind narrows upstream: rotated rects, freeform curves, multi-subpath figures become `path`.
1017
1017
 
@@ -23,7 +23,7 @@ const require_csv_write = require("./csv/write.cjs");
23
23
  const require_read$1 = require("./read-CahCZMwG.cjs");
24
24
  const require_svg_write = require("./svg/write.cjs");
25
25
  const require_ports_abort = require("./ports/abort.cjs");
26
- const require_reconstruct_presentations_prod = require("./reconstruct-presentations-prod-CA4no_aw.cjs");
26
+ const require_reconstruct_presentations_prod = require("./reconstruct-presentations-prod-CsvGlvP2.cjs");
27
27
  const require_convert_pdf_package_tables = require("./convert/pdf-package-tables.cjs");
28
28
  const require_convert_variant_bridges = require("./convert/variant-bridges.cjs");
29
29
  const require_convert_capability = require("./convert/capability.cjs");
@@ -23,7 +23,7 @@ import { buildCsvText } from "./csv/write.js";
23
23
  import { i as readSvgContent } from "./read-BSH--XB4.js";
24
24
  import { buildSvgText } from "./svg/write.js";
25
25
  import { throwIfAborted } from "./ports/abort.js";
26
- import { _ as reconstructWordprocessing, g as reconstructSpreadsheet, i as reconstructPresentation, r as reconstructDrawing } from "./reconstruct-presentations-prod-CC8ct-5O.js";
26
+ import { _ as reconstructWordprocessing, g as reconstructSpreadsheet, i as reconstructPresentation, r as reconstructDrawing } from "./reconstruct-presentations-prod-D1KbGPxa.js";
27
27
  import { stampPdfPackageTables } from "./convert/pdf-package-tables.js";
28
28
  import { drawingToPresentation, presentationToDrawing, presentationToWordprocessing, spreadsheetToWordprocessing, wordprocessingToPresentation } from "./convert/variant-bridges.js";
29
29
  import { UnsupportedConversionError } from "./convert/capability.js";
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
2
+ const require_composition_plan = require("../composition-plan-DJQgPvBj.cjs");
3
3
  exports.buildCompositionGraph = require_composition_plan.buildCompositionGraph;
4
4
  exports.isContentFormat = require_composition_plan.isContentFormat;
5
5
  exports.isSourceContentFormat = require_composition_plan.isSourceContentFormat;
@@ -1,2 +1,2 @@
1
- import { a as shortestPath, i as resolveCompositionPlan, n as isContentFormat, r as isSourceContentFormat, t as buildCompositionGraph } from "../composition-plan-DQ8lPKIW.js";
1
+ import { a as shortestPath, i as resolveCompositionPlan, n as isContentFormat, r as isSourceContentFormat, t as buildCompositionGraph } from "../composition-plan-DMTkmk8n.js";
2
2
  export { buildCompositionGraph, isContentFormat, isSourceContentFormat, resolveCompositionPlan, shortestPath };
@@ -7,7 +7,7 @@ const require_ports_abort = require("../ports/abort.cjs");
7
7
  const require_sheets = require("../sheets-BQCpNXsE.cjs");
8
8
  const require_layout_drawing = require("../layout/drawing.cjs");
9
9
  const require_convert_capability = require("./capability.cjs");
10
- const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
10
+ const require_composition_plan = require("../composition-plan-DJQgPvBj.cjs");
11
11
  let document_schema_js = require("document-schema.js");
12
12
  let pdf_codec = require("pdf-codec");
13
13
  //#region src/convert/composition-to-pdf.ts
@@ -6,7 +6,7 @@ import { throwIfAborted } from "../ports/abort.js";
6
6
  import { l as convertSpreadsheetToLayout } from "../sheets-DWDvGN-g.js";
7
7
  import { convertDrawingToLayout } from "../layout/drawing.js";
8
8
  import { UnsupportedConversionError } from "./capability.js";
9
- import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, g as executeBridge, i as resolveCompositionPlan, s as FORMAT_NODES, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-DQ8lPKIW.js";
9
+ import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, g as executeBridge, i as resolveCompositionPlan, s as FORMAT_NODES, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-DMTkmk8n.js";
10
10
  import { assembleTree } from "document-schema.js";
11
11
  import { createFontMeasurer, createFontRegistry, loadMathFont, writePdf } from "pdf-codec";
12
12
  //#region src/convert/composition-to-pdf.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
2
+ const require_composition_plan = require("../composition-plan-DJQgPvBj.cjs");
3
3
  exports.CONTENT_FORMATS = require_composition_plan.CONTENT_FORMATS;
4
4
  exports.FORMAT_NODES = require_composition_plan.FORMAT_NODES;
5
5
  exports.LAYOUT_CAPABLE = require_composition_plan.LAYOUT_CAPABLE;
@@ -1,2 +1,2 @@
1
- import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, f as RECONSTRUCTORS, g as executeBridge, h as convertDocumentFromPdf, l as MAX_CONVERSION_PATH_NODES, m as TRANSFORMS, o as CONTENT_FORMATS, p as THROUGH_PDF_EDGE_COST, s as FORMAT_NODES, u as READ_ONLY_CONTENT_FORMATS, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-DQ8lPKIW.js";
1
+ import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, f as RECONSTRUCTORS, g as executeBridge, h as convertDocumentFromPdf, l as MAX_CONVERSION_PATH_NODES, m as TRANSFORMS, o as CONTENT_FORMATS, p as THROUGH_PDF_EDGE_COST, s as FORMAT_NODES, u as READ_ONLY_CONTENT_FORMATS, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-DMTkmk8n.js";
2
2
  export { CONTENT_FORMATS, FORMAT_NODES, LAYOUT_CAPABLE, MAX_CONVERSION_PATH_NODES, READ_ONLY_CONTENT_FORMATS, READ_ONLY_FORMAT_NODES, RECONSTRUCTORS, THROUGH_PDF_EDGE_COST, TRANSFORMS, convertDocumentFromPdf, executeBridge, executeFromPdf, isReadOnlyContentFormat, isTextFormatNode, runCompositionPlan };
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CA4no_aw.cjs");
2
+ const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CsvGlvP2.cjs");
3
3
  const require_convert_pdf_package_tables = require("./pdf-package-tables.cjs");
4
- const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
4
+ const require_composition_plan = require("../composition-plan-DJQgPvBj.cjs");
5
5
  const require_codecs_read = require("../codecs/read.cjs");
6
6
  let document_schema_js = require("document-schema.js");
7
7
  let pdf_codec_read = require("pdf-codec/read");
@@ -1,6 +1,6 @@
1
- import { _ as reconstructWordprocessing } from "../reconstruct-presentations-prod-CC8ct-5O.js";
1
+ import { _ as reconstructWordprocessing } from "../reconstruct-presentations-prod-D1KbGPxa.js";
2
2
  import { stampPdfPackageTables } from "./pdf-package-tables.js";
3
- import { h as convertDocumentFromPdf } from "../composition-plan-DQ8lPKIW.js";
3
+ import { h as convertDocumentFromPdf } from "../composition-plan-DMTkmk8n.js";
4
4
  import { CONTENT_READERS } from "../codecs/read.js";
5
5
  import { assembleTree } from "document-schema.js";
6
6
  import { readPdf } from "pdf-codec/read";
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_convert_capability = require("./capability.cjs");
3
3
  const require_convert_port = require("./port.cjs");
4
- const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
4
+ const require_composition_plan = require("../composition-plan-DJQgPvBj.cjs");
5
5
  const require_convert_composition_to_pdf = require("./composition-to-pdf.cjs");
6
6
  const require_convert_convert = require("./convert.cjs");
7
7
  //#region src/convert/local.ts
@@ -1,6 +1,6 @@
1
1
  import { UnsupportedConversionError } from "./capability.js";
2
2
  import { DOCUMENT_FORMATS } from "./port.js";
3
- import { i as resolveCompositionPlan } from "../composition-plan-DQ8lPKIW.js";
3
+ import { i as resolveCompositionPlan } from "../composition-plan-DMTkmk8n.js";
4
4
  import { convertDocument } from "./composition-to-pdf.js";
5
5
  import { odfToPdf } from "./convert.js";
6
6
  //#region src/convert/local.ts
package/dist/index.cjs CHANGED
@@ -93,7 +93,7 @@ const require_ports_abort = require("./ports/abort.cjs");
93
93
  const require_sheets = require("./sheets-BQCpNXsE.cjs");
94
94
  const require_layout_drawing = require("./layout/drawing.cjs");
95
95
  const require_layout_lattice = require("./layout/lattice.cjs");
96
- const require_reconstruct_presentations_prod = require("./reconstruct-presentations-prod-CA4no_aw.cjs");
96
+ const require_reconstruct_presentations_prod = require("./reconstruct-presentations-prod-CsvGlvP2.cjs");
97
97
  const require_hsqldb_script = require("./hsqldb/script.cjs");
98
98
  const require_odb_csv = require("./odb/csv.cjs");
99
99
  const require_hsqldb_rowformat = require("./hsqldb/rowformat.cjs");
@@ -107,7 +107,7 @@ const require_firebird_backup = require("./firebird/backup.cjs");
107
107
  const require_odb_read = require("./odb/read.cjs");
108
108
  const require_odb_spreadsheet = require("./odb/spreadsheet.cjs");
109
109
  const require_convert_port = require("./convert/port.cjs");
110
- const require_composition_plan = require("./composition-plan-CSHdRyji.cjs");
110
+ const require_composition_plan = require("./composition-plan-DJQgPvBj.cjs");
111
111
  const require_convert_composition_to_pdf = require("./convert/composition-to-pdf.cjs");
112
112
  const require_convert_convert = require("./convert/convert.cjs");
113
113
  const require_package_codec = require("./package-codec.cjs");
package/dist/index.js CHANGED
@@ -92,7 +92,7 @@ import { throwIfAborted } from "./ports/abort.js";
92
92
  import { l as convertSpreadsheetToLayout } from "./sheets-DWDvGN-g.js";
93
93
  import { convertDrawingToLayout } from "./layout/drawing.js";
94
94
  import { detectGridLattice } from "./layout/lattice.js";
95
- import { _ as reconstructWordprocessing, g as reconstructSpreadsheet, i as reconstructPresentation, r as reconstructDrawing } from "./reconstruct-presentations-prod-CC8ct-5O.js";
95
+ import { _ as reconstructWordprocessing, g as reconstructSpreadsheet, i as reconstructPresentation, r as reconstructDrawing } from "./reconstruct-presentations-prod-D1KbGPxa.js";
96
96
  import { HsqldbScriptParseError, displayTextFor, parseHsqldbScript } from "./hsqldb/script.js";
97
97
  import { OdbTableNotFoundError, OdbTableNotSpecifiedError } from "./odb/csv.js";
98
98
  import { HsqldbRowFormatError } from "./hsqldb/rowformat.js";
@@ -106,7 +106,7 @@ import { FirebirdBackupFormatError, SUPPORTED_BACKUP_FORMAT_VERSION, readFirebir
106
106
  import { OdbNoEmbeddedDataSourceError, OdbUnsupportedFormatError, readOdbTables } from "./odb/read.js";
107
107
  import { odbTablesToSpreadsheetDocument } from "./odb/spreadsheet.js";
108
108
  import { DOCUMENT_FORMATS, DocumentFormatSchema } from "./convert/port.js";
109
- import { i as resolveCompositionPlan } from "./composition-plan-DQ8lPKIW.js";
109
+ import { i as resolveCompositionPlan } from "./composition-plan-DMTkmk8n.js";
110
110
  import { convertDocument } from "./convert/composition-to-pdf.js";
111
111
  import { OdmUnresolvedSectionError, csvToMarkdown, csvToOds, csvToPdf, csvToXlsx, docToMarkdown, docToPdf, docxToMarkdown, docxToOdt, docxToPdf, docxToPptx, epubToPdf, markdownToCsv, markdownToDocx, markdownToOdt, markdownToPdf, markdownToXlsx, odbReportToDocx, odbReportToOdt, odbReportToPdf, odbToCsv, odbToXlsx, odfToPdf, odgToPdf, odgToSvg, odmToPdf, odpToOdt, odpToPdf, odpToPptx, odsToCsv, odsToPdf, odsToXlsx, odtToDocx, odtToMarkdown, odtToOdp, odtToPdf, pptToMarkdown, pptToPdf, pptxToDocx, pptxToOdp, pptxToPdf, rtfToPdf, svgToOdg, svgToPdf, xlsToMarkdown, xlsToPdf, xlsxToCsv, xlsxToMarkdown, xlsxToOds, xlsxToPdf } from "./convert/convert.js";
112
112
  import { UnsupportedPackageFormatError, decodeDocumentPackage, decodeOdbPackage, encodeDocumentPackage } from "./package-codec.js";
@@ -0,0 +1,202 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_layout_reconstruct_lines = require("./reconstruct-lines.cjs");
3
+ let pdf_codec_fonts = require("pdf-codec/fonts");
4
+ let pdf_codec_afm_widths = require("pdf-codec/afm-widths");
5
+ //#region src/layout/reading-order.ts
6
+ const MIN_COLUMN_LINES = 3;
7
+ const MIN_PROSE_MEASURE_CHARS = 20;
8
+ const SPACE_CHAR_CODE = 32;
9
+ const AFM_UNITS_PER_EM = 1e3;
10
+ /**
11
+ * Splits a page's text items into reading-order flows at the column gutters the page's geometry states.
12
+ * @param items - The page's text items in content-stream order, after any table recovery has removed the text it claimed.
13
+ * @returns The flows in reading order, and a locator placing anything else among them. A page with no gutter yields one flow whose lines are `clusterIntoLines(items)`.
14
+ */
15
+ function readingOrder(items) {
16
+ const bands = segment(items);
17
+ const regions = [];
18
+ collectRegions(bands, [], regions);
19
+ return {
20
+ regions,
21
+ locate: (xPt, yPt) => locate(bands, xPt, yPt)
22
+ };
23
+ }
24
+ /**
25
+ * Orders two placed blocks for reading: by reading-order path first, then top to bottom by y.
26
+ * @returns Negative when `a` is read first, positive when `b` is, zero when neither precedes the other.
27
+ */
28
+ function compareReadingPosition(a, b) {
29
+ const length = Math.min(a.path.length, b.path.length);
30
+ for (let i = 0; i < length; i++) {
31
+ const difference = a.path[i] - b.path[i];
32
+ if (difference !== 0) return difference;
33
+ }
34
+ return a.path.length - b.path.length || b.yPt - a.yPt;
35
+ }
36
+ function segment(items) {
37
+ const lines = require_layout_reconstruct_lines.clusterIntoLines(items);
38
+ const split = findColumnSplit(lines);
39
+ if (split === void 0) return [flowBand(lines)];
40
+ const before = lines.slice(0, split.startLine);
41
+ const band = lines.slice(split.startLine, split.endLine);
42
+ const after = lines.slice(split.endLine);
43
+ return [
44
+ ...before.length > 0 ? segment(itemsOf(before)) : [],
45
+ {
46
+ kind: "columns",
47
+ gutterXPt: (split.loPt + split.hiPt) / 2,
48
+ left: segment(split.left),
49
+ right: segment(split.right),
50
+ topPt: band[0].baselineY,
51
+ bottomPt: band.at(-1).baselineY
52
+ },
53
+ ...after.length > 0 ? segment(itemsOf(after)) : []
54
+ ];
55
+ }
56
+ function flowBand(lines) {
57
+ return {
58
+ kind: "flow",
59
+ lines,
60
+ topPt: lines[0]?.baselineY ?? Number.POSITIVE_INFINITY,
61
+ bottomPt: lines.at(-1)?.baselineY ?? Number.NEGATIVE_INFINITY
62
+ };
63
+ }
64
+ function itemsOf(lines) {
65
+ return lines.flatMap((line) => line.items);
66
+ }
67
+ function collectRegions(bands, prefix, into) {
68
+ bands.forEach((band, index) => {
69
+ if (band.kind === "flow") into.push({
70
+ lines: band.lines,
71
+ path: [...prefix, index]
72
+ });
73
+ else {
74
+ collectRegions(band.left, [
75
+ ...prefix,
76
+ index,
77
+ 0
78
+ ], into);
79
+ collectRegions(band.right, [
80
+ ...prefix,
81
+ index,
82
+ 1
83
+ ], into);
84
+ }
85
+ });
86
+ }
87
+ function locate(bands, xPt, yPt) {
88
+ let index = bands.length - 1;
89
+ for (let i = 0; i + 1 < bands.length; i++) if (yPt >= (bands[i].bottomPt + bands[i + 1].topPt) / 2) {
90
+ index = i;
91
+ break;
92
+ }
93
+ const band = bands[index];
94
+ if (band.kind === "flow") return [index];
95
+ const side = xPt < band.gutterXPt ? 0 : 1;
96
+ return [
97
+ index,
98
+ side,
99
+ ...locate(side === 0 ? band.left : band.right, xPt, yPt)
100
+ ];
101
+ }
102
+ function findColumnSplit(lines) {
103
+ const items = itemsOf(lines);
104
+ if (lines.length < MIN_COLUMN_LINES || !items.every(isMeasurable)) return;
105
+ const runs = gutterRuns(lines, Math.min(...items.map(spaceAdvancePt))).sort((a, b) => b.endLine - b.startLine - (a.endLine - a.startLine) || b.hiPt - b.loPt - (a.hiPt - a.loPt) || a.startLine - b.startLine);
106
+ for (const run of runs) {
107
+ const runItems = itemsOf(lines.slice(run.startLine, run.endLine));
108
+ if (run.hiPt - run.loPt <= Math.max(...runItems.map(spaceAdvancePt))) continue;
109
+ const left = runItems.filter((item) => rightEdge(item) <= run.loPt);
110
+ const right = runItems.filter((item) => item.xPt >= run.hiPt);
111
+ if (isRunningProse(left, run.hiPt - run.loPt) && isRunningProse(right, run.hiPt - run.loPt)) return {
112
+ ...run,
113
+ left,
114
+ right
115
+ };
116
+ }
117
+ }
118
+ function isMeasurable(item) {
119
+ return item.widthPt !== void 0 && item.writingMode === void 0 && (item.rotationDeg ?? 0) === 0;
120
+ }
121
+ function rightEdge(item) {
122
+ return item.xPt + (item.widthPt ?? 0);
123
+ }
124
+ function spaceAdvancePt(item) {
125
+ const { standardName } = (0, pdf_codec_fonts.resolveStandardFont)(item.font.family, item.font.weight === "bold", item.font.style === "italic");
126
+ const advance = (0, pdf_codec_afm_widths.afmWidthOfCode)(standardName, SPACE_CHAR_CODE);
127
+ if (advance === void 0) throw new Error(`${standardName} has no AFM width for the space glyph`);
128
+ return advance / AFM_UNITS_PER_EM * item.sizePt;
129
+ }
130
+ function freeSpans(line) {
131
+ const sorted = [...line.items].sort((a, b) => a.xPt - b.xPt);
132
+ const spans = [];
133
+ let reachedPt = Number.NEGATIVE_INFINITY;
134
+ for (const item of sorted) {
135
+ if (item.xPt > reachedPt) spans.push({
136
+ loPt: reachedPt,
137
+ hiPt: item.xPt
138
+ });
139
+ reachedPt = Math.max(reachedPt, rightEdge(item));
140
+ }
141
+ spans.push({
142
+ loPt: reachedPt,
143
+ hiPt: Number.POSITIVE_INFINITY
144
+ });
145
+ return spans;
146
+ }
147
+ function gutterRuns(lines, minWidthPt) {
148
+ const runs = [];
149
+ let open = /* @__PURE__ */ new Map();
150
+ const record = (span, endLine) => {
151
+ if (Number.isFinite(span.loPt) && Number.isFinite(span.hiPt) && endLine - span.startLine >= MIN_COLUMN_LINES) runs.push({
152
+ ...span,
153
+ endLine
154
+ });
155
+ };
156
+ lines.forEach((line, lineIndex) => {
157
+ const next = /* @__PURE__ */ new Map();
158
+ const keep = (loPt, hiPt, startLine) => {
159
+ if (hiPt - loPt <= minWidthPt) return;
160
+ const key = `${String(loPt)}|${String(hiPt)}`;
161
+ const existing = next.get(key);
162
+ if (existing === void 0 || startLine < existing.startLine) next.set(key, {
163
+ loPt,
164
+ hiPt,
165
+ startLine
166
+ });
167
+ };
168
+ const spans = freeSpans(line);
169
+ for (const strip of open.values()) for (const span of spans) keep(Math.max(strip.loPt, span.loPt), Math.min(strip.hiPt, span.hiPt), strip.startLine);
170
+ for (const span of spans) keep(span.loPt, span.hiPt, lineIndex);
171
+ for (const [key, strip] of open) if (next.get(key)?.startLine !== strip.startLine) record(strip, lineIndex);
172
+ open = next;
173
+ });
174
+ for (const strip of open.values()) record(strip, lines.length);
175
+ return runs;
176
+ }
177
+ function isRunningProse(items, gutterWidthPt) {
178
+ const lines = require_layout_reconstruct_lines.clusterIntoLines(items);
179
+ if (lines.length < MIN_COLUMN_LINES) return false;
180
+ const characters = items.reduce((sum, item) => sum + item.text.length, 0);
181
+ const advancePt = items.reduce((sum, item) => sum + (item.widthPt ?? 0), 0);
182
+ if (characters === 0 || advancePt === 0) return false;
183
+ const leftPt = Math.min(...items.map((item) => item.xPt));
184
+ const rightPt = Math.max(...items.map(rightEdge));
185
+ if ((rightPt - leftPt) / (advancePt / characters) < MIN_PROSE_MEASURE_CHARS) return false;
186
+ for (const line of lines) for (const span of freeSpans(line)) if (Number.isFinite(span.loPt) && Number.isFinite(span.hiPt) && span.hiPt - span.loPt >= gutterWidthPt) return false;
187
+ let wrapped = 0;
188
+ for (let i = 0; i + 1 < lines.length; i++) {
189
+ const lineRightPt = Math.max(...lines[i].items.map(rightEdge));
190
+ const nextFirst = lines[i + 1].items[0];
191
+ if (rightPt - lineRightPt < firstWordWidthPt(nextFirst) + spaceAdvancePt(nextFirst)) wrapped++;
192
+ }
193
+ return wrapped * 2 > lines.length - 1;
194
+ }
195
+ function firstWordWidthPt(item) {
196
+ const text = item.text.trimStart();
197
+ const firstWord = text.split(/\s/u)[0] ?? text;
198
+ return text.length === 0 ? 0 : (item.widthPt ?? 0) * firstWord.length / item.text.length;
199
+ }
200
+ //#endregion
201
+ exports.compareReadingPosition = compareReadingPosition;
202
+ exports.readingOrder = readingOrder;
@@ -0,0 +1,40 @@
1
+ import { o as TextLine } from "../reconstruct-lines-B7t66ICm.cjs";
2
+ import { LayoutText } from "pdf-codec";
3
+ //#region src/layout/reading-order.d.ts
4
+ /** One flow of text in reading order: the lines clustered from it, and its position among the page's flows. */
5
+ interface ReadingRegion {
6
+ /** The flow's own lines, top to bottom, exactly as clusterIntoLines returns them for the flow's items. */
7
+ readonly lines: readonly TextLine[];
8
+ /** The flow's place in reading order: compared element by element, a smaller path is read first. */
9
+ readonly path: readonly number[];
10
+ }
11
+ /** A page's text split into reading-order flows. */
12
+ interface ReadingOrder {
13
+ /** Every flow on the page, in reading order. A page with no column gutter has exactly one, holding every item. */
14
+ readonly regions: readonly ReadingRegion[];
15
+ /**
16
+ * The reading-order path of a point on the page, for placing something that is not text (an image, a recovered table, a drawing) among the flows: the band its y falls in and, inside a column band, the column its x falls in.
17
+ * @param xPt - PDF-space x of the thing being placed; `Number.NEGATIVE_INFINITY` for something with no single column, which places it in the leftmost one.
18
+ * @param yPt - PDF-space y the caller orders that thing by among the flows' own baselines.
19
+ */
20
+ locate: (xPt: number, yPt: number) => readonly number[];
21
+ }
22
+ /**
23
+ * Splits a page's text items into reading-order flows at the column gutters the page's geometry states.
24
+ * @param items - The page's text items in content-stream order, after any table recovery has removed the text it claimed.
25
+ * @returns The flows in reading order, and a locator placing anything else among them. A page with no gutter yields one flow whose lines are `clusterIntoLines(items)`.
26
+ */
27
+ declare function readingOrder(items: readonly LayoutText[]): ReadingOrder;
28
+ /**
29
+ * Orders two placed blocks for reading: by reading-order path first, then top to bottom by y.
30
+ * @returns Negative when `a` is read first, positive when `b` is, zero when neither precedes the other.
31
+ */
32
+ declare function compareReadingPosition(a: {
33
+ readonly path: readonly number[];
34
+ readonly yPt: number;
35
+ }, b: {
36
+ readonly path: readonly number[];
37
+ readonly yPt: number;
38
+ }): number;
39
+ //#endregion
40
+ export { ReadingOrder, ReadingRegion, compareReadingPosition, readingOrder };
@@ -0,0 +1,40 @@
1
+ import { o as TextLine } from "../reconstruct-lines-B7t66ICm.js";
2
+ import { LayoutText } from "pdf-codec";
3
+ //#region src/layout/reading-order.d.ts
4
+ /** One flow of text in reading order: the lines clustered from it, and its position among the page's flows. */
5
+ interface ReadingRegion {
6
+ /** The flow's own lines, top to bottom, exactly as clusterIntoLines returns them for the flow's items. */
7
+ readonly lines: readonly TextLine[];
8
+ /** The flow's place in reading order: compared element by element, a smaller path is read first. */
9
+ readonly path: readonly number[];
10
+ }
11
+ /** A page's text split into reading-order flows. */
12
+ interface ReadingOrder {
13
+ /** Every flow on the page, in reading order. A page with no column gutter has exactly one, holding every item. */
14
+ readonly regions: readonly ReadingRegion[];
15
+ /**
16
+ * The reading-order path of a point on the page, for placing something that is not text (an image, a recovered table, a drawing) among the flows: the band its y falls in and, inside a column band, the column its x falls in.
17
+ * @param xPt - PDF-space x of the thing being placed; `Number.NEGATIVE_INFINITY` for something with no single column, which places it in the leftmost one.
18
+ * @param yPt - PDF-space y the caller orders that thing by among the flows' own baselines.
19
+ */
20
+ locate: (xPt: number, yPt: number) => readonly number[];
21
+ }
22
+ /**
23
+ * Splits a page's text items into reading-order flows at the column gutters the page's geometry states.
24
+ * @param items - The page's text items in content-stream order, after any table recovery has removed the text it claimed.
25
+ * @returns The flows in reading order, and a locator placing anything else among them. A page with no gutter yields one flow whose lines are `clusterIntoLines(items)`.
26
+ */
27
+ declare function readingOrder(items: readonly LayoutText[]): ReadingOrder;
28
+ /**
29
+ * Orders two placed blocks for reading: by reading-order path first, then top to bottom by y.
30
+ * @returns Negative when `a` is read first, positive when `b` is, zero when neither precedes the other.
31
+ */
32
+ declare function compareReadingPosition(a: {
33
+ readonly path: readonly number[];
34
+ readonly yPt: number;
35
+ }, b: {
36
+ readonly path: readonly number[];
37
+ readonly yPt: number;
38
+ }): number;
39
+ //#endregion
40
+ export { ReadingOrder, ReadingRegion, compareReadingPosition, readingOrder };
@@ -0,0 +1,200 @@
1
+ import { clusterIntoLines } from "./reconstruct-lines.js";
2
+ import { resolveStandardFont } from "pdf-codec/fonts";
3
+ import { afmWidthOfCode } from "pdf-codec/afm-widths";
4
+ //#region src/layout/reading-order.ts
5
+ const MIN_COLUMN_LINES = 3;
6
+ const MIN_PROSE_MEASURE_CHARS = 20;
7
+ const SPACE_CHAR_CODE = 32;
8
+ const AFM_UNITS_PER_EM = 1e3;
9
+ /**
10
+ * Splits a page's text items into reading-order flows at the column gutters the page's geometry states.
11
+ * @param items - The page's text items in content-stream order, after any table recovery has removed the text it claimed.
12
+ * @returns The flows in reading order, and a locator placing anything else among them. A page with no gutter yields one flow whose lines are `clusterIntoLines(items)`.
13
+ */
14
+ function readingOrder(items) {
15
+ const bands = segment(items);
16
+ const regions = [];
17
+ collectRegions(bands, [], regions);
18
+ return {
19
+ regions,
20
+ locate: (xPt, yPt) => locate(bands, xPt, yPt)
21
+ };
22
+ }
23
+ /**
24
+ * Orders two placed blocks for reading: by reading-order path first, then top to bottom by y.
25
+ * @returns Negative when `a` is read first, positive when `b` is, zero when neither precedes the other.
26
+ */
27
+ function compareReadingPosition(a, b) {
28
+ const length = Math.min(a.path.length, b.path.length);
29
+ for (let i = 0; i < length; i++) {
30
+ const difference = a.path[i] - b.path[i];
31
+ if (difference !== 0) return difference;
32
+ }
33
+ return a.path.length - b.path.length || b.yPt - a.yPt;
34
+ }
35
+ function segment(items) {
36
+ const lines = clusterIntoLines(items);
37
+ const split = findColumnSplit(lines);
38
+ if (split === void 0) return [flowBand(lines)];
39
+ const before = lines.slice(0, split.startLine);
40
+ const band = lines.slice(split.startLine, split.endLine);
41
+ const after = lines.slice(split.endLine);
42
+ return [
43
+ ...before.length > 0 ? segment(itemsOf(before)) : [],
44
+ {
45
+ kind: "columns",
46
+ gutterXPt: (split.loPt + split.hiPt) / 2,
47
+ left: segment(split.left),
48
+ right: segment(split.right),
49
+ topPt: band[0].baselineY,
50
+ bottomPt: band.at(-1).baselineY
51
+ },
52
+ ...after.length > 0 ? segment(itemsOf(after)) : []
53
+ ];
54
+ }
55
+ function flowBand(lines) {
56
+ return {
57
+ kind: "flow",
58
+ lines,
59
+ topPt: lines[0]?.baselineY ?? Number.POSITIVE_INFINITY,
60
+ bottomPt: lines.at(-1)?.baselineY ?? Number.NEGATIVE_INFINITY
61
+ };
62
+ }
63
+ function itemsOf(lines) {
64
+ return lines.flatMap((line) => line.items);
65
+ }
66
+ function collectRegions(bands, prefix, into) {
67
+ bands.forEach((band, index) => {
68
+ if (band.kind === "flow") into.push({
69
+ lines: band.lines,
70
+ path: [...prefix, index]
71
+ });
72
+ else {
73
+ collectRegions(band.left, [
74
+ ...prefix,
75
+ index,
76
+ 0
77
+ ], into);
78
+ collectRegions(band.right, [
79
+ ...prefix,
80
+ index,
81
+ 1
82
+ ], into);
83
+ }
84
+ });
85
+ }
86
+ function locate(bands, xPt, yPt) {
87
+ let index = bands.length - 1;
88
+ for (let i = 0; i + 1 < bands.length; i++) if (yPt >= (bands[i].bottomPt + bands[i + 1].topPt) / 2) {
89
+ index = i;
90
+ break;
91
+ }
92
+ const band = bands[index];
93
+ if (band.kind === "flow") return [index];
94
+ const side = xPt < band.gutterXPt ? 0 : 1;
95
+ return [
96
+ index,
97
+ side,
98
+ ...locate(side === 0 ? band.left : band.right, xPt, yPt)
99
+ ];
100
+ }
101
+ function findColumnSplit(lines) {
102
+ const items = itemsOf(lines);
103
+ if (lines.length < MIN_COLUMN_LINES || !items.every(isMeasurable)) return;
104
+ const runs = gutterRuns(lines, Math.min(...items.map(spaceAdvancePt))).sort((a, b) => b.endLine - b.startLine - (a.endLine - a.startLine) || b.hiPt - b.loPt - (a.hiPt - a.loPt) || a.startLine - b.startLine);
105
+ for (const run of runs) {
106
+ const runItems = itemsOf(lines.slice(run.startLine, run.endLine));
107
+ if (run.hiPt - run.loPt <= Math.max(...runItems.map(spaceAdvancePt))) continue;
108
+ const left = runItems.filter((item) => rightEdge(item) <= run.loPt);
109
+ const right = runItems.filter((item) => item.xPt >= run.hiPt);
110
+ if (isRunningProse(left, run.hiPt - run.loPt) && isRunningProse(right, run.hiPt - run.loPt)) return {
111
+ ...run,
112
+ left,
113
+ right
114
+ };
115
+ }
116
+ }
117
+ function isMeasurable(item) {
118
+ return item.widthPt !== void 0 && item.writingMode === void 0 && (item.rotationDeg ?? 0) === 0;
119
+ }
120
+ function rightEdge(item) {
121
+ return item.xPt + (item.widthPt ?? 0);
122
+ }
123
+ function spaceAdvancePt(item) {
124
+ const { standardName } = resolveStandardFont(item.font.family, item.font.weight === "bold", item.font.style === "italic");
125
+ const advance = afmWidthOfCode(standardName, SPACE_CHAR_CODE);
126
+ if (advance === void 0) throw new Error(`${standardName} has no AFM width for the space glyph`);
127
+ return advance / AFM_UNITS_PER_EM * item.sizePt;
128
+ }
129
+ function freeSpans(line) {
130
+ const sorted = [...line.items].sort((a, b) => a.xPt - b.xPt);
131
+ const spans = [];
132
+ let reachedPt = Number.NEGATIVE_INFINITY;
133
+ for (const item of sorted) {
134
+ if (item.xPt > reachedPt) spans.push({
135
+ loPt: reachedPt,
136
+ hiPt: item.xPt
137
+ });
138
+ reachedPt = Math.max(reachedPt, rightEdge(item));
139
+ }
140
+ spans.push({
141
+ loPt: reachedPt,
142
+ hiPt: Number.POSITIVE_INFINITY
143
+ });
144
+ return spans;
145
+ }
146
+ function gutterRuns(lines, minWidthPt) {
147
+ const runs = [];
148
+ let open = /* @__PURE__ */ new Map();
149
+ const record = (span, endLine) => {
150
+ if (Number.isFinite(span.loPt) && Number.isFinite(span.hiPt) && endLine - span.startLine >= MIN_COLUMN_LINES) runs.push({
151
+ ...span,
152
+ endLine
153
+ });
154
+ };
155
+ lines.forEach((line, lineIndex) => {
156
+ const next = /* @__PURE__ */ new Map();
157
+ const keep = (loPt, hiPt, startLine) => {
158
+ if (hiPt - loPt <= minWidthPt) return;
159
+ const key = `${String(loPt)}|${String(hiPt)}`;
160
+ const existing = next.get(key);
161
+ if (existing === void 0 || startLine < existing.startLine) next.set(key, {
162
+ loPt,
163
+ hiPt,
164
+ startLine
165
+ });
166
+ };
167
+ const spans = freeSpans(line);
168
+ for (const strip of open.values()) for (const span of spans) keep(Math.max(strip.loPt, span.loPt), Math.min(strip.hiPt, span.hiPt), strip.startLine);
169
+ for (const span of spans) keep(span.loPt, span.hiPt, lineIndex);
170
+ for (const [key, strip] of open) if (next.get(key)?.startLine !== strip.startLine) record(strip, lineIndex);
171
+ open = next;
172
+ });
173
+ for (const strip of open.values()) record(strip, lines.length);
174
+ return runs;
175
+ }
176
+ function isRunningProse(items, gutterWidthPt) {
177
+ const lines = clusterIntoLines(items);
178
+ if (lines.length < MIN_COLUMN_LINES) return false;
179
+ const characters = items.reduce((sum, item) => sum + item.text.length, 0);
180
+ const advancePt = items.reduce((sum, item) => sum + (item.widthPt ?? 0), 0);
181
+ if (characters === 0 || advancePt === 0) return false;
182
+ const leftPt = Math.min(...items.map((item) => item.xPt));
183
+ const rightPt = Math.max(...items.map(rightEdge));
184
+ if ((rightPt - leftPt) / (advancePt / characters) < MIN_PROSE_MEASURE_CHARS) return false;
185
+ for (const line of lines) for (const span of freeSpans(line)) if (Number.isFinite(span.loPt) && Number.isFinite(span.hiPt) && span.hiPt - span.loPt >= gutterWidthPt) return false;
186
+ let wrapped = 0;
187
+ for (let i = 0; i + 1 < lines.length; i++) {
188
+ const lineRightPt = Math.max(...lines[i].items.map(rightEdge));
189
+ const nextFirst = lines[i + 1].items[0];
190
+ if (rightPt - lineRightPt < firstWordWidthPt(nextFirst) + spaceAdvancePt(nextFirst)) wrapped++;
191
+ }
192
+ return wrapped * 2 > lines.length - 1;
193
+ }
194
+ function firstWordWidthPt(item) {
195
+ const text = item.text.trimStart();
196
+ const firstWord = text.split(/\s/u)[0] ?? text;
197
+ return text.length === 0 ? 0 : (item.widthPt ?? 0) * firstWord.length / item.text.length;
198
+ }
199
+ //#endregion
200
+ export { compareReadingPosition, readingOrder };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CA4no_aw.cjs");
2
+ const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CsvGlvP2.cjs");
3
3
  exports.layoutItemToVector = require_reconstruct_presentations_prod.layoutItemToVector;
4
4
  exports.lineToParagraph = require_reconstruct_presentations_prod.lineToParagraph;
5
5
  exports.reconstructDrawing = require_reconstruct_presentations_prod.reconstructDrawing;
@@ -1,2 +1,2 @@
1
- import { a as reconstructSheet, i as reconstructPresentation, n as lineToParagraph, o as recoverPageVectors, r as reconstructDrawing, s as splitLineByLargeGaps, t as layoutItemToVector } from "../reconstruct-presentations-prod-CC8ct-5O.js";
1
+ import { a as reconstructSheet, i as reconstructPresentation, n as lineToParagraph, o as recoverPageVectors, r as reconstructDrawing, s as splitLineByLargeGaps, t as layoutItemToVector } from "../reconstruct-presentations-prod-D1KbGPxa.js";
2
2
  export { layoutItemToVector, lineToParagraph, reconstructDrawing, reconstructPresentation, reconstructSheet, recoverPageVectors, splitLineByLargeGaps };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CA4no_aw.cjs");
2
+ const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CsvGlvP2.cjs");
3
3
  exports.buildGridFromLattice = require_reconstruct_presentations_prod.buildGridFromLattice;
4
4
  exports.buildGridFromTextClustering = require_reconstruct_presentations_prod.buildGridFromTextClustering;
5
5
  exports.recoverTables = require_reconstruct_presentations_prod.recoverTables;
@@ -1,2 +1,2 @@
1
- import { b as recoverTables, v as buildGridFromLattice, y as buildGridFromTextClustering } from "../reconstruct-presentations-prod-CC8ct-5O.js";
1
+ import { b as recoverTables, v as buildGridFromLattice, y as buildGridFromTextClustering } from "../reconstruct-presentations-prod-D1KbGPxa.js";
2
2
  export { buildGridFromLattice, buildGridFromTextClustering, recoverTables };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CA4no_aw.cjs");
2
+ const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CsvGlvP2.cjs");
3
3
  exports.CELL_TYPES = require_reconstruct_presentations_prod.CELL_TYPES;
4
4
  exports.DEFAULT_COLUMN_WIDTH_FALLBACK_PT = require_reconstruct_presentations_prod.DEFAULT_COLUMN_WIDTH_FALLBACK_PT;
5
5
  exports.NO_ITEMS = require_reconstruct_presentations_prod.NO_ITEMS;
@@ -1,2 +1,2 @@
1
- import { _ as reconstructWordprocessing, c as CELL_TYPES, d as ROW_TYPES, f as TABLE_TYPES, g as reconstructSpreadsheet, h as nearestOfType, l as DEFAULT_COLUMN_WIDTH_FALLBACK_PT, m as indexStructure, p as ZERO_MARGINS, u as NO_ITEMS } from "../reconstruct-presentations-prod-CC8ct-5O.js";
1
+ import { _ as reconstructWordprocessing, c as CELL_TYPES, d as ROW_TYPES, f as TABLE_TYPES, g as reconstructSpreadsheet, h as nearestOfType, l as DEFAULT_COLUMN_WIDTH_FALLBACK_PT, m as indexStructure, p as ZERO_MARGINS, u as NO_ITEMS } from "../reconstruct-presentations-prod-D1KbGPxa.js";
2
2
  export { CELL_TYPES, DEFAULT_COLUMN_WIDTH_FALLBACK_PT, NO_ITEMS, ROW_TYPES, TABLE_TYPES, ZERO_MARGINS, indexStructure, nearestOfType, reconstructSpreadsheet, reconstructWordprocessing };
@@ -5,6 +5,7 @@ const require_layout_cell_typing = require("./layout/cell-typing.cjs");
5
5
  const require_ports_abort = require("./ports/abort.cjs");
6
6
  const require_layout_reconstruct_lines = require("./layout/reconstruct-lines.cjs");
7
7
  const require_layout_lattice = require("./layout/lattice.cjs");
8
+ const require_layout_reading_order = require("./layout/reading-order.cjs");
8
9
  let document_schema_js = require("document-schema.js");
9
10
  let pdf_codec_layout = require("pdf-codec/layout");
10
11
  //#region src/layout/reconstruct-tables.ts
@@ -519,19 +520,19 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
519
520
  }
520
521
  const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
521
522
  const imageItems = page.items.filter((i) => i.kind === "image");
522
- const lines = require_layout_reconstruct_lines.clusterIntoLines(textItems);
523
- const paragraphs = require_layout_reconstruct_lines.clusterIntoParagraphs(lines);
523
+ const order = require_layout_reading_order.readingOrder(textItems);
524
524
  const recordDivisions = (block, items) => {
525
525
  if (structure !== void 0) divisionByBlock.set(block, divisionChainOf(items, structure));
526
526
  };
527
527
  const positioned = [];
528
- for (const paragraph of paragraphs) {
528
+ for (const { lines, path } of order.regions) for (const paragraph of require_layout_reconstruct_lines.clusterIntoParagraphs(lines)) {
529
529
  const paragraphItems = paragraph.lines.flatMap((line) => line.items);
530
530
  const structural = structure === void 0 ? void 0 : structureHeadingLevel(paragraphItems, structure);
531
531
  const headingLevel = structural !== void 0 ? structural.level : headingLevelOf(paragraph, headingLevels);
532
532
  const block = require_layout_reconstruct_lines.paragraphToContentParagraph(paragraph, pageIndex, headingLevel);
533
533
  recordDivisions(block, paragraphItems);
534
534
  positioned.push({
535
+ path,
535
536
  yPt: paragraph.lines[0].baselineY,
536
537
  block
537
538
  });
@@ -554,6 +555,7 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
554
555
  });
555
556
  recordDivisions(block, [img]);
556
557
  positioned.push({
558
+ path: order.locate(img.xPt + img.widthPt / 2, img.yPt),
557
559
  yPt: img.yPt,
558
560
  block
559
561
  });
@@ -562,6 +564,7 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
562
564
  for (const recovered of recoveredTables) {
563
565
  recordDivisions(recovered.table, [...recovered.consumedText]);
564
566
  positioned.push({
567
+ path: order.locate(recovered.frame.xPt + recovered.frame.widthPt / 2, recovered.topYPt),
565
568
  yPt: recovered.topYPt,
566
569
  block: recovered.table
567
570
  });
@@ -571,11 +574,12 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
571
574
  const vectorItems = page.items.filter((item) => !claimedItems.has(item) && layoutItemToVector(item, page.heightPt) !== void 0);
572
575
  recordDivisions(recoveredVectors.block, vectorItems);
573
576
  positioned.push({
577
+ path: order.locate(Number.NEGATIVE_INFINITY, recoveredVectors.topYPt),
574
578
  yPt: recoveredVectors.topYPt,
575
579
  block: recoveredVectors.block
576
580
  });
577
581
  }
578
- positioned.sort((a, b) => b.yPt - a.yPt);
582
+ positioned.sort(require_layout_reading_order.compareReadingPosition);
579
583
  const blocks = positioned.map((p) => p.block);
580
584
  reconcileLinks(blocks, page, pageIndex);
581
585
  appendAnnotationConstructs(blocks, page, pageIndex);
@@ -772,7 +776,7 @@ function reconstructSlide(page, pageIndex, images, structure) {
772
776
  }
773
777
  const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
774
778
  const imageItems = page.items.filter((i) => i.kind === "image");
775
- const textShapes = clusterIntoBlocks(require_layout_reconstruct_lines.clusterIntoLines(textItems)).map((block) => blockToShape(block, page.heightPt, pageIndex));
779
+ const textShapes = require_layout_reading_order.readingOrder(textItems).regions.flatMap((region) => clusterIntoBlocks(region.lines)).map((block) => blockToShape(block, page.heightPt, pageIndex));
776
780
  const imageShapes = [];
777
781
  for (const img of imageItems) {
778
782
  const shape = imageToShape(img, page.heightPt, pageIndex, images);
@@ -5,6 +5,7 @@ import { inferCellValue } from "./layout/cell-typing.js";
5
5
  import { throwIfAborted } from "./ports/abort.js";
6
6
  import { HALF_POINT_BUCKET_PT, bucketCounts, clusterIntoLines, clusterIntoParagraphs, estimateModalLineSpacing, fontSizesClose, gapExceeds, lineBox, modeOf, paragraphToContentParagraph, pushRunsForLine, textBoxOfItem, textItemToContentRun, textItemVerticalExtent } from "./layout/reconstruct-lines.js";
7
7
  import { detectGridLattice, findColumnIndex, findRowIndex } from "./layout/lattice.js";
8
+ import { compareReadingPosition, readingOrder } from "./layout/reading-order.js";
8
9
  import { denseTableRows } from "document-schema.js";
9
10
  import { sourcePageIndex } from "pdf-codec/layout";
10
11
  //#region src/layout/reconstruct-tables.ts
@@ -519,19 +520,19 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
519
520
  }
520
521
  const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
521
522
  const imageItems = page.items.filter((i) => i.kind === "image");
522
- const lines = clusterIntoLines(textItems);
523
- const paragraphs = clusterIntoParagraphs(lines);
523
+ const order = readingOrder(textItems);
524
524
  const recordDivisions = (block, items) => {
525
525
  if (structure !== void 0) divisionByBlock.set(block, divisionChainOf(items, structure));
526
526
  };
527
527
  const positioned = [];
528
- for (const paragraph of paragraphs) {
528
+ for (const { lines, path } of order.regions) for (const paragraph of clusterIntoParagraphs(lines)) {
529
529
  const paragraphItems = paragraph.lines.flatMap((line) => line.items);
530
530
  const structural = structure === void 0 ? void 0 : structureHeadingLevel(paragraphItems, structure);
531
531
  const headingLevel = structural !== void 0 ? structural.level : headingLevelOf(paragraph, headingLevels);
532
532
  const block = paragraphToContentParagraph(paragraph, pageIndex, headingLevel);
533
533
  recordDivisions(block, paragraphItems);
534
534
  positioned.push({
535
+ path,
535
536
  yPt: paragraph.lines[0].baselineY,
536
537
  block
537
538
  });
@@ -554,6 +555,7 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
554
555
  });
555
556
  recordDivisions(block, [img]);
556
557
  positioned.push({
558
+ path: order.locate(img.xPt + img.widthPt / 2, img.yPt),
557
559
  yPt: img.yPt,
558
560
  block
559
561
  });
@@ -562,6 +564,7 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
562
564
  for (const recovered of recoveredTables) {
563
565
  recordDivisions(recovered.table, [...recovered.consumedText]);
564
566
  positioned.push({
567
+ path: order.locate(recovered.frame.xPt + recovered.frame.widthPt / 2, recovered.topYPt),
565
568
  yPt: recovered.topYPt,
566
569
  block: recovered.table
567
570
  });
@@ -571,11 +574,12 @@ function reconstructPageBlocks(page, pageIndex, images, headingLevels, form, str
571
574
  const vectorItems = page.items.filter((item) => !claimedItems.has(item) && layoutItemToVector(item, page.heightPt) !== void 0);
572
575
  recordDivisions(recoveredVectors.block, vectorItems);
573
576
  positioned.push({
577
+ path: order.locate(Number.NEGATIVE_INFINITY, recoveredVectors.topYPt),
574
578
  yPt: recoveredVectors.topYPt,
575
579
  block: recoveredVectors.block
576
580
  });
577
581
  }
578
- positioned.sort((a, b) => b.yPt - a.yPt);
582
+ positioned.sort(compareReadingPosition);
579
583
  const blocks = positioned.map((p) => p.block);
580
584
  reconcileLinks(blocks, page, pageIndex);
581
585
  appendAnnotationConstructs(blocks, page, pageIndex);
@@ -772,7 +776,7 @@ function reconstructSlide(page, pageIndex, images, structure) {
772
776
  }
773
777
  const textItems = page.items.filter((i) => i.kind === "text" && !consumedText.has(i));
774
778
  const imageItems = page.items.filter((i) => i.kind === "image");
775
- const textShapes = clusterIntoBlocks(clusterIntoLines(textItems)).map((block) => blockToShape(block, page.heightPt, pageIndex));
779
+ const textShapes = readingOrder(textItems).regions.flatMap((region) => clusterIntoBlocks(region.lines)).map((block) => blockToShape(block, page.heightPt, pageIndex));
776
780
  const imageShapes = [];
777
781
  for (const img of imageItems) {
778
782
  const shape = imageToShape(img, page.heightPt, pageIndex, images);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js.documents",
3
- "version": "14.6.0",
3
+ "version": "14.6.2",
4
4
  "description": "Converts between docx, pptx, odt, odp, ods, odg, xlsx, csv, svg, markdown, rtf, doc, xls, ppt and PDF through one shared, Zod-typed content schema (wpd read-only), plus read+write editors for OOXML and OpenDocument. Worker-isomorphic.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -77,21 +77,21 @@
77
77
  ],
78
78
  "license": "MIT",
79
79
  "dependencies": {
80
- "archive-codec": "1.11.26",
80
+ "archive-codec": "1.11.27",
81
81
  "byte-codec": "2.0.2",
82
- "doc-codec": "6.1.23",
83
- "document-schema.js": "7.16.1",
84
- "epub-codec": "5.1.16",
82
+ "doc-codec": "6.1.24",
83
+ "document-schema.js": "7.16.2",
84
+ "epub-codec": "5.1.17",
85
85
  "fflate": "0.8.3",
86
- "markdown-codec": "11.1.16",
87
- "odf.js": "11.2.2",
88
- "ooxml.js": "12.2.32",
89
- "pdf-codec": "5.5.0",
90
- "ppt-codec": "2.2.23",
91
- "rtf-codec": "8.1.24",
86
+ "markdown-codec": "11.1.17",
87
+ "odf.js": "11.2.3",
88
+ "ooxml.js": "12.2.33",
89
+ "pdf-codec": "5.5.1",
90
+ "ppt-codec": "2.2.24",
91
+ "rtf-codec": "8.1.25",
92
92
  "temml": "0.13.4",
93
- "wpd-codec": "5.1.22",
94
- "xls-codec": "4.16.5",
93
+ "wpd-codec": "5.1.23",
94
+ "xls-codec": "4.16.6",
95
95
  "zod": "4.4.3"
96
96
  },
97
97
  "devDependencies": {