js.documents 14.6.4 → 14.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{composition-plan-DMTkmk8n.js → composition-plan-BLhc55_x.js} +1 -1
- package/dist/{composition-plan-DJQgPvBj.cjs → composition-plan-CgjS-XZU.cjs} +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/lattice.cjs +28 -5
- package/dist/layout/lattice.d.cts +16 -1
- package/dist/layout/lattice.d.ts +16 -1
- package/dist/layout/lattice.js +25 -6
- package/dist/layout/reading-order.cjs +2 -0
- package/dist/layout/reading-order.d.cts +3 -1
- package/dist/layout/reading-order.d.ts +3 -1
- package/dist/layout/reading-order.js +1 -1
- package/dist/layout/reconstruct-lines.cjs +41 -11
- package/dist/layout/reconstruct-lines.js +42 -12
- 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/layout/ruled-rows.cjs +214 -0
- package/dist/layout/ruled-rows.d.cts +11 -0
- package/dist/layout/ruled-rows.d.ts +11 -0
- package/dist/layout/ruled-rows.js +213 -0
- package/dist/{reconstruct-presentations-prod-D1KbGPxa.js → reconstruct-presentations-prod-DV7UQqMT.js} +19 -5
- package/dist/{reconstruct-presentations-prod-CsvGlvP2.cjs → reconstruct-presentations-prod-DtpKKgRY.cjs} +19 -5
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { stampFrame } from "./shared.js";
|
|
2
2
|
import { resolveStandardFont } from "pdf-codec/fonts";
|
|
3
3
|
import { STANDARD_METRICS } from "pdf-codec/afm-widths";
|
|
4
|
-
import { runGapPt, runsShareBaseline } from "pdf-codec/text-group";
|
|
4
|
+
import { attachScriptRuns, runGapPt, runsShareBaseline, scriptPositionOf } from "pdf-codec/text-group";
|
|
5
5
|
//#region src/layout/reconstruct-lines.ts
|
|
6
6
|
const LINE_BASELINE_TOLERANCE_FACTOR = .5;
|
|
7
7
|
const AFM_UNITS_PER_EM = 1e3;
|
|
@@ -27,13 +27,14 @@ function dropDuplicatePaints(items) {
|
|
|
27
27
|
return kept;
|
|
28
28
|
}
|
|
29
29
|
function dropOverlappingRepeatsWithinLine(items) {
|
|
30
|
-
const
|
|
30
|
+
const keptByText = /* @__PURE__ */ new Map();
|
|
31
31
|
const kept = [];
|
|
32
32
|
for (const item of items) {
|
|
33
33
|
const textKey = item.text.trim();
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
|
|
34
|
+
const earlier = keptByText.get(textKey);
|
|
35
|
+
if (earlier?.some((prior) => item.xPt < prior.xPt + (prior.widthPt ?? 0) && Math.abs(item.yPt - prior.yPt) <= DUPLICATE_PAINT_BUCKET_PT) === true) continue;
|
|
36
|
+
if (earlier === void 0) keptByText.set(textKey, [item]);
|
|
37
|
+
else earlier.push(item);
|
|
37
38
|
kept.push(item);
|
|
38
39
|
}
|
|
39
40
|
return kept;
|
|
@@ -126,12 +127,23 @@ function clusterIntoLines(items) {
|
|
|
126
127
|
});
|
|
127
128
|
else line.items.push(item);
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
+
const lines = attachScriptRuns(working, (line) => line.items).filter(({ runs }) => runs.length > 0).map(({ line, runs }) => ({
|
|
131
|
+
...line,
|
|
132
|
+
items: runs
|
|
133
|
+
}));
|
|
134
|
+
for (const line of lines) {
|
|
130
135
|
line.items.sort((a, b) => a.xPt - b.xPt);
|
|
131
136
|
line.items = dropOverlappingRepeatsWithinLine(line.items);
|
|
132
137
|
}
|
|
133
|
-
|
|
134
|
-
return
|
|
138
|
+
lines.sort((a, b) => b.baselineY - a.baselineY);
|
|
139
|
+
return lines;
|
|
140
|
+
}
|
|
141
|
+
function scriptPositionOnLine(item, line) {
|
|
142
|
+
for (const base of line.items) {
|
|
143
|
+
if (base === item) continue;
|
|
144
|
+
const position = scriptPositionOf(base, item);
|
|
145
|
+
if (position !== void 0) return position;
|
|
146
|
+
}
|
|
135
147
|
}
|
|
136
148
|
function textItemVerticalExtent(item) {
|
|
137
149
|
const { standardName } = resolveStandardFont(item.font.family, item.font.weight === "bold", item.font.style === "italic");
|
|
@@ -191,13 +203,32 @@ function pushRunsForLine(runs, line, pageIndex) {
|
|
|
191
203
|
if (itemIndex > 0) {
|
|
192
204
|
const prevItem = line.items[itemIndex - 1];
|
|
193
205
|
if (gapExceeds(prevItem, item, 2 * item.sizePt)) runs.push({ text: " " });
|
|
194
|
-
else if (gapExceeds(prevItem, item, .5)) runs
|
|
206
|
+
else if (gapExceeds(prevItem, item, .5)) appendWordSpace(runs);
|
|
195
207
|
}
|
|
196
208
|
const run = textItemToContentRun(item);
|
|
197
209
|
stampFrame(run, pageIndex, textBoxOfItem(item, pageIndex));
|
|
198
|
-
|
|
210
|
+
const verticalAlign = scriptPositionOnLine(item, line);
|
|
211
|
+
const core = item.text.trim();
|
|
212
|
+
if (verticalAlign === void 0 || core.length === 0) {
|
|
213
|
+
runs.push(run);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const leading = item.text.slice(0, item.text.length - item.text.trimStart().length);
|
|
217
|
+
const trailing = item.text.slice(item.text.trimEnd().length);
|
|
218
|
+
if (leading.length > 0) runs.push({ text: leading });
|
|
219
|
+
runs.push({
|
|
220
|
+
...run,
|
|
221
|
+
text: core,
|
|
222
|
+
verticalAlign
|
|
223
|
+
});
|
|
224
|
+
if (trailing.length > 0) runs.push({ text: trailing });
|
|
199
225
|
});
|
|
200
226
|
}
|
|
227
|
+
function appendWordSpace(runs) {
|
|
228
|
+
const last = runs[runs.length - 1];
|
|
229
|
+
if (last.verticalAlign === void 0) last.text += " ";
|
|
230
|
+
else runs.push({ text: " " });
|
|
231
|
+
}
|
|
201
232
|
function bucketCounts(values, bucketSize) {
|
|
202
233
|
const counts = /* @__PURE__ */ new Map();
|
|
203
234
|
for (const v of values) {
|
|
@@ -280,8 +311,7 @@ function paragraphToContentParagraph(paragraph, pageIndex, headingLevel) {
|
|
|
280
311
|
paragraph.lines.forEach((line, lineIndex) => {
|
|
281
312
|
stampFrame(result, pageIndex, lineBox(line, pageIndex));
|
|
282
313
|
if (lineIndex > 0) {
|
|
283
|
-
|
|
284
|
-
if (lastRun !== void 0) lastRun.text += " ";
|
|
314
|
+
if (result.runs.length > 0) appendWordSpace(result.runs);
|
|
285
315
|
}
|
|
286
316
|
pushRunsForLine(result.runs, line, pageIndex);
|
|
287
317
|
});
|
|
@@ -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-DtpKKgRY.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-DV7UQqMT.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-DtpKKgRY.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-DV7UQqMT.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-DtpKKgRY.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-DV7UQqMT.js";
|
|
2
2
|
export { CELL_TYPES, DEFAULT_COLUMN_WIDTH_FALLBACK_PT, NO_ITEMS, ROW_TYPES, TABLE_TYPES, ZERO_MARGINS, indexStructure, nearestOfType, reconstructSpreadsheet, reconstructWordprocessing };
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_layout_reconstruct_lines = require("./reconstruct-lines.cjs");
|
|
3
|
+
const require_layout_lattice = require("./lattice.cjs");
|
|
4
|
+
const require_layout_reading_order = require("./reading-order.cjs");
|
|
5
|
+
//#region src/layout/ruled-rows.ts
|
|
6
|
+
/**
|
|
7
|
+
* The grid of a table ruled between its rows and set apart by gutters between its columns, when the page holds one.
|
|
8
|
+
* @param items - the page's items; rules are read from its lines, paths and thin rectangles, and text from its text items
|
|
9
|
+
* @returns a lattice whose row boundaries are the rules (and the top of an unruled heading row above them) and whose column boundaries are the table's two ends and the middle of each gutter, a row that crosses a gutter spanning the columns it covers; undefined when no set of rules of one span holds two rows of text, fewer than two rows hold text in more than one place, the rows leave no gutter they all share, or every column reads as running prose
|
|
10
|
+
*/
|
|
11
|
+
function detectRuledRowLattice(items) {
|
|
12
|
+
const rules = rulesOfOneTable(items);
|
|
13
|
+
if (rules === void 0) return;
|
|
14
|
+
const text = items.filter((item) => item.kind === "text" && item.text.trim().length > 0 && item.xPt >= rules.leftPt && item.xPt < rules.rightPt);
|
|
15
|
+
if (text.some((item) => item.widthPt === void 0 || item.writingMode !== void 0 || (item.rotationDeg ?? 0) !== 0)) return;
|
|
16
|
+
const ruledRows = rules.positionsDescPt.slice(1).map((lowerPt, row) => text.filter((item) => item.yPt > lowerPt && item.yPt < rules.positionsDescPt[row]));
|
|
17
|
+
const widestSpacePt = Math.max(...ruledRows.flat().map(require_layout_reading_order.spaceAdvancePt));
|
|
18
|
+
const shared = sharedGutters(ruledRows, widestSpacePt);
|
|
19
|
+
if (shared === void 0) return;
|
|
20
|
+
const { gutters } = shared;
|
|
21
|
+
const dividersPt = gutters.map((gutter) => (gutter.loPt + gutter.hiPt) / 2);
|
|
22
|
+
const topRulePt = rules.positionsDescPt[0];
|
|
23
|
+
const heading = headingRow(text.filter((item) => item.yPt > topRulePt), topRulePt, dividersPt, require_layout_reconstruct_lines.clusterIntoLines(ruledRows.flat()), widestSpacePt);
|
|
24
|
+
const rows = heading === void 0 ? ruledRows : [heading.items, ...ruledRows];
|
|
25
|
+
if (shared.rowsWithColumns + (heading !== void 0 && textRuns(heading.items, widestSpacePt).length > 1 ? 1 : 0) < 2) return;
|
|
26
|
+
const columnBoundariesAscPt = [
|
|
27
|
+
rules.leftPt,
|
|
28
|
+
...dividersPt,
|
|
29
|
+
rules.rightPt
|
|
30
|
+
];
|
|
31
|
+
if (holdsDrawing(items, rules, heading?.topPt ?? rules.positionsDescPt[0])) return;
|
|
32
|
+
const narrowestGutterPt = Math.min(...gutters.map((gutter) => gutter.hiPt - gutter.loPt));
|
|
33
|
+
if (columnBoundariesAscPt.slice(1).map((rightPt, column) => rows.flat().filter((item) => item.xPt >= columnBoundariesAscPt[column] && item.xPt < rightPt)).every((column) => require_layout_reading_order.isRunningProse(column, narrowestGutterPt))) return;
|
|
34
|
+
const bands = [...heading === void 0 ? [] : [heading], ...ruledRows.map((rowItems, row) => ({
|
|
35
|
+
items: rowItems,
|
|
36
|
+
topPt: rules.positionsDescPt[row]
|
|
37
|
+
}))].flatMap((band) => rowsOfBand(band.items, band.topPt, dividersPt, widestSpacePt));
|
|
38
|
+
return {
|
|
39
|
+
rowBoundariesDescPt: [...bands.map((band) => band.topPt), rules.positionsDescPt.at(-1)],
|
|
40
|
+
columnBoundariesAscPt,
|
|
41
|
+
regions: bands.flatMap((band, rowIndex) => rowRegions(band.items, rowIndex, dividersPt, widestSpacePt)),
|
|
42
|
+
sourceItems: rules.sourceItems
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function rulesOfOneTable(items) {
|
|
46
|
+
const groups = [];
|
|
47
|
+
for (const rule of require_layout_lattice.horizontalRules(items)) {
|
|
48
|
+
const group = groups.find((members) => members.every((member) => spansOverlap(member, rule)));
|
|
49
|
+
if (group === void 0) groups.push([rule]);
|
|
50
|
+
else group.push(rule);
|
|
51
|
+
}
|
|
52
|
+
const best = groups.filter((members) => members.length >= 3).map((members) => ({
|
|
53
|
+
members,
|
|
54
|
+
leftPt: Math.min(...members.map((rule) => rule.leftPt)),
|
|
55
|
+
rightPt: Math.max(...members.map((rule) => rule.rightPt))
|
|
56
|
+
})).sort((a, b) => b.members.length - a.members.length || b.rightPt - b.leftPt - (a.rightPt - a.leftPt))[0];
|
|
57
|
+
if (best === void 0) return;
|
|
58
|
+
return {
|
|
59
|
+
positionsDescPt: best.members.map((rule) => rule.position).sort((a, b) => b - a),
|
|
60
|
+
leftPt: best.leftPt,
|
|
61
|
+
rightPt: best.rightPt,
|
|
62
|
+
sourceItems: new Set(best.members.flatMap((rule) => rule.items))
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function holdsDrawing(items, rules, topPt) {
|
|
66
|
+
const bottomPt = rules.positionsDescPt.at(-1);
|
|
67
|
+
const widthPt = rules.rightPt - rules.leftPt;
|
|
68
|
+
const inside = (box) => box.loXPt < rules.rightPt - 3 && box.hiXPt > rules.leftPt + 3 && box.loYPt < topPt && box.hiYPt > bottomPt;
|
|
69
|
+
const spansRow = (box) => box.hiXPt - box.loXPt >= require_layout_lattice.EDGE_COVERAGE_RATIO * widthPt;
|
|
70
|
+
const horizontal = (box) => box.hiYPt - box.loYPt <= 3 && box.hiXPt - box.loXPt > box.hiYPt - box.loYPt;
|
|
71
|
+
return items.some((item) => {
|
|
72
|
+
if (rules.sourceItems.has(item)) return false;
|
|
73
|
+
const box = boxOf(item);
|
|
74
|
+
return box !== void 0 && inside(box) && !horizontal(box) && !(item.kind !== "image" && item.kind !== "ellipse" && spansRow(box));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function boxOf(item) {
|
|
78
|
+
switch (item.kind) {
|
|
79
|
+
case "image":
|
|
80
|
+
case "rect":
|
|
81
|
+
case "ellipse": return {
|
|
82
|
+
loXPt: item.xPt,
|
|
83
|
+
hiXPt: item.xPt + item.widthPt,
|
|
84
|
+
loYPt: item.yPt,
|
|
85
|
+
hiYPt: item.yPt + item.heightPt
|
|
86
|
+
};
|
|
87
|
+
case "line": return {
|
|
88
|
+
loXPt: Math.min(item.x1Pt, item.x2Pt),
|
|
89
|
+
hiXPt: Math.max(item.x1Pt, item.x2Pt),
|
|
90
|
+
loYPt: Math.min(item.y1Pt, item.y2Pt),
|
|
91
|
+
hiYPt: Math.max(item.y1Pt, item.y2Pt)
|
|
92
|
+
};
|
|
93
|
+
case "path": {
|
|
94
|
+
const xs = item.subpaths.flatMap((subpath) => [subpath.startXPt, ...subpath.segments.flatMap((segment) => segment.kind === "cubic" ? [
|
|
95
|
+
segment.c1xPt,
|
|
96
|
+
segment.c2xPt,
|
|
97
|
+
segment.xPt
|
|
98
|
+
] : [segment.xPt])]);
|
|
99
|
+
const ys = item.subpaths.flatMap((subpath) => [subpath.startYPt, ...subpath.segments.flatMap((segment) => segment.kind === "cubic" ? [
|
|
100
|
+
segment.c1yPt,
|
|
101
|
+
segment.c2yPt,
|
|
102
|
+
segment.yPt
|
|
103
|
+
] : [segment.yPt])]);
|
|
104
|
+
return {
|
|
105
|
+
loXPt: Math.min(...xs),
|
|
106
|
+
hiXPt: Math.max(...xs),
|
|
107
|
+
loYPt: Math.min(...ys),
|
|
108
|
+
hiYPt: Math.max(...ys)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
case "text":
|
|
112
|
+
case "link":
|
|
113
|
+
case "internalLink": return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function spansOverlap(a, b) {
|
|
117
|
+
const overlapPt = Math.min(a.rightPt, b.rightPt) - Math.max(a.leftPt, b.leftPt);
|
|
118
|
+
const unionPt = Math.max(a.rightPt, b.rightPt) - Math.min(a.leftPt, b.leftPt);
|
|
119
|
+
return overlapPt >= require_layout_lattice.EDGE_COVERAGE_RATIO * unionPt;
|
|
120
|
+
}
|
|
121
|
+
function textRuns(row, widestSpacePt) {
|
|
122
|
+
const runs = [];
|
|
123
|
+
for (const item of [...row].sort((a, b) => a.xPt - b.xPt)) {
|
|
124
|
+
const last = runs.at(-1);
|
|
125
|
+
if (last !== void 0 && item.xPt - last.hiPt <= widestSpacePt) last.hiPt = Math.max(last.hiPt, rightEdge(item));
|
|
126
|
+
else runs.push({
|
|
127
|
+
loPt: item.xPt,
|
|
128
|
+
hiPt: rightEdge(item)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return runs;
|
|
132
|
+
}
|
|
133
|
+
function sharedGutters(rows, widestSpacePt) {
|
|
134
|
+
const gapsByRow = rows.map((row) => textRuns(row, widestSpacePt)).filter((runs) => runs.length > 1).map((runs) => runs.slice(1).map((run, index) => ({
|
|
135
|
+
loPt: runs[index].hiPt,
|
|
136
|
+
hiPt: run.loPt
|
|
137
|
+
})));
|
|
138
|
+
const [first, ...rest] = gapsByRow;
|
|
139
|
+
if (first === void 0) return;
|
|
140
|
+
const gutters = rest.reduce((shared, gaps) => shared.flatMap((gutter) => gaps.flatMap((gap) => {
|
|
141
|
+
const loPt = Math.max(gutter.loPt, gap.loPt);
|
|
142
|
+
const hiPt = Math.min(gutter.hiPt, gap.hiPt);
|
|
143
|
+
return hiPt - loPt > widestSpacePt ? [{
|
|
144
|
+
loPt,
|
|
145
|
+
hiPt
|
|
146
|
+
}] : [];
|
|
147
|
+
})), first);
|
|
148
|
+
return gutters.length > 0 ? {
|
|
149
|
+
gutters,
|
|
150
|
+
rowsWithColumns: gapsByRow.length
|
|
151
|
+
} : void 0;
|
|
152
|
+
}
|
|
153
|
+
function headingRow(above, topRulePt, dividersPt, tableLines, widestSpacePt) {
|
|
154
|
+
const reachPt = require_layout_reconstruct_lines.PARAGRAPH_GAP_MULTIPLIER * require_layout_reconstruct_lines.estimateModalLineSpacing(tableLines);
|
|
155
|
+
const items = [];
|
|
156
|
+
let lowerPt = topRulePt;
|
|
157
|
+
for (const line of [...require_layout_reconstruct_lines.clusterIntoLines(above)].reverse()) {
|
|
158
|
+
if (line.items.some((item) => dividersPt.some((dividerPt) => item.xPt < dividerPt && rightEdge(item) > dividerPt)) || line.baselineY - lowerPt > reachPt) break;
|
|
159
|
+
items.push(...line.items);
|
|
160
|
+
lowerPt = line.baselineY;
|
|
161
|
+
}
|
|
162
|
+
if (columnsHolding(items, dividersPt, widestSpacePt).size < 2) return;
|
|
163
|
+
return {
|
|
164
|
+
items,
|
|
165
|
+
topPt: Math.max(...items.map((item) => item.yPt + item.sizePt))
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function columnsHolding(items, dividersPt, widestSpacePt) {
|
|
169
|
+
return new Set(textRuns(items, widestSpacePt).map((run) => dividersPt.filter((dividerPt) => dividerPt <= run.loPt).length));
|
|
170
|
+
}
|
|
171
|
+
function rowsOfBand(items, topPt, dividersPt, widestSpacePt) {
|
|
172
|
+
const lines = require_layout_reconstruct_lines.clusterIntoLines(items);
|
|
173
|
+
const first = lines[0];
|
|
174
|
+
const opening = first === void 0 ? /* @__PURE__ */ new Set() : columnsHolding(first.items, dividersPt, widestSpacePt);
|
|
175
|
+
if (!(lines.length > 1 && opening.size > 1 && lines.every((line) => {
|
|
176
|
+
const occupied = columnsHolding(line.items, dividersPt, widestSpacePt);
|
|
177
|
+
return [...opening].every((column) => occupied.has(column));
|
|
178
|
+
}))) return [{
|
|
179
|
+
items: [...items],
|
|
180
|
+
topPt
|
|
181
|
+
}];
|
|
182
|
+
return lines.map((line, index) => ({
|
|
183
|
+
items: [...line.items],
|
|
184
|
+
topPt: index === 0 ? topPt : (lines[index - 1].baselineY + line.baselineY) / 2
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
function rowRegions(row, rowIndex, dividersPt, widestSpacePt) {
|
|
188
|
+
const runs = textRuns(row, widestSpacePt);
|
|
189
|
+
const regions = [];
|
|
190
|
+
let colStart = 0;
|
|
191
|
+
dividersPt.forEach((dividerPt, index) => {
|
|
192
|
+
if (!runs.some((run) => run.loPt < dividerPt && run.hiPt > dividerPt)) {
|
|
193
|
+
regions.push({
|
|
194
|
+
rowStart: rowIndex,
|
|
195
|
+
rowEnd: rowIndex + 1,
|
|
196
|
+
colStart,
|
|
197
|
+
colEnd: index + 1
|
|
198
|
+
});
|
|
199
|
+
colStart = index + 1;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
regions.push({
|
|
203
|
+
rowStart: rowIndex,
|
|
204
|
+
rowEnd: rowIndex + 1,
|
|
205
|
+
colStart,
|
|
206
|
+
colEnd: dividersPt.length + 1
|
|
207
|
+
});
|
|
208
|
+
return regions;
|
|
209
|
+
}
|
|
210
|
+
function rightEdge(item) {
|
|
211
|
+
return item.xPt + (item.widthPt ?? 0);
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
exports.detectRuledRowLattice = detectRuledRowLattice;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GridLattice } from "./lattice.cjs";
|
|
2
|
+
import { LayoutItem } from "pdf-codec";
|
|
3
|
+
//#region src/layout/ruled-rows.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The grid of a table ruled between its rows and set apart by gutters between its columns, when the page holds one.
|
|
6
|
+
* @param items - the page's items; rules are read from its lines, paths and thin rectangles, and text from its text items
|
|
7
|
+
* @returns a lattice whose row boundaries are the rules (and the top of an unruled heading row above them) and whose column boundaries are the table's two ends and the middle of each gutter, a row that crosses a gutter spanning the columns it covers; undefined when no set of rules of one span holds two rows of text, fewer than two rows hold text in more than one place, the rows leave no gutter they all share, or every column reads as running prose
|
|
8
|
+
*/
|
|
9
|
+
declare function detectRuledRowLattice(items: readonly LayoutItem[]): GridLattice | undefined;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { detectRuledRowLattice };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GridLattice } from "./lattice.js";
|
|
2
|
+
import { LayoutItem } from "pdf-codec";
|
|
3
|
+
//#region src/layout/ruled-rows.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The grid of a table ruled between its rows and set apart by gutters between its columns, when the page holds one.
|
|
6
|
+
* @param items - the page's items; rules are read from its lines, paths and thin rectangles, and text from its text items
|
|
7
|
+
* @returns a lattice whose row boundaries are the rules (and the top of an unruled heading row above them) and whose column boundaries are the table's two ends and the middle of each gutter, a row that crosses a gutter spanning the columns it covers; undefined when no set of rules of one span holds two rows of text, fewer than two rows hold text in more than one place, the rows leave no gutter they all share, or every column reads as running prose
|
|
8
|
+
*/
|
|
9
|
+
declare function detectRuledRowLattice(items: readonly LayoutItem[]): GridLattice | undefined;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { detectRuledRowLattice };
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { PARAGRAPH_GAP_MULTIPLIER, clusterIntoLines, estimateModalLineSpacing } from "./reconstruct-lines.js";
|
|
2
|
+
import { EDGE_COVERAGE_RATIO, horizontalRules } from "./lattice.js";
|
|
3
|
+
import { isRunningProse, spaceAdvancePt } from "./reading-order.js";
|
|
4
|
+
//#region src/layout/ruled-rows.ts
|
|
5
|
+
/**
|
|
6
|
+
* The grid of a table ruled between its rows and set apart by gutters between its columns, when the page holds one.
|
|
7
|
+
* @param items - the page's items; rules are read from its lines, paths and thin rectangles, and text from its text items
|
|
8
|
+
* @returns a lattice whose row boundaries are the rules (and the top of an unruled heading row above them) and whose column boundaries are the table's two ends and the middle of each gutter, a row that crosses a gutter spanning the columns it covers; undefined when no set of rules of one span holds two rows of text, fewer than two rows hold text in more than one place, the rows leave no gutter they all share, or every column reads as running prose
|
|
9
|
+
*/
|
|
10
|
+
function detectRuledRowLattice(items) {
|
|
11
|
+
const rules = rulesOfOneTable(items);
|
|
12
|
+
if (rules === void 0) return;
|
|
13
|
+
const text = items.filter((item) => item.kind === "text" && item.text.trim().length > 0 && item.xPt >= rules.leftPt && item.xPt < rules.rightPt);
|
|
14
|
+
if (text.some((item) => item.widthPt === void 0 || item.writingMode !== void 0 || (item.rotationDeg ?? 0) !== 0)) return;
|
|
15
|
+
const ruledRows = rules.positionsDescPt.slice(1).map((lowerPt, row) => text.filter((item) => item.yPt > lowerPt && item.yPt < rules.positionsDescPt[row]));
|
|
16
|
+
const widestSpacePt = Math.max(...ruledRows.flat().map(spaceAdvancePt));
|
|
17
|
+
const shared = sharedGutters(ruledRows, widestSpacePt);
|
|
18
|
+
if (shared === void 0) return;
|
|
19
|
+
const { gutters } = shared;
|
|
20
|
+
const dividersPt = gutters.map((gutter) => (gutter.loPt + gutter.hiPt) / 2);
|
|
21
|
+
const topRulePt = rules.positionsDescPt[0];
|
|
22
|
+
const heading = headingRow(text.filter((item) => item.yPt > topRulePt), topRulePt, dividersPt, clusterIntoLines(ruledRows.flat()), widestSpacePt);
|
|
23
|
+
const rows = heading === void 0 ? ruledRows : [heading.items, ...ruledRows];
|
|
24
|
+
if (shared.rowsWithColumns + (heading !== void 0 && textRuns(heading.items, widestSpacePt).length > 1 ? 1 : 0) < 2) return;
|
|
25
|
+
const columnBoundariesAscPt = [
|
|
26
|
+
rules.leftPt,
|
|
27
|
+
...dividersPt,
|
|
28
|
+
rules.rightPt
|
|
29
|
+
];
|
|
30
|
+
if (holdsDrawing(items, rules, heading?.topPt ?? rules.positionsDescPt[0])) return;
|
|
31
|
+
const narrowestGutterPt = Math.min(...gutters.map((gutter) => gutter.hiPt - gutter.loPt));
|
|
32
|
+
if (columnBoundariesAscPt.slice(1).map((rightPt, column) => rows.flat().filter((item) => item.xPt >= columnBoundariesAscPt[column] && item.xPt < rightPt)).every((column) => isRunningProse(column, narrowestGutterPt))) return;
|
|
33
|
+
const bands = [...heading === void 0 ? [] : [heading], ...ruledRows.map((rowItems, row) => ({
|
|
34
|
+
items: rowItems,
|
|
35
|
+
topPt: rules.positionsDescPt[row]
|
|
36
|
+
}))].flatMap((band) => rowsOfBand(band.items, band.topPt, dividersPt, widestSpacePt));
|
|
37
|
+
return {
|
|
38
|
+
rowBoundariesDescPt: [...bands.map((band) => band.topPt), rules.positionsDescPt.at(-1)],
|
|
39
|
+
columnBoundariesAscPt,
|
|
40
|
+
regions: bands.flatMap((band, rowIndex) => rowRegions(band.items, rowIndex, dividersPt, widestSpacePt)),
|
|
41
|
+
sourceItems: rules.sourceItems
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function rulesOfOneTable(items) {
|
|
45
|
+
const groups = [];
|
|
46
|
+
for (const rule of horizontalRules(items)) {
|
|
47
|
+
const group = groups.find((members) => members.every((member) => spansOverlap(member, rule)));
|
|
48
|
+
if (group === void 0) groups.push([rule]);
|
|
49
|
+
else group.push(rule);
|
|
50
|
+
}
|
|
51
|
+
const best = groups.filter((members) => members.length >= 3).map((members) => ({
|
|
52
|
+
members,
|
|
53
|
+
leftPt: Math.min(...members.map((rule) => rule.leftPt)),
|
|
54
|
+
rightPt: Math.max(...members.map((rule) => rule.rightPt))
|
|
55
|
+
})).sort((a, b) => b.members.length - a.members.length || b.rightPt - b.leftPt - (a.rightPt - a.leftPt))[0];
|
|
56
|
+
if (best === void 0) return;
|
|
57
|
+
return {
|
|
58
|
+
positionsDescPt: best.members.map((rule) => rule.position).sort((a, b) => b - a),
|
|
59
|
+
leftPt: best.leftPt,
|
|
60
|
+
rightPt: best.rightPt,
|
|
61
|
+
sourceItems: new Set(best.members.flatMap((rule) => rule.items))
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function holdsDrawing(items, rules, topPt) {
|
|
65
|
+
const bottomPt = rules.positionsDescPt.at(-1);
|
|
66
|
+
const widthPt = rules.rightPt - rules.leftPt;
|
|
67
|
+
const inside = (box) => box.loXPt < rules.rightPt - 3 && box.hiXPt > rules.leftPt + 3 && box.loYPt < topPt && box.hiYPt > bottomPt;
|
|
68
|
+
const spansRow = (box) => box.hiXPt - box.loXPt >= EDGE_COVERAGE_RATIO * widthPt;
|
|
69
|
+
const horizontal = (box) => box.hiYPt - box.loYPt <= 3 && box.hiXPt - box.loXPt > box.hiYPt - box.loYPt;
|
|
70
|
+
return items.some((item) => {
|
|
71
|
+
if (rules.sourceItems.has(item)) return false;
|
|
72
|
+
const box = boxOf(item);
|
|
73
|
+
return box !== void 0 && inside(box) && !horizontal(box) && !(item.kind !== "image" && item.kind !== "ellipse" && spansRow(box));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function boxOf(item) {
|
|
77
|
+
switch (item.kind) {
|
|
78
|
+
case "image":
|
|
79
|
+
case "rect":
|
|
80
|
+
case "ellipse": return {
|
|
81
|
+
loXPt: item.xPt,
|
|
82
|
+
hiXPt: item.xPt + item.widthPt,
|
|
83
|
+
loYPt: item.yPt,
|
|
84
|
+
hiYPt: item.yPt + item.heightPt
|
|
85
|
+
};
|
|
86
|
+
case "line": return {
|
|
87
|
+
loXPt: Math.min(item.x1Pt, item.x2Pt),
|
|
88
|
+
hiXPt: Math.max(item.x1Pt, item.x2Pt),
|
|
89
|
+
loYPt: Math.min(item.y1Pt, item.y2Pt),
|
|
90
|
+
hiYPt: Math.max(item.y1Pt, item.y2Pt)
|
|
91
|
+
};
|
|
92
|
+
case "path": {
|
|
93
|
+
const xs = item.subpaths.flatMap((subpath) => [subpath.startXPt, ...subpath.segments.flatMap((segment) => segment.kind === "cubic" ? [
|
|
94
|
+
segment.c1xPt,
|
|
95
|
+
segment.c2xPt,
|
|
96
|
+
segment.xPt
|
|
97
|
+
] : [segment.xPt])]);
|
|
98
|
+
const ys = item.subpaths.flatMap((subpath) => [subpath.startYPt, ...subpath.segments.flatMap((segment) => segment.kind === "cubic" ? [
|
|
99
|
+
segment.c1yPt,
|
|
100
|
+
segment.c2yPt,
|
|
101
|
+
segment.yPt
|
|
102
|
+
] : [segment.yPt])]);
|
|
103
|
+
return {
|
|
104
|
+
loXPt: Math.min(...xs),
|
|
105
|
+
hiXPt: Math.max(...xs),
|
|
106
|
+
loYPt: Math.min(...ys),
|
|
107
|
+
hiYPt: Math.max(...ys)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
case "text":
|
|
111
|
+
case "link":
|
|
112
|
+
case "internalLink": return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function spansOverlap(a, b) {
|
|
116
|
+
const overlapPt = Math.min(a.rightPt, b.rightPt) - Math.max(a.leftPt, b.leftPt);
|
|
117
|
+
const unionPt = Math.max(a.rightPt, b.rightPt) - Math.min(a.leftPt, b.leftPt);
|
|
118
|
+
return overlapPt >= EDGE_COVERAGE_RATIO * unionPt;
|
|
119
|
+
}
|
|
120
|
+
function textRuns(row, widestSpacePt) {
|
|
121
|
+
const runs = [];
|
|
122
|
+
for (const item of [...row].sort((a, b) => a.xPt - b.xPt)) {
|
|
123
|
+
const last = runs.at(-1);
|
|
124
|
+
if (last !== void 0 && item.xPt - last.hiPt <= widestSpacePt) last.hiPt = Math.max(last.hiPt, rightEdge(item));
|
|
125
|
+
else runs.push({
|
|
126
|
+
loPt: item.xPt,
|
|
127
|
+
hiPt: rightEdge(item)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return runs;
|
|
131
|
+
}
|
|
132
|
+
function sharedGutters(rows, widestSpacePt) {
|
|
133
|
+
const gapsByRow = rows.map((row) => textRuns(row, widestSpacePt)).filter((runs) => runs.length > 1).map((runs) => runs.slice(1).map((run, index) => ({
|
|
134
|
+
loPt: runs[index].hiPt,
|
|
135
|
+
hiPt: run.loPt
|
|
136
|
+
})));
|
|
137
|
+
const [first, ...rest] = gapsByRow;
|
|
138
|
+
if (first === void 0) return;
|
|
139
|
+
const gutters = rest.reduce((shared, gaps) => shared.flatMap((gutter) => gaps.flatMap((gap) => {
|
|
140
|
+
const loPt = Math.max(gutter.loPt, gap.loPt);
|
|
141
|
+
const hiPt = Math.min(gutter.hiPt, gap.hiPt);
|
|
142
|
+
return hiPt - loPt > widestSpacePt ? [{
|
|
143
|
+
loPt,
|
|
144
|
+
hiPt
|
|
145
|
+
}] : [];
|
|
146
|
+
})), first);
|
|
147
|
+
return gutters.length > 0 ? {
|
|
148
|
+
gutters,
|
|
149
|
+
rowsWithColumns: gapsByRow.length
|
|
150
|
+
} : void 0;
|
|
151
|
+
}
|
|
152
|
+
function headingRow(above, topRulePt, dividersPt, tableLines, widestSpacePt) {
|
|
153
|
+
const reachPt = PARAGRAPH_GAP_MULTIPLIER * estimateModalLineSpacing(tableLines);
|
|
154
|
+
const items = [];
|
|
155
|
+
let lowerPt = topRulePt;
|
|
156
|
+
for (const line of [...clusterIntoLines(above)].reverse()) {
|
|
157
|
+
if (line.items.some((item) => dividersPt.some((dividerPt) => item.xPt < dividerPt && rightEdge(item) > dividerPt)) || line.baselineY - lowerPt > reachPt) break;
|
|
158
|
+
items.push(...line.items);
|
|
159
|
+
lowerPt = line.baselineY;
|
|
160
|
+
}
|
|
161
|
+
if (columnsHolding(items, dividersPt, widestSpacePt).size < 2) return;
|
|
162
|
+
return {
|
|
163
|
+
items,
|
|
164
|
+
topPt: Math.max(...items.map((item) => item.yPt + item.sizePt))
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function columnsHolding(items, dividersPt, widestSpacePt) {
|
|
168
|
+
return new Set(textRuns(items, widestSpacePt).map((run) => dividersPt.filter((dividerPt) => dividerPt <= run.loPt).length));
|
|
169
|
+
}
|
|
170
|
+
function rowsOfBand(items, topPt, dividersPt, widestSpacePt) {
|
|
171
|
+
const lines = clusterIntoLines(items);
|
|
172
|
+
const first = lines[0];
|
|
173
|
+
const opening = first === void 0 ? /* @__PURE__ */ new Set() : columnsHolding(first.items, dividersPt, widestSpacePt);
|
|
174
|
+
if (!(lines.length > 1 && opening.size > 1 && lines.every((line) => {
|
|
175
|
+
const occupied = columnsHolding(line.items, dividersPt, widestSpacePt);
|
|
176
|
+
return [...opening].every((column) => occupied.has(column));
|
|
177
|
+
}))) return [{
|
|
178
|
+
items: [...items],
|
|
179
|
+
topPt
|
|
180
|
+
}];
|
|
181
|
+
return lines.map((line, index) => ({
|
|
182
|
+
items: [...line.items],
|
|
183
|
+
topPt: index === 0 ? topPt : (lines[index - 1].baselineY + line.baselineY) / 2
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
function rowRegions(row, rowIndex, dividersPt, widestSpacePt) {
|
|
187
|
+
const runs = textRuns(row, widestSpacePt);
|
|
188
|
+
const regions = [];
|
|
189
|
+
let colStart = 0;
|
|
190
|
+
dividersPt.forEach((dividerPt, index) => {
|
|
191
|
+
if (!runs.some((run) => run.loPt < dividerPt && run.hiPt > dividerPt)) {
|
|
192
|
+
regions.push({
|
|
193
|
+
rowStart: rowIndex,
|
|
194
|
+
rowEnd: rowIndex + 1,
|
|
195
|
+
colStart,
|
|
196
|
+
colEnd: index + 1
|
|
197
|
+
});
|
|
198
|
+
colStart = index + 1;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
regions.push({
|
|
202
|
+
rowStart: rowIndex,
|
|
203
|
+
rowEnd: rowIndex + 1,
|
|
204
|
+
colStart,
|
|
205
|
+
colEnd: dividersPt.length + 1
|
|
206
|
+
});
|
|
207
|
+
return regions;
|
|
208
|
+
}
|
|
209
|
+
function rightEdge(item) {
|
|
210
|
+
return item.xPt + (item.widthPt ?? 0);
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { detectRuledRowLattice };
|
|
@@ -6,6 +6,7 @@ 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
8
|
import { compareReadingPosition, readingOrder } from "./layout/reading-order.js";
|
|
9
|
+
import { detectRuledRowLattice } from "./layout/ruled-rows.js";
|
|
9
10
|
import { denseTableRows } from "document-schema.js";
|
|
10
11
|
import { sourcePageIndex } from "pdf-codec/layout";
|
|
11
12
|
//#region src/layout/reconstruct-tables.ts
|
|
@@ -14,8 +15,23 @@ function recoverTables(page, pageIndex, structure) {
|
|
|
14
15
|
const tagged = recoverTaggedTables(page, pageIndex, structure);
|
|
15
16
|
if (tagged.length > 0) return tagged;
|
|
16
17
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const grid = detectGridLattice(page.items);
|
|
19
|
+
const gridItems = grid?.sourceItems ?? NO_ITEMS;
|
|
20
|
+
return [grid, detectRuledRowLattice(page.items.filter((item) => !gridItems.has(item)))].filter((lattice) => lattice !== void 0).filter((lattice, index, lattices) => index === 0 || !latticesOverlap(lattices[0], lattice)).flatMap((lattice) => {
|
|
21
|
+
const table = recoverTable(page, pageIndex, lattice);
|
|
22
|
+
return table === void 0 ? [] : [table];
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function latticesOverlap(a, b) {
|
|
26
|
+
const aLeft = a.columnBoundariesAscPt[0];
|
|
27
|
+
const aRight = a.columnBoundariesAscPt.at(-1);
|
|
28
|
+
const aTop = a.rowBoundariesDescPt[0];
|
|
29
|
+
const aBottom = a.rowBoundariesDescPt.at(-1);
|
|
30
|
+
const bLeft = b.columnBoundariesAscPt[0];
|
|
31
|
+
const bRight = b.columnBoundariesAscPt.at(-1);
|
|
32
|
+
const bTop = b.rowBoundariesDescPt[0];
|
|
33
|
+
const bBottom = b.rowBoundariesDescPt.at(-1);
|
|
34
|
+
return aLeft < bRight && bLeft < aRight && aBottom < bTop && bBottom < aTop;
|
|
19
35
|
}
|
|
20
36
|
function recoverTaggedTables(page, pageIndex, structure) {
|
|
21
37
|
const cellsByTable = /* @__PURE__ */ new Map();
|
|
@@ -126,9 +142,7 @@ function indexRegions(lattice, rowCount, columnCount) {
|
|
|
126
142
|
for (const region of lattice.regions) for (let i = region.rowStart; i < region.rowEnd; i++) for (let j = region.colStart; j < region.colEnd; j++) regionAt[i][j] = region;
|
|
127
143
|
return regionAt;
|
|
128
144
|
}
|
|
129
|
-
function recoverTable(page, pageIndex) {
|
|
130
|
-
const lattice = detectGridLattice(page.items);
|
|
131
|
-
if (lattice === void 0) return;
|
|
145
|
+
function recoverTable(page, pageIndex, lattice) {
|
|
132
146
|
const rowCount = lattice.rowBoundariesDescPt.length - 1;
|
|
133
147
|
const columnCount = lattice.columnBoundariesAscPt.length - 1;
|
|
134
148
|
const regionAt = indexRegions(lattice, rowCount, columnCount);
|