reamkit 1.30.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 +63 -0
- 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/provider.d.ts +17 -0
- package/dist/esm/core/fonts/provider.js +27 -2
- package/dist/esm/core/fonts/remote-fonts.js +8 -1
- package/dist/esm/core/ir/flow.d.ts +71 -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/cff-outline.d.ts +27 -0
- package/dist/esm/pdf-reader/cff-outline.js +169 -21
- package/dist/esm/pdf-reader/content.d.ts +53 -0
- package/dist/esm/pdf-reader/content.js +9 -1
- package/dist/esm/pdf-reader/display.d.ts +36 -0
- package/dist/esm/pdf-reader/display.js +66 -1
- package/dist/esm/pdf-reader/document.js +5 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
- package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
- 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 +131 -5
- package/dist/esm/pdf-reader/flow-build.js +345 -26
- package/dist/esm/pdf-reader/font.js +321 -36
- package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
- package/dist/esm/pdf-reader/glyf-outline.js +135 -1
- package/dist/esm/pdf-reader/glyph-names.js +154 -1
- package/dist/esm/pdf-reader/layout.d.ts +93 -0
- package/dist/esm/pdf-reader/layout.js +1703 -214
- 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/tagged.js +182 -23
- package/dist/esm/pdf-reader/text.d.ts +6 -3
- package/dist/esm/pdf-reader/text.js +98 -9
- 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.js +71 -1
- 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 +207 -21
- package/dist/esm/word/drawing-parser.d.ts +5 -3
- package/dist/esm/word/drawing-parser.js +49 -8
- 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 +14 -2
- package/dist/esm/word/run-properties.js +26 -0
- package/package.json +8 -3
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { editableEmbedding } from "../core/font/ttf-build.js";
|
|
2
|
+
import { isRightToLeft } from "./content.js";
|
|
3
|
+
import { ASCENDER, NATURAL_LINE_EM } from "./flow-build.js";
|
|
4
|
+
//#region src/pdf-reader/face-outlines.ts
|
|
5
|
+
/**
|
|
6
|
+
* Fold one content stream's shown codes into the document's.
|
|
7
|
+
*
|
|
8
|
+
* @param into The document's codes, added to.
|
|
9
|
+
* @param from One stream's (`InterpretResult.shown`).
|
|
10
|
+
*/
|
|
11
|
+
function addShown(into, from) {
|
|
12
|
+
for (const [font, codes] of from) {
|
|
13
|
+
let set = into.get(font);
|
|
14
|
+
if (!set) into.set(font, set = /* @__PURE__ */ new Set());
|
|
15
|
+
for (const code of codes) set.add(code);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The outlines of every face the pages painted characters in, keyed by the name
|
|
20
|
+
* a run in the face carries.
|
|
21
|
+
*
|
|
22
|
+
* A face is left out where its program cannot be trusted to say what the page
|
|
23
|
+
* showed: where more than a fifth of its characters have no glyph the reader
|
|
24
|
+
* can find, the way to its glyphs is not the one the page took, and a face
|
|
25
|
+
* embedded that way would show the wrong letters in place of a substitute's
|
|
26
|
+
* right ones. A character whose glyph cannot be found is left out alone — a
|
|
27
|
+
* reader draws it in another face.
|
|
28
|
+
*
|
|
29
|
+
* So is a face that shows a script which is SHAPED: an Arabic letter takes one
|
|
30
|
+
* of four forms by its neighbours, and a face rebuilt here carries one glyph
|
|
31
|
+
* per character and none of the rules that choose between them. Nor can it
|
|
32
|
+
* carry the part of such a face it could: ArabicCIDTrueType.pdf maps half its
|
|
33
|
+
* glyphs to presentation forms, which are shaped already, and half to plain
|
|
34
|
+
* letters — embedded, each line came out in two faces, a bold word with light
|
|
35
|
+
* letters in it.
|
|
36
|
+
*
|
|
37
|
+
* @param shown The codes the pages painted, per font.
|
|
38
|
+
* @param spacing How the pages space each face's text apart where they move the
|
|
39
|
+
* pen rather than draw a glyph (see {@link pageSpacing}).
|
|
40
|
+
* @returns Run font name → the face's outlines.
|
|
41
|
+
*/
|
|
42
|
+
function faceOutlinesOf(shown, spacing = NO_SPACING) {
|
|
43
|
+
const faces = /* @__PURE__ */ new Map();
|
|
44
|
+
for (const [font, codes] of shown) {
|
|
45
|
+
const program = font.program;
|
|
46
|
+
if (!program || font.name === void 0) continue;
|
|
47
|
+
let face = faces.get(font.name);
|
|
48
|
+
if (!face) {
|
|
49
|
+
face = {
|
|
50
|
+
font,
|
|
51
|
+
program,
|
|
52
|
+
glyphs: /* @__PURE__ */ new Map(),
|
|
53
|
+
ligatures: /* @__PURE__ */ new Map(),
|
|
54
|
+
sought: 0,
|
|
55
|
+
missed: 0,
|
|
56
|
+
shaped: false
|
|
57
|
+
};
|
|
58
|
+
faces.set(font.name, face);
|
|
59
|
+
}
|
|
60
|
+
if (program.fsType !== void 0 && (face.fsType === void 0 || editableEmbedding(face.fsType))) face.fsType = program.fsType;
|
|
61
|
+
for (const code of [...codes].sort((a, b) => a - b)) gather(face, font, program, code);
|
|
62
|
+
}
|
|
63
|
+
const out = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const [name, face] of faces) {
|
|
65
|
+
const outlines = outlinesOf(face, spacing.wordGaps.get(name), spacing.kerning.get(name));
|
|
66
|
+
if (outlines) out.set(name, outlines);
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
var NO_SPACING = {
|
|
71
|
+
wordGaps: /* @__PURE__ */ new Map(),
|
|
72
|
+
kerning: /* @__PURE__ */ new Map()
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* §9.4.3 — what the gaps between a face's runs say about how it is spaced.
|
|
76
|
+
*
|
|
77
|
+
* The interpreter emits a run for every string of a `TJ` array, so where the
|
|
78
|
+
* page nudges the pen inside a word — which is how a producer KERNS it — two
|
|
79
|
+
* runs meet with the nudge between them, and the letters on either side are
|
|
80
|
+
* the pair. bigboundingbox.pdf kerns its Calibri by eight pairs in one short
|
|
81
|
+
* line, and set without them the line came out 0.67pt longer than the page's
|
|
82
|
+
* and no longer fit the cell it was measured into. Each pair is the median of
|
|
83
|
+
* every nudge the page gives it.
|
|
84
|
+
*
|
|
85
|
+
* A wider gap is the white between two words, where the page moves the pen
|
|
86
|
+
* instead of showing a space; the face's space is the lower quartile of them,
|
|
87
|
+
* which a justified line's stretched gaps do not reach.
|
|
88
|
+
*
|
|
89
|
+
* @param pages The runs each page set, in the order they were painted.
|
|
90
|
+
* @returns Per face, its word gap and its kerning pairs.
|
|
91
|
+
*/
|
|
92
|
+
function pageSpacing(pages) {
|
|
93
|
+
const gaps = /* @__PURE__ */ new Map();
|
|
94
|
+
const nudges = /* @__PURE__ */ new Map();
|
|
95
|
+
const beside = /* @__PURE__ */ new Map();
|
|
96
|
+
for (const runs of pages) for (let i = 1; i < runs.length; i++) {
|
|
97
|
+
const a = runs[i - 1];
|
|
98
|
+
const b = runs[i];
|
|
99
|
+
const name = a.fontName;
|
|
100
|
+
if (name === void 0 || b.fontName !== name || a.angleDeg !== void 0) continue;
|
|
101
|
+
if (b.angleDeg !== void 0 || a.fontSizePt !== b.fontSizePt || !(a.fontSizePt > 0)) continue;
|
|
102
|
+
if (Math.abs(a.y - b.y) > a.fontSizePt * SAME_LINE_EM) continue;
|
|
103
|
+
if (isRightToLeft(a.text) || isRightToLeft(b.text)) continue;
|
|
104
|
+
const left = [...a.text].at(-1);
|
|
105
|
+
const right = [...b.text][0];
|
|
106
|
+
if (left === void 0 || right === void 0) continue;
|
|
107
|
+
const gap = (b.x - a.endX) / a.fontSizePt * 1e3;
|
|
108
|
+
if (/\s/u.test(left) || /\s/u.test(right)) {
|
|
109
|
+
if (gap > MIN_KERN && gap < MAX_KERN) {
|
|
110
|
+
let list = beside.get(name);
|
|
111
|
+
if (!list) beside.set(name, list = []);
|
|
112
|
+
list.push(b.x - a.endX);
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (gap >= MIN_WORD_GAP && gap <= MAX_WORD_GAP) {
|
|
117
|
+
let list = gaps.get(name);
|
|
118
|
+
if (!list) gaps.set(name, list = []);
|
|
119
|
+
list.push(gap);
|
|
120
|
+
} else if (gap > MIN_KERN && gap < MAX_KERN) {
|
|
121
|
+
let pairs = nudges.get(name);
|
|
122
|
+
if (!pairs) nudges.set(name, pairs = /* @__PURE__ */ new Map());
|
|
123
|
+
const pair = `${left}${right}`;
|
|
124
|
+
let list = pairs.get(pair);
|
|
125
|
+
if (!list) pairs.set(pair, list = []);
|
|
126
|
+
list.push([gap, b.x - a.endX]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const wordGaps = /* @__PURE__ */ new Map();
|
|
130
|
+
for (const [name, list] of gaps) {
|
|
131
|
+
if (list.length < MIN_GAP_SAMPLES) continue;
|
|
132
|
+
list.sort((p, q) => p - q);
|
|
133
|
+
wordGaps.set(name, list[Math.floor(list.length / 4)]);
|
|
134
|
+
}
|
|
135
|
+
const kerning = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const [name, pairs] of nudges) {
|
|
137
|
+
if (tracked([...pairs.values()].flat().map(([, points]) => points), beside.get(name) ?? [])) continue;
|
|
138
|
+
const kept = /* @__PURE__ */ new Map();
|
|
139
|
+
for (const [pair, list] of pairs) {
|
|
140
|
+
const inEm = median(list.map(([em]) => em));
|
|
141
|
+
const inPt = median(list.map(([, points]) => points));
|
|
142
|
+
if (Math.abs(inEm) < MIN_KERN_VALUE || Math.abs(inPt) < MIN_KERN_PT) continue;
|
|
143
|
+
if (list.length >= 2 && list.every(([, points]) => Math.abs(points - inPt) <= TWIP_PT) || Math.abs(inPt) >= SURE_KERN_PT) kept.set(pair, inEm);
|
|
144
|
+
}
|
|
145
|
+
if (kept.size >= MIN_KERNED_PAIRS) kerning.set(name, kept);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
wordGaps,
|
|
149
|
+
kerning
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Whether a face's nudges are TRACKING rather than kerning: one amount between
|
|
154
|
+
* nearly every two letters, and beside its spaces too, where no face is kerned.
|
|
155
|
+
* bug1157493.pdf sets a line of Courier half a tenth of an em tight, every
|
|
156
|
+
* letter and every space alike; read as kerning pairs, its letters closed up
|
|
157
|
+
* and its spaces did not.
|
|
158
|
+
*
|
|
159
|
+
* @param nudges The face's nudges between letters, in points.
|
|
160
|
+
* @param beside Its nudges beside a space, in points.
|
|
161
|
+
*/
|
|
162
|
+
function tracked(nudges, beside) {
|
|
163
|
+
if (beside.length < MIN_TRACKED_SPACES || nudges.length === 0) return false;
|
|
164
|
+
const typical = median([...nudges, ...beside]);
|
|
165
|
+
if (Math.abs(typical) < MIN_KERN_PT) return false;
|
|
166
|
+
const near = (points) => Math.abs(points - typical) <= TWIP_PT;
|
|
167
|
+
const all = [...nudges, ...beside];
|
|
168
|
+
return beside.filter(near).length >= MIN_TRACKED_SPACES && all.filter(near).length >= all.length * TRACKED_SHARE;
|
|
169
|
+
}
|
|
170
|
+
/** How many nudges beside spaces, and what share of all, make a face tracked. */
|
|
171
|
+
var MIN_TRACKED_SPACES = 2;
|
|
172
|
+
var TRACKED_SHARE = .8;
|
|
173
|
+
/**
|
|
174
|
+
* The faces a page KERNED: those with kerning pairs (see {@link pageSpacing},
|
|
175
|
+
* which keeps a face's pairs only where there are enough of them to say it was
|
|
176
|
+
* kerning and not an accident of how its strings were cut).
|
|
177
|
+
*
|
|
178
|
+
* @param spacing How the pages space their faces (see {@link pageSpacing}).
|
|
179
|
+
* @returns The run font names of the kerned faces.
|
|
180
|
+
*/
|
|
181
|
+
function kernedFaces(spacing) {
|
|
182
|
+
return new Set(spacing.kerning.keys());
|
|
183
|
+
}
|
|
184
|
+
/** The middle of some numbers: the mean of the middle two where they are even. */
|
|
185
|
+
function median(values) {
|
|
186
|
+
const sorted = [...values].sort((p, q) => p - q);
|
|
187
|
+
const mid = sorted.length / 2;
|
|
188
|
+
return sorted.length % 2 === 1 ? sorted[Math.floor(mid)] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
189
|
+
}
|
|
190
|
+
/** Two runs within this much of an em of each other's baseline share a line. */
|
|
191
|
+
var SAME_LINE_EM = .1;
|
|
192
|
+
/** A gap between words: wider than a kern, narrower than a column's white. */
|
|
193
|
+
var MIN_WORD_GAP = 100;
|
|
194
|
+
var MAX_WORD_GAP = 600;
|
|
195
|
+
/**
|
|
196
|
+
* A nudge inside a word, in thousandths of an em: kerning tightens a pair by as
|
|
197
|
+
* much as a quarter of an em and opens one by far less — a gap wider than this
|
|
198
|
+
* is a space the page did not draw.
|
|
199
|
+
*/
|
|
200
|
+
var MIN_KERN = -250;
|
|
201
|
+
var MAX_KERN = 60;
|
|
202
|
+
/** A nudge smaller than this is the rounding of a producer's positions, not a kern. */
|
|
203
|
+
var MIN_KERN_VALUE = 2;
|
|
204
|
+
/**
|
|
205
|
+
* …and so is one smaller than a twip and a half, in points: Word lays a line
|
|
206
|
+
* out in twips and writes the difference from the face's own widths as a
|
|
207
|
+
* nudge. annotation-highlight.pdf's Calibri is nudged three to six thousandths
|
|
208
|
+
* of an em between letters no kerning table pairs, and taken for kerns they
|
|
209
|
+
* moved its words a fraction of a pixel off the page's.
|
|
210
|
+
*/
|
|
211
|
+
var MIN_KERN_PT = .075;
|
|
212
|
+
/** A twip, in points: the grid a Word page's positions are rounded to. */
|
|
213
|
+
var TWIP_PT = .05;
|
|
214
|
+
/**
|
|
215
|
+
* A nudge this wide is a kern seen once: rounding moves a letter a twip or
|
|
216
|
+
* two, and bug793632.pdf's Calibri is nudged two twips either way — "st" in
|
|
217
|
+
* and "on" out — once each, by no kerning at all.
|
|
218
|
+
*/
|
|
219
|
+
var SURE_KERN_PT = .15;
|
|
220
|
+
/** How many pairs make a face one the page kerned. */
|
|
221
|
+
var MIN_KERNED_PAIRS = 2;
|
|
222
|
+
/** How many gaps it takes to say how wide a face's space is. */
|
|
223
|
+
var MIN_GAP_SAMPLES = 3;
|
|
224
|
+
/** Read one code's glyph into its face, where it stands for one character or a ligature. */
|
|
225
|
+
function gather(face, font, program, code) {
|
|
226
|
+
const chars = [...font.decode([code])];
|
|
227
|
+
if (chars.length > 1) {
|
|
228
|
+
gatherLigature(face, font, program, code, chars);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (chars.length !== 1) return;
|
|
232
|
+
const char = chars[0];
|
|
233
|
+
if (face.glyphs.has(char) || !embeddable(char)) return;
|
|
234
|
+
if (shaped(char)) {
|
|
235
|
+
face.shaped = true;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const blank = WHITE.test(char);
|
|
239
|
+
if (!blank) face.sought++;
|
|
240
|
+
const outline = program.glyph(code);
|
|
241
|
+
if (outline === void 0 || outline.length === 0 && !blank) {
|
|
242
|
+
if (!blank) face.missed++;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
face.glyphs.set(char, {
|
|
246
|
+
outline,
|
|
247
|
+
advance: font.width(code)
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* §9.10.2 — a code that stands for a run of letters is a LIGATURE: one glyph
|
|
252
|
+
* the page drew for "fi", "ffl" or Calibri's "tt". The text carries the letters
|
|
253
|
+
* and the rebuilt face the glyph, so a reader that joins them draws what the
|
|
254
|
+
* page drew — set apart, attachment.pdf's "attachment" came out with two
|
|
255
|
+
* crossbars where the page has one.
|
|
256
|
+
*
|
|
257
|
+
* Only letters, and no more than {@link MAX_LIGATURE} of them: a producer that
|
|
258
|
+
* maps a glyph to a longer string — a logo to a company's name — has not drawn
|
|
259
|
+
* a ligature.
|
|
260
|
+
*/
|
|
261
|
+
function gatherLigature(face, font, program, code, chars) {
|
|
262
|
+
const key = chars.join("");
|
|
263
|
+
if (chars.length > MAX_LIGATURE || face.ligatures.has(key)) return;
|
|
264
|
+
if (!chars.every((c) => /^\p{L}$/u.test(c) && !shaped(c))) return;
|
|
265
|
+
const outline = program.glyph(code);
|
|
266
|
+
if (outline === void 0 || outline.length === 0) return;
|
|
267
|
+
face.ligatures.set(key, {
|
|
268
|
+
outline,
|
|
269
|
+
advance: font.width(code)
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/** The most letters one ligature joins: ffi, ffl. */
|
|
273
|
+
var MAX_LIGATURE = 3;
|
|
274
|
+
/** A face's gathered glyphs as {@link FaceOutlines}, where it is one to embed. */
|
|
275
|
+
function outlinesOf(face, gap, kerning) {
|
|
276
|
+
if (face.shaped || face.sought === 0 || face.missed > face.sought * MAX_MISSED_SHARE) return;
|
|
277
|
+
const glyphs = new Map(face.glyphs);
|
|
278
|
+
const program = face.program;
|
|
279
|
+
if (!glyphs.has(" ")) glyphs.set(" ", {
|
|
280
|
+
outline: [],
|
|
281
|
+
advance: spaceAdvance(face, gap)
|
|
282
|
+
});
|
|
283
|
+
if (!glyphs.has("\xA0")) glyphs.set("\xA0", glyphs.get(" "));
|
|
284
|
+
const { yMin, yMax } = extent(glyphs);
|
|
285
|
+
const pairs = new Map([...kerning ?? []].filter(([pair]) => [...pair].every((c) => glyphs.has(c))));
|
|
286
|
+
const ligatures = new Map([...face.ligatures].filter(([letters]) => [...letters].every((c) => glyphs.has(c))));
|
|
287
|
+
return {
|
|
288
|
+
glyphs,
|
|
289
|
+
...pairs.size > 0 ? { kerning: pairs } : {},
|
|
290
|
+
...ligatures.size > 0 ? { ligatures } : {},
|
|
291
|
+
...face.fsType !== void 0 ? { fsType: face.fsType } : {},
|
|
292
|
+
bold: face.font.bold === true,
|
|
293
|
+
italic: face.font.italic === true,
|
|
294
|
+
postScriptName: program.postScriptName,
|
|
295
|
+
ascent: Math.max(ASCENDER * 1e3, yMax),
|
|
296
|
+
descent: Math.min((ASCENDER - NATURAL_LINE_EM) * 1e3, yMin),
|
|
297
|
+
...program.capHeight !== void 0 ? { capHeight: program.capHeight } : {},
|
|
298
|
+
...program.xHeight !== void 0 ? { xHeight: program.xHeight } : {},
|
|
299
|
+
italicAngle: program.italicAngle,
|
|
300
|
+
fixedPitch: program.fixedPitch
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/** A face may miss no more than this share of the glyphs looked for. */
|
|
304
|
+
var MAX_MISSED_SHARE = .2;
|
|
305
|
+
/**
|
|
306
|
+
* The advance of a space the page never showed: the width the file states for
|
|
307
|
+
* the face's space (see `ContentFont.spaceWidth`), where it states one; the
|
|
308
|
+
* width the program gives its space glyph — which a subset keeps though it
|
|
309
|
+
* drops the outline;
|
|
310
|
+
* the white the page itself leaves between the face's words; the one advance
|
|
311
|
+
* every glyph has, in a fixed-pitch face; a quarter of an em otherwise.
|
|
312
|
+
* bigboundingbox.pdf sets its Calibri in a subset that keeps no space and no
|
|
313
|
+
* way to one, and spaces the words by hand: with a quarter of an em for
|
|
314
|
+
* Calibri's 226 thousandths, "Enter the amount you are paying above" no longer
|
|
315
|
+
* fit its cell.
|
|
316
|
+
*/
|
|
317
|
+
function spaceAdvance(face, gap) {
|
|
318
|
+
const width = face.font.spaceWidth;
|
|
319
|
+
if (width !== void 0 && width > 0 && width < 1e3) return width;
|
|
320
|
+
if (face.program.spaceAdvance !== void 0) return face.program.spaceAdvance;
|
|
321
|
+
if (gap !== void 0) return gap;
|
|
322
|
+
if (face.program.fixedPitch) {
|
|
323
|
+
const first = [...face.glyphs.values()].find((g) => g.advance > 0);
|
|
324
|
+
if (first) return first.advance;
|
|
325
|
+
}
|
|
326
|
+
return 250;
|
|
327
|
+
}
|
|
328
|
+
/** How high and how low the glyphs' contours reach, in thousandths of an em. */
|
|
329
|
+
function extent(glyphs) {
|
|
330
|
+
let yMin = 0;
|
|
331
|
+
let yMax = 0;
|
|
332
|
+
for (const glyph of glyphs.values()) for (const seg of glyph.outline) {
|
|
333
|
+
if (seg.op === "close") continue;
|
|
334
|
+
yMin = Math.min(yMin, seg.y * 1e3);
|
|
335
|
+
yMax = Math.max(yMax, seg.y * 1e3);
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
yMin,
|
|
339
|
+
yMax
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/** Whitespace: a glyph that rightly draws nothing. */
|
|
343
|
+
var WHITE = /^\s$/u;
|
|
344
|
+
/** Whether a character is one a face carries at all: not a control or a format character. */
|
|
345
|
+
function embeddable(char) {
|
|
346
|
+
return !/^[\p{Cc}\p{Cf}\p{Cs}\uFFFD]$/u.test(char);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Whether a character is a letter of a script whose glyphs a face's shaping
|
|
350
|
+
* rules choose — which a rebuilt face does not have.
|
|
351
|
+
*/
|
|
352
|
+
function shaped(char) {
|
|
353
|
+
const cp = char.codePointAt(0);
|
|
354
|
+
if (cp >= 64336 && cp <= 65023 || cp >= 65136 && cp <= 65279) return false;
|
|
355
|
+
if (cp >= 4352 && cp <= 4607 || cp >= 43360 && cp <= 43391) return true;
|
|
356
|
+
if (cp >= 55216 && cp <= 55295) return true;
|
|
357
|
+
return SHAPED.test(char);
|
|
358
|
+
}
|
|
359
|
+
/** The scripts whose glyphs a face's shaping rules choose. */
|
|
360
|
+
var SHAPED = /\p{Script=Arabic}|\p{Script=Syriac}|\p{Script=Thaana}|\p{Script=Nko}|\p{Script=Mandaic}|\p{Script=Devanagari}|\p{Script=Bengali}|\p{Script=Gurmukhi}|\p{Script=Gujarati}|\p{Script=Oriya}|\p{Script=Tamil}|\p{Script=Telugu}|\p{Script=Kannada}|\p{Script=Malayalam}|\p{Script=Sinhala}|\p{Script=Thai}|\p{Script=Lao}|\p{Script=Tibetan}|\p{Script=Myanmar}|\p{Script=Khmer}|\p{Script=Mongolian}|\p{Script=Javanese}|\p{Script=Balinese}/u;
|
|
361
|
+
//#endregion
|
|
362
|
+
export { addShown, faceOutlinesOf, kernedFaces, pageSpacing };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { TextRun } from './content.js';
|
|
2
|
+
import { PdfImage } from './images.js';
|
|
3
|
+
import { PdfVector } from './vector.js';
|
|
4
|
+
/** One drawing on a page, with the words set on it. Page space, y-up. */
|
|
5
|
+
export interface PageFigure {
|
|
6
|
+
readonly minX: number;
|
|
7
|
+
readonly minY: number;
|
|
8
|
+
readonly maxX: number;
|
|
9
|
+
readonly maxY: number;
|
|
10
|
+
/** The paths that draw it, in no particular order. */
|
|
11
|
+
readonly vectors: ReadonlyArray<PdfVector>;
|
|
12
|
+
/** The pictures it holds. */
|
|
13
|
+
readonly images: ReadonlyArray<PdfImage>;
|
|
14
|
+
/** The words set on it. */
|
|
15
|
+
readonly labels: ReadonlyArray<TextRun>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The figures a page draws.
|
|
19
|
+
*
|
|
20
|
+
* A figure is paths that stand together — each within half an em of the next —
|
|
21
|
+
* and DRAW something: a curve or a slanted line among them. Straight lines
|
|
22
|
+
* and boxes alone are a ruling, which is a table's or a form's, and a page
|
|
23
|
+
* reads those as text. It has words set on it, or it is only artwork, which
|
|
24
|
+
* the page anchors as it always has; and none of its words run across it
|
|
25
|
+
* line after line, which is prose set on a ground, not a label.
|
|
26
|
+
*
|
|
27
|
+
* @param vectors The page's paths, where the page shows them.
|
|
28
|
+
* @param images The page's pictures, likewise.
|
|
29
|
+
* @param runs The page's text, likewise.
|
|
30
|
+
* @param sheet The page's size.
|
|
31
|
+
* @returns The figures, top of the page first.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pageFigures(vectors: ReadonlyArray<PdfVector>, images: ReadonlyArray<PdfImage>, runs: ReadonlyArray<TextRun>, sheet: {
|
|
34
|
+
readonly width: number;
|
|
35
|
+
readonly height: number;
|
|
36
|
+
}): Array<PageFigure>;
|
|
37
|
+
/**
|
|
38
|
+
* Whether a run is lettering the file states no character for: glyphs traced
|
|
39
|
+
* as paths (see `PdfVector.glyph`), and a label only by where they stand.
|
|
40
|
+
*
|
|
41
|
+
* @param run A run of the page's text.
|
|
42
|
+
*/
|
|
43
|
+
export declare function tracedRun(run: TextRun): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* A figure's paths with the traced letters painted one after another joined
|
|
46
|
+
* into one: the letters of a label are one shape, where a shape a letter made
|
|
47
|
+
* a chart of six hundred.
|
|
48
|
+
*
|
|
49
|
+
* @param paths The figure's paths, in the order the page painted them.
|
|
50
|
+
* @returns The paths to draw, in that order.
|
|
51
|
+
*/
|
|
52
|
+
export declare function joinLetters(paths: ReadonlyArray<PdfVector>): Array<PdfVector>;
|