reamkit 1.29.0 → 1.31.0
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 +60 -24
- package/dist/esm/core/converter/project.js +3 -1
- package/dist/esm/core/crypto/offcrypto.js +1 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +65 -0
- package/dist/esm/core/drawingml/shape-render.js +13 -1
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/ttf-build.d.ts +113 -0
- package/dist/esm/core/font/ttf-build.js +1224 -0
- package/dist/esm/core/font/ttf-subset.d.ts +19 -0
- package/dist/esm/core/font/ttf-subset.js +14 -1
- package/dist/esm/core/fonts/index.d.ts +1 -1
- package/dist/esm/core/fonts/provider.d.ts +17 -0
- package/dist/esm/core/fonts/provider.js +27 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +8 -0
- package/dist/esm/core/fonts/remote-fonts.js +108 -18
- package/dist/esm/core/ir/flow.d.ts +88 -1
- package/dist/esm/core/numbering/index.d.ts +1 -1
- package/dist/esm/core/numbering/state.d.ts +11 -1
- package/dist/esm/core/numbering/state.js +10 -1
- package/dist/esm/core/style-cascade/resolver.js +35 -4
- package/dist/esm/core/style-cascade/types.d.ts +19 -1
- package/dist/esm/core/style-cascade/types.js +3 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/layout/page-doc.d.ts +10 -3
- package/dist/esm/layout/styled-layout.d.ts +48 -7
- package/dist/esm/layout/styled-layout.js +881 -119
- package/dist/esm/layout/turned-section.d.ts +38 -0
- package/dist/esm/layout/turned-section.js +193 -0
- package/dist/esm/pdf/styled-page-emitter.js +78 -2
- package/dist/esm/pdf-reader/annot-draw.js +93 -1
- package/dist/esm/pdf-reader/annots.d.ts +18 -0
- package/dist/esm/pdf-reader/annots.js +86 -9
- package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
- package/dist/esm/pdf-reader/cff-outline.js +169 -21
- package/dist/esm/pdf-reader/cmap.js +5 -2
- package/dist/esm/pdf-reader/content.d.ts +70 -4
- package/dist/esm/pdf-reader/content.js +172 -12
- package/dist/esm/pdf-reader/display.d.ts +36 -0
- package/dist/esm/pdf-reader/display.js +82 -1
- package/dist/esm/pdf-reader/document.js +5 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +25 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +78 -9
- package/dist/esm/pdf-reader/encodings.d.ts +8 -0
- package/dist/esm/pdf-reader/encodings.js +25 -3
- package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
- package/dist/esm/pdf-reader/face-outlines.js +362 -0
- package/dist/esm/pdf-reader/figures.d.ts +52 -0
- package/dist/esm/pdf-reader/figures.js +433 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +224 -7
- package/dist/esm/pdf-reader/flow-build.js +545 -39
- package/dist/esm/pdf-reader/font.d.ts +27 -1
- package/dist/esm/pdf-reader/font.js +632 -63
- package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
- package/dist/esm/pdf-reader/glyf-outline.js +148 -3
- package/dist/esm/pdf-reader/glyph-names.js +154 -1
- package/dist/esm/pdf-reader/glyph-shapes.d.ts +18 -0
- package/dist/esm/pdf-reader/glyph-shapes.js +57 -0
- package/dist/esm/pdf-reader/image-decode.js +70 -4
- package/dist/esm/pdf-reader/images.d.ts +5 -0
- package/dist/esm/pdf-reader/images.js +4 -2
- package/dist/esm/pdf-reader/jbig2.d.ts +40 -1
- package/dist/esm/pdf-reader/jbig2.js +78 -16
- package/dist/esm/pdf-reader/jpeg.d.ts +6 -3
- package/dist/esm/pdf-reader/jpeg.js +21 -1
- package/dist/esm/pdf-reader/layout.d.ts +119 -2
- package/dist/esm/pdf-reader/layout.js +2219 -184
- package/dist/esm/pdf-reader/lexer.d.ts +10 -0
- package/dist/esm/pdf-reader/lexer.js +17 -0
- package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
- package/dist/esm/pdf-reader/page-numbers.js +167 -0
- package/dist/esm/pdf-reader/pattern-tint.d.ts +11 -1
- package/dist/esm/pdf-reader/pattern-tint.js +21 -3
- package/dist/esm/pdf-reader/regions.d.ts +25 -0
- package/dist/esm/pdf-reader/regions.js +167 -0
- package/dist/esm/pdf-reader/shading.d.ts +58 -2
- package/dist/esm/pdf-reader/shading.js +181 -11
- package/dist/esm/pdf-reader/struct-tree.js +112 -8
- package/dist/esm/pdf-reader/tagged.js +207 -28
- package/dist/esm/pdf-reader/text-rules.js +1 -1
- package/dist/esm/pdf-reader/text.d.ts +6 -3
- package/dist/esm/pdf-reader/text.js +106 -22
- package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
- package/dist/esm/pdf-reader/type1-outline.js +63 -8
- package/dist/esm/pdf-reader/vector.d.ts +8 -2
- package/dist/esm/pdf-reader/vector.js +105 -6
- package/dist/esm/word/doc/doc-reader.js +6 -2
- package/dist/esm/word/doc/doc-text.d.ts +6 -0
- package/dist/esm/word/doc/doc-text.js +19 -1
- package/dist/esm/word/document-parser.d.ts +2 -2
- package/dist/esm/word/document-parser.js +11 -1
- package/dist/esm/word/docx-reader.js +5 -3
- package/dist/esm/word/docx-writer.js +341 -54
- package/dist/esm/word/drawing-parser.d.ts +5 -3
- package/dist/esm/word/drawing-parser.js +56 -10
- package/dist/esm/word/font-embed.d.ts +30 -0
- package/dist/esm/word/font-embed.js +173 -0
- package/dist/esm/word/font-table.d.ts +10 -0
- package/dist/esm/word/font-table.js +13 -1
- package/dist/esm/word/index.js +1 -1
- package/dist/esm/word/numbering-parser.d.ts +3 -1
- package/dist/esm/word/numbering-parser.js +2 -1
- package/dist/esm/word/paragraph-properties.d.ts +7 -6
- package/dist/esm/word/paragraph-properties.js +16 -2
- package/dist/esm/word/run-properties.js +26 -0
- package/package.json +12 -5
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
//#region src/pdf-reader/figures.ts
|
|
2
|
+
/** No thicker than this, a path is a line. */
|
|
3
|
+
var THIN_PT = 1.5;
|
|
4
|
+
/**
|
|
5
|
+
* A line longer than this many ems RULES the page: the rule over a caption,
|
|
6
|
+
* the line under a heading, a table's border. A drawing's own strokes are
|
|
7
|
+
* shorter, and a long one inside a drawing — a chart's axis — is taken with
|
|
8
|
+
* the drawing it stands in once that is found.
|
|
9
|
+
*/
|
|
10
|
+
var RULE_EM = 3;
|
|
11
|
+
/** How close, in ems, two paths stand to be parts of one drawing. */
|
|
12
|
+
var JOIN_EM = .5;
|
|
13
|
+
/** The fewest paths a figure is drawn with: a box and a frame are not one. */
|
|
14
|
+
var MIN_PATHS = 6;
|
|
15
|
+
/** Past this share of the sheet, a drawing is the page's ground or frame. */
|
|
16
|
+
var MOST_OF_PAGE = .5;
|
|
17
|
+
/** How far across a figure a line of its words may run and still be a label. */
|
|
18
|
+
var LABEL_FILL = .6;
|
|
19
|
+
/** Past this many lines that run across it, what a drawing holds is prose. */
|
|
20
|
+
var PROSE_LINES = 2;
|
|
21
|
+
/**
|
|
22
|
+
* How far apart, in ems of the text, two parts of one figure may stand. The
|
|
23
|
+
* parts of comments.pdf's Figure 6 stand twenty-six points apart at 9pt.
|
|
24
|
+
*/
|
|
25
|
+
var NEAR_EM = 3;
|
|
26
|
+
/** Type smaller than this share of the text's is a drawing's, not the text's. */
|
|
27
|
+
var SMALL_SHARE = .85;
|
|
28
|
+
/** How much larger than the text a figure's words may be set. */
|
|
29
|
+
var LARGEST_LABEL = 1.2;
|
|
30
|
+
/** How far, in its own ems, small type may stand off a drawing and label it. */
|
|
31
|
+
var CAPTION_EM = 2;
|
|
32
|
+
/** What a glyph mapped to no character reads as (see `UNMAPPED` in ./layout). */
|
|
33
|
+
var UNMAPPED = "�";
|
|
34
|
+
/** Where an annotation's appearance stands in the page's painting order. */
|
|
35
|
+
var ANNOTATION_ORDER = Number.MAX_SAFE_INTEGER;
|
|
36
|
+
/**
|
|
37
|
+
* How many ems of its own type a line of words on a drawing may run before it
|
|
38
|
+
* is a line of prose, however wide the drawing: a label is a word or three.
|
|
39
|
+
*/
|
|
40
|
+
var LABEL_EM = 15;
|
|
41
|
+
/** Past this many paths a page is artwork through and through. */
|
|
42
|
+
var MOST_PATHS = 4e3;
|
|
43
|
+
/**
|
|
44
|
+
* The figures a page draws.
|
|
45
|
+
*
|
|
46
|
+
* A figure is paths that stand together — each within half an em of the next —
|
|
47
|
+
* and DRAW something: a curve or a slanted line among them. Straight lines
|
|
48
|
+
* and boxes alone are a ruling, which is a table's or a form's, and a page
|
|
49
|
+
* reads those as text. It has words set on it, or it is only artwork, which
|
|
50
|
+
* the page anchors as it always has; and none of its words run across it
|
|
51
|
+
* line after line, which is prose set on a ground, not a label.
|
|
52
|
+
*
|
|
53
|
+
* @param vectors The page's paths, where the page shows them.
|
|
54
|
+
* @param images The page's pictures, likewise.
|
|
55
|
+
* @param runs The page's text, likewise.
|
|
56
|
+
* @param sheet The page's size.
|
|
57
|
+
* @returns The figures, top of the page first.
|
|
58
|
+
*/
|
|
59
|
+
function pageFigures(vectors, images, runs, sheet) {
|
|
60
|
+
const words = runs.filter((r) => r.text.replaceAll(UNMAPPED, "").trim().length > 0);
|
|
61
|
+
const traced = runs.filter((r) => r.type3 !== true && tracedRun(r));
|
|
62
|
+
if (words.length === 0 || vectors.length === 0 || vectors.length > MOST_PATHS) return [];
|
|
63
|
+
const size = median(words.map((r) => r.fontSizePt).filter((s) => s > 0)) || 10;
|
|
64
|
+
const gap = size * JOIN_EM;
|
|
65
|
+
const pageArea = Math.max(1, sheet.width * sheet.height);
|
|
66
|
+
const huge = (b) => (b.maxX - b.minX) * (b.maxY - b.minY) > pageArea * MOST_OF_PAGE;
|
|
67
|
+
const thin = (v) => Math.min(v.maxX - v.minX, v.maxY - v.minY) <= THIN_PT;
|
|
68
|
+
const rule = (v) => thin(v) && Math.max(v.maxX - v.minX, v.maxY - v.minY) > size * RULE_EM;
|
|
69
|
+
const lettering = words.filter((r) => r.type3 === true).map(inkOf);
|
|
70
|
+
const letter = (v) => v.glyph === true || lettering.some((b) => (v.minX + v.maxX) / 2 >= b.minX - 1 && (v.minX + v.maxX) / 2 <= b.maxX + 1 && (v.minY + v.maxY) / 2 >= b.minY - 1 && (v.minY + v.maxY) / 2 <= b.maxY + 1);
|
|
71
|
+
const own = vectors.filter((v) => v.orderKey[0] !== ANNOTATION_ORDER);
|
|
72
|
+
const seeds = own.filter((v) => !rule(v) && !huge(v) && !letter(v));
|
|
73
|
+
const parent = seeds.map((_, k) => k);
|
|
74
|
+
const find = (k) => {
|
|
75
|
+
while (parent[k] !== k) {
|
|
76
|
+
parent[k] = parent[parent[k]];
|
|
77
|
+
k = parent[k];
|
|
78
|
+
}
|
|
79
|
+
return k;
|
|
80
|
+
};
|
|
81
|
+
const order = seeds.map((_, k) => k).sort((a, b) => seeds[a].minX - seeds[b].minX);
|
|
82
|
+
for (let i = 0; i < order.length; i++) {
|
|
83
|
+
const a = seeds[order[i]];
|
|
84
|
+
for (let j = i + 1; j < order.length; j++) {
|
|
85
|
+
const b = seeds[order[j]];
|
|
86
|
+
if (b.minX > a.maxX + gap) break;
|
|
87
|
+
if (b.minY <= a.maxY + gap && b.maxY >= a.minY - gap) parent[find(order[i])] = find(order[j]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
91
|
+
seeds.forEach((v, k) => {
|
|
92
|
+
const root = find(k);
|
|
93
|
+
const members = clusters.get(root);
|
|
94
|
+
if (members) members.push(v);
|
|
95
|
+
else clusters.set(root, [v]);
|
|
96
|
+
});
|
|
97
|
+
const found = [];
|
|
98
|
+
const pieces = [];
|
|
99
|
+
for (const members of clusters.values()) {
|
|
100
|
+
const box = boxOf(members);
|
|
101
|
+
if (huge(box)) continue;
|
|
102
|
+
if (members.length < MIN_PATHS || !(members.some(draws) || barChart(members))) pieces.push({
|
|
103
|
+
box,
|
|
104
|
+
vectors: members
|
|
105
|
+
});
|
|
106
|
+
else found.push({
|
|
107
|
+
box,
|
|
108
|
+
vectors: members
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (found.length === 0) return [];
|
|
112
|
+
const near = size * NEAR_EM;
|
|
113
|
+
const text = words.filter((r) => r.type3 !== true && r.fontSizePt >= size * SMALL_SHARE);
|
|
114
|
+
const textBetween = (a, b) => {
|
|
115
|
+
const both = union(a, b);
|
|
116
|
+
return text.some((r) => {
|
|
117
|
+
const ink = inkOf(r);
|
|
118
|
+
return touches(both, ink, 0) && !touches(a, ink, 0) && !touches(b, ink, 0);
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const rules = own.filter(rule);
|
|
122
|
+
const taken = /* @__PURE__ */ new Set();
|
|
123
|
+
let changed = true;
|
|
124
|
+
while (changed) {
|
|
125
|
+
changed = false;
|
|
126
|
+
for (let i = 0; i < found.length; i++) {
|
|
127
|
+
for (let j = found.length - 1; j > i; j--) {
|
|
128
|
+
const [a, b] = [found[i].box, found[j].box];
|
|
129
|
+
if (!touches(a, b, gap) && !(touches(a, b, near) && !textBetween(a, b))) continue;
|
|
130
|
+
found[i].vectors.push(...found[j].vectors);
|
|
131
|
+
found[i].box = union(a, b);
|
|
132
|
+
found.splice(j, 1);
|
|
133
|
+
changed = true;
|
|
134
|
+
}
|
|
135
|
+
for (let j = pieces.length - 1; j >= 0; j--) {
|
|
136
|
+
const [a, b] = [found[i].box, pieces[j].box];
|
|
137
|
+
if (!touches(a, b, gap) && !(touches(a, b, near) && !textBetween(a, b))) continue;
|
|
138
|
+
found[i].vectors.push(...pieces[j].vectors);
|
|
139
|
+
found[i].box = union(a, b);
|
|
140
|
+
pieces.splice(j, 1);
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
for (const r of rules) {
|
|
144
|
+
if (taken.has(r) || !mostlyWithin(r, found[i].box)) continue;
|
|
145
|
+
taken.add(r);
|
|
146
|
+
found[i].vectors.push(r);
|
|
147
|
+
found[i].box = union(found[i].box, r);
|
|
148
|
+
changed = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const figures = [];
|
|
153
|
+
const labelled = /* @__PURE__ */ new Set();
|
|
154
|
+
const lettered = /* @__PURE__ */ new Set();
|
|
155
|
+
for (const f of found) {
|
|
156
|
+
const free = [...words, ...traced].filter((r) => r.type3 !== true && !labelled.has(r));
|
|
157
|
+
const labels = free.filter((r) => touches(f.box, inkOf(r), 0));
|
|
158
|
+
let box = labels.reduce((b, r) => union(b, inkOf(r)), f.box);
|
|
159
|
+
for (let grown = true; grown;) {
|
|
160
|
+
grown = false;
|
|
161
|
+
for (const r of free) {
|
|
162
|
+
if (labels.includes(r) || r.fontSizePt >= size * SMALL_SHARE) continue;
|
|
163
|
+
const ink = inkOf(r);
|
|
164
|
+
if (!touches(box, ink, r.fontSizePt * CAPTION_EM) || ink.minX < box.minX - r.fontSizePt) continue;
|
|
165
|
+
if (ink.maxX > box.maxX + r.fontSizePt) continue;
|
|
166
|
+
labels.push(r);
|
|
167
|
+
box = union(box, ink);
|
|
168
|
+
grown = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (labels.length === 0 || runsAcross(labels, box, size)) continue;
|
|
172
|
+
if (labels.some((r) => r.fontSizePt > size * LARGEST_LABEL)) continue;
|
|
173
|
+
const spaces = runs.filter((r) => r.text.trim().length === 0 && r.text.length > 0 && touches(box, inkOf(r), 0));
|
|
174
|
+
for (const r of [...labels, ...spaces]) labelled.add(r);
|
|
175
|
+
const held = images.filter((img) => mostlyInside(img, box));
|
|
176
|
+
const letters = lettersOf(labels.filter(tracedRun), vectors).filter((v) => !lettered.has(v));
|
|
177
|
+
for (const v of letters) lettered.add(v);
|
|
178
|
+
figures.push({
|
|
179
|
+
...box,
|
|
180
|
+
vectors: [...f.vectors, ...letters],
|
|
181
|
+
images: held,
|
|
182
|
+
labels: [...labels, ...spaces]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return figures.sort((a, b) => b.maxY - a.maxY);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Whether a run is lettering the file states no character for: glyphs traced
|
|
189
|
+
* as paths (see `PdfVector.glyph`), and a label only by where they stand.
|
|
190
|
+
*
|
|
191
|
+
* @param run A run of the page's text.
|
|
192
|
+
*/
|
|
193
|
+
function tracedRun(run) {
|
|
194
|
+
return run.text.includes(UNMAPPED) && run.text.replaceAll(UNMAPPED, "").trim() === "";
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The glyphs traced for some runs: the paths traced as glyphs whose middles
|
|
198
|
+
* stand in the runs' ink.
|
|
199
|
+
*
|
|
200
|
+
* @param runs The traced runs.
|
|
201
|
+
* @param paths The page's paths.
|
|
202
|
+
*/
|
|
203
|
+
function lettersOf(runs, paths) {
|
|
204
|
+
const inks = runs.map(inkOf);
|
|
205
|
+
return paths.filter((v) => {
|
|
206
|
+
if (v.glyph !== true) return false;
|
|
207
|
+
const x = (v.minX + v.maxX) / 2;
|
|
208
|
+
const y = (v.minY + v.maxY) / 2;
|
|
209
|
+
return inks.some((b) => x >= b.minX - 1 && x <= b.maxX + 1 && y >= b.minY - 1 && y <= b.maxY + 1);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* A figure's paths with the traced letters painted one after another joined
|
|
214
|
+
* into one: the letters of a label are one shape, where a shape a letter made
|
|
215
|
+
* a chart of six hundred.
|
|
216
|
+
*
|
|
217
|
+
* @param paths The figure's paths, in the order the page painted them.
|
|
218
|
+
* @returns The paths to draw, in that order.
|
|
219
|
+
*/
|
|
220
|
+
function joinLetters(paths) {
|
|
221
|
+
const out = [];
|
|
222
|
+
let letters = [];
|
|
223
|
+
const flush = () => {
|
|
224
|
+
const [first, ...rest] = letters;
|
|
225
|
+
if (first !== void 0) out.push(rest.length === 0 ? first : {
|
|
226
|
+
...first,
|
|
227
|
+
...boxOf(letters),
|
|
228
|
+
segs: letters.flatMap((v) => v.segs)
|
|
229
|
+
});
|
|
230
|
+
letters = [];
|
|
231
|
+
};
|
|
232
|
+
for (const v of paths) {
|
|
233
|
+
const last = letters[letters.length - 1];
|
|
234
|
+
if (last !== void 0 && (!v.glyph || last.fillHex !== v.fillHex || last.alpha !== v.alpha)) flush();
|
|
235
|
+
if (v.glyph === true) letters.push(v);
|
|
236
|
+
else out.push(v);
|
|
237
|
+
}
|
|
238
|
+
flush();
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
/** Whether a path draws: a curve, or a line that is neither level nor upright. */
|
|
242
|
+
function draws(v) {
|
|
243
|
+
let x = 0;
|
|
244
|
+
let y = 0;
|
|
245
|
+
for (const s of v.segs) {
|
|
246
|
+
if (s.op === "cubic") return true;
|
|
247
|
+
if (s.op === "line" && Math.abs(s.x - x) > THIN_PT && Math.abs(s.y - y) > THIN_PT) return true;
|
|
248
|
+
if (s.op !== "close") {
|
|
249
|
+
x = s.x;
|
|
250
|
+
y = s.y;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Whether paths draw a BAR CHART: filled rectangles, six and more, standing on
|
|
257
|
+
* one line and as long as each other's figures make them — the one drawing
|
|
258
|
+
* made of nothing but straight lines and boxes that a ruling is not. A
|
|
259
|
+
* table's shading stands its boxes on one line too, but each as long as the
|
|
260
|
+
* last.
|
|
261
|
+
*
|
|
262
|
+
* comments.pdf sets Figures 10 and 12 so, bars of gradient and of flat grey.
|
|
263
|
+
* With no curve among them they were taken for a ruling: their labels were
|
|
264
|
+
* read as lines of the text, Figure 12 and the column beside it came back as
|
|
265
|
+
* one table, and Figure 10's caption, the first line left on its page, set
|
|
266
|
+
* the page's top margin 352 points down.
|
|
267
|
+
*
|
|
268
|
+
* @param paths The paths of one cluster.
|
|
269
|
+
*/
|
|
270
|
+
function barChart(paths) {
|
|
271
|
+
const bars = paths.filter((v) => v.fillHex !== void 0 || v.gradient !== void 0).flatMap(rectanglesOf);
|
|
272
|
+
const standing = (base, length) => {
|
|
273
|
+
const lengths = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const b of bars) {
|
|
275
|
+
const at = Math.round(base(b) / BAR_LINE_PT);
|
|
276
|
+
const long = length(b);
|
|
277
|
+
if (long > THIN_PT) lengths.set(at, [...lengths.get(at) ?? [], long]);
|
|
278
|
+
}
|
|
279
|
+
return [...lengths.values()].some((all) => all.length >= MIN_PATHS && Math.max(...all) >= Math.min(...all) * BAR_SPREAD);
|
|
280
|
+
};
|
|
281
|
+
return standing((b) => b.minY, (b) => b.maxY - b.minY) || standing((b) => b.minX, (b) => b.maxX - b.minX);
|
|
282
|
+
}
|
|
283
|
+
/** How near, in points, bars' feet stand to stand on one line. */
|
|
284
|
+
var BAR_LINE_PT = .5;
|
|
285
|
+
/** How much longer the longest bar of a chart is than the shortest, at the least. */
|
|
286
|
+
var BAR_SPREAD = 2;
|
|
287
|
+
/** The upright rectangles a path is drawn with, one per subpath that is one. */
|
|
288
|
+
function rectanglesOf(v) {
|
|
289
|
+
const out = [];
|
|
290
|
+
let corners = [];
|
|
291
|
+
let curved = false;
|
|
292
|
+
const close = () => {
|
|
293
|
+
const xs = new Set(corners.map((c) => Math.round(c.x * 10)));
|
|
294
|
+
const ys = new Set(corners.map((c) => Math.round(c.y * 10)));
|
|
295
|
+
if (!curved && corners.length >= 4 && corners.length <= 5 && xs.size === 2 && ys.size === 2) out.push({
|
|
296
|
+
minX: Math.min(...corners.map((c) => c.x)),
|
|
297
|
+
maxX: Math.max(...corners.map((c) => c.x)),
|
|
298
|
+
minY: Math.min(...corners.map((c) => c.y)),
|
|
299
|
+
maxY: Math.max(...corners.map((c) => c.y))
|
|
300
|
+
});
|
|
301
|
+
corners = [];
|
|
302
|
+
curved = false;
|
|
303
|
+
};
|
|
304
|
+
for (const s of v.segs) if (s.op === "move") {
|
|
305
|
+
close();
|
|
306
|
+
corners.push({
|
|
307
|
+
x: s.x,
|
|
308
|
+
y: s.y
|
|
309
|
+
});
|
|
310
|
+
} else if (s.op === "line") corners.push({
|
|
311
|
+
x: s.x,
|
|
312
|
+
y: s.y
|
|
313
|
+
});
|
|
314
|
+
else if (s.op === "cubic") curved = true;
|
|
315
|
+
close();
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Whether the words on a drawing are prose: lines that run across most of it,
|
|
320
|
+
* more than a caption's worth. A label is short, a paragraph set on a ground
|
|
321
|
+
* fills it.
|
|
322
|
+
*
|
|
323
|
+
* A line is its words as far as the next gap wider than an em. Two boxes side
|
|
324
|
+
* by side put their labels on one baseline — comments.pdf's "Record" and
|
|
325
|
+
* "Enter" a hundred and twenty points apart — and taken as one line they ran
|
|
326
|
+
* across the figure like a line of its text.
|
|
327
|
+
*/
|
|
328
|
+
function runsAcross(labels, box, size) {
|
|
329
|
+
const byLine = [];
|
|
330
|
+
for (const r of labels) {
|
|
331
|
+
const line = byLine.find((l) => Math.abs(l.y - r.y) <= size * .3);
|
|
332
|
+
if (line) {
|
|
333
|
+
line.inks.push(inkOf(r));
|
|
334
|
+
line.em = Math.max(line.em, r.fontSizePt);
|
|
335
|
+
} else byLine.push({
|
|
336
|
+
y: r.y,
|
|
337
|
+
em: r.fontSizePt,
|
|
338
|
+
inks: [inkOf(r)]
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const width = box.maxX - box.minX;
|
|
342
|
+
let across = 0;
|
|
343
|
+
for (const { em, inks } of byLine) {
|
|
344
|
+
inks.sort((a, b) => a.minX - b.minX);
|
|
345
|
+
let from = inks[0].minX;
|
|
346
|
+
let to = inks[0].maxX;
|
|
347
|
+
for (const ink of [...inks.slice(1), void 0]) {
|
|
348
|
+
if (ink !== void 0 && ink.minX - to <= size) {
|
|
349
|
+
to = Math.max(to, ink.maxX);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (to - from >= Math.min(width * LABEL_FILL, em * LABEL_EM)) across++;
|
|
353
|
+
if (ink !== void 0) {
|
|
354
|
+
from = ink.minX;
|
|
355
|
+
to = ink.maxX;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return across >= PROSE_LINES;
|
|
360
|
+
}
|
|
361
|
+
/** The box a run's glyphs stand in: a fifth of the size below the baseline, the rest above. */
|
|
362
|
+
function inkOf(r) {
|
|
363
|
+
const s = r.fontSizePt;
|
|
364
|
+
if (Math.abs(r.angleDeg ?? 0) < .5) return {
|
|
365
|
+
minX: Math.min(r.x, r.endX),
|
|
366
|
+
minY: r.y - s * .2,
|
|
367
|
+
maxX: Math.max(r.x, r.endX),
|
|
368
|
+
maxY: r.y + s * .8
|
|
369
|
+
};
|
|
370
|
+
return {
|
|
371
|
+
minX: Math.min(r.x, r.endX) - s * .8,
|
|
372
|
+
minY: Math.min(r.y, r.endY) - s * .8,
|
|
373
|
+
maxX: Math.max(r.x, r.endX) + s * .8,
|
|
374
|
+
maxY: Math.max(r.y, r.endY) + s * .8
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function boxOf(members) {
|
|
378
|
+
return members.reduce(union, { ...members[0] });
|
|
379
|
+
}
|
|
380
|
+
function union(a, b) {
|
|
381
|
+
return {
|
|
382
|
+
minX: Math.min(a.minX, b.minX),
|
|
383
|
+
minY: Math.min(a.minY, b.minY),
|
|
384
|
+
maxX: Math.max(a.maxX, b.maxX),
|
|
385
|
+
maxY: Math.max(a.maxY, b.maxY)
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function touches(a, b, slack) {
|
|
389
|
+
return b.minX <= a.maxX + slack && b.maxX >= a.minX - slack && b.minY <= a.maxY + slack && b.maxY >= a.minY - slack;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Whether a rule lies along a drawing rather than meeting it: across it within
|
|
393
|
+
* a point, and at least half its length inside it. A table's border that ends
|
|
394
|
+
* where a figure begins is not the figure's.
|
|
395
|
+
*/
|
|
396
|
+
function mostlyWithin(r, box) {
|
|
397
|
+
const across = r.maxX - r.minX >= r.maxY - r.minY;
|
|
398
|
+
const [from, to, lo, hi] = across ? [
|
|
399
|
+
r.minX,
|
|
400
|
+
r.maxX,
|
|
401
|
+
box.minX,
|
|
402
|
+
box.maxX
|
|
403
|
+
] : [
|
|
404
|
+
r.minY,
|
|
405
|
+
r.maxY,
|
|
406
|
+
box.minY,
|
|
407
|
+
box.maxY
|
|
408
|
+
];
|
|
409
|
+
const [side, sideEnd, sideLo, sideHi] = across ? [
|
|
410
|
+
r.minY,
|
|
411
|
+
r.maxY,
|
|
412
|
+
box.minY,
|
|
413
|
+
box.maxY
|
|
414
|
+
] : [
|
|
415
|
+
r.minX,
|
|
416
|
+
r.maxX,
|
|
417
|
+
box.minX,
|
|
418
|
+
box.maxX
|
|
419
|
+
];
|
|
420
|
+
if (sideEnd < sideLo - 1 || side > sideHi + 1) return false;
|
|
421
|
+
return Math.min(to, hi) - Math.max(from, lo) >= (to - from) / 2;
|
|
422
|
+
}
|
|
423
|
+
/** Whether most of a picture lies inside a box. */
|
|
424
|
+
function mostlyInside(img, box) {
|
|
425
|
+
return Math.max(0, Math.min(img.x + img.widthPt, box.maxX) - Math.max(img.x, box.minX)) * Math.max(0, Math.min(img.y + img.heightPt, box.maxY) - Math.max(img.y, box.minY)) > img.widthPt * img.heightPt / 2;
|
|
426
|
+
}
|
|
427
|
+
function median(values) {
|
|
428
|
+
if (values.length === 0) return 0;
|
|
429
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
430
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
433
|
+
export { joinLetters, pageFigures, tracedRun };
|