js.documents 7.2.0 → 7.2.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.
|
@@ -44,8 +44,86 @@ function dropOverlappingRepeatsWithinLine(items) {
|
|
|
44
44
|
}
|
|
45
45
|
return kept;
|
|
46
46
|
}
|
|
47
|
+
const REDRAW_PASS_REWIND_TOLERANCE_PT = 2;
|
|
48
|
+
const MIN_REDRAW_PASS_TEXT_LENGTH = 12;
|
|
49
|
+
const REDRAW_PASS_SIMILARITY_THRESHOLD = .6;
|
|
50
|
+
function groupByBaselineInDocumentOrder(items) {
|
|
51
|
+
const groups = [];
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
const tolerance = LINE_BASELINE_TOLERANCE_FACTOR * item.sizePt;
|
|
54
|
+
const group = groups.find((g) => Math.abs(g.baselineY - item.yPt) <= tolerance);
|
|
55
|
+
if (group === void 0) groups.push({
|
|
56
|
+
baselineY: item.yPt,
|
|
57
|
+
items: [item]
|
|
58
|
+
});
|
|
59
|
+
else group.items.push(item);
|
|
60
|
+
}
|
|
61
|
+
return groups.map((g) => g.items);
|
|
62
|
+
}
|
|
63
|
+
function splitIntoPasses(group) {
|
|
64
|
+
const passes = [];
|
|
65
|
+
for (const item of group) {
|
|
66
|
+
const current = passes.at(-1);
|
|
67
|
+
const itemRightPt = item.xPt + (item.widthPt ?? 0);
|
|
68
|
+
if (current !== void 0 && item.xPt >= current.endXPt - REDRAW_PASS_REWIND_TOLERANCE_PT) {
|
|
69
|
+
current.items.push(item);
|
|
70
|
+
current.endXPt = Math.max(current.endXPt, itemRightPt);
|
|
71
|
+
} else passes.push({
|
|
72
|
+
items: [item],
|
|
73
|
+
startXPt: item.xPt,
|
|
74
|
+
endXPt: itemRightPt
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return passes;
|
|
78
|
+
}
|
|
79
|
+
function passText(pass) {
|
|
80
|
+
return pass.items.map((item) => item.text).join("").trim();
|
|
81
|
+
}
|
|
82
|
+
function passesOverlap(a, b) {
|
|
83
|
+
return a.startXPt < b.endXPt && b.startXPt < a.endXPt;
|
|
84
|
+
}
|
|
85
|
+
function levenshteinDistance(a, b) {
|
|
86
|
+
const memo = /* @__PURE__ */ new Map();
|
|
87
|
+
function distance(i, j) {
|
|
88
|
+
if (i === 0) return j;
|
|
89
|
+
if (j === 0) return i;
|
|
90
|
+
const key = `${String(i)}:${String(j)}`;
|
|
91
|
+
const cached = memo.get(key);
|
|
92
|
+
if (cached !== void 0) return cached;
|
|
93
|
+
const cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
|
|
94
|
+
const result = Math.min(distance(i - 1, j) + 1, distance(i, j - 1) + 1, distance(i - 1, j - 1) + cost);
|
|
95
|
+
memo.set(key, result);
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
return distance(a.length, b.length);
|
|
99
|
+
}
|
|
100
|
+
function textSimilarity(a, b) {
|
|
101
|
+
const maxLength = Math.max(a.length, b.length);
|
|
102
|
+
if (maxLength === 0) return 1;
|
|
103
|
+
return 1 - levenshteinDistance(a, b) / maxLength;
|
|
104
|
+
}
|
|
105
|
+
function dropFuzzyRedrawnPasses(items) {
|
|
106
|
+
const toDrop = /* @__PURE__ */ new Set();
|
|
107
|
+
for (const group of groupByBaselineInDocumentOrder(items)) {
|
|
108
|
+
const passes = splitIntoPasses(group);
|
|
109
|
+
if (passes.length < 2) continue;
|
|
110
|
+
const [first, ...rest] = passes;
|
|
111
|
+
if (first === void 0) continue;
|
|
112
|
+
const firstText = passText(first);
|
|
113
|
+
if (firstText.length < MIN_REDRAW_PASS_TEXT_LENGTH) continue;
|
|
114
|
+
for (const candidate of rest) {
|
|
115
|
+
const candidateText = passText(candidate);
|
|
116
|
+
if (candidateText.length < MIN_REDRAW_PASS_TEXT_LENGTH) continue;
|
|
117
|
+
if (!passesOverlap(first, candidate)) continue;
|
|
118
|
+
if (textSimilarity(firstText, candidateText) < REDRAW_PASS_SIMILARITY_THRESHOLD) continue;
|
|
119
|
+
for (const item of candidate.items) toDrop.add(item);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (toDrop.size === 0) return items;
|
|
123
|
+
return items.filter((item) => !toDrop.has(item));
|
|
124
|
+
}
|
|
47
125
|
function clusterIntoLines(items) {
|
|
48
|
-
const sorted = [...dropDuplicatePaints(items)].sort((a, b) => b.yPt - a.yPt || a.xPt - b.xPt);
|
|
126
|
+
const sorted = [...dropDuplicatePaints(dropFuzzyRedrawnPasses(items))].sort((a, b) => b.yPt - a.yPt || a.xPt - b.xPt);
|
|
49
127
|
const working = [];
|
|
50
128
|
for (const item of sorted) {
|
|
51
129
|
const tolerance = LINE_BASELINE_TOLERANCE_FACTOR * item.sizePt;
|
|
@@ -43,8 +43,86 @@ function dropOverlappingRepeatsWithinLine(items) {
|
|
|
43
43
|
}
|
|
44
44
|
return kept;
|
|
45
45
|
}
|
|
46
|
+
const REDRAW_PASS_REWIND_TOLERANCE_PT = 2;
|
|
47
|
+
const MIN_REDRAW_PASS_TEXT_LENGTH = 12;
|
|
48
|
+
const REDRAW_PASS_SIMILARITY_THRESHOLD = .6;
|
|
49
|
+
function groupByBaselineInDocumentOrder(items) {
|
|
50
|
+
const groups = [];
|
|
51
|
+
for (const item of items) {
|
|
52
|
+
const tolerance = LINE_BASELINE_TOLERANCE_FACTOR * item.sizePt;
|
|
53
|
+
const group = groups.find((g) => Math.abs(g.baselineY - item.yPt) <= tolerance);
|
|
54
|
+
if (group === void 0) groups.push({
|
|
55
|
+
baselineY: item.yPt,
|
|
56
|
+
items: [item]
|
|
57
|
+
});
|
|
58
|
+
else group.items.push(item);
|
|
59
|
+
}
|
|
60
|
+
return groups.map((g) => g.items);
|
|
61
|
+
}
|
|
62
|
+
function splitIntoPasses(group) {
|
|
63
|
+
const passes = [];
|
|
64
|
+
for (const item of group) {
|
|
65
|
+
const current = passes.at(-1);
|
|
66
|
+
const itemRightPt = item.xPt + (item.widthPt ?? 0);
|
|
67
|
+
if (current !== void 0 && item.xPt >= current.endXPt - REDRAW_PASS_REWIND_TOLERANCE_PT) {
|
|
68
|
+
current.items.push(item);
|
|
69
|
+
current.endXPt = Math.max(current.endXPt, itemRightPt);
|
|
70
|
+
} else passes.push({
|
|
71
|
+
items: [item],
|
|
72
|
+
startXPt: item.xPt,
|
|
73
|
+
endXPt: itemRightPt
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return passes;
|
|
77
|
+
}
|
|
78
|
+
function passText(pass) {
|
|
79
|
+
return pass.items.map((item) => item.text).join("").trim();
|
|
80
|
+
}
|
|
81
|
+
function passesOverlap(a, b) {
|
|
82
|
+
return a.startXPt < b.endXPt && b.startXPt < a.endXPt;
|
|
83
|
+
}
|
|
84
|
+
function levenshteinDistance(a, b) {
|
|
85
|
+
const memo = /* @__PURE__ */ new Map();
|
|
86
|
+
function distance(i, j) {
|
|
87
|
+
if (i === 0) return j;
|
|
88
|
+
if (j === 0) return i;
|
|
89
|
+
const key = `${String(i)}:${String(j)}`;
|
|
90
|
+
const cached = memo.get(key);
|
|
91
|
+
if (cached !== void 0) return cached;
|
|
92
|
+
const cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
|
|
93
|
+
const result = Math.min(distance(i - 1, j) + 1, distance(i, j - 1) + 1, distance(i - 1, j - 1) + cost);
|
|
94
|
+
memo.set(key, result);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
return distance(a.length, b.length);
|
|
98
|
+
}
|
|
99
|
+
function textSimilarity(a, b) {
|
|
100
|
+
const maxLength = Math.max(a.length, b.length);
|
|
101
|
+
if (maxLength === 0) return 1;
|
|
102
|
+
return 1 - levenshteinDistance(a, b) / maxLength;
|
|
103
|
+
}
|
|
104
|
+
function dropFuzzyRedrawnPasses(items) {
|
|
105
|
+
const toDrop = /* @__PURE__ */ new Set();
|
|
106
|
+
for (const group of groupByBaselineInDocumentOrder(items)) {
|
|
107
|
+
const passes = splitIntoPasses(group);
|
|
108
|
+
if (passes.length < 2) continue;
|
|
109
|
+
const [first, ...rest] = passes;
|
|
110
|
+
if (first === void 0) continue;
|
|
111
|
+
const firstText = passText(first);
|
|
112
|
+
if (firstText.length < MIN_REDRAW_PASS_TEXT_LENGTH) continue;
|
|
113
|
+
for (const candidate of rest) {
|
|
114
|
+
const candidateText = passText(candidate);
|
|
115
|
+
if (candidateText.length < MIN_REDRAW_PASS_TEXT_LENGTH) continue;
|
|
116
|
+
if (!passesOverlap(first, candidate)) continue;
|
|
117
|
+
if (textSimilarity(firstText, candidateText) < REDRAW_PASS_SIMILARITY_THRESHOLD) continue;
|
|
118
|
+
for (const item of candidate.items) toDrop.add(item);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (toDrop.size === 0) return items;
|
|
122
|
+
return items.filter((item) => !toDrop.has(item));
|
|
123
|
+
}
|
|
46
124
|
function clusterIntoLines(items) {
|
|
47
|
-
const sorted = [...dropDuplicatePaints(items)].sort((a, b) => b.yPt - a.yPt || a.xPt - b.xPt);
|
|
125
|
+
const sorted = [...dropDuplicatePaints(dropFuzzyRedrawnPasses(items))].sort((a, b) => b.yPt - a.yPt || a.xPt - b.xPt);
|
|
48
126
|
const working = [];
|
|
49
127
|
for (const item of sorted) {
|
|
50
128
|
const tolerance = LINE_BASELINE_TOLERANCE_FACTOR * item.sizePt;
|
package/package.json
CHANGED