reamkit 1.30.0 → 1.32.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 +70 -24
- package/dist/esm/core/bidi/index.d.ts +10 -0
- package/dist/esm/core/bidi/index.js +18 -2
- package/dist/esm/core/converter/facade.js +10 -5
- package/dist/esm/core/converter/project.js +4 -1
- package/dist/esm/core/converter/ream.js +17 -6
- package/dist/esm/core/converter/sheet-images.d.ts +17 -0
- package/dist/esm/core/converter/sheet-images.js +33 -0
- 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 +175 -0
- package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
- package/dist/esm/core/drawingml/chart-geometry.js +198 -108
- package/dist/esm/core/drawingml/chart-parser.js +73 -2
- package/dist/esm/core/drawingml/colors.d.ts +6 -0
- package/dist/esm/core/drawingml/colors.js +23 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
- package/dist/esm/core/drawingml/shape-render.js +4 -2
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/measure.d.ts +9 -0
- package/dist/esm/core/font/measure.js +9 -1
- 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/families.js +4 -2
- 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/fonts/scripts.js +4 -2
- package/dist/esm/core/ir/flow.d.ts +77 -1
- package/dist/esm/core/ir/sheet.d.ts +43 -0
- package/dist/esm/core/number-format.js +16 -11
- 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/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
- package/dist/esm/core/style-cascade/resolver.js +39 -6
- package/dist/esm/core/style-cascade/types.d.ts +19 -1
- package/dist/esm/core/style-cascade/types.js +3 -0
- package/dist/esm/excel/column-bands.d.ts +39 -6
- package/dist/esm/excel/column-bands.js +41 -17
- package/dist/esm/excel/comments-parser.js +19 -0
- package/dist/esm/excel/conditional-format.d.ts +7 -3
- package/dist/esm/excel/conditional-format.js +77 -39
- package/dist/esm/excel/formula/context.d.ts +18 -0
- package/dist/esm/excel/formula/eval.js +51 -0
- package/dist/esm/excel/formula/index.d.ts +2 -2
- package/dist/esm/excel/formula/lexer.d.ts +1 -1
- package/dist/esm/excel/formula/lexer.js +41 -0
- package/dist/esm/excel/formula/parser.d.ts +25 -0
- package/dist/esm/excel/formula/parser.js +95 -0
- package/dist/esm/excel/print-model.d.ts +38 -1
- package/dist/esm/excel/print-model.js +522 -80
- package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -1
- package/dist/esm/excel/sheet-drawing.d.ts +38 -2
- package/dist/esm/excel/sheet-drawing.js +47 -13
- package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
- package/dist/esm/excel/sheet-shape-parser.js +18 -6
- package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
- package/dist/esm/excel/sheet-to-flow.js +434 -47
- package/dist/esm/excel/styles-parser.d.ts +9 -0
- package/dist/esm/excel/styles-parser.js +80 -26
- package/dist/esm/excel/table-parser.js +5 -0
- package/dist/esm/excel/table-style-presets.d.ts +26 -0
- package/dist/esm/excel/table-style-presets.js +1555 -0
- package/dist/esm/excel/table-style.d.ts +13 -0
- package/dist/esm/excel/table-style.js +75 -0
- package/dist/esm/excel/tint.d.ts +10 -0
- package/dist/esm/excel/tint.js +75 -0
- package/dist/esm/excel/vml-drawing.d.ts +41 -3
- package/dist/esm/excel/vml-drawing.js +143 -10
- package/dist/esm/excel/worksheet-parser.js +89 -15
- package/dist/esm/excel/xls/biff-reader.js +10 -2
- package/dist/esm/excel/xlsx-reader.js +195 -14
- package/dist/esm/excel/xlsx-writer.js +8 -7
- package/dist/esm/html/html-writer.js +202 -20
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/layout/fit-pages.d.ts +11 -0
- package/dist/esm/layout/fit-pages.js +86 -0
- package/dist/esm/layout/line-paint.d.ts +58 -0
- package/dist/esm/layout/line-paint.js +107 -0
- package/dist/esm/layout/page-doc.d.ts +61 -3
- package/dist/esm/layout/page-doc.js +141 -1
- package/dist/esm/layout/styled-layout.d.ts +55 -7
- package/dist/esm/layout/styled-layout.js +1154 -169
- package/dist/esm/layout/turned-section.d.ts +38 -0
- package/dist/esm/layout/turned-section.js +205 -0
- package/dist/esm/markdown/markdown-writer.js +17 -6
- package/dist/esm/pdf/styled-page-emitter.js +114 -60
- package/dist/esm/pdf/styled-page-renderer.js +1 -1
- 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/svg/svg-text.d.ts +58 -0
- package/dist/esm/svg/svg-text.js +322 -0
- package/dist/esm/svg/svg-writer.js +135 -71
- 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 +218 -23
- 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 +12 -3
|
@@ -5,6 +5,7 @@ import { textMarkupOf } from "./annot-draw.js";
|
|
|
5
5
|
import { appearanceContent, collectPageAppearances } from "./annots.js";
|
|
6
6
|
import { buildContentFont } from "./font.js";
|
|
7
7
|
import { hiddenProperties, hiddenXObject } from "./optional-content.js";
|
|
8
|
+
import { addShown } from "./face-outlines.js";
|
|
8
9
|
import { patternTint, tintedHex } from "./pattern-tint.js";
|
|
9
10
|
//#region src/pdf-reader/text.ts
|
|
10
11
|
var MAX_FORM_DEPTH = 8;
|
|
@@ -15,22 +16,24 @@ var MAX_FORM_DEPTH = 8;
|
|
|
15
16
|
* origin falls inside a `/Link` annotation's `/Rect` with that link's URI (EP8)
|
|
16
17
|
* so hyperlinks survive.
|
|
17
18
|
*
|
|
18
|
-
* @param file
|
|
19
|
-
* @param page
|
|
19
|
+
* @param file The owning {@link PdfFile}.
|
|
20
|
+
* @param page The page to extract.
|
|
21
|
+
* @param painted Where to gather the codes each font painted, for a caller
|
|
22
|
+
* that embeds the faces (see `./face-outlines`).
|
|
20
23
|
* @returns The page's runs, each carrying an `href` when it sits under a link.
|
|
21
24
|
*/
|
|
22
|
-
function extractPageText(file, page) {
|
|
25
|
+
function extractPageText(file, page, painted) {
|
|
23
26
|
const runs = [];
|
|
24
|
-
collectRuns(file, page.resources, file.pageContent(page), IDENTITY, 0, /* @__PURE__ */ new Set(), runs);
|
|
27
|
+
collectRuns(file, page.resources, file.pageContent(page), IDENTITY, 0, /* @__PURE__ */ new Set(), runs, painted);
|
|
25
28
|
const own = runs.length;
|
|
26
|
-
for (const appearance of collectPageAppearances(file, page)) collectRuns(file, appearance.resources ?? page.resources, appearanceContent(file, appearance), appearance.ctm, 1, new Set([appearance.stream]), runs);
|
|
29
|
+
for (const appearance of collectPageAppearances(file, page)) collectRuns(file, appearance.resources ?? page.resources, appearanceContent(file, appearance), appearance.ctm, 1, new Set([appearance.stream]), runs, painted);
|
|
27
30
|
for (let i = own; i < runs.length; i++) runs[i] = {
|
|
28
31
|
...runs[i],
|
|
29
32
|
annotation: true
|
|
30
33
|
};
|
|
31
34
|
const links = collectLinks(file, page);
|
|
32
35
|
const marks = collectTextMarkup(file, page);
|
|
33
|
-
const shown = withoutRestrikes(runs);
|
|
36
|
+
const shown = withAccentsComposed(withoutRestrikes(runs));
|
|
34
37
|
if (links.length === 0 && marks.length === 0) return shown;
|
|
35
38
|
return shown.flatMap((run) => {
|
|
36
39
|
const link = links.find((l) => inRect(run.x, run.y, l.rect));
|
|
@@ -168,6 +171,91 @@ function withoutRestrikes(runs) {
|
|
|
168
171
|
}
|
|
169
172
|
/** How near, in ems, a re-strike of the same text lands to the one it thickens. */
|
|
170
173
|
var RESTRIKE_EM = .08;
|
|
174
|
+
/**
|
|
175
|
+
* §9.4.3 — an accent struck over a letter, composed with it.
|
|
176
|
+
*
|
|
177
|
+
* TeX sets an accented letter its font has no glyph for as two: the accent,
|
|
178
|
+
* and the letter drawn back under it — "ï" is a dieresis and a dotless i.
|
|
179
|
+
* Read as they come, comments.pdf's "naïve" came back "na¨ıve", the accent a
|
|
180
|
+
* character of the word. A run that is one spacing accent, with the pen taken
|
|
181
|
+
* back under it for the run after it, is the accent of that run's first letter
|
|
182
|
+
* — or, where the accent was struck after its letter, of the last letter of the
|
|
183
|
+
* run before it. The two are written as the one character Unicode composes them
|
|
184
|
+
* into, a dotless i or j taking back its dot's place under the accent.
|
|
185
|
+
*
|
|
186
|
+
* @param runs The page's runs, in painting order.
|
|
187
|
+
* @returns The runs with each such accent composed into its letter.
|
|
188
|
+
*/
|
|
189
|
+
function withAccentsComposed(runs) {
|
|
190
|
+
const out = [];
|
|
191
|
+
for (let i = 0; i < runs.length; i++) {
|
|
192
|
+
const run = runs[i];
|
|
193
|
+
const mark = ACCENTS.get(run.text);
|
|
194
|
+
const next = runs[i + 1];
|
|
195
|
+
const prev = out[out.length - 1];
|
|
196
|
+
if (mark !== void 0 && next !== void 0 && struckOver(run, next, "first")) {
|
|
197
|
+
const [first = "", ...rest] = [...next.text];
|
|
198
|
+
out.push({
|
|
199
|
+
...next,
|
|
200
|
+
text: accented(first, mark) + rest.join("")
|
|
201
|
+
});
|
|
202
|
+
i++;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (mark !== void 0 && prev !== void 0 && struckOver(run, prev, "last")) {
|
|
206
|
+
const chars = [...prev.text];
|
|
207
|
+
const last = chars.pop() ?? "";
|
|
208
|
+
out[out.length - 1] = {
|
|
209
|
+
...prev,
|
|
210
|
+
text: chars.join("") + accented(last, mark)
|
|
211
|
+
};
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
out.push(run);
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Whether an accent stands over the first letter of a run (the pen taken back
|
|
220
|
+
* under it) or over the last (the pen taken back to strike it): on one
|
|
221
|
+
* baseline, give or take the lift an accent over a capital takes, and
|
|
222
|
+
* overlapping it by more than a hair.
|
|
223
|
+
*/
|
|
224
|
+
function struckOver(accent, base, which) {
|
|
225
|
+
if (accent.angleDeg !== void 0 || base.angleDeg !== void 0) return false;
|
|
226
|
+
const size = base.fontSizePt || accent.fontSizePt || 10;
|
|
227
|
+
if (Math.abs(accent.y - base.y) > size * ACCENT_LIFT_EM) return false;
|
|
228
|
+
if (!/^\p{L}/u.test(which === "first" ? base.text : [...base.text].at(-1) ?? "")) return false;
|
|
229
|
+
const hair = size * ACCENT_OVERLAP_EM;
|
|
230
|
+
return which === "first" ? base.x < accent.endX - hair && base.x > accent.x - size : accent.x < base.endX - hair && accent.x > base.x;
|
|
231
|
+
}
|
|
232
|
+
/** A letter with an accent composed onto it: a dotless i or j takes back its dot's place. */
|
|
233
|
+
function accented(letter, mark) {
|
|
234
|
+
return ((letter === "ı" ? "i" : letter === "ȷ" ? "j" : letter) + mark).normalize("NFC");
|
|
235
|
+
}
|
|
236
|
+
/** How far over the line, in ems, TeX lifts an accent to stand over a capital. */
|
|
237
|
+
var ACCENT_LIFT_EM = .6;
|
|
238
|
+
/** How far, in ems, an accent must overlap its letter to stand over it. */
|
|
239
|
+
var ACCENT_OVERLAP_EM = .05;
|
|
240
|
+
/** The spacing accents TeX strikes over a letter, and the combining marks they are. */
|
|
241
|
+
var ACCENTS = new Map([
|
|
242
|
+
["`", "̀"],
|
|
243
|
+
["´", "́"],
|
|
244
|
+
["ˆ", "̂"],
|
|
245
|
+
["^", "̂"],
|
|
246
|
+
["˜", "̃"],
|
|
247
|
+
["~", "̃"],
|
|
248
|
+
["¯", "̄"],
|
|
249
|
+
["ˉ", "̄"],
|
|
250
|
+
["˘", "̆"],
|
|
251
|
+
["˙", "̇"],
|
|
252
|
+
["¨", "̈"],
|
|
253
|
+
["˚", "̊"],
|
|
254
|
+
["˝", "̋"],
|
|
255
|
+
["ˇ", "̌"],
|
|
256
|
+
["¸", "̧"],
|
|
257
|
+
["˛", "̨"]
|
|
258
|
+
]);
|
|
171
259
|
var spaceCache = /* @__PURE__ */ new WeakMap();
|
|
172
260
|
/** The page's shading patterns, read once per resource dictionary. */
|
|
173
261
|
var shadingCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -187,14 +275,15 @@ function spacesOf(file, resources) {
|
|
|
187
275
|
spaceCache.set(resources, made);
|
|
188
276
|
return made;
|
|
189
277
|
}
|
|
190
|
-
function collectRuns(file, resources, content, baseCtm, depth, visiting, out) {
|
|
278
|
+
function collectRuns(file, resources, content, baseCtm, depth, visiting, out, shown) {
|
|
191
279
|
const result = interpretContent(content, buildFonts(file, resources), baseCtm, shadingsOf(file, resources), void 0, spacesOf(file, resources), hiddenProperties(file, resources));
|
|
192
280
|
out.push(...result.texts.map((r) => withPatternColour(file, resources, r, visiting)));
|
|
281
|
+
if (shown) addShown(shown, result.shown);
|
|
193
282
|
if (depth >= MAX_FORM_DEPTH) return;
|
|
194
283
|
for (const glyph of result.glyphs) {
|
|
195
284
|
if (visiting.has(glyph.stream)) continue;
|
|
196
285
|
visiting.add(glyph.stream);
|
|
197
|
-
collectRuns(file, glyph.resources ?? resources, file.streamData(glyph.stream), glyph.ctm, depth + 1, visiting, out);
|
|
286
|
+
collectRuns(file, glyph.resources ?? resources, file.streamData(glyph.stream), glyph.ctm, depth + 1, visiting, out, shown);
|
|
198
287
|
visiting.delete(glyph.stream);
|
|
199
288
|
}
|
|
200
289
|
if (!resources) return;
|
|
@@ -208,7 +297,7 @@ function collectRuns(file, resources, content, baseCtm, depth, visiting, out) {
|
|
|
208
297
|
if (!(sub instanceof PdfName) || sub.value !== "Form") continue;
|
|
209
298
|
visiting.add(stream);
|
|
210
299
|
const formRes = file.get(stream.dict, "Resources");
|
|
211
|
-
collectRuns(file, formRes instanceof Map ? formRes : resources, file.streamData(stream), multiply(matrixOf(file, stream.dict), placement.ctm), depth + 1, visiting, out);
|
|
300
|
+
collectRuns(file, formRes instanceof Map ? formRes : resources, file.streamData(stream), multiply(matrixOf(file, stream.dict), placement.ctm), depth + 1, visiting, out, shown);
|
|
212
301
|
visiting.delete(stream);
|
|
213
302
|
}
|
|
214
303
|
}
|
|
@@ -10,6 +10,17 @@ export interface Type1Font {
|
|
|
10
10
|
readonly has: (name: string) => boolean;
|
|
11
11
|
/** The program's own `/Encoding`: code → glyph name, where it states one. */
|
|
12
12
|
readonly encoding?: ReadonlyMap<number, string>;
|
|
13
|
+
/**
|
|
14
|
+
* The embedding the program's licence allows — the OS/2 `fsType` Adobe
|
|
15
|
+
* writes into `FontInfo` as `/FSType`, where the program states one.
|
|
16
|
+
*/
|
|
17
|
+
readonly fsType?: number;
|
|
18
|
+
/**
|
|
19
|
+
* §6.4 — how far the pen moves after a glyph, in thousandths of an em: the
|
|
20
|
+
* width its `hsbw` (or `sbw`) states. `undefined` where the program holds no
|
|
21
|
+
* such glyph or its charstring states no width first.
|
|
22
|
+
*/
|
|
23
|
+
readonly advance: (name: string) => number | undefined;
|
|
13
24
|
}
|
|
14
25
|
/**
|
|
15
26
|
* Read an embedded Type 1 program.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { postScriptFsType } from "./cff-outline.js";
|
|
1
2
|
//#region src/pdf-reader/type1-outline.ts
|
|
2
3
|
/**
|
|
3
4
|
* Read an embedded Type 1 program.
|
|
@@ -31,7 +32,13 @@ function type1Font(program) {
|
|
|
31
32
|
return out;
|
|
32
33
|
},
|
|
33
34
|
has: (name) => parsed.charstrings.has(name),
|
|
34
|
-
|
|
35
|
+
advance: (name) => {
|
|
36
|
+
const charstring = parsed.charstrings.get(name);
|
|
37
|
+
const width = charstring ? statedWidth(charstring) : void 0;
|
|
38
|
+
return width === void 0 ? void 0 : width * parsed.scale * 1e3;
|
|
39
|
+
},
|
|
40
|
+
...parsed.encoding ? { encoding: parsed.encoding } : {},
|
|
41
|
+
...parsed.fsType !== void 0 ? { fsType: parsed.fsType } : {}
|
|
35
42
|
};
|
|
36
43
|
}
|
|
37
44
|
function parseType1(program) {
|
|
@@ -47,11 +54,13 @@ function parseType1(program) {
|
|
|
47
54
|
const charstrings = readCharstrings(plain, text, lenIV);
|
|
48
55
|
if (charstrings.size === 0) return void 0;
|
|
49
56
|
const encoding = readEncoding(clear);
|
|
57
|
+
const fsType = postScriptFsType(clear);
|
|
50
58
|
return {
|
|
51
59
|
charstrings,
|
|
52
60
|
subrs: readSubrs(plain, text, lenIV),
|
|
53
61
|
...encoding ? { encoding } : {},
|
|
54
|
-
scale: readMatrixScale(clear) ?? .001
|
|
62
|
+
scale: readMatrixScale(clear) ?? .001,
|
|
63
|
+
...fsType !== void 0 ? { fsType } : {}
|
|
55
64
|
};
|
|
56
65
|
}
|
|
57
66
|
/** The `eexec` key (§7), and the one every charstring is encrypted with (§6.2). */
|
|
@@ -211,6 +220,7 @@ function runGlyph(font, program, depth) {
|
|
|
211
220
|
let inFlex = false;
|
|
212
221
|
let x = 0;
|
|
213
222
|
let y = 0;
|
|
223
|
+
let sideBearing = 0;
|
|
214
224
|
let open = false;
|
|
215
225
|
let steps = 0;
|
|
216
226
|
const moveTo = () => {
|
|
@@ -255,6 +265,7 @@ function runGlyph(font, program, depth) {
|
|
|
255
265
|
case 13:
|
|
256
266
|
x = stack[0] ?? 0;
|
|
257
267
|
y = 0;
|
|
268
|
+
sideBearing = x;
|
|
258
269
|
stack.length = 0;
|
|
259
270
|
break;
|
|
260
271
|
case 9:
|
|
@@ -404,18 +415,25 @@ function runGlyph(font, program, depth) {
|
|
|
404
415
|
break;
|
|
405
416
|
}
|
|
406
417
|
if (op2 === 6) {
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
418
|
+
const [asb = 0, adx = 0, ady = 0, bchar = 0, achar = 0] = stack;
|
|
419
|
+
if (open) out.push({ op: "close" });
|
|
420
|
+
open = false;
|
|
421
|
+
const part = (charCode, dx, dy) => {
|
|
422
|
+
const name = STANDARD_ENCODING[charCode];
|
|
423
|
+
const charstring = name !== void 0 ? font.charstrings.get(name) : void 0;
|
|
424
|
+
if (!charstring || depth >= 2) return;
|
|
410
425
|
const drawn = runGlyph(font, charstring, depth + 1);
|
|
411
|
-
if (drawn) out.push(...drawn.map((seg) => unscale(seg, font.scale)));
|
|
412
|
-
}
|
|
426
|
+
if (drawn) out.push(...drawn.map((seg) => shift(unscale(seg, font.scale), dx, dy)));
|
|
427
|
+
};
|
|
428
|
+
part(bchar, 0, 0);
|
|
429
|
+
part(achar, adx + sideBearing - asb, ady);
|
|
413
430
|
stack.length = 0;
|
|
414
431
|
return false;
|
|
415
432
|
}
|
|
416
433
|
if (op2 === 7) {
|
|
417
434
|
x = stack[0] ?? 0;
|
|
418
435
|
y = stack[1] ?? 0;
|
|
436
|
+
sideBearing = x;
|
|
419
437
|
stack.length = 0;
|
|
420
438
|
break;
|
|
421
439
|
}
|
|
@@ -457,10 +475,47 @@ function scaleSeg(seg, k) {
|
|
|
457
475
|
y: seg.y * k
|
|
458
476
|
};
|
|
459
477
|
}
|
|
460
|
-
/**
|
|
478
|
+
/**
|
|
479
|
+
* §6.4 — the width a charstring states before it draws: the second operand of
|
|
480
|
+
* `hsbw` (`sbx wx hsbw`), or the third of `sbw` (`sbx sby wx wy sbw`).
|
|
481
|
+
*/
|
|
482
|
+
function statedWidth(code) {
|
|
483
|
+
const stack = [];
|
|
484
|
+
for (let i = 0; i < code.length && stack.length <= 4;) {
|
|
485
|
+
const b = code[i++];
|
|
486
|
+
if (b >= 32 && b <= 246) stack.push(b - 139);
|
|
487
|
+
else if (b >= 247 && b <= 250) stack.push((b - 247) * 256 + (code[i++] ?? 0) + 108);
|
|
488
|
+
else if (b >= 251 && b <= 254) stack.push(-(b - 251) * 256 - (code[i++] ?? 0) - 108);
|
|
489
|
+
else if (b === 255) {
|
|
490
|
+
stack.push(code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3] | 0);
|
|
491
|
+
i += 4;
|
|
492
|
+
} else if (b === 13) return stack[1];
|
|
493
|
+
else if (b === 12 && code[i] === 7) return stack[2];
|
|
494
|
+
else return void 0;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/** The parts of a `seac` come back already scaled; this puts them back in glyph space. */
|
|
461
498
|
function unscale(seg, k) {
|
|
462
499
|
return scaleSeg(seg, k === 0 ? 1 : 1 / k);
|
|
463
500
|
}
|
|
501
|
+
/** A segment moved by `(dx, dy)`: where a `seac` puts its accent. */
|
|
502
|
+
function shift(seg, dx, dy) {
|
|
503
|
+
if (seg.op === "close" || dx === 0 && dy === 0) return seg;
|
|
504
|
+
if (seg.op === "cubic") return {
|
|
505
|
+
op: "cubic",
|
|
506
|
+
x1: seg.x1 + dx,
|
|
507
|
+
y1: seg.y1 + dy,
|
|
508
|
+
x2: seg.x2 + dx,
|
|
509
|
+
y2: seg.y2 + dy,
|
|
510
|
+
x: seg.x + dx,
|
|
511
|
+
y: seg.y + dy
|
|
512
|
+
};
|
|
513
|
+
return {
|
|
514
|
+
op: seg.op,
|
|
515
|
+
x: seg.x + dx,
|
|
516
|
+
y: seg.y + dy
|
|
517
|
+
};
|
|
518
|
+
}
|
|
464
519
|
/** §C.1 — the names `seac` selects its two parts by, at the codes it states. */
|
|
465
520
|
var STANDARD_ENCODING = {
|
|
466
521
|
32: "space",
|
|
@@ -266,15 +266,23 @@ function overlaps(a, b) {
|
|
|
266
266
|
* path intersection, and where the two merely overlap the answer is the smaller
|
|
267
267
|
* of them — bounded, never larger than the truth.
|
|
268
268
|
*
|
|
269
|
+
* A path is measured by where its curves reach, not where their handles stand,
|
|
270
|
+
* and one that reaches past its clip by no more than a sliver is inside it. An
|
|
271
|
+
* appearance's `/BBox` is drawn round the path it holds: comments.pdf signs its
|
|
272
|
+
* sixth page with a stamp of sixty-seven strokes that its box meets exactly,
|
|
273
|
+
* and a handle standing 0.03pt past the box made the signature a path round
|
|
274
|
+
* its clip — it came back a black bar the width of the text.
|
|
275
|
+
*
|
|
269
276
|
* @param v The painted path as the interpreter saw it.
|
|
270
277
|
* @returns The path to draw, or `undefined` when the clip leaves nothing.
|
|
271
278
|
*/
|
|
272
279
|
function clipped(v) {
|
|
273
280
|
const clip = v.clip;
|
|
274
281
|
if (!clip) return v;
|
|
275
|
-
const b =
|
|
282
|
+
const b = reachOf(v.segs);
|
|
276
283
|
if (!b) return v;
|
|
277
284
|
if (clip.minX >= b.maxX || clip.maxX <= b.minX || clip.minY >= b.maxY || clip.maxY <= b.minY) return;
|
|
285
|
+
if (b.minX >= clip.minX - SLIVER_PT && b.maxX <= clip.maxX + SLIVER_PT && b.minY >= clip.minY - SLIVER_PT && b.maxY <= clip.maxY + SLIVER_PT) return v;
|
|
278
286
|
const pathArea = Math.max(0, b.maxX - b.minX) * Math.max(0, b.maxY - b.minY);
|
|
279
287
|
if (Math.max(0, clip.maxX - clip.minX) * Math.max(0, clip.maxY - clip.minY) >= pathArea) return v;
|
|
280
288
|
return {
|
|
@@ -283,6 +291,68 @@ function clipped(v) {
|
|
|
283
291
|
segs: clip.segs
|
|
284
292
|
};
|
|
285
293
|
}
|
|
294
|
+
/** How far past its clip a path may reach and still be inside it: a box's rounding. */
|
|
295
|
+
var SLIVER_PT = .5;
|
|
296
|
+
/**
|
|
297
|
+
* The box a path's curves reach: its points, and the turns of its curves —
|
|
298
|
+
* a Bézier stays inside its handles, and rarely reaches them.
|
|
299
|
+
*
|
|
300
|
+
* @param segs The path.
|
|
301
|
+
*/
|
|
302
|
+
function reachOf(segs) {
|
|
303
|
+
let minX = Infinity;
|
|
304
|
+
let minY = Infinity;
|
|
305
|
+
let maxX = -Infinity;
|
|
306
|
+
let maxY = -Infinity;
|
|
307
|
+
const add = (x, y) => {
|
|
308
|
+
minX = Math.min(minX, x);
|
|
309
|
+
minY = Math.min(minY, y);
|
|
310
|
+
maxX = Math.max(maxX, x);
|
|
311
|
+
maxY = Math.max(maxY, y);
|
|
312
|
+
};
|
|
313
|
+
let [x, y] = [0, 0];
|
|
314
|
+
let [startX, startY] = [0, 0];
|
|
315
|
+
for (const s of segs) {
|
|
316
|
+
if (s.op === "close") {
|
|
317
|
+
[x, y] = [startX, startY];
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (s.op === "cubic") {
|
|
321
|
+
const [x0, y0] = [x, y];
|
|
322
|
+
for (const t of [...turnsOf(x0, s.x1, s.x2, s.x), ...turnsOf(y0, s.y1, s.y2, s.y)]) add(bezierAt(x0, s.x1, s.x2, s.x, t), bezierAt(y0, s.y1, s.y2, s.y, t));
|
|
323
|
+
}
|
|
324
|
+
add(s.x, s.y);
|
|
325
|
+
[x, y] = [s.x, s.y];
|
|
326
|
+
if (s.op === "move") [startX, startY] = [s.x, s.y];
|
|
327
|
+
}
|
|
328
|
+
return Number.isFinite(minX) ? {
|
|
329
|
+
minX,
|
|
330
|
+
minY,
|
|
331
|
+
maxX,
|
|
332
|
+
maxY
|
|
333
|
+
} : void 0;
|
|
334
|
+
}
|
|
335
|
+
/** Where, between its ends, one coordinate of a cubic Bézier turns back. */
|
|
336
|
+
function turnsOf(p0, p1, p2, p3) {
|
|
337
|
+
const a = p3 - 3 * p2 + 3 * p1 - p0;
|
|
338
|
+
const b = 2 * (p2 - 2 * p1 + p0);
|
|
339
|
+
const c = p1 - p0;
|
|
340
|
+
let roots = [];
|
|
341
|
+
if (Math.abs(a) < FLAT) {
|
|
342
|
+
if (Math.abs(b) >= FLAT) roots = [-c / b];
|
|
343
|
+
} else {
|
|
344
|
+
const d = b * b - 4 * a * c;
|
|
345
|
+
if (d >= 0) roots = [(-b + Math.sqrt(d)) / (2 * a), (-b - Math.sqrt(d)) / (2 * a)];
|
|
346
|
+
}
|
|
347
|
+
return roots.filter((t) => t > 0 && t < 1);
|
|
348
|
+
}
|
|
349
|
+
/** A coefficient this small is none: the curve's derivative is of lower degree. */
|
|
350
|
+
var FLAT = 1e-12;
|
|
351
|
+
/** One coordinate of a cubic Bézier at `t`. */
|
|
352
|
+
function bezierAt(p0, p1, p2, p3, t) {
|
|
353
|
+
const u = 1 - t;
|
|
354
|
+
return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;
|
|
355
|
+
}
|
|
286
356
|
function bbox(segs) {
|
|
287
357
|
let minX = Infinity;
|
|
288
358
|
let minY = Infinity;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FontResource, LaidOutDocument, TextLineItem } from '../layout/page-doc.js';
|
|
2
|
+
import { ImageCrop } from '../core/document-model/index.js';
|
|
3
|
+
import { VectorShape } from '../core/vector.js';
|
|
4
|
+
/** The glyph outlines of a page's faces, each defined once and used by id. */
|
|
5
|
+
export declare class SvgGlyphs {
|
|
6
|
+
private readonly sources;
|
|
7
|
+
private readonly ids;
|
|
8
|
+
private readonly defs;
|
|
9
|
+
private readonly faceIds;
|
|
10
|
+
/**
|
|
11
|
+
* The id of the path that draws glyph `gid` of `font`, defining it on first
|
|
12
|
+
* use: `null` for a glyph with no ink (a space), `undefined` where the face
|
|
13
|
+
* carries no outlines this reads — the caller then sets the token as text.
|
|
14
|
+
*
|
|
15
|
+
* @param font The face.
|
|
16
|
+
* @param gid The glyph.
|
|
17
|
+
* @returns The `<path>` id, `null`, or `undefined`.
|
|
18
|
+
*/
|
|
19
|
+
use(font: FontResource, gid: number): string | null | undefined;
|
|
20
|
+
/** The `<defs>` holding every glyph used, or nothing when none was. */
|
|
21
|
+
defsXml(): string;
|
|
22
|
+
private sourceOf;
|
|
23
|
+
}
|
|
24
|
+
/** What drawing a line needs from the writer around it. */
|
|
25
|
+
export interface SvgLineCtx {
|
|
26
|
+
readonly glyphs: SvgGlyphs;
|
|
27
|
+
/** The document-wide counter generated ids are numbered from. */
|
|
28
|
+
readonly ids: {
|
|
29
|
+
n: number;
|
|
30
|
+
};
|
|
31
|
+
readonly laid: LaidOutDocument;
|
|
32
|
+
/** A picture in a box, as the writer draws one. */
|
|
33
|
+
readonly image: (out: Array<string>, placed: PlacedImage) => void;
|
|
34
|
+
/** A vector shape, as the writer draws one. */
|
|
35
|
+
readonly shape: (out: Array<string>, shape: VectorShape) => void;
|
|
36
|
+
}
|
|
37
|
+
/** A picture placed in a box on the page — its top-left corner and size, in points. */
|
|
38
|
+
export interface PlacedImage {
|
|
39
|
+
readonly resourceName: string;
|
|
40
|
+
readonly x: number;
|
|
41
|
+
readonly y: number;
|
|
42
|
+
readonly width: number;
|
|
43
|
+
readonly height: number;
|
|
44
|
+
readonly crop?: ImageCrop;
|
|
45
|
+
readonly rotationDeg?: number;
|
|
46
|
+
readonly flipH?: boolean;
|
|
47
|
+
readonly flipV?: boolean;
|
|
48
|
+
readonly alpha?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Draw one laid-out line: what stands behind its glyphs, then the glyphs, a
|
|
52
|
+
* picture or formula wherever one stands in it.
|
|
53
|
+
*
|
|
54
|
+
* @param out The SVG fragment sink.
|
|
55
|
+
* @param item The line, as the layout placed it.
|
|
56
|
+
* @param ctx The glyph definitions and the writer's own drawing.
|
|
57
|
+
*/
|
|
58
|
+
export declare function emitSvgLine(out: Array<string>, item: TextLineItem, ctx: SvgLineCtx): void;
|