js.documents 7.2.4 → 7.2.5

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.
@@ -106,24 +106,24 @@ function dedupeAxisLines(segments, descending) {
106
106
  }
107
107
  const MIN_GRIDLINE_COUNT_PER_AXIS = 3;
108
108
  const EDGE_COVERAGE_RATIO = .9;
109
- function bestRunCoverageRatio(ranges, aPt, bPt) {
109
+ function unionCoverageRatio(ranges, aPt, bPt) {
110
110
  const lo = Math.min(aPt, bPt);
111
111
  const hi = Math.max(aPt, bPt);
112
112
  if (hi <= lo) return 1;
113
- let bestPt = 0;
113
+ let coveredPt = 0;
114
114
  for (const range of ranges) {
115
115
  const overlapLo = Math.max(range.startPt, lo);
116
116
  const overlapHi = Math.min(range.endPt, hi);
117
- if (overlapHi > overlapLo) bestPt = Math.max(bestPt, overlapHi - overlapLo);
117
+ if (overlapHi > overlapLo) coveredPt += overlapHi - overlapLo;
118
118
  }
119
- return bestPt / (hi - lo);
119
+ return coveredPt / (hi - lo);
120
120
  }
121
121
  function hasClosedOuterRectangle(rowLines, columnLines) {
122
122
  const leftXPt = columnLines[0].position;
123
123
  const rightXPt = columnLines[columnLines.length - 1].position;
124
124
  const topYPt = rowLines[0].position;
125
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;
126
+ return unionCoverageRatio(rowLines[0].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && unionCoverageRatio(rowLines[rowLines.length - 1].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && unionCoverageRatio(columnLines[0].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO && unionCoverageRatio(columnLines[columnLines.length - 1].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO;
127
127
  }
128
128
  function unionFind(size) {
129
129
  return Array.from({ length: size }, (_, i) => i);
@@ -154,7 +154,7 @@ function findCellRegions(rowLines, columnLines) {
154
154
  const rowBottomPt = rowLines[i + 1].position;
155
155
  for (let j = 0; j < colCount - 1; j++) {
156
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));
157
+ if (unionCoverageRatio(divider.ranges, rowBottomPt, rowTopPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i, j + 1));
158
158
  }
159
159
  }
160
160
  for (let j = 0; j < colCount; j++) {
@@ -162,7 +162,7 @@ function findCellRegions(rowLines, columnLines) {
162
162
  const colRightPt = columnLines[j + 1].position;
163
163
  for (let i = 0; i < rowCount - 1; i++) {
164
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));
165
+ if (unionCoverageRatio(divider.ranges, colLeftPt, colRightPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i + 1, j));
166
166
  }
167
167
  }
168
168
  const cellsByRoot = /* @__PURE__ */ new Map();
@@ -204,6 +204,22 @@ function findCellRegions(rowLines, columnLines) {
204
204
  regions.sort((a, b) => a.rowStart - b.rowStart || a.colStart - b.colStart);
205
205
  return regions;
206
206
  }
207
+ function segmentsCross(h, v) {
208
+ return v.position >= h.startPt - POSITION_DEDUPE_TOLERANCE_PT && v.position <= h.endPt + POSITION_DEDUPE_TOLERANCE_PT && h.position >= v.startPt - POSITION_DEDUPE_TOLERANCE_PT && h.position <= v.endPt + POSITION_DEDUPE_TOLERANCE_PT;
209
+ }
210
+ function clusterSegments(horizontal, vertical) {
211
+ const all = [...horizontal, ...vertical];
212
+ const parents = unionFind(all.length);
213
+ for (let i = 0; i < horizontal.length; i++) for (let j = 0; j < vertical.length; j++) if (segmentsCross(horizontal[i], vertical[j])) union(parents, i, horizontal.length + j);
214
+ const clusters = /* @__PURE__ */ new Map();
215
+ for (let i = 0; i < all.length; i++) {
216
+ const root = findRoot(parents, i);
217
+ const cluster = clusters.get(root);
218
+ if (cluster === void 0) clusters.set(root, [all[i]]);
219
+ else cluster.push(all[i]);
220
+ }
221
+ return [...clusters.values()];
222
+ }
207
223
  function detectGridLattice(items) {
208
224
  const horizontal = [];
209
225
  const vertical = [];
@@ -212,20 +228,30 @@ function detectGridLattice(items) {
212
228
  if (classified === void 0) continue;
213
229
  (classified.axis === "horizontal" ? horizontal : vertical).push(classified);
214
230
  }
215
- const rowLines = dedupeAxisLines(horizontal, true);
216
- const columnLines = dedupeAxisLines(vertical, false);
217
- if (rowLines.length < MIN_GRIDLINE_COUNT_PER_AXIS || columnLines.length < MIN_GRIDLINE_COUNT_PER_AXIS) return;
218
- if (!hasClosedOuterRectangle(rowLines, columnLines)) return;
219
- const regions = findCellRegions(rowLines, columnLines);
220
- if (regions === void 0) return;
221
- const sourceItems = /* @__PURE__ */ new Set();
222
- for (const line of [...rowLines, ...columnLines]) for (const item of line.items) sourceItems.add(item);
223
- return {
224
- rowBoundariesDescPt: rowLines.map((l) => l.position),
225
- columnBoundariesAscPt: columnLines.map((l) => l.position),
226
- regions,
227
- sourceItems
228
- };
231
+ let best;
232
+ let bestAreaPt2 = 0;
233
+ for (const cluster of clusterSegments(horizontal, vertical)) {
234
+ const clusterHorizontal = cluster.filter((s) => s.axis === "horizontal");
235
+ const clusterVertical = cluster.filter((s) => s.axis === "vertical");
236
+ const rowLines = dedupeAxisLines(clusterHorizontal, true);
237
+ const columnLines = dedupeAxisLines(clusterVertical, false);
238
+ if (rowLines.length < MIN_GRIDLINE_COUNT_PER_AXIS || columnLines.length < MIN_GRIDLINE_COUNT_PER_AXIS) continue;
239
+ if (!hasClosedOuterRectangle(rowLines, columnLines)) continue;
240
+ const regions = findCellRegions(rowLines, columnLines);
241
+ if (regions === void 0) continue;
242
+ const areaPt2 = (columnLines[columnLines.length - 1].position - columnLines[0].position) * (rowLines[0].position - rowLines[rowLines.length - 1].position);
243
+ if (best !== void 0 && areaPt2 <= bestAreaPt2) continue;
244
+ const sourceItems = /* @__PURE__ */ new Set();
245
+ for (const line of [...rowLines, ...columnLines]) for (const item of line.items) sourceItems.add(item);
246
+ best = {
247
+ rowBoundariesDescPt: rowLines.map((l) => l.position),
248
+ columnBoundariesAscPt: columnLines.map((l) => l.position),
249
+ regions,
250
+ sourceItems
251
+ };
252
+ bestAreaPt2 = areaPt2;
253
+ }
254
+ return best;
229
255
  }
230
256
  const OUTER_EDGE_TOLERANCE_PT = 3;
231
257
  function findRowIndex(rowBoundariesDescPt, yPt) {
@@ -105,24 +105,24 @@ function dedupeAxisLines(segments, descending) {
105
105
  }
106
106
  const MIN_GRIDLINE_COUNT_PER_AXIS = 3;
107
107
  const EDGE_COVERAGE_RATIO = .9;
108
- function bestRunCoverageRatio(ranges, aPt, bPt) {
108
+ function unionCoverageRatio(ranges, aPt, bPt) {
109
109
  const lo = Math.min(aPt, bPt);
110
110
  const hi = Math.max(aPt, bPt);
111
111
  if (hi <= lo) return 1;
112
- let bestPt = 0;
112
+ let coveredPt = 0;
113
113
  for (const range of ranges) {
114
114
  const overlapLo = Math.max(range.startPt, lo);
115
115
  const overlapHi = Math.min(range.endPt, hi);
116
- if (overlapHi > overlapLo) bestPt = Math.max(bestPt, overlapHi - overlapLo);
116
+ if (overlapHi > overlapLo) coveredPt += overlapHi - overlapLo;
117
117
  }
118
- return bestPt / (hi - lo);
118
+ return coveredPt / (hi - lo);
119
119
  }
120
120
  function hasClosedOuterRectangle(rowLines, columnLines) {
121
121
  const leftXPt = columnLines[0].position;
122
122
  const rightXPt = columnLines[columnLines.length - 1].position;
123
123
  const topYPt = rowLines[0].position;
124
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;
125
+ return unionCoverageRatio(rowLines[0].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && unionCoverageRatio(rowLines[rowLines.length - 1].ranges, leftXPt, rightXPt) >= EDGE_COVERAGE_RATIO && unionCoverageRatio(columnLines[0].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO && unionCoverageRatio(columnLines[columnLines.length - 1].ranges, bottomYPt, topYPt) >= EDGE_COVERAGE_RATIO;
126
126
  }
127
127
  function unionFind(size) {
128
128
  return Array.from({ length: size }, (_, i) => i);
@@ -153,7 +153,7 @@ function findCellRegions(rowLines, columnLines) {
153
153
  const rowBottomPt = rowLines[i + 1].position;
154
154
  for (let j = 0; j < colCount - 1; j++) {
155
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));
156
+ if (unionCoverageRatio(divider.ranges, rowBottomPt, rowTopPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i, j + 1));
157
157
  }
158
158
  }
159
159
  for (let j = 0; j < colCount; j++) {
@@ -161,7 +161,7 @@ function findCellRegions(rowLines, columnLines) {
161
161
  const colRightPt = columnLines[j + 1].position;
162
162
  for (let i = 0; i < rowCount - 1; i++) {
163
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));
164
+ if (unionCoverageRatio(divider.ranges, colLeftPt, colRightPt) < EDGE_COVERAGE_RATIO) union(parents, indexOf(i, j), indexOf(i + 1, j));
165
165
  }
166
166
  }
167
167
  const cellsByRoot = /* @__PURE__ */ new Map();
@@ -203,6 +203,22 @@ function findCellRegions(rowLines, columnLines) {
203
203
  regions.sort((a, b) => a.rowStart - b.rowStart || a.colStart - b.colStart);
204
204
  return regions;
205
205
  }
206
+ function segmentsCross(h, v) {
207
+ return v.position >= h.startPt - POSITION_DEDUPE_TOLERANCE_PT && v.position <= h.endPt + POSITION_DEDUPE_TOLERANCE_PT && h.position >= v.startPt - POSITION_DEDUPE_TOLERANCE_PT && h.position <= v.endPt + POSITION_DEDUPE_TOLERANCE_PT;
208
+ }
209
+ function clusterSegments(horizontal, vertical) {
210
+ const all = [...horizontal, ...vertical];
211
+ const parents = unionFind(all.length);
212
+ for (let i = 0; i < horizontal.length; i++) for (let j = 0; j < vertical.length; j++) if (segmentsCross(horizontal[i], vertical[j])) union(parents, i, horizontal.length + j);
213
+ const clusters = /* @__PURE__ */ new Map();
214
+ for (let i = 0; i < all.length; i++) {
215
+ const root = findRoot(parents, i);
216
+ const cluster = clusters.get(root);
217
+ if (cluster === void 0) clusters.set(root, [all[i]]);
218
+ else cluster.push(all[i]);
219
+ }
220
+ return [...clusters.values()];
221
+ }
206
222
  function detectGridLattice(items) {
207
223
  const horizontal = [];
208
224
  const vertical = [];
@@ -211,20 +227,30 @@ function detectGridLattice(items) {
211
227
  if (classified === void 0) continue;
212
228
  (classified.axis === "horizontal" ? horizontal : vertical).push(classified);
213
229
  }
214
- const rowLines = dedupeAxisLines(horizontal, true);
215
- const columnLines = dedupeAxisLines(vertical, false);
216
- if (rowLines.length < MIN_GRIDLINE_COUNT_PER_AXIS || columnLines.length < MIN_GRIDLINE_COUNT_PER_AXIS) return;
217
- if (!hasClosedOuterRectangle(rowLines, columnLines)) return;
218
- const regions = findCellRegions(rowLines, columnLines);
219
- if (regions === void 0) return;
220
- const sourceItems = /* @__PURE__ */ new Set();
221
- for (const line of [...rowLines, ...columnLines]) for (const item of line.items) sourceItems.add(item);
222
- return {
223
- rowBoundariesDescPt: rowLines.map((l) => l.position),
224
- columnBoundariesAscPt: columnLines.map((l) => l.position),
225
- regions,
226
- sourceItems
227
- };
230
+ let best;
231
+ let bestAreaPt2 = 0;
232
+ for (const cluster of clusterSegments(horizontal, vertical)) {
233
+ const clusterHorizontal = cluster.filter((s) => s.axis === "horizontal");
234
+ const clusterVertical = cluster.filter((s) => s.axis === "vertical");
235
+ const rowLines = dedupeAxisLines(clusterHorizontal, true);
236
+ const columnLines = dedupeAxisLines(clusterVertical, false);
237
+ if (rowLines.length < MIN_GRIDLINE_COUNT_PER_AXIS || columnLines.length < MIN_GRIDLINE_COUNT_PER_AXIS) continue;
238
+ if (!hasClosedOuterRectangle(rowLines, columnLines)) continue;
239
+ const regions = findCellRegions(rowLines, columnLines);
240
+ if (regions === void 0) continue;
241
+ const areaPt2 = (columnLines[columnLines.length - 1].position - columnLines[0].position) * (rowLines[0].position - rowLines[rowLines.length - 1].position);
242
+ if (best !== void 0 && areaPt2 <= bestAreaPt2) continue;
243
+ const sourceItems = /* @__PURE__ */ new Set();
244
+ for (const line of [...rowLines, ...columnLines]) for (const item of line.items) sourceItems.add(item);
245
+ best = {
246
+ rowBoundariesDescPt: rowLines.map((l) => l.position),
247
+ columnBoundariesAscPt: columnLines.map((l) => l.position),
248
+ regions,
249
+ sourceItems
250
+ };
251
+ bestAreaPt2 = areaPt2;
252
+ }
253
+ return best;
228
254
  }
229
255
  const OUTER_EDGE_TOLERANCE_PT = 3;
230
256
  function findRowIndex(rowBoundariesDescPt, yPt) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js.documents",
3
- "version": "7.2.4",
3
+ "version": "7.2.5",
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": {