js.documents 14.6.0 → 14.6.1
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 +1 -1
- package/dist/{composition-plan-CSHdRyji.cjs → composition-plan-DJQgPvBj.cjs} +1 -1
- package/dist/{composition-plan-DQ8lPKIW.js → composition-plan-DMTkmk8n.js} +1 -1
- package/dist/convert/composition-plan.cjs +1 -1
- package/dist/convert/composition-plan.js +1 -1
- package/dist/convert/composition-to-pdf.cjs +1 -1
- package/dist/convert/composition-to-pdf.js +1 -1
- package/dist/convert/composition.cjs +1 -1
- package/dist/convert/composition.js +1 -1
- package/dist/convert/from-pdf.cjs +2 -2
- package/dist/convert/from-pdf.js +2 -2
- package/dist/convert/local.cjs +1 -1
- package/dist/convert/local.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +2 -2
- package/dist/layout/reading-order.cjs +202 -0
- package/dist/layout/reading-order.d.cts +40 -0
- package/dist/layout/reading-order.d.ts +40 -0
- package/dist/layout/reading-order.js +200 -0
- package/dist/layout/reconstruct-presentations-prod.cjs +1 -1
- package/dist/layout/reconstruct-presentations-prod.js +1 -1
- package/dist/layout/reconstruct-tables.cjs +1 -1
- package/dist/layout/reconstruct-tables.js +1 -1
- package/dist/layout/reconstruct.cjs +1 -1
- package/dist/layout/reconstruct.js +1 -1
- package/dist/{reconstruct-presentations-prod-CA4no_aw.cjs → reconstruct-presentations-prod-CsvGlvP2.cjs} +9 -5
- package/dist/{reconstruct-presentations-prod-CC8ct-5O.js → reconstruct-presentations-prod-D1KbGPxa.js} +9 -5
- package/package.json +1 -1
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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");
|
package/dist/convert/from-pdf.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { _ as reconstructWordprocessing } from "../reconstruct-presentations-prod-
|
|
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-
|
|
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";
|
package/dist/convert/local.cjs
CHANGED
|
@@ -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-
|
|
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
|
package/dist/convert/local.js
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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.
|
|
3
|
+
"version": "14.6.1",
|
|
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": {
|