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
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
|
|
1
|
+
import { knowsFamily, resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
|
|
2
2
|
import { parseTtf } from "../core/font/ttf-parser.js";
|
|
3
3
|
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
4
4
|
import { parseToUnicodeCMap } from "./cmap.js";
|
|
5
5
|
import { decodePredefined, predefinedCMap, splitPredefined } from "./predefined-cmap.js";
|
|
6
6
|
import { isZapfDingbats, zapfDingbatsChar } from "./dingbats.js";
|
|
7
7
|
import { textForGlyphName } from "./glyph-names.js";
|
|
8
|
-
import {
|
|
8
|
+
import { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable, winAnsiLatinName } from "./encodings.js";
|
|
9
|
+
import { cffCidToGid, cffFsType, cffNameToGid, cffOutlineSource, cffSpaceAdvance, openTypeCff } from "./cff-outline.js";
|
|
9
10
|
import { type1Font } from "./type1-outline.js";
|
|
10
|
-
import {
|
|
11
|
-
import { outlineSource, postGlyphNames } from "./glyf-outline.js";
|
|
11
|
+
import { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance } from "./glyf-outline.js";
|
|
12
12
|
import { standardFace, standardWidth } from "./standard-widths.js";
|
|
13
|
-
import { embeddedFontName, hasLiftableProgram } from "./embedded-fonts.js";
|
|
13
|
+
import { eachPageFont, embeddedFontName, hasLiftableProgram, programStyle } from "./embedded-fonts.js";
|
|
14
14
|
//#region src/pdf-reader/font.ts
|
|
15
15
|
/** §9.10.2 — what a composite code nothing can answer for comes to. */
|
|
16
16
|
var UNANSWERABLE = "�";
|
|
@@ -40,6 +40,8 @@ function buildContentFont(file, fontDict) {
|
|
|
40
40
|
if (isType0 && !encodingNamed) codeBytes = parsed.codeBytes;
|
|
41
41
|
}
|
|
42
42
|
const fromProgram = isType0 && toUnicode.size === 0 ? embeddedCmap(file, fontDict) : void 0;
|
|
43
|
+
const fromCids = isType0 && toUnicode.size === 0 && fromProgram === void 0 ? unicodeCids(file, fontDict) : void 0;
|
|
44
|
+
const fromCore = isType0 && toUnicode.size === 0 && fromProgram === void 0 && fromCids === void 0 ? coreGlyphOrder(file, fontDict) : void 0;
|
|
43
45
|
let programFallback = null;
|
|
44
46
|
const fromProgramFor = (code) => {
|
|
45
47
|
if (!isType0 || toUnicode.size === 0) return void 0;
|
|
@@ -47,16 +49,28 @@ function buildContentFont(file, fontDict) {
|
|
|
47
49
|
return programFallback?.get(code);
|
|
48
50
|
};
|
|
49
51
|
const glyphs = isType0 ? void 0 : simpleGlyphs(file, fontDict);
|
|
52
|
+
let byIndex = null;
|
|
53
|
+
const numbering = isType0 ? void 0 : numberingOf(differences(file, fontDict).values());
|
|
54
|
+
const fromIndex = (name) => {
|
|
55
|
+
const gid = numberedGlyph(name, numbering);
|
|
56
|
+
if (gid === void 0) return void 0;
|
|
57
|
+
byIndex ??= programCharacters(file, fontDict);
|
|
58
|
+
return byIndex?.get(gid);
|
|
59
|
+
};
|
|
50
60
|
const fromNames = !isType0 ? namedGlyphs(file, fontDict, toUnicode.size > 0, {
|
|
51
61
|
draws: (name) => glyphs?.byName(name) !== void 0,
|
|
52
|
-
blank: (name) => glyphs?.blank(name) === true
|
|
62
|
+
blank: (name) => glyphs?.blank(name) === true,
|
|
63
|
+
character: fromIndex
|
|
53
64
|
}) : void 0;
|
|
54
65
|
const glyphNames = isType0 ? /* @__PURE__ */ new Map() : differences(file, fontDict);
|
|
55
|
-
const unicode = fromProgram ?? (toUnicode.size > 0 ? toUnicode : fromNames ?? toUnicode);
|
|
66
|
+
const unicode = fromProgram ?? fromCids ?? fromCore ?? (toUnicode.size > 0 ? toUnicode : fromNames ?? toUnicode);
|
|
56
67
|
const bytesPerCode = codeBytes;
|
|
57
68
|
const indices = !isType0 && glyphs?.indexed === true && fromNames === void 0 && toUnicode.size === 0 && glyphNames.size === 0;
|
|
58
69
|
const dingbats = !isType0 && isZapfDingbats(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
|
|
59
|
-
const
|
|
70
|
+
const stated = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
|
|
71
|
+
const statesEncoding = stated instanceof Map || stated instanceof PdfName && baseEncodingTable(stated.value) !== void 0;
|
|
72
|
+
const numbered = !isType0 && toUnicode.size === 0 && glyphNames.size === 0 && stated === PDF_NULL && glyphs?.numbersGlyphs?.() === true;
|
|
73
|
+
const baseNames = isType0 ? void 0 : statesEncoding ? baseEncoding(file, fontDict) : glyphs?.builtIn ?? baseEncoding(file, fontDict);
|
|
60
74
|
const fromBase = /* @__PURE__ */ new Map();
|
|
61
75
|
for (const [code, glyph] of baseNames ?? []) {
|
|
62
76
|
const text = textForGlyphName(glyph);
|
|
@@ -66,18 +80,24 @@ function buildContentFont(file, fontDict) {
|
|
|
66
80
|
const style = faceStyle(file, fontDict, isType0);
|
|
67
81
|
const name = runFontName(file, fontDict, isType0);
|
|
68
82
|
const type3 = asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) === "Type3" ? type3Face(file, fontDict) : void 0;
|
|
69
|
-
const decodeOne = (code) => (indices ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
|
|
83
|
+
const decodeOne = (code) => (indices || numbered ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (glyphNames.get(code) === NOTDEF ? "" : void 0) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
|
|
84
|
+
const composite = isType0 ? compositeGlyphs(file, fontDict) : void 0;
|
|
70
85
|
const simple = simpleWidths(file, fontDict, decodeOne);
|
|
71
|
-
const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple(code) * type3.matrix[0] * 1e3 : simple;
|
|
86
|
+
const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple.width(code) * type3.matrix[0] * 1e3 : simple.width;
|
|
87
|
+
const spaceWidth = isType0 || type3 || namesOf.get(32) !== "space" && unicode.get(32) !== " " ? void 0 : simple.stated(32);
|
|
88
|
+
const fixedPitch = !isType0 && !type3 && fixedPitchOf(file, fontDict);
|
|
72
89
|
return {
|
|
73
90
|
bytesPerCode,
|
|
74
91
|
...named ? { splitCodes: (b) => splitPredefined(named, b) } : {},
|
|
75
|
-
...isType0 ? outlineOf(
|
|
92
|
+
...isType0 ? outlineOf(composite, decodeOne) : simpleOutlineOf(decodeOne, namesOf, glyphs, numbered),
|
|
93
|
+
...name !== void 0 && !type3 ? programOf(file, fontDict, isType0, isType0 ? compositeGlyph(composite, fontDict, file) : simpleGlyph(glyphs, namesOf, baseNames === baseEncodingTable("WinAnsiEncoding")), isType0 ? composite : glyphs) : {},
|
|
76
94
|
...named?.vertical ? { verticalAdvance: cidVerticalAdvance(file, fontDict) } : {},
|
|
77
95
|
...type3 ? { type3 } : {},
|
|
78
96
|
...name !== void 0 ? { name } : {},
|
|
79
|
-
decode: (codes) => codes.map((c) => readable(decodeOne(c))).join(""),
|
|
97
|
+
decode: (codes) => codes.map((c) => lettersOf(readable(decodeOne(c)))).join(""),
|
|
80
98
|
width,
|
|
99
|
+
...spaceWidth !== void 0 ? { spaceWidth } : {},
|
|
100
|
+
...fixedPitch ? { fixedPitch: true } : {},
|
|
81
101
|
...style
|
|
82
102
|
};
|
|
83
103
|
}
|
|
@@ -114,6 +134,32 @@ function readable(text) {
|
|
|
114
134
|
}
|
|
115
135
|
return out;
|
|
116
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Unicode's presentation forms of the Latin ligatures, U+FB00–U+FB06, as the
|
|
139
|
+
* letters they join.
|
|
140
|
+
*
|
|
141
|
+
* A producer maps a ligature glyph to its compatibility character, and carried
|
|
142
|
+
* as that the word neither searches nor edits as the letters it is — and a
|
|
143
|
+
* face with no such form sets it in another's, wider: copy_paste_ligatures.pdf
|
|
144
|
+
* is one line in Times, and its "fffiflffifflſtst" pushed the line off its page. The long
|
|
145
|
+
* s of `ſt` is kept: it is a letter of its own.
|
|
146
|
+
*/
|
|
147
|
+
function lettersOf(text) {
|
|
148
|
+
return LIGATURE.test(text) ? text.replace(LIGATURES, (c) => LATIN_LIGATURES.get(c) ?? c) : text;
|
|
149
|
+
}
|
|
150
|
+
var LIGATURE = /[\uFB00-\uFB06]/u;
|
|
151
|
+
var LIGATURES = /[\uFB00-\uFB06]/gu;
|
|
152
|
+
var LATIN_LIGATURES = new Map([
|
|
153
|
+
["ff", "ff"],
|
|
154
|
+
["fi", "fi"],
|
|
155
|
+
["fl", "fl"],
|
|
156
|
+
["ffi", "ffi"],
|
|
157
|
+
["ffl", "ffl"],
|
|
158
|
+
["ſt", "ſt"],
|
|
159
|
+
["st", "st"]
|
|
160
|
+
]);
|
|
161
|
+
/** The glyph name that selects no glyph (§9.6.6.1). */
|
|
162
|
+
var NOTDEF = ".notdef";
|
|
117
163
|
/** The last Unicode code point in the BMP, and the surrogate block inside it. */
|
|
118
164
|
var BMP_END = 65535;
|
|
119
165
|
var SURROGATE_FIRST = 55296;
|
|
@@ -133,10 +179,130 @@ var SURROGATE_LAST = 57343;
|
|
|
133
179
|
*/
|
|
134
180
|
function embeddedCmap(file, fontDict) {
|
|
135
181
|
const cidFont = descendantFont(file, fontDict);
|
|
182
|
+
const byGlyph = programCharacters(file, cidFont);
|
|
183
|
+
if (byGlyph === void 0) return void 0;
|
|
184
|
+
const cidToGid = readCidToGid(file, cidFont);
|
|
185
|
+
if (!cidToGid) return byGlyph;
|
|
186
|
+
const out = /* @__PURE__ */ new Map();
|
|
187
|
+
cidToGid.forEach((gid, cid) => {
|
|
188
|
+
const text = byGlyph.get(gid);
|
|
189
|
+
if (text !== void 0) out.set(cid, text);
|
|
190
|
+
});
|
|
191
|
+
return out.size > 0 ? out : void 0;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* §9.7.4 — the characters of a composite font that embeds no program, names a
|
|
195
|
+
* core face and states no `/ToUnicode`: its codes are glyph indices into the
|
|
196
|
+
* face the reader's machine carries under that name, and the core faces put
|
|
197
|
+
* their first 258 glyphs in the standard Macintosh order (the `post` table's
|
|
198
|
+
* format 1 names).
|
|
199
|
+
*
|
|
200
|
+
* issue11242_reduced.pdf shows "VAT Code" in a Verdana it does not embed.
|
|
201
|
+
* Nothing in the file says what its codes are but the face's own order, and
|
|
202
|
+
* read as characters they are "9$7&RGH"; read as nothing, the page came back
|
|
203
|
+
* blank. Only faces known to keep that order are read this way — a face's
|
|
204
|
+
* order is its own, and Calibri's is not this one.
|
|
205
|
+
*
|
|
206
|
+
* @param file The owning file.
|
|
207
|
+
* @param fontDict The `/Type0` font dictionary.
|
|
208
|
+
* @returns Code → character, or `undefined` where the font is not such a one.
|
|
209
|
+
*/
|
|
210
|
+
function coreGlyphOrder(file, fontDict) {
|
|
211
|
+
const encoding = asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL));
|
|
212
|
+
if (encoding !== "Identity-H" && encoding !== "Identity-V") return void 0;
|
|
213
|
+
const cidFont = descendantFont(file, fontDict);
|
|
214
|
+
if (asName(file.resolve(cidFont.get("Subtype") ?? PDF_NULL)) !== "CIDFontType2") return void 0;
|
|
215
|
+
const map = file.resolve(cidFont.get("CIDToGIDMap") ?? PDF_NULL);
|
|
216
|
+
if (map !== PDF_NULL && !(map instanceof PdfName && map.value === "Identity")) return void 0;
|
|
217
|
+
const descriptor = file.resolve(cidFont.get("FontDescriptor") ?? PDF_NULL);
|
|
218
|
+
if (descriptor instanceof Map && [
|
|
219
|
+
"FontFile",
|
|
220
|
+
"FontFile2",
|
|
221
|
+
"FontFile3"
|
|
222
|
+
].some((k) => descriptor.has(k))) return void 0;
|
|
223
|
+
const family = familyOfFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
|
|
224
|
+
if (!CORE_FACES.has(family.toLowerCase().replace(/[^a-z]/gu, ""))) return void 0;
|
|
225
|
+
const out = /* @__PURE__ */ new Map();
|
|
226
|
+
for (let gid = 0; gid < MAC_GLYPHS; gid++) {
|
|
227
|
+
const name = macGlyphName(gid);
|
|
228
|
+
const text = name === void 0 ? void 0 : textForGlyphName(name);
|
|
229
|
+
if (text !== void 0 && text !== "") out.set(gid, text);
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
/** The core faces whose first glyphs stand in the standard Macintosh order. */
|
|
234
|
+
var CORE_FACES = new Set([
|
|
235
|
+
"arial",
|
|
236
|
+
"timesnewroman",
|
|
237
|
+
"couriernew",
|
|
238
|
+
"verdana",
|
|
239
|
+
"tahoma",
|
|
240
|
+
"georgia",
|
|
241
|
+
"trebuchetms"
|
|
242
|
+
]);
|
|
243
|
+
/** How many glyphs the standard Macintosh order names. */
|
|
244
|
+
var MAC_GLYPHS = 258;
|
|
245
|
+
/**
|
|
246
|
+
* §9.7.4.2 — the CIDs of a composite font that ARE characters.
|
|
247
|
+
*
|
|
248
|
+
* A `/CIDToGIDMap` stream says the CID is not the glyph index, and TCPDF —
|
|
249
|
+
* with tFPDF and mPDF after it — makes it the character's own Unicode value,
|
|
250
|
+
* mapping every character of the face to its glyph whether the subset kept
|
|
251
|
+
* the glyph or not. Without a `/ToUnicode`, and with a subset that carries no
|
|
252
|
+
* `cmap`, that was the only statement of what the glyphs are and it went
|
|
253
|
+
* unread: bug1650302_reduced.pdf's "Výbava na přání" came back as drawings,
|
|
254
|
+
* the `ř` missing from them. Read as characters, the words come back.
|
|
255
|
+
*
|
|
256
|
+
* Only where the map itself says so. A producer that keeps the ORIGINAL
|
|
257
|
+
* glyph index as the CID routes it through a stream too, and read as Unicode
|
|
258
|
+
* complex_ttf_font.pdf's Arabic would come back as `$&')`. What tells the two
|
|
259
|
+
* apart is the space: a map of characters sends U+0020 to a glyph that draws
|
|
260
|
+
* nothing and no control character anywhere, where a map of indices sends 3 —
|
|
261
|
+
* the space in the fonts they come from — and 32 to a glyph with ink on it.
|
|
262
|
+
*/
|
|
263
|
+
function unicodeCids(file, fontDict) {
|
|
264
|
+
const encoding = asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL));
|
|
265
|
+
if (encoding !== "Identity-H" && encoding !== "Identity-V") return void 0;
|
|
266
|
+
const cidFont = descendantFont(file, fontDict);
|
|
267
|
+
const cidToGid = readCidToGid(file, cidFont);
|
|
268
|
+
if (!cidToGid || cidToGid.slice(0, SPACE).some((gid) => gid !== 0)) return void 0;
|
|
269
|
+
const space = cidToGid[SPACE];
|
|
270
|
+
if (space === void 0 || space === 0) return void 0;
|
|
136
271
|
const descriptor = file.resolve(cidFont.get("FontDescriptor") ?? PDF_NULL);
|
|
137
272
|
if (!(descriptor instanceof Map)) return void 0;
|
|
138
273
|
const program = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
|
|
139
274
|
if (!(program instanceof PdfStream)) return void 0;
|
|
275
|
+
let blank;
|
|
276
|
+
try {
|
|
277
|
+
const glyf = outlineSource(file.streamData(program));
|
|
278
|
+
blank = glyf !== void 0 && space < glyf.count && glyf.path(space) === void 0;
|
|
279
|
+
} catch {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!blank) return void 0;
|
|
283
|
+
const out = /* @__PURE__ */ new Map();
|
|
284
|
+
cidToGid.forEach((gid, cid) => {
|
|
285
|
+
if (gid !== 0 && !(cid >= SURROGATE_FIRST && cid <= SURROGATE_LAST)) out.set(cid, String.fromCharCode(cid));
|
|
286
|
+
});
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
/** U+0020, the one character whose glyph every text face leaves blank. */
|
|
290
|
+
var SPACE = 32;
|
|
291
|
+
/**
|
|
292
|
+
* The character each glyph of a font's embedded TrueType program stands for:
|
|
293
|
+
* its `cmap` read backwards.
|
|
294
|
+
*
|
|
295
|
+
* @param file The document.
|
|
296
|
+
* @param owner The dictionary that holds the descriptor — the font itself, or
|
|
297
|
+
* a composite font's descendant.
|
|
298
|
+
* @returns Glyph index → character, or `undefined` where there is no program
|
|
299
|
+
* or it maps nothing.
|
|
300
|
+
*/
|
|
301
|
+
function programCharacters(file, owner) {
|
|
302
|
+
const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
|
|
303
|
+
if (!(descriptor instanceof Map)) return void 0;
|
|
304
|
+
const program = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
|
|
305
|
+
if (!(program instanceof PdfStream)) return void 0;
|
|
140
306
|
let glyphOf;
|
|
141
307
|
try {
|
|
142
308
|
glyphOf = parseTtf(file.streamData(program)).glyphForCodepoint;
|
|
@@ -154,57 +320,80 @@ function embeddedCmap(file, fontDict) {
|
|
|
154
320
|
}
|
|
155
321
|
if (gid > 0 && !byGlyph.has(gid)) byGlyph.set(gid, String.fromCodePoint(cp));
|
|
156
322
|
}
|
|
157
|
-
|
|
158
|
-
const cidToGid = readCidToGid(file, cidFont);
|
|
159
|
-
if (!cidToGid) return byGlyph;
|
|
160
|
-
const out = /* @__PURE__ */ new Map();
|
|
161
|
-
cidToGid.forEach((gid, cid) => {
|
|
162
|
-
const text = byGlyph.get(gid);
|
|
163
|
-
if (text !== void 0) out.set(cid, text);
|
|
164
|
-
});
|
|
165
|
-
return out.size > 0 ? out : void 0;
|
|
323
|
+
return byGlyph.size > 0 ? byGlyph : void 0;
|
|
166
324
|
}
|
|
167
325
|
/**
|
|
168
|
-
* §9.
|
|
326
|
+
* §9.7.4 — a composite font's embedded program, read by glyph: the code is a
|
|
327
|
+
* CID, `/CIDToGIDMap` (or, for a CID-keyed CFF, its charset) turns that into a
|
|
328
|
+
* glyph index, and the program holds the contours.
|
|
169
329
|
*
|
|
170
|
-
* The
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* was dropped.
|
|
175
|
-
*
|
|
176
|
-
* Deliberately NOT a fallback for text: a code the font CAN answer for is set
|
|
177
|
-
* as type, and only the unanswerable ones are traced.
|
|
178
|
-
*
|
|
179
|
-
* @param file The owning file.
|
|
180
|
-
* @param fontDict The Type 0 font dictionary.
|
|
181
|
-
* @param decodeOne What one code comes to, to tell the two cases apart.
|
|
182
|
-
* @returns The `outline` field of a {@link ContentFont}, or nothing where the
|
|
183
|
-
* program carries no outlines this reads.
|
|
330
|
+
* @param file The owning file.
|
|
331
|
+
* @param fontDict The Type 0 font dictionary.
|
|
332
|
+
* @returns The program's outlines and how a CID reaches them, or `undefined`
|
|
333
|
+
* where the font embeds no program this reads.
|
|
184
334
|
*/
|
|
185
|
-
function
|
|
335
|
+
function compositeGlyphs(file, fontDict) {
|
|
186
336
|
const cidFont = descendantFont(file, fontDict);
|
|
187
337
|
const descriptor = file.resolve(cidFont.get("FontDescriptor") ?? PDF_NULL);
|
|
188
|
-
if (!(descriptor instanceof Map)) return
|
|
338
|
+
if (!(descriptor instanceof Map)) return void 0;
|
|
189
339
|
const truetype = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
|
|
190
340
|
const compact = file.resolve(descriptor.get("FontFile3") ?? PDF_NULL);
|
|
191
341
|
const program = truetype instanceof PdfStream ? truetype : compact;
|
|
192
|
-
if (!(program instanceof PdfStream)) return
|
|
342
|
+
if (!(program instanceof PdfStream)) return void 0;
|
|
193
343
|
let source;
|
|
194
344
|
let charsetCids;
|
|
345
|
+
let fsType;
|
|
346
|
+
let spaceAdvance;
|
|
195
347
|
try {
|
|
196
348
|
const bytes = file.streamData(program);
|
|
197
349
|
source = outlineSource(bytes);
|
|
350
|
+
fsType = sfntFsType(bytes);
|
|
351
|
+
spaceAdvance = sfntSpaceAdvance(bytes);
|
|
198
352
|
if (!source) {
|
|
199
353
|
const cff = openTypeCff(bytes) ?? bytes;
|
|
200
354
|
source = cffOutlineSource(cff);
|
|
201
355
|
charsetCids = source ? cffCidToGid(cff) : void 0;
|
|
356
|
+
fsType ??= cffFsType(cff);
|
|
202
357
|
}
|
|
203
358
|
} catch {
|
|
204
|
-
return
|
|
359
|
+
return;
|
|
205
360
|
}
|
|
206
|
-
if (!source) return
|
|
361
|
+
if (!source) return void 0;
|
|
207
362
|
const cidToGid = readCidToGid(file, cidFont);
|
|
363
|
+
return {
|
|
364
|
+
source,
|
|
365
|
+
glyphOf: (cid) => {
|
|
366
|
+
const mapped = cidToGid ? cidToGid[cid] : cid;
|
|
367
|
+
if (mapped === void 0) return void 0;
|
|
368
|
+
return charsetCids ? charsetCids.get(mapped) : mapped;
|
|
369
|
+
},
|
|
370
|
+
looseGlyphOf: (cid) => {
|
|
371
|
+
const mapped = cidToGid ? cidToGid[cid] ?? 0 : cid;
|
|
372
|
+
return charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
|
|
373
|
+
},
|
|
374
|
+
...fsType !== void 0 ? { fsType } : {},
|
|
375
|
+
...spaceAdvance !== void 0 ? { spaceAdvance } : {}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* §9.6.6 — the outline a code draws, for a code that stands for no character.
|
|
380
|
+
*
|
|
381
|
+
* The glyph is there even when the character is not: `/Encoding /Identity-H`
|
|
382
|
+
* makes the code a CID, `/CIDToGIDMap` turns that into a glyph index, and the
|
|
383
|
+
* embedded program holds the contours. complex_ttf_font.pdf is eight lines of
|
|
384
|
+
* Arabic in a subset with no `cmap` and no `/ToUnicode`, and every one of them
|
|
385
|
+
* was dropped.
|
|
386
|
+
*
|
|
387
|
+
* Deliberately NOT a fallback for text: a code the font CAN answer for is set
|
|
388
|
+
* as type, and only the unanswerable ones are traced.
|
|
389
|
+
*
|
|
390
|
+
* @param glyphs The font's program, read by glyph (see `compositeGlyphs`).
|
|
391
|
+
* @param decodeOne What one code comes to, to tell the two cases apart.
|
|
392
|
+
* @returns The `outline` field of a {@link ContentFont}, or nothing where the
|
|
393
|
+
* program carries no outlines this reads.
|
|
394
|
+
*/
|
|
395
|
+
function outlineOf(glyphs, decodeOne) {
|
|
396
|
+
if (!glyphs) return {};
|
|
208
397
|
return { outline: {
|
|
209
398
|
matrix: [
|
|
210
399
|
1,
|
|
@@ -216,13 +405,28 @@ function outlineOf(file, fontDict, decodeOne) {
|
|
|
216
405
|
],
|
|
217
406
|
path: (code) => {
|
|
218
407
|
if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
|
|
219
|
-
|
|
220
|
-
const gid = charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
|
|
221
|
-
return source.path(gid);
|
|
408
|
+
return glyphs.source.path(glyphs.looseGlyphOf(code));
|
|
222
409
|
}
|
|
223
410
|
} };
|
|
224
411
|
}
|
|
225
412
|
/**
|
|
413
|
+
* §9.7.5.2 — the glyph ANY code of a composite font draws, for a writer that
|
|
414
|
+
* embeds the face.
|
|
415
|
+
*
|
|
416
|
+
* Only under `Identity-H` or `Identity-V`, where the code is the CID: any other
|
|
417
|
+
* CMap turns codes into CIDs by a table of its own, and a glyph looked up by
|
|
418
|
+
* the code there would be some other character's.
|
|
419
|
+
*/
|
|
420
|
+
function compositeGlyph(glyphs, fontDict, file) {
|
|
421
|
+
const encoding = asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL));
|
|
422
|
+
if (!glyphs || encoding !== "Identity-H" && encoding !== "Identity-V") return void 0;
|
|
423
|
+
return (code) => {
|
|
424
|
+
const gid = glyphs.glyphOf(code);
|
|
425
|
+
if (gid === void 0 || gid <= 0 || gid >= glyphs.source.count) return void 0;
|
|
426
|
+
return glyphs.source.path(gid) ?? [];
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
226
430
|
* §9.6.6 — the outline a SIMPLE font's code draws, for a code that stands for
|
|
227
431
|
* no character.
|
|
228
432
|
*
|
|
@@ -237,9 +441,12 @@ function outlineOf(file, fontDict, decodeOne) {
|
|
|
237
441
|
* @param fontDict The simple font's dictionary.
|
|
238
442
|
* @param decodeOne What one code comes to, to tell the two cases apart.
|
|
239
443
|
* @param nameOf The glyph name a code selects, where the font states one.
|
|
444
|
+
* @param numbered Whether the program numbers its glyphs (see
|
|
445
|
+
* {@link numbersGlyphs}), which its `cmap` then reaches by
|
|
446
|
+
* the code.
|
|
240
447
|
* @returns The `outline` field of a {@link ContentFont}, or nothing.
|
|
241
448
|
*/
|
|
242
|
-
function simpleOutlineOf(decodeOne, nameOf, source) {
|
|
449
|
+
function simpleOutlineOf(decodeOne, nameOf, source, numbered) {
|
|
243
450
|
if (!source) return {};
|
|
244
451
|
return { outline: {
|
|
245
452
|
matrix: [
|
|
@@ -252,6 +459,10 @@ function simpleOutlineOf(decodeOne, nameOf, source) {
|
|
|
252
459
|
],
|
|
253
460
|
path: (code) => {
|
|
254
461
|
if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
|
|
462
|
+
if (numbered) {
|
|
463
|
+
const segs = source.glyph(code, void 0);
|
|
464
|
+
return segs !== void 0 && segs.length > 0 ? [...segs] : void 0;
|
|
465
|
+
}
|
|
255
466
|
const name = nameOf.get(code) ?? source.builtIn?.get(code);
|
|
256
467
|
if (name === void 0) return source.byIndex?.(code);
|
|
257
468
|
return source.byName(name);
|
|
@@ -278,6 +489,7 @@ function simpleGlyphs(file, fontDict) {
|
|
|
278
489
|
const typeOne = file.resolve(descriptor.get("FontFile") ?? PDF_NULL);
|
|
279
490
|
const truetype = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
|
|
280
491
|
const compact = file.resolve(descriptor.get("FontFile3") ?? PDF_NULL);
|
|
492
|
+
const numbering = numberingOf(differences(file, fontDict).values());
|
|
281
493
|
try {
|
|
282
494
|
if (typeOne instanceof PdfStream) {
|
|
283
495
|
const face = type1Font(file.streamData(typeOne));
|
|
@@ -285,20 +497,34 @@ function simpleGlyphs(file, fontDict) {
|
|
|
285
497
|
return {
|
|
286
498
|
byName: face.path,
|
|
287
499
|
blank: (name) => face.has(name) && face.path(name) === void 0,
|
|
288
|
-
...face.encoding ? { builtIn: face.encoding } : {}
|
|
500
|
+
...face.encoding ? { builtIn: face.encoding } : {},
|
|
501
|
+
glyph: (_code, name) => name === void 0 || name === NOTDEF || !face.has(name) ? void 0 : face.path(name) ?? [],
|
|
502
|
+
...face.fsType !== void 0 ? { fsType: face.fsType } : {},
|
|
503
|
+
...spaceOf(face.advance("space"))
|
|
289
504
|
};
|
|
290
505
|
}
|
|
291
506
|
const stream = compact instanceof PdfStream ? compact : truetype;
|
|
292
507
|
if (!(stream instanceof PdfStream)) return void 0;
|
|
293
508
|
const bytes = file.streamData(stream);
|
|
294
509
|
const glyf = outlineSource(bytes);
|
|
510
|
+
const fsType = sfntFsType(bytes);
|
|
295
511
|
if (glyf) {
|
|
296
512
|
const named = postGlyphNames(bytes);
|
|
297
513
|
const gidOf = (name) => {
|
|
298
|
-
const gid = named?.get(name) ?? numberedGlyph(name);
|
|
514
|
+
const gid = named?.get(name) ?? numberedGlyph(name, numbering);
|
|
299
515
|
return gid !== void 0 && gid < glyf.count ? gid : void 0;
|
|
300
516
|
};
|
|
517
|
+
const flags = asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0);
|
|
518
|
+
const encoding = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
|
|
519
|
+
const byCode = trueTypeGlyphs(bytes, (flags & FLAG_SYMBOLIC) !== 0 && (flags & FLAG_NONSYMBOLIC) === 0, encoding instanceof Map || encoding instanceof PdfName, gidOf);
|
|
301
520
|
return {
|
|
521
|
+
glyph: (code, name) => {
|
|
522
|
+
const gid = byCode(code, name);
|
|
523
|
+
if (gid === void 0 || gid <= 0 || gid >= glyf.count) return void 0;
|
|
524
|
+
return glyf.path(gid) ?? [];
|
|
525
|
+
},
|
|
526
|
+
...fsType !== void 0 ? { fsType } : {},
|
|
527
|
+
...spaceOf(sfntSpaceAdvance(bytes)),
|
|
302
528
|
byName: (name) => {
|
|
303
529
|
const gid = gidOf(name);
|
|
304
530
|
return gid === void 0 ? void 0 : glyf.path(gid);
|
|
@@ -308,7 +534,8 @@ function simpleGlyphs(file, fontDict) {
|
|
|
308
534
|
return gid !== void 0 && glyf.path(gid) === void 0;
|
|
309
535
|
},
|
|
310
536
|
byIndex: (gid) => gid < glyf.count ? glyf.path(gid) : void 0,
|
|
311
|
-
indexed: !glyf.cmap
|
|
537
|
+
indexed: !glyf.cmap,
|
|
538
|
+
numbersGlyphs: () => numbersGlyphs(bytes, glyf, named, (code) => byCode(code, void 0))
|
|
312
539
|
};
|
|
313
540
|
}
|
|
314
541
|
const cff = openTypeCff(bytes) ?? bytes;
|
|
@@ -316,10 +543,17 @@ function simpleGlyphs(file, fontDict) {
|
|
|
316
543
|
if (!outlines) return void 0;
|
|
317
544
|
const names = cffNameToGid(cff);
|
|
318
545
|
const gidOf = (name) => {
|
|
319
|
-
const gid = names?.get(name) ?? numberedGlyph(name);
|
|
546
|
+
const gid = names?.get(name) ?? numberedGlyph(name, numbering);
|
|
320
547
|
return gid !== void 0 && gid < outlines.count ? gid : void 0;
|
|
321
548
|
};
|
|
549
|
+
const stated = fsType ?? cffFsType(cff);
|
|
322
550
|
return {
|
|
551
|
+
glyph: (_code, name) => {
|
|
552
|
+
const gid = name === void 0 || name === NOTDEF ? void 0 : gidOf(name);
|
|
553
|
+
return gid === void 0 || gid === 0 ? void 0 : outlines.path(gid) ?? [];
|
|
554
|
+
},
|
|
555
|
+
...stated !== void 0 ? { fsType: stated } : {},
|
|
556
|
+
...spaceOf(sfntSpaceAdvance(bytes) ?? cffSpaceAdvance(cff)),
|
|
323
557
|
byName: (name) => {
|
|
324
558
|
const gid = gidOf(name);
|
|
325
559
|
return gid === void 0 ? void 0 : outlines.path(gid);
|
|
@@ -333,6 +567,190 @@ function simpleGlyphs(file, fontDict) {
|
|
|
333
567
|
return;
|
|
334
568
|
}
|
|
335
569
|
}
|
|
570
|
+
/** The `spaceAdvance` field, where a program states a sensible one. */
|
|
571
|
+
function spaceOf(advance) {
|
|
572
|
+
return advance !== void 0 && advance > 0 && advance < 1e3 ? { spaceAdvance: advance } : {};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* §9.6.6.4 — how a SIMPLE TrueType font's code reaches a glyph: through the
|
|
576
|
+
* program's `cmap`, and which subtable decides what the code is looked up as.
|
|
577
|
+
*
|
|
578
|
+
* A face that states an encoding turns the code into its glyph NAME, and the
|
|
579
|
+
* name into a character for the Unicode subtable (3,1), a Mac Roman code for
|
|
580
|
+
* (1,0), or an entry of the program's own `post` names — and past ASCII, only
|
|
581
|
+
* that: the same byte is another letter in Mac Roman, and a glyph found by the
|
|
582
|
+
* code there is the wrong one. A symbolic face with a Windows symbol subtable
|
|
583
|
+
* (3,0), and a face that states no encoding, is looked up by the CODE: in
|
|
584
|
+
* (3,0), where it stands in one of four ranges (itself, or behind 0xF000,
|
|
585
|
+
* 0xF100 or 0xF200), or byte for byte in (1,0) — its names the fallback, as a
|
|
586
|
+
* viewer tries them. A program with no `cmap` at all is read by index: the
|
|
587
|
+
* code IS the glyph.
|
|
588
|
+
*
|
|
589
|
+
* @param program The raw `/FontFile2` bytes.
|
|
590
|
+
* @param symbolic Whether `/Flags` call the face symbolic (bit 3, not bit 6).
|
|
591
|
+
* @param hasEncoding Whether the font dictionary states an `/Encoding`.
|
|
592
|
+
* @param byName The glyph a name selects through `post` or the
|
|
593
|
+
* numbered-name convention (see {@link numberedGlyph}).
|
|
594
|
+
* @returns Code (and the name the encoding gives it) → glyph index, or
|
|
595
|
+
* `undefined` where nothing reaches one.
|
|
596
|
+
*/
|
|
597
|
+
function trueTypeGlyphs(program, symbolic, hasEncoding, byName) {
|
|
598
|
+
let tables;
|
|
599
|
+
return (code, name) => {
|
|
600
|
+
tables ??= cmapSubtables(program);
|
|
601
|
+
const unicode = tables.get("3,1") ?? tables.get("0,3") ?? tables.get("0,1") ?? tables.get("0,0");
|
|
602
|
+
const symbol = tables.get("3,0");
|
|
603
|
+
const roman = tables.get("1,0");
|
|
604
|
+
const bySymbol = () => {
|
|
605
|
+
if (symbol) {
|
|
606
|
+
for (const high of SYMBOL_RANGES) {
|
|
607
|
+
const gid = symbol(high | code);
|
|
608
|
+
if (gid > 0) return gid;
|
|
609
|
+
}
|
|
610
|
+
return 0;
|
|
611
|
+
}
|
|
612
|
+
return roman ? roman(code) : 0;
|
|
613
|
+
};
|
|
614
|
+
const byGlyphName = () => {
|
|
615
|
+
if (name === void 0 || name === NOTDEF) return 0;
|
|
616
|
+
const text = textForGlyphName(name);
|
|
617
|
+
const cp = text !== void 0 && [...text].length === 1 ? text.codePointAt(0) : void 0;
|
|
618
|
+
const viaUnicode = unicode && cp !== void 0 ? unicode(cp) : 0;
|
|
619
|
+
if (viaUnicode > 0) return viaUnicode;
|
|
620
|
+
const mac = macRomanCode(name);
|
|
621
|
+
const viaRoman = roman && mac !== void 0 ? roman(mac) : 0;
|
|
622
|
+
if (viaRoman > 0) return viaRoman;
|
|
623
|
+
return byName(name) ?? 0;
|
|
624
|
+
};
|
|
625
|
+
const gid = symbolic && symbol !== void 0 ? bySymbol() || byGlyphName() : hasEncoding ? byGlyphName() || (code < ASCII_END ? bySymbol() : 0) : bySymbol() || byGlyphName() || (unicode ? unicode(code) : 0);
|
|
626
|
+
if (gid > 0) return gid;
|
|
627
|
+
return tables.size === 0 ? code : void 0;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
/** Below this, a byte is the same character in every encoding a simple font uses. */
|
|
631
|
+
var ASCII_END = 128;
|
|
632
|
+
/**
|
|
633
|
+
* §9.6.6.4 — whether a TrueType program NUMBERS its glyphs, where a font would
|
|
634
|
+
* code characters with them: its codes are the order some producer came to
|
|
635
|
+
* each glyph in, and read as Latin-1 they spell nothing.
|
|
636
|
+
*
|
|
637
|
+
* Nothing in such a program says what its codes are — no Unicode subtable, no
|
|
638
|
+
* glyph names — and its SPACE says what they are not. A text face's blank
|
|
639
|
+
* glyph is its space, and Latin-1 puts the space at 32. A program that reaches
|
|
640
|
+
* no glyph at 32, and a blank one where Latin-1 reads a mark, is not coded in
|
|
641
|
+
* Latin-1: comments.pdf's chart faces put their spaces at 35 and 34, "#" and
|
|
642
|
+
* `"`.
|
|
643
|
+
*
|
|
644
|
+
* @param program The raw `/FontFile2` bytes.
|
|
645
|
+
* @param glyf Its outlines.
|
|
646
|
+
* @param named The glyphs its `post` table names.
|
|
647
|
+
* @param gidOf The glyph a code reaches, looked up as the text is.
|
|
648
|
+
*/
|
|
649
|
+
function numbersGlyphs(program, glyf, named, gidOf) {
|
|
650
|
+
if (named !== void 0 && named.size > 0) return false;
|
|
651
|
+
const tables = cmapSubtables(program);
|
|
652
|
+
if (UNICODE_SUBTABLES.some((key) => tables.has(key))) return false;
|
|
653
|
+
if (!tables.has("1,0") && !tables.has("3,0")) return false;
|
|
654
|
+
const reached = (code) => {
|
|
655
|
+
const gid = gidOf(code);
|
|
656
|
+
return gid !== void 0 && gid > 0 && gid < glyf.count ? gid : void 0;
|
|
657
|
+
};
|
|
658
|
+
if (reached(SPACE) !== void 0) return false;
|
|
659
|
+
const blanks = [];
|
|
660
|
+
for (let code = 0; code <= 255; code++) {
|
|
661
|
+
const gid = reached(code);
|
|
662
|
+
if (gid !== void 0 && glyf.path(gid) === void 0) blanks.push(code);
|
|
663
|
+
}
|
|
664
|
+
return blanks.length > 0 && blanks.length <= MOST_BLANKS && blanks.every(inkedInLatin1);
|
|
665
|
+
}
|
|
666
|
+
/** The `cmap` subtables that key a program's glyphs by character. */
|
|
667
|
+
var UNICODE_SUBTABLES = [
|
|
668
|
+
"3,1",
|
|
669
|
+
"0,3",
|
|
670
|
+
"0,1",
|
|
671
|
+
"0,0"
|
|
672
|
+
];
|
|
673
|
+
/**
|
|
674
|
+
* How many blank glyphs a text face has: its space and, beside it, a no-break
|
|
675
|
+
* one. A program with more has emptied the glyphs it does not use, and its
|
|
676
|
+
* codes may be Latin-1 for all its blanks say.
|
|
677
|
+
*/
|
|
678
|
+
var MOST_BLANKS = 2;
|
|
679
|
+
/** Whether Latin-1 reads a code as a mark: not a control, a space or a soft hyphen. */
|
|
680
|
+
function inkedInLatin1(code) {
|
|
681
|
+
return code > 32 && code < 127 || code > 160 && code <= 255 && code !== 173;
|
|
682
|
+
}
|
|
683
|
+
/** §9.6.6.4 — where a symbolic face's (3,0) subtable may put a one-byte code. */
|
|
684
|
+
var SYMBOL_RANGES = [
|
|
685
|
+
0,
|
|
686
|
+
61440,
|
|
687
|
+
61696,
|
|
688
|
+
61952
|
|
689
|
+
];
|
|
690
|
+
/** §9.8.2 `/Flags` — bit 3 is Symbolic, bit 6 Nonsymbolic (bits numbered from 1). */
|
|
691
|
+
var FLAG_SYMBOLIC = 4;
|
|
692
|
+
var FLAG_NONSYMBOLIC = 32;
|
|
693
|
+
/** The Mac Roman code of a glyph name, which is what a (1,0) `cmap` is keyed by. */
|
|
694
|
+
function macRomanCode(name) {
|
|
695
|
+
if (!macRomanCodes) {
|
|
696
|
+
macRomanCodes = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const [code, glyph] of baseEncodingTable("MacRomanEncoding") ?? []) if (!macRomanCodes.has(glyph)) macRomanCodes.set(glyph, code);
|
|
698
|
+
}
|
|
699
|
+
return macRomanCodes.get(name);
|
|
700
|
+
}
|
|
701
|
+
var macRomanCodes;
|
|
702
|
+
/**
|
|
703
|
+
* §9.6.6 — the glyph ANY code of a simple font draws, for a writer that embeds
|
|
704
|
+
* the face: the name the encoding gives the code, as the outline path reads it.
|
|
705
|
+
*
|
|
706
|
+
* WinAnsiEncoding's Latin-1 half is named too (Annex D.2), though the text
|
|
707
|
+
* reads it as Latin-1 without a name: unnamed, an é in a program addressed by
|
|
708
|
+
* name selected nothing — and in a TrueType one, the Mac Roman subtable's È.
|
|
709
|
+
*
|
|
710
|
+
* @param glyphs The program, read by name and code.
|
|
711
|
+
* @param nameOf The name each code has: `/Differences` over the base encoding.
|
|
712
|
+
* @param winAnsi Whether that base encoding is WinAnsiEncoding.
|
|
713
|
+
*/
|
|
714
|
+
function simpleGlyph(glyphs, nameOf, winAnsi) {
|
|
715
|
+
if (!glyphs) return void 0;
|
|
716
|
+
return (code) => glyphs.glyph(code, nameOf.get(code) ?? (winAnsi ? winAnsiLatinName(code) : void 0) ?? glyphs.builtIn?.get(code));
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* §9.9 — the {@link FaceProgram} of a font whose program this reads: the glyph
|
|
720
|
+
* lookup, the licence, and what the descriptor states (§9.8.1).
|
|
721
|
+
*
|
|
722
|
+
* @param file The owning file.
|
|
723
|
+
* @param fontDict The font dictionary.
|
|
724
|
+
* @param isType0 Whether it is a composite font, whose descendant owns the
|
|
725
|
+
* descriptor.
|
|
726
|
+
* @param glyph The glyph each code draws, where the program can be read.
|
|
727
|
+
* @param facts What the program states of itself: its licence and its space.
|
|
728
|
+
* @returns The `program` field of a {@link ContentFont}, or nothing.
|
|
729
|
+
*/
|
|
730
|
+
function programOf(file, fontDict, isType0, glyph, facts) {
|
|
731
|
+
if (!glyph) return {};
|
|
732
|
+
const owner = isType0 ? descendantFont(file, fontDict) : fontDict;
|
|
733
|
+
const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
|
|
734
|
+
if (!(descriptor instanceof Map)) return {};
|
|
735
|
+
const stated = (key) => {
|
|
736
|
+
const v = file.resolve(descriptor.get(key) ?? PDF_NULL);
|
|
737
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
738
|
+
};
|
|
739
|
+
const flags = stated("Flags") ?? 0;
|
|
740
|
+
const capHeight = stated("CapHeight");
|
|
741
|
+
const xHeight = stated("XHeight");
|
|
742
|
+
const base = asName(file.resolve(owner.get("BaseFont") ?? fontDict.get("BaseFont") ?? PDF_NULL));
|
|
743
|
+
return { program: {
|
|
744
|
+
glyph,
|
|
745
|
+
...facts?.fsType !== void 0 ? { fsType: facts.fsType } : {},
|
|
746
|
+
...facts?.spaceAdvance !== void 0 ? { spaceAdvance: facts.spaceAdvance } : {},
|
|
747
|
+
postScriptName: base.replace(/^[A-Z]{6}\+/u, ""),
|
|
748
|
+
...capHeight !== void 0 && capHeight > 0 ? { capHeight } : {},
|
|
749
|
+
...xHeight !== void 0 && xHeight > 0 ? { xHeight } : {},
|
|
750
|
+
italicAngle: stated("ItalicAngle") ?? 0,
|
|
751
|
+
fixedPitch: (flags & FLAG_FIXED_PITCH) !== 0
|
|
752
|
+
} };
|
|
753
|
+
}
|
|
336
754
|
/**
|
|
337
755
|
* The glyph index a name carries, for the names that carry one.
|
|
338
756
|
*
|
|
@@ -340,9 +758,13 @@ function simpleGlyphs(file, fontDict) {
|
|
|
340
758
|
* INDEX — `g24`, `glyph24`, `index24`, `cid24` — and that name is then the only
|
|
341
759
|
* way back to the outline. bug1151216.pdf names them `g24`, `g381`, `g3`, and
|
|
342
760
|
* its three lines of prices are drawn from nothing else.
|
|
761
|
+
*
|
|
762
|
+
* @param name The glyph's name.
|
|
763
|
+
* @param numbering How the font writes the number, where its names say (see
|
|
764
|
+
* {@link numberingOf}); four digits are hexadecimal otherwise.
|
|
343
765
|
*/
|
|
344
|
-
function numberedGlyph(name) {
|
|
345
|
-
const hex = /^g([0-9a-f]{4})$/u.exec(name);
|
|
766
|
+
function numberedGlyph(name, numbering) {
|
|
767
|
+
const hex = numbering !== "decimal" ? /^g([0-9a-f]{4})$/u.exec(name) : null;
|
|
346
768
|
if (hex) return Number.parseInt(hex[1], 16);
|
|
347
769
|
const m = /^(?:g|glyph|index|cid|G)(\d+)$/u.exec(name);
|
|
348
770
|
const n = m ? Number(m[1]) : NaN;
|
|
@@ -350,6 +772,33 @@ function numberedGlyph(name) {
|
|
|
350
772
|
}
|
|
351
773
|
/** No font holds more glyphs than this; a bigger number is not an index. */
|
|
352
774
|
var MAX_GLYPH_INDEX = 65536;
|
|
775
|
+
/**
|
|
776
|
+
* How a font's index names write their numbers, where its names say.
|
|
777
|
+
*
|
|
778
|
+
* A subsetter that pads the number to four digits writes it in hexadecimal —
|
|
779
|
+
* bug1027533.pdf's `g0024` is glyph 36 — and one that does not pad writes it
|
|
780
|
+
* in decimal. One name cannot tell the two apart: bug1151216.pdf names its
|
|
781
|
+
* glyphs `g24`, `g381` and `g1004`, and `g1004` is four digits either way.
|
|
782
|
+
* Read as hexadecimal it is glyph 4100, which no subset of a thousand glyphs
|
|
783
|
+
* holds: five of the file's codes drew nothing, fell back to Latin-1, and its
|
|
784
|
+
* prices came back "$@'' for 1" set over the glyphs that were drawn. The rest
|
|
785
|
+
* of the font's names say which it is — a leading zero or a letter is
|
|
786
|
+
* hexadecimal, a number of any length but four is decimal.
|
|
787
|
+
*
|
|
788
|
+
* @param names The glyph names the font's `/Differences` gives.
|
|
789
|
+
* @returns The numbering, or `undefined` where the names do not say.
|
|
790
|
+
*/
|
|
791
|
+
function numberingOf(names) {
|
|
792
|
+
let hex = false;
|
|
793
|
+
let decimal = false;
|
|
794
|
+
for (const name of names) {
|
|
795
|
+
const digits = /^g([0-9a-f]+)$/u.exec(name)?.[1];
|
|
796
|
+
if (digits === void 0) continue;
|
|
797
|
+
if (/[a-f]/u.test(digits) || digits.length === 4 && digits.startsWith("0")) hex = true;
|
|
798
|
+
else if (digits.length !== 4) decimal = true;
|
|
799
|
+
}
|
|
800
|
+
return hex === decimal ? void 0 : hex ? "hex" : "decimal";
|
|
801
|
+
}
|
|
353
802
|
/** §9.7.4.2 `/CIDToGIDMap` — a stream of two-byte glyph indices, CID by CID. */
|
|
354
803
|
function readCidToGid(file, cidFont) {
|
|
355
804
|
const map = file.resolve(cidFont.get("CIDToGIDMap") ?? PDF_NULL);
|
|
@@ -419,7 +868,7 @@ function namedGlyphs(file, fontDict, statesUnicode, glyph) {
|
|
|
419
868
|
if (names.size === 0) return void 0;
|
|
420
869
|
const out = /* @__PURE__ */ new Map();
|
|
421
870
|
for (const [code, name] of names) {
|
|
422
|
-
const text = textForGlyphName(name);
|
|
871
|
+
const text = textForGlyphName(name) ?? glyph.character(name);
|
|
423
872
|
if (text !== void 0) {
|
|
424
873
|
out.set(code, text);
|
|
425
874
|
continue;
|
|
@@ -532,7 +981,8 @@ function differences(file, fontDict) {
|
|
|
532
981
|
function runFontName(file, fontDict, isType0) {
|
|
533
982
|
const name = embeddedFontName(file, fontDict);
|
|
534
983
|
if (name === void 0 || hasLiftableProgram(file, fontDict)) return name;
|
|
535
|
-
|
|
984
|
+
const face = familyOfFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
|
|
985
|
+
if (knowsFamily(name) || knowsFamily(face)) return name;
|
|
536
986
|
const owner = isType0 ? descendantFont(file, fontDict) : fontDict;
|
|
537
987
|
const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
|
|
538
988
|
if (!(descriptor instanceof Map)) return name;
|
|
@@ -541,6 +991,101 @@ function runFontName(file, fontDict, isType0) {
|
|
|
541
991
|
if ((flags & FLAG_SERIF) !== 0) return `${name} serif`;
|
|
542
992
|
return name;
|
|
543
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* For every face a run may name, the family a word processor knows it by —
|
|
996
|
+
* keyed by the name the run carries (see {@link FaceFamily}).
|
|
997
|
+
*
|
|
998
|
+
* A PDF names the FACE and a .docx names a family: written as the face,
|
|
999
|
+
* `inter-semibold` was a font no reader has, and LibreOffice set the whole of
|
|
1000
|
+
* an invoice drawn in Inter in its default serif. The family is the one the
|
|
1001
|
+
* descriptor states (§9.8.1 `/FontFamily`) or, where it states none, the one
|
|
1002
|
+
* the PostScript name is made of.
|
|
1003
|
+
*
|
|
1004
|
+
* @param file The document.
|
|
1005
|
+
* @param pages The pages whose faces are wanted.
|
|
1006
|
+
* @returns Run font name → its family.
|
|
1007
|
+
*/
|
|
1008
|
+
function collectFaceFamilies(file, pages) {
|
|
1009
|
+
const out = /* @__PURE__ */ new Map();
|
|
1010
|
+
eachPageFont(file, pages, (fontDict) => {
|
|
1011
|
+
const isType0 = asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) === "Type0";
|
|
1012
|
+
const key = runFontName(file, fontDict, isType0);
|
|
1013
|
+
if (key === void 0 || out.has(key)) return;
|
|
1014
|
+
const owner = isType0 ? descendantFont(file, fontDict) : fontDict;
|
|
1015
|
+
const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
|
|
1016
|
+
const stated = descriptor instanceof Map ? file.resolve(descriptor.get("FontFamily") ?? PDF_NULL) : PDF_NULL;
|
|
1017
|
+
const family = typeof stated === "string" && /^[\x20-\x7e]+$/u.test(stated.trim()) ? stated.trim() : familyOfFace(cidFontName(file, fontDict, isType0));
|
|
1018
|
+
if (family.length === 0) return;
|
|
1019
|
+
const flags = descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0) : 0;
|
|
1020
|
+
out.set(key, {
|
|
1021
|
+
family,
|
|
1022
|
+
generic: genericOf(family, flags)
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
return out;
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* §9.7.6.1 — a composite font's `/BaseFont` is its CIDFont's name, a hyphen and
|
|
1029
|
+
* the name of the CMap it is encoded by (`HeiseiMin-W3-UniJIS-UCS2-H`); the
|
|
1030
|
+
* face is the part before the CMap's.
|
|
1031
|
+
*/
|
|
1032
|
+
function cidFontName(file, fontDict, isType0) {
|
|
1033
|
+
const base = asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL));
|
|
1034
|
+
const cmap = isType0 ? asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL)) : "";
|
|
1035
|
+
return cmap !== "" && base.endsWith(`-${cmap}`) ? base.slice(0, -cmap.length - 1) : base;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* The family a PostScript face name is made of: `Inter-SemiBold` → `Inter`,
|
|
1039
|
+
* `ArialMT` → `Arial`, `TimesNewRomanPS-BoldMT` → `Times New Roman`.
|
|
1040
|
+
*
|
|
1041
|
+
* §9.6.2.1 names a face `Family-Style` (Word writes `Family,Style`), with the
|
|
1042
|
+
* family's words run together. What follows the separator is a style only if
|
|
1043
|
+
* it is made of style words — `MS-Mincho` is a family of its own — and the
|
|
1044
|
+
* words come apart where the capitals say they do.
|
|
1045
|
+
*/
|
|
1046
|
+
function familyOfFace(baseFont) {
|
|
1047
|
+
const name = plainFace(baseFont);
|
|
1048
|
+
const cut = /^(.+?)[-,]([^-,]+)$/u.exec(name);
|
|
1049
|
+
const whole = cut && STYLE_WORDS.test(cut[2]) ? cut[1] : cut ? name.replace(/-/gu, " ") : GLUED_STYLE.exec(name)?.[1] ?? name;
|
|
1050
|
+
return (whole.replace(/(?:PSMT|PS|MT)$/u, "") || whole).replace(/([a-z])([A-Z])/gu, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/gu, "$1 $2").replace(/\bDeja Vu\b/u, "DejaVu").replace(/\s+/gu, " ").trim();
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* A face name with what producers write around it taken off: the subset tag
|
|
1054
|
+
* (§9.6.4) and, after the face, the charset Acrobat's Office export appends
|
|
1055
|
+
* (`Arial,Bold-WinCharSetFFFF`, `-H2`), the encoding a CIDFont was cut for
|
|
1056
|
+
* (`-Identity-H`, `-OneByteIdentityH`), a serial number, and the slant or
|
|
1057
|
+
* stretch an interpreter synthesised (`-Slant_167`, `-Extend_850`). Left on,
|
|
1058
|
+
* bug900822.pdf's .docx asked for "Courier New,Bold Win Char Set FFFF", which
|
|
1059
|
+
* no machine has, over the Courier New every machine does.
|
|
1060
|
+
*/
|
|
1061
|
+
function plainFace(baseFont) {
|
|
1062
|
+
let name = baseFont.replace(/^[A-Z]{6}\+/u, "").replace(/^[!-/:-@[-`{-~]+/u, "").trim();
|
|
1063
|
+
for (let next = name.replace(PRODUCER_TAIL, ""); next !== name;) {
|
|
1064
|
+
name = next;
|
|
1065
|
+
next = name.replace(PRODUCER_TAIL, "");
|
|
1066
|
+
}
|
|
1067
|
+
return name;
|
|
1068
|
+
}
|
|
1069
|
+
/** One piece of producer debris at the end of a face name (see `plainFace`). */
|
|
1070
|
+
var PRODUCER_TAIL = /[-_](?:WinCharSet[0-9A-F]+|Identity-[HV]|OneByteIdentity[HV]|H\d+|\d+|(?:Slant|Extend)_\d+)$/iu;
|
|
1071
|
+
/**
|
|
1072
|
+
* A style run on to a family with no separator — `CalibriBold` — which is the
|
|
1073
|
+
* family's name only with the style taken off. Whether the face IS bold is the
|
|
1074
|
+
* program's to say (see `faceStyle`): `NewBasrahBold` is a family of its own.
|
|
1075
|
+
*/
|
|
1076
|
+
var GLUED_STYLE = /^(.*?[a-z])((?:Semi|Demi|Extra|Ultra)?(?:Bold|Black|Heavy)(?:Italic|Oblique)?|Italic|Oblique)$/u;
|
|
1077
|
+
/** A PostScript style part, made of nothing but style words (`SemiBoldItalic`, `BoldMT`, `Regu`). */
|
|
1078
|
+
var STYLE_WORDS = /^(?:regular|regu|roman|book|normal|plain|medium|medi|light|thin|hairline|extra|ultra|semi|demi|bold|bd|black|heavy|italic|ital|it|oblique|obl|condensed|cond|narrow|compressed|extended|mt|ps)+$/iu;
|
|
1079
|
+
/** §17.8.3.10 — the kind of face a family is, from the descriptor or, failing that, its name. */
|
|
1080
|
+
function genericOf(family, flags) {
|
|
1081
|
+
if (!knowsFamily(family)) {
|
|
1082
|
+
if ((flags & FLAG_FIXED_PITCH) !== 0) return "modern";
|
|
1083
|
+
if ((flags & FLAG_SERIF) !== 0) return "roman";
|
|
1084
|
+
}
|
|
1085
|
+
const key = resolveFamilyStyle(family).key;
|
|
1086
|
+
if (key === "cousine") return "modern";
|
|
1087
|
+
return key === "tinos" || key === "caladea" ? "roman" : "swiss";
|
|
1088
|
+
}
|
|
544
1089
|
/** §9.8.2 `/Flags` — bit 1 is FixedPitch, bit 2 Serif (bits numbered from 1). */
|
|
545
1090
|
var FLAG_FIXED_PITCH = 1;
|
|
546
1091
|
var FLAG_SERIF = 2;
|
|
@@ -575,8 +1120,11 @@ function faceStyle(file, fontDict, isType0) {
|
|
|
575
1120
|
const flags = asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0);
|
|
576
1121
|
const weightVal = file.resolve(descriptor.get("FontWeight") ?? PDF_NULL);
|
|
577
1122
|
const slant = asNumber(file.resolve(descriptor.get("ItalicAngle") ?? PDF_NULL), 0);
|
|
578
|
-
const
|
|
579
|
-
const
|
|
1123
|
+
const stated = typeof weightVal === "number" && weightVal >= LIGHTEST_WEIGHT;
|
|
1124
|
+
const own = programStyle(file, fontDict);
|
|
1125
|
+
const forced = (flags & FLAG_FORCE_BOLD) !== 0 && !named.light;
|
|
1126
|
+
const bold = stated ? asNumber(weightVal, 0) >= BOLD_WEIGHT : forced || own?.bold === true || named.bold;
|
|
1127
|
+
const italic = slant !== 0 || (flags & FLAG_ITALIC) !== 0 || own?.italic === true || named.italic;
|
|
580
1128
|
return {
|
|
581
1129
|
...bold ? { bold: true } : {},
|
|
582
1130
|
...italic ? { italic: true } : {}
|
|
@@ -597,11 +1145,13 @@ function faceStyle(file, fontDict, isType0) {
|
|
|
597
1145
|
* set two lines heavy that no reader sets heavy. `Times-Bold` is.
|
|
598
1146
|
*/
|
|
599
1147
|
function styleFromName(baseFont) {
|
|
600
|
-
const name = baseFont
|
|
1148
|
+
const name = plainFace(baseFont);
|
|
601
1149
|
const style = /[-,]([A-Za-z]+)$/u.exec(name)?.[1] ?? "";
|
|
1150
|
+
const bold = /bold|black|heavy|semib|demi/iu.test(style);
|
|
602
1151
|
return {
|
|
603
|
-
bold
|
|
604
|
-
italic: /italic|oblique/iu.test(style)
|
|
1152
|
+
bold,
|
|
1153
|
+
italic: /italic|oblique/iu.test(style),
|
|
1154
|
+
light: !bold && /light|thin|hairline|book|regular|roman|normal|medium/iu.test(style)
|
|
605
1155
|
};
|
|
606
1156
|
}
|
|
607
1157
|
/** §9.7.4 — a `/Type0` font's one descendant CIDFont, which owns the descriptor. */
|
|
@@ -610,6 +1160,23 @@ function descendantFont(file, fontDict) {
|
|
|
610
1160
|
const first = Array.isArray(descFonts) ? file.resolve(descFonts[0] ?? PDF_NULL) : PDF_NULL;
|
|
611
1161
|
return first instanceof Map ? first : /* @__PURE__ */ new Map();
|
|
612
1162
|
}
|
|
1163
|
+
/**
|
|
1164
|
+
* §9.8.2 — whether every glyph of a simple font is as wide as the next: the
|
|
1165
|
+
* descriptor's FixedPitch flag, the standard Courier, or a /Widths array that
|
|
1166
|
+
* says so itself. pdfTeX sets no flag on Computer Modern's typewriter face,
|
|
1167
|
+
* and its widths do: all of them 525. Ten widths at the least — a face that
|
|
1168
|
+
* shows four digits shows four equal widths and is no typewriter's.
|
|
1169
|
+
*/
|
|
1170
|
+
function fixedPitchOf(file, fontDict) {
|
|
1171
|
+
const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
|
|
1172
|
+
if (((descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0) : 0) & FLAG_FIXED_PITCH) !== 0) return true;
|
|
1173
|
+
if (/^courier/iu.test(plainFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL))))) return true;
|
|
1174
|
+
const stated = file.resolve(fontDict.get("Widths") ?? PDF_NULL);
|
|
1175
|
+
const widths = (Array.isArray(stated) ? stated : []).map((w) => file.resolve(w)).filter((w) => typeof w === "number" && w > 0);
|
|
1176
|
+
return widths.length >= FIXED_PITCH_WIDTHS && widths.every((w) => Math.abs(w - widths[0]) < .5);
|
|
1177
|
+
}
|
|
1178
|
+
/** How many widths it takes to say every glyph is as wide as the next. */
|
|
1179
|
+
var FIXED_PITCH_WIDTHS = 10;
|
|
613
1180
|
function simpleWidths(file, fontDict, decodeOne) {
|
|
614
1181
|
const first = asNumber(file.resolve(fontDict.get("FirstChar") ?? PDF_NULL), 0);
|
|
615
1182
|
const widthsVal = file.resolve(fontDict.get("Widths") ?? PDF_NULL);
|
|
@@ -617,12 +1184,14 @@ function simpleWidths(file, fontDict, decodeOne) {
|
|
|
617
1184
|
const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
|
|
618
1185
|
const missing = descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("MissingWidth") ?? PDF_NULL), 0) : 0;
|
|
619
1186
|
const face = standardFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
|
|
620
|
-
|
|
1187
|
+
const stated = (code) => {
|
|
621
1188
|
const w = widths[code - first];
|
|
622
1189
|
if (typeof w === "number") return w;
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1190
|
+
return face === void 0 ? void 0 : standardWidth(face, code, decodeOne(code));
|
|
1191
|
+
};
|
|
1192
|
+
return {
|
|
1193
|
+
width: (code) => stated(code) ?? (missing > 0 ? missing : 500),
|
|
1194
|
+
stated
|
|
626
1195
|
};
|
|
627
1196
|
}
|
|
628
1197
|
function cidWidths(file, fontDict) {
|
|
@@ -661,4 +1230,4 @@ function asNumber(v, dflt) {
|
|
|
661
1230
|
return typeof v === "number" ? v : dflt;
|
|
662
1231
|
}
|
|
663
1232
|
//#endregion
|
|
664
|
-
export { buildContentFont };
|
|
1233
|
+
export { buildContentFont, collectFaceFamilies };
|