js.documents 6.1.1 → 6.1.3
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/dist/layout/lattice.cjs +117 -41
- package/dist/layout/lattice.d.cts +8 -1
- package/dist/layout/lattice.d.ts +8 -1
- package/dist/layout/lattice.js +117 -41
- package/dist/layout/reconstruct.cjs +31 -8
- package/dist/layout/reconstruct.js +31 -8
- package/package.json +6 -6
package/dist/layout/lattice.cjs
CHANGED
|
@@ -74,28 +74,21 @@ function classifyAxisLine(seg) {
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
const POSITION_DEDUPE_TOLERANCE_PT = .5;
|
|
77
|
-
function
|
|
77
|
+
function mergedRanges(segments) {
|
|
78
78
|
const sorted = [...segments].sort((a, b) => a.startPt - b.startPt);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
const ranges = [{
|
|
80
|
+
startPt: sorted[0].startPt,
|
|
81
|
+
endPt: sorted[0].endPt
|
|
82
|
+
}];
|
|
83
83
|
for (const segment of sorted.slice(1)) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
bestStart = runStart;
|
|
91
|
-
bestEnd = runEnd;
|
|
92
|
-
}
|
|
84
|
+
const last = ranges[ranges.length - 1];
|
|
85
|
+
if (segment.startPt <= last.endPt + POSITION_DEDUPE_TOLERANCE_PT) last.endPt = Math.max(last.endPt, segment.endPt);
|
|
86
|
+
else ranges.push({
|
|
87
|
+
startPt: segment.startPt,
|
|
88
|
+
endPt: segment.endPt
|
|
89
|
+
});
|
|
93
90
|
}
|
|
94
|
-
return
|
|
95
|
-
spanPt: bestEnd - bestStart,
|
|
96
|
-
startPt: bestStart,
|
|
97
|
-
endPt: bestEnd
|
|
98
|
-
};
|
|
91
|
+
return ranges;
|
|
99
92
|
}
|
|
100
93
|
function dedupeAxisLines(segments, descending) {
|
|
101
94
|
const sorted = [...segments].sort((a, b) => descending ? b.position - a.position : a.position - b.position);
|
|
@@ -105,24 +98,103 @@ function dedupeAxisLines(segments, descending) {
|
|
|
105
98
|
if (last !== void 0 && Math.abs(candidate.position - last[0].position) <= POSITION_DEDUPE_TOLERANCE_PT) last.push(candidate);
|
|
106
99
|
else groups.push([candidate]);
|
|
107
100
|
}
|
|
108
|
-
return groups.map((group) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
startPt,
|
|
114
|
-
endPt,
|
|
115
|
-
items: group.map((segment) => segment.item)
|
|
116
|
-
};
|
|
117
|
-
});
|
|
101
|
+
return groups.map((group) => ({
|
|
102
|
+
position: group[0].position,
|
|
103
|
+
ranges: mergedRanges(group),
|
|
104
|
+
items: group.map((segment) => segment.item)
|
|
105
|
+
}));
|
|
118
106
|
}
|
|
119
107
|
const MIN_GRIDLINE_COUNT_PER_AXIS = 3;
|
|
120
|
-
const
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
const EDGE_COVERAGE_RATIO = .9;
|
|
109
|
+
function bestRunCoverageRatio(ranges, aPt, bPt) {
|
|
110
|
+
const lo = Math.min(aPt, bPt);
|
|
111
|
+
const hi = Math.max(aPt, bPt);
|
|
112
|
+
if (hi <= lo) return 1;
|
|
113
|
+
let bestPt = 0;
|
|
114
|
+
for (const range of ranges) {
|
|
115
|
+
const overlapLo = Math.max(range.startPt, lo);
|
|
116
|
+
const overlapHi = Math.min(range.endPt, hi);
|
|
117
|
+
if (overlapHi > overlapLo) bestPt = Math.max(bestPt, overlapHi - overlapLo);
|
|
118
|
+
}
|
|
119
|
+
return bestPt / (hi - lo);
|
|
120
|
+
}
|
|
121
|
+
function hasClosedOuterRectangle(rowLines, columnLines) {
|
|
122
|
+
const leftXPt = columnLines[0].position;
|
|
123
|
+
const rightXPt = columnLines[columnLines.length - 1].position;
|
|
124
|
+
const topYPt = rowLines[0].position;
|
|
125
|
+
const bottomYPt = rowLines[rowLines.length - 1].position;
|
|
126
|
+
return bestRunCoverageRatio(rowLines[0].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && bestRunCoverageRatio(rowLines[rowLines.length - 1].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && bestRunCoverageRatio(columnLines[0].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO && bestRunCoverageRatio(columnLines[columnLines.length - 1].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO;
|
|
127
|
+
}
|
|
128
|
+
function unionFind(size) {
|
|
129
|
+
return Array.from({ length: size }, (_, i) => i);
|
|
130
|
+
}
|
|
131
|
+
function findRoot(parents, index) {
|
|
132
|
+
let root = index;
|
|
133
|
+
while (parents[root] !== root) root = parents[root];
|
|
134
|
+
let current = index;
|
|
135
|
+
while (parents[current] !== root) {
|
|
136
|
+
const next = parents[current];
|
|
137
|
+
parents[current] = root;
|
|
138
|
+
current = next;
|
|
139
|
+
}
|
|
140
|
+
return root;
|
|
141
|
+
}
|
|
142
|
+
function union(parents, a, b) {
|
|
143
|
+
const rootA = findRoot(parents, a);
|
|
144
|
+
const rootB = findRoot(parents, b);
|
|
145
|
+
if (rootA !== rootB) parents[rootA] = rootB;
|
|
146
|
+
}
|
|
147
|
+
function findCellRegions(rowLines, columnLines) {
|
|
148
|
+
const rowCount = rowLines.length - 1;
|
|
149
|
+
const colCount = columnLines.length - 1;
|
|
150
|
+
const indexOf = (row, col) => row * colCount + col;
|
|
151
|
+
const parents = unionFind(rowCount * colCount);
|
|
152
|
+
for (let i = 0; i < rowCount; i++) {
|
|
153
|
+
const rowTopPt = rowLines[i].position;
|
|
154
|
+
const rowBottomPt = rowLines[i + 1].position;
|
|
155
|
+
for (let j = 0; j < colCount - 1; j++) {
|
|
156
|
+
const divider = columnLines[j + 1];
|
|
157
|
+
if (bestRunCoverageRatio(divider.ranges, rowBottomPt, rowTopPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i, j + 1));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (let j = 0; j < colCount; j++) {
|
|
161
|
+
const colLeftPt = columnLines[j].position;
|
|
162
|
+
const colRightPt = columnLines[j + 1].position;
|
|
163
|
+
for (let i = 0; i < rowCount - 1; i++) {
|
|
164
|
+
const divider = rowLines[i + 1];
|
|
165
|
+
if (bestRunCoverageRatio(divider.ranges, colLeftPt, colRightPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i + 1, j));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const cellsByRoot = /* @__PURE__ */ new Map();
|
|
169
|
+
for (let i = 0; i < rowCount; i++) for (let j = 0; j < colCount; j++) {
|
|
170
|
+
const root = findRoot(parents, indexOf(i, j));
|
|
171
|
+
const cells = cellsByRoot.get(root);
|
|
172
|
+
if (cells === void 0) cellsByRoot.set(root, [{
|
|
173
|
+
row: i,
|
|
174
|
+
col: j
|
|
175
|
+
}]);
|
|
176
|
+
else cells.push({
|
|
177
|
+
row: i,
|
|
178
|
+
col: j
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (cellsByRoot.size <= 1) return;
|
|
182
|
+
const regions = [];
|
|
183
|
+
for (const cells of cellsByRoot.values()) {
|
|
184
|
+
const rowStart = Math.min(...cells.map((c) => c.row));
|
|
185
|
+
const rowEnd = Math.max(...cells.map((c) => c.row)) + 1;
|
|
186
|
+
const colStart = Math.min(...cells.map((c) => c.col));
|
|
187
|
+
const colEnd = Math.max(...cells.map((c) => c.col)) + 1;
|
|
188
|
+
if (cells.length !== (rowEnd - rowStart) * (colEnd - colStart)) return;
|
|
189
|
+
regions.push({
|
|
190
|
+
rowStart,
|
|
191
|
+
rowEnd,
|
|
192
|
+
colStart,
|
|
193
|
+
colEnd
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
regions.sort((a, b) => a.rowStart - b.rowStart || a.colStart - b.colStart);
|
|
197
|
+
return regions;
|
|
126
198
|
}
|
|
127
199
|
function detectGridLattice(items) {
|
|
128
200
|
const horizontal = [];
|
|
@@ -132,14 +204,18 @@ function detectGridLattice(items) {
|
|
|
132
204
|
if (classified === void 0) continue;
|
|
133
205
|
(classified.axis === "horizontal" ? horizontal : vertical).push(classified);
|
|
134
206
|
}
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
if (
|
|
207
|
+
const rowLines = dedupeAxisLines(horizontal, true);
|
|
208
|
+
const columnLines = dedupeAxisLines(vertical, false);
|
|
209
|
+
if (rowLines.length < MIN_GRIDLINE_COUNT_PER_AXIS || columnLines.length < MIN_GRIDLINE_COUNT_PER_AXIS) return;
|
|
210
|
+
if (!hasClosedOuterRectangle(rowLines, columnLines)) return;
|
|
211
|
+
const regions = findCellRegions(rowLines, columnLines);
|
|
212
|
+
if (regions === void 0) return;
|
|
138
213
|
const sourceItems = /* @__PURE__ */ new Set();
|
|
139
|
-
for (const line of [...
|
|
214
|
+
for (const line of [...rowLines, ...columnLines]) for (const item of line.items) sourceItems.add(item);
|
|
140
215
|
return {
|
|
141
|
-
rowBoundariesDescPt:
|
|
142
|
-
columnBoundariesAscPt:
|
|
216
|
+
rowBoundariesDescPt: rowLines.map((l) => l.position),
|
|
217
|
+
columnBoundariesAscPt: columnLines.map((l) => l.position),
|
|
218
|
+
regions,
|
|
143
219
|
sourceItems
|
|
144
220
|
};
|
|
145
221
|
}
|
|
@@ -8,13 +8,20 @@ interface LineSegment {
|
|
|
8
8
|
readonly y2Pt: number;
|
|
9
9
|
}
|
|
10
10
|
declare function extractLineCandidates(items: readonly LayoutItem[]): LineSegment[];
|
|
11
|
+
interface TableRegion {
|
|
12
|
+
readonly rowStart: number;
|
|
13
|
+
readonly rowEnd: number;
|
|
14
|
+
readonly colStart: number;
|
|
15
|
+
readonly colEnd: number;
|
|
16
|
+
}
|
|
11
17
|
interface GridLattice {
|
|
12
18
|
readonly rowBoundariesDescPt: readonly number[];
|
|
13
19
|
readonly columnBoundariesAscPt: readonly number[];
|
|
20
|
+
readonly regions: readonly TableRegion[];
|
|
14
21
|
readonly sourceItems: ReadonlySet<LayoutItem>;
|
|
15
22
|
}
|
|
16
23
|
declare function detectGridLattice(items: readonly LayoutItem[]): GridLattice | undefined;
|
|
17
24
|
declare function findRowIndex(rowBoundariesDescPt: readonly number[], yPt: number): number | undefined;
|
|
18
25
|
declare function findColumnIndex(columnBoundariesAscPt: readonly number[], xPt: number): number | undefined;
|
|
19
26
|
//#endregion
|
|
20
|
-
export { GridLattice, LineSegment, detectGridLattice, extractLineCandidates, findColumnIndex, findRowIndex };
|
|
27
|
+
export { GridLattice, LineSegment, TableRegion, detectGridLattice, extractLineCandidates, findColumnIndex, findRowIndex };
|
package/dist/layout/lattice.d.ts
CHANGED
|
@@ -8,13 +8,20 @@ interface LineSegment {
|
|
|
8
8
|
readonly y2Pt: number;
|
|
9
9
|
}
|
|
10
10
|
declare function extractLineCandidates(items: readonly LayoutItem[]): LineSegment[];
|
|
11
|
+
interface TableRegion {
|
|
12
|
+
readonly rowStart: number;
|
|
13
|
+
readonly rowEnd: number;
|
|
14
|
+
readonly colStart: number;
|
|
15
|
+
readonly colEnd: number;
|
|
16
|
+
}
|
|
11
17
|
interface GridLattice {
|
|
12
18
|
readonly rowBoundariesDescPt: readonly number[];
|
|
13
19
|
readonly columnBoundariesAscPt: readonly number[];
|
|
20
|
+
readonly regions: readonly TableRegion[];
|
|
14
21
|
readonly sourceItems: ReadonlySet<LayoutItem>;
|
|
15
22
|
}
|
|
16
23
|
declare function detectGridLattice(items: readonly LayoutItem[]): GridLattice | undefined;
|
|
17
24
|
declare function findRowIndex(rowBoundariesDescPt: readonly number[], yPt: number): number | undefined;
|
|
18
25
|
declare function findColumnIndex(columnBoundariesAscPt: readonly number[], xPt: number): number | undefined;
|
|
19
26
|
//#endregion
|
|
20
|
-
export { GridLattice, LineSegment, detectGridLattice, extractLineCandidates, findColumnIndex, findRowIndex };
|
|
27
|
+
export { GridLattice, LineSegment, TableRegion, detectGridLattice, extractLineCandidates, findColumnIndex, findRowIndex };
|
package/dist/layout/lattice.js
CHANGED
|
@@ -73,28 +73,21 @@ function classifyAxisLine(seg) {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
const POSITION_DEDUPE_TOLERANCE_PT = .5;
|
|
76
|
-
function
|
|
76
|
+
function mergedRanges(segments) {
|
|
77
77
|
const sorted = [...segments].sort((a, b) => a.startPt - b.startPt);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
const ranges = [{
|
|
79
|
+
startPt: sorted[0].startPt,
|
|
80
|
+
endPt: sorted[0].endPt
|
|
81
|
+
}];
|
|
82
82
|
for (const segment of sorted.slice(1)) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
bestStart = runStart;
|
|
90
|
-
bestEnd = runEnd;
|
|
91
|
-
}
|
|
83
|
+
const last = ranges[ranges.length - 1];
|
|
84
|
+
if (segment.startPt <= last.endPt + POSITION_DEDUPE_TOLERANCE_PT) last.endPt = Math.max(last.endPt, segment.endPt);
|
|
85
|
+
else ranges.push({
|
|
86
|
+
startPt: segment.startPt,
|
|
87
|
+
endPt: segment.endPt
|
|
88
|
+
});
|
|
92
89
|
}
|
|
93
|
-
return
|
|
94
|
-
spanPt: bestEnd - bestStart,
|
|
95
|
-
startPt: bestStart,
|
|
96
|
-
endPt: bestEnd
|
|
97
|
-
};
|
|
90
|
+
return ranges;
|
|
98
91
|
}
|
|
99
92
|
function dedupeAxisLines(segments, descending) {
|
|
100
93
|
const sorted = [...segments].sort((a, b) => descending ? b.position - a.position : a.position - b.position);
|
|
@@ -104,24 +97,103 @@ function dedupeAxisLines(segments, descending) {
|
|
|
104
97
|
if (last !== void 0 && Math.abs(candidate.position - last[0].position) <= POSITION_DEDUPE_TOLERANCE_PT) last.push(candidate);
|
|
105
98
|
else groups.push([candidate]);
|
|
106
99
|
}
|
|
107
|
-
return groups.map((group) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
startPt,
|
|
113
|
-
endPt,
|
|
114
|
-
items: group.map((segment) => segment.item)
|
|
115
|
-
};
|
|
116
|
-
});
|
|
100
|
+
return groups.map((group) => ({
|
|
101
|
+
position: group[0].position,
|
|
102
|
+
ranges: mergedRanges(group),
|
|
103
|
+
items: group.map((segment) => segment.item)
|
|
104
|
+
}));
|
|
117
105
|
}
|
|
118
106
|
const MIN_GRIDLINE_COUNT_PER_AXIS = 3;
|
|
119
|
-
const
|
|
120
|
-
function
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
107
|
+
const EDGE_COVERAGE_RATIO = .9;
|
|
108
|
+
function bestRunCoverageRatio(ranges, aPt, bPt) {
|
|
109
|
+
const lo = Math.min(aPt, bPt);
|
|
110
|
+
const hi = Math.max(aPt, bPt);
|
|
111
|
+
if (hi <= lo) return 1;
|
|
112
|
+
let bestPt = 0;
|
|
113
|
+
for (const range of ranges) {
|
|
114
|
+
const overlapLo = Math.max(range.startPt, lo);
|
|
115
|
+
const overlapHi = Math.min(range.endPt, hi);
|
|
116
|
+
if (overlapHi > overlapLo) bestPt = Math.max(bestPt, overlapHi - overlapLo);
|
|
117
|
+
}
|
|
118
|
+
return bestPt / (hi - lo);
|
|
119
|
+
}
|
|
120
|
+
function hasClosedOuterRectangle(rowLines, columnLines) {
|
|
121
|
+
const leftXPt = columnLines[0].position;
|
|
122
|
+
const rightXPt = columnLines[columnLines.length - 1].position;
|
|
123
|
+
const topYPt = rowLines[0].position;
|
|
124
|
+
const bottomYPt = rowLines[rowLines.length - 1].position;
|
|
125
|
+
return bestRunCoverageRatio(rowLines[0].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && bestRunCoverageRatio(rowLines[rowLines.length - 1].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && bestRunCoverageRatio(columnLines[0].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO && bestRunCoverageRatio(columnLines[columnLines.length - 1].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO;
|
|
126
|
+
}
|
|
127
|
+
function unionFind(size) {
|
|
128
|
+
return Array.from({ length: size }, (_, i) => i);
|
|
129
|
+
}
|
|
130
|
+
function findRoot(parents, index) {
|
|
131
|
+
let root = index;
|
|
132
|
+
while (parents[root] !== root) root = parents[root];
|
|
133
|
+
let current = index;
|
|
134
|
+
while (parents[current] !== root) {
|
|
135
|
+
const next = parents[current];
|
|
136
|
+
parents[current] = root;
|
|
137
|
+
current = next;
|
|
138
|
+
}
|
|
139
|
+
return root;
|
|
140
|
+
}
|
|
141
|
+
function union(parents, a, b) {
|
|
142
|
+
const rootA = findRoot(parents, a);
|
|
143
|
+
const rootB = findRoot(parents, b);
|
|
144
|
+
if (rootA !== rootB) parents[rootA] = rootB;
|
|
145
|
+
}
|
|
146
|
+
function findCellRegions(rowLines, columnLines) {
|
|
147
|
+
const rowCount = rowLines.length - 1;
|
|
148
|
+
const colCount = columnLines.length - 1;
|
|
149
|
+
const indexOf = (row, col) => row * colCount + col;
|
|
150
|
+
const parents = unionFind(rowCount * colCount);
|
|
151
|
+
for (let i = 0; i < rowCount; i++) {
|
|
152
|
+
const rowTopPt = rowLines[i].position;
|
|
153
|
+
const rowBottomPt = rowLines[i + 1].position;
|
|
154
|
+
for (let j = 0; j < colCount - 1; j++) {
|
|
155
|
+
const divider = columnLines[j + 1];
|
|
156
|
+
if (bestRunCoverageRatio(divider.ranges, rowBottomPt, rowTopPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i, j + 1));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (let j = 0; j < colCount; j++) {
|
|
160
|
+
const colLeftPt = columnLines[j].position;
|
|
161
|
+
const colRightPt = columnLines[j + 1].position;
|
|
162
|
+
for (let i = 0; i < rowCount - 1; i++) {
|
|
163
|
+
const divider = rowLines[i + 1];
|
|
164
|
+
if (bestRunCoverageRatio(divider.ranges, colLeftPt, colRightPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i + 1, j));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const cellsByRoot = /* @__PURE__ */ new Map();
|
|
168
|
+
for (let i = 0; i < rowCount; i++) for (let j = 0; j < colCount; j++) {
|
|
169
|
+
const root = findRoot(parents, indexOf(i, j));
|
|
170
|
+
const cells = cellsByRoot.get(root);
|
|
171
|
+
if (cells === void 0) cellsByRoot.set(root, [{
|
|
172
|
+
row: i,
|
|
173
|
+
col: j
|
|
174
|
+
}]);
|
|
175
|
+
else cells.push({
|
|
176
|
+
row: i,
|
|
177
|
+
col: j
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (cellsByRoot.size <= 1) return;
|
|
181
|
+
const regions = [];
|
|
182
|
+
for (const cells of cellsByRoot.values()) {
|
|
183
|
+
const rowStart = Math.min(...cells.map((c) => c.row));
|
|
184
|
+
const rowEnd = Math.max(...cells.map((c) => c.row)) + 1;
|
|
185
|
+
const colStart = Math.min(...cells.map((c) => c.col));
|
|
186
|
+
const colEnd = Math.max(...cells.map((c) => c.col)) + 1;
|
|
187
|
+
if (cells.length !== (rowEnd - rowStart) * (colEnd - colStart)) return;
|
|
188
|
+
regions.push({
|
|
189
|
+
rowStart,
|
|
190
|
+
rowEnd,
|
|
191
|
+
colStart,
|
|
192
|
+
colEnd
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
regions.sort((a, b) => a.rowStart - b.rowStart || a.colStart - b.colStart);
|
|
196
|
+
return regions;
|
|
125
197
|
}
|
|
126
198
|
function detectGridLattice(items) {
|
|
127
199
|
const horizontal = [];
|
|
@@ -131,14 +203,18 @@ function detectGridLattice(items) {
|
|
|
131
203
|
if (classified === void 0) continue;
|
|
132
204
|
(classified.axis === "horizontal" ? horizontal : vertical).push(classified);
|
|
133
205
|
}
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
206
|
+
const rowLines = dedupeAxisLines(horizontal, true);
|
|
207
|
+
const columnLines = dedupeAxisLines(vertical, false);
|
|
208
|
+
if (rowLines.length < MIN_GRIDLINE_COUNT_PER_AXIS || columnLines.length < MIN_GRIDLINE_COUNT_PER_AXIS) return;
|
|
209
|
+
if (!hasClosedOuterRectangle(rowLines, columnLines)) return;
|
|
210
|
+
const regions = findCellRegions(rowLines, columnLines);
|
|
211
|
+
if (regions === void 0) return;
|
|
137
212
|
const sourceItems = /* @__PURE__ */ new Set();
|
|
138
|
-
for (const line of [...
|
|
213
|
+
for (const line of [...rowLines, ...columnLines]) for (const item of line.items) sourceItems.add(item);
|
|
139
214
|
return {
|
|
140
|
-
rowBoundariesDescPt:
|
|
141
|
-
columnBoundariesAscPt:
|
|
215
|
+
rowBoundariesDescPt: rowLines.map((l) => l.position),
|
|
216
|
+
columnBoundariesAscPt: columnLines.map((l) => l.position),
|
|
217
|
+
regions,
|
|
142
218
|
sourceItems
|
|
143
219
|
};
|
|
144
220
|
}
|
|
@@ -1129,19 +1129,28 @@ function textItemsPdfBox(items) {
|
|
|
1129
1129
|
function cellBlocksFromItems(items, pageIndex) {
|
|
1130
1130
|
return clusterIntoLines(items).map((line) => lineToParagraph(line, pageIndex));
|
|
1131
1131
|
}
|
|
1132
|
+
function indexRegions(lattice, rowCount, columnCount) {
|
|
1133
|
+
const regionAt = Array.from({ length: rowCount }, () => new Array(columnCount));
|
|
1134
|
+
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;
|
|
1135
|
+
return regionAt;
|
|
1136
|
+
}
|
|
1132
1137
|
function recoverTable(page, pageIndex) {
|
|
1133
1138
|
const lattice = require_layout_lattice.detectGridLattice(page.items);
|
|
1134
1139
|
if (lattice === void 0) return;
|
|
1135
1140
|
const rowCount = lattice.rowBoundariesDescPt.length - 1;
|
|
1136
1141
|
const columnCount = lattice.columnBoundariesAscPt.length - 1;
|
|
1137
|
-
const
|
|
1142
|
+
const regionAt = indexRegions(lattice, rowCount, columnCount);
|
|
1143
|
+
const textByRegion = /* @__PURE__ */ new Map();
|
|
1138
1144
|
const consumedText = /* @__PURE__ */ new Set();
|
|
1139
1145
|
for (const item of page.items) {
|
|
1140
1146
|
if (item.kind !== "text") continue;
|
|
1141
1147
|
const row = require_layout_lattice.findRowIndex(lattice.rowBoundariesDescPt, item.yPt);
|
|
1142
1148
|
const column = require_layout_lattice.findColumnIndex(lattice.columnBoundariesAscPt, item.xPt);
|
|
1143
1149
|
if (row === void 0 || column === void 0) continue;
|
|
1144
|
-
|
|
1150
|
+
const region = regionAt[row][column];
|
|
1151
|
+
const existing = textByRegion.get(region);
|
|
1152
|
+
if (existing === void 0) textByRegion.set(region, [item]);
|
|
1153
|
+
else existing.push(item);
|
|
1145
1154
|
consumedText.add(item);
|
|
1146
1155
|
}
|
|
1147
1156
|
if (consumedText.size === 0) return;
|
|
@@ -1150,12 +1159,25 @@ function recoverTable(page, pageIndex) {
|
|
|
1150
1159
|
const rows = [];
|
|
1151
1160
|
for (let i = 0; i < rowCount; i++) {
|
|
1152
1161
|
const cells = [];
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1162
|
+
let j = 0;
|
|
1163
|
+
while (j < columnCount) {
|
|
1164
|
+
const region = regionAt[i][j];
|
|
1165
|
+
if (region.rowStart !== i) {
|
|
1166
|
+
cells.push({ blocks: [] });
|
|
1167
|
+
j = region.colEnd;
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
const colSpan = region.colEnd - region.colStart;
|
|
1171
|
+
const rowSpan = region.rowEnd - region.rowStart;
|
|
1172
|
+
const cell = {
|
|
1173
|
+
blocks: cellBlocksFromItems(textByRegion.get(region) ?? [], pageIndex),
|
|
1174
|
+
colSpan: colSpan > 1 ? colSpan : void 0,
|
|
1175
|
+
rowSpan: rowSpan > 1 ? rowSpan : void 0
|
|
1176
|
+
};
|
|
1177
|
+
const cellLeftXPt = lattice.columnBoundariesAscPt[region.colStart];
|
|
1178
|
+
const cellRightXPt = lattice.columnBoundariesAscPt[region.colEnd];
|
|
1179
|
+
const cellTopYPt = lattice.rowBoundariesDescPt[region.rowStart];
|
|
1180
|
+
const cellBottomYPt = lattice.rowBoundariesDescPt[region.rowEnd];
|
|
1159
1181
|
require_layout_shared.stampFrame(cell, pageIndex, {
|
|
1160
1182
|
xPt: cellLeftXPt,
|
|
1161
1183
|
yPt: cellBottomYPt,
|
|
@@ -1163,6 +1185,7 @@ function recoverTable(page, pageIndex) {
|
|
|
1163
1185
|
heightPt: cellTopYPt - cellBottomYPt
|
|
1164
1186
|
});
|
|
1165
1187
|
cells.push(cell);
|
|
1188
|
+
j = region.colEnd;
|
|
1166
1189
|
}
|
|
1167
1190
|
rows.push({
|
|
1168
1191
|
cells,
|
|
@@ -1128,19 +1128,28 @@ function textItemsPdfBox(items) {
|
|
|
1128
1128
|
function cellBlocksFromItems(items, pageIndex) {
|
|
1129
1129
|
return clusterIntoLines(items).map((line) => lineToParagraph(line, pageIndex));
|
|
1130
1130
|
}
|
|
1131
|
+
function indexRegions(lattice, rowCount, columnCount) {
|
|
1132
|
+
const regionAt = Array.from({ length: rowCount }, () => new Array(columnCount));
|
|
1133
|
+
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;
|
|
1134
|
+
return regionAt;
|
|
1135
|
+
}
|
|
1131
1136
|
function recoverTable(page, pageIndex) {
|
|
1132
1137
|
const lattice = detectGridLattice(page.items);
|
|
1133
1138
|
if (lattice === void 0) return;
|
|
1134
1139
|
const rowCount = lattice.rowBoundariesDescPt.length - 1;
|
|
1135
1140
|
const columnCount = lattice.columnBoundariesAscPt.length - 1;
|
|
1136
|
-
const
|
|
1141
|
+
const regionAt = indexRegions(lattice, rowCount, columnCount);
|
|
1142
|
+
const textByRegion = /* @__PURE__ */ new Map();
|
|
1137
1143
|
const consumedText = /* @__PURE__ */ new Set();
|
|
1138
1144
|
for (const item of page.items) {
|
|
1139
1145
|
if (item.kind !== "text") continue;
|
|
1140
1146
|
const row = findRowIndex(lattice.rowBoundariesDescPt, item.yPt);
|
|
1141
1147
|
const column = findColumnIndex(lattice.columnBoundariesAscPt, item.xPt);
|
|
1142
1148
|
if (row === void 0 || column === void 0) continue;
|
|
1143
|
-
|
|
1149
|
+
const region = regionAt[row][column];
|
|
1150
|
+
const existing = textByRegion.get(region);
|
|
1151
|
+
if (existing === void 0) textByRegion.set(region, [item]);
|
|
1152
|
+
else existing.push(item);
|
|
1144
1153
|
consumedText.add(item);
|
|
1145
1154
|
}
|
|
1146
1155
|
if (consumedText.size === 0) return;
|
|
@@ -1149,12 +1158,25 @@ function recoverTable(page, pageIndex) {
|
|
|
1149
1158
|
const rows = [];
|
|
1150
1159
|
for (let i = 0; i < rowCount; i++) {
|
|
1151
1160
|
const cells = [];
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1161
|
+
let j = 0;
|
|
1162
|
+
while (j < columnCount) {
|
|
1163
|
+
const region = regionAt[i][j];
|
|
1164
|
+
if (region.rowStart !== i) {
|
|
1165
|
+
cells.push({ blocks: [] });
|
|
1166
|
+
j = region.colEnd;
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
const colSpan = region.colEnd - region.colStart;
|
|
1170
|
+
const rowSpan = region.rowEnd - region.rowStart;
|
|
1171
|
+
const cell = {
|
|
1172
|
+
blocks: cellBlocksFromItems(textByRegion.get(region) ?? [], pageIndex),
|
|
1173
|
+
colSpan: colSpan > 1 ? colSpan : void 0,
|
|
1174
|
+
rowSpan: rowSpan > 1 ? rowSpan : void 0
|
|
1175
|
+
};
|
|
1176
|
+
const cellLeftXPt = lattice.columnBoundariesAscPt[region.colStart];
|
|
1177
|
+
const cellRightXPt = lattice.columnBoundariesAscPt[region.colEnd];
|
|
1178
|
+
const cellTopYPt = lattice.rowBoundariesDescPt[region.rowStart];
|
|
1179
|
+
const cellBottomYPt = lattice.rowBoundariesDescPt[region.rowEnd];
|
|
1158
1180
|
stampFrame(cell, pageIndex, {
|
|
1159
1181
|
xPt: cellLeftXPt,
|
|
1160
1182
|
yPt: cellBottomYPt,
|
|
@@ -1162,6 +1184,7 @@ function recoverTable(page, pageIndex) {
|
|
|
1162
1184
|
heightPt: cellTopYPt - cellBottomYPt
|
|
1163
1185
|
});
|
|
1164
1186
|
cells.push(cell);
|
|
1187
|
+
j = region.colEnd;
|
|
1165
1188
|
}
|
|
1166
1189
|
rows.push({
|
|
1167
1190
|
cells,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js.documents",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.3",
|
|
4
4
|
"description": "Bidirectional docx/pptx <-> PDF conversion and a read+write editable OOXML document model, built on ooxml.js and Zod 4 codecs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"license": "MIT",
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"byte-codec": "^1.2.0",
|
|
81
|
-
"document-schema.js": "^5.
|
|
81
|
+
"document-schema.js": "^5.2.0",
|
|
82
82
|
"fflate": "^0.8.3",
|
|
83
|
-
"markdown-codec": "^6.1.
|
|
84
|
-
"odf.js": "^6.1.
|
|
85
|
-
"ooxml.js": "^6.
|
|
86
|
-
"pdf-codec": "^3.5.
|
|
83
|
+
"markdown-codec": "^6.1.1",
|
|
84
|
+
"odf.js": "^6.1.1",
|
|
85
|
+
"ooxml.js": "^6.2.0",
|
|
86
|
+
"pdf-codec": "^3.5.1",
|
|
87
87
|
"temml": "0.13.4",
|
|
88
88
|
"zod": "^4.4.3"
|
|
89
89
|
},
|