pdf-codec 5.8.1 → 5.8.2
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 +2 -0
- package/dist/builtin-encoding.cjs +1 -1
- package/dist/builtin-encoding.js +2 -2
- package/dist/core-font-glyph-orders.cjs +97 -0
- package/dist/core-font-glyph-orders.d.cts +23 -1
- package/dist/core-font-glyph-orders.d.ts +23 -1
- package/dist/core-font-glyph-orders.js +95 -1
- package/dist/font-read.cjs +11 -0
- package/dist/font-read.js +11 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -334,6 +334,8 @@ A TrueType subset can lose its glyph names and its `cmap` both and keep nothing
|
|
|
334
334
|
|
|
335
335
|
A `/ToUnicode` CMap that a producer writes for the Symbol or Wingdings font (Word does, for a bullet, a tick or an arrow it draws in them) gives each glyph the private-use code point its 8-bit code makes in the 0xF000 range, which identifies the glyph in that one font and nothing else: 0xFC is a check mark in Wingdings and a piece of a right brace in Symbol. `symbol-font-private-use.ts` reads such a code point as the character the named font draws at that code, applied to the text a `/ToUnicode` CMap gave for a Symbol, SymbolMT or Wingdings font, by name, and to no other font: Symbol through its published encoding (the assembly pieces of a tall bracket and the integral extension as their Unicode characters), Wingdings through `wingdings-characters.ts`, the table of Unicode's own Wingdings document. A code the font has no character at, and Wingdings 2 and 3, Webdings and ZapfDingbats, stay as the producer wrote them.
|
|
336
336
|
|
|
337
|
+
Two ways a producer names a core face other than the PostScript name the face's own tables carry are read through the same carried glyph order. A TrueType program Windows-era producers embed under the face's Windows name (`TimesNewRoman`, `Arial,Bold`) is the face `TimesNewRomanPSMT` or `Arial-BoldMT`, still accepted glyph by glyph against the program's own advance widths. And Adobe Acrobat's optimiser rewrites a TrueType face as a CFF subset that names each glyph `G` followed by its glyph id in the face (`G36` is the capital A of Times New Roman) under a font name carrying a size suffix (`Times.New.Roman092`, `Arial.Bold0133`): `coreFaceTextsOfNumberedGlyphs` reads those names through the face's order, but only for a font whose dictionary widths agree with the face's advances over at least five numbered glyphs and nine in ten of them, so a subset of another face, or one too small to tell, is read as before. The Symbol face and the Type 3 fonts such producers also write are not covered: the first has no carried order, and a Type 3 glyph is a drawing procedure with no character.
|
|
338
|
+
|
|
337
339
|
**Where nothing states an answer, the answer is the replacement character plus a `text/unmapped-encoding` diagnostic, never a guess.** Cases that reach it in practice: a symbolic font with no embedded program and no `/ToUnicode`; a subsetted font that both strips its glyph names and maps its glyphs only from private-use code points; a nameless, `cmap`-less subset of a face whose glyph order this package does not carry, or of a carried face at a glyph its recorded releases do not number; and a composite font whose `/Encoding` is a CMap other than Identity-H or Identity-V, whose codes reach CIDs this reader does not map. A private-use code point identifies a glyph inside one font and says nothing about the character it draws, so it is treated as no answer rather than a wrong one.
|
|
338
340
|
|
|
339
341
|
## Grouping text runs into lines and words
|
|
@@ -92,7 +92,7 @@ function coreFaceSource(font, fontName) {
|
|
|
92
92
|
const unitsPerEm = require_font_tables.parseHead(font)?.unitsPerEm;
|
|
93
93
|
const advanceOf = advanceWidths(font);
|
|
94
94
|
if (name === void 0 || unitsPerEm === void 0 || advanceOf === void 0) return;
|
|
95
|
-
const order = require_core_font_glyph_orders.coreFaceGlyphOrder(require_font_style.withoutSubsetTag(name), unitsPerEm);
|
|
95
|
+
const order = require_core_font_glyph_orders.coreFaceGlyphOrder(require_core_font_glyph_orders.coreFaceNameOfFontName(name) ?? require_font_style.withoutSubsetTag(name), unitsPerEm);
|
|
96
96
|
if (order === void 0) return;
|
|
97
97
|
return (glyphId) => order(glyphId, advanceOf(glyphId));
|
|
98
98
|
}
|
package/dist/builtin-encoding.js
CHANGED
|
@@ -2,7 +2,7 @@ import { standardGlyphName } from "./encoding-standard-tables.js";
|
|
|
2
2
|
import { hasBytes, parseSfnt, sfntTableBytes, u16, u8 } from "./sfnt.js";
|
|
3
3
|
import { cffStringForSid, parseCffDict, readCffIndex } from "./cff.js";
|
|
4
4
|
import { readCmapSubtables } from "./cmap-table.js";
|
|
5
|
-
import { coreFaceGlyphOrder } from "./core-font-glyph-orders.js";
|
|
5
|
+
import { coreFaceGlyphOrder, coreFaceNameOfFontName } from "./core-font-glyph-orders.js";
|
|
6
6
|
import { withoutSubsetTag } from "./font-style.js";
|
|
7
7
|
import { parseHead, parseMaxp, parseName, parsePostGlyphNames } from "./font-tables.js";
|
|
8
8
|
import { glyphNameToUnicode } from "./glyph-names.js";
|
|
@@ -91,7 +91,7 @@ function coreFaceSource(font, fontName) {
|
|
|
91
91
|
const unitsPerEm = parseHead(font)?.unitsPerEm;
|
|
92
92
|
const advanceOf = advanceWidths(font);
|
|
93
93
|
if (name === void 0 || unitsPerEm === void 0 || advanceOf === void 0) return;
|
|
94
|
-
const order = coreFaceGlyphOrder(withoutSubsetTag(name), unitsPerEm);
|
|
94
|
+
const order = coreFaceGlyphOrder(coreFaceNameOfFontName(name) ?? withoutSubsetTag(name), unitsPerEm);
|
|
95
95
|
if (order === void 0) return;
|
|
96
96
|
return (glyphId) => order(glyphId, advanceOf(glyphId));
|
|
97
97
|
}
|
|
@@ -58,5 +58,102 @@ function coreFaceGlyphOrder(postScriptName, unitsPerEm) {
|
|
|
58
58
|
if (face?.unitsPerEm !== unitsPerEm) return;
|
|
59
59
|
return (glyphId, advanceWidth) => face.advances[glyphId] === advanceWidth ? face.texts[glyphId] : void 0;
|
|
60
60
|
}
|
|
61
|
+
/** The PostScript names of the core faces, by the family and style a font name spells with its punctuation, subset tag and Distiller size suffix taken off. */
|
|
62
|
+
const CORE_FACE_BY_SPELLING = /* @__PURE__ */ new Map([
|
|
63
|
+
["arial", "ArialMT"],
|
|
64
|
+
["arialmt", "ArialMT"],
|
|
65
|
+
["arialbold", "Arial-BoldMT"],
|
|
66
|
+
["arialboldmt", "Arial-BoldMT"],
|
|
67
|
+
["arialitalic", "Arial-ItalicMT"],
|
|
68
|
+
["arialitalicmt", "Arial-ItalicMT"],
|
|
69
|
+
["arialbolditalic", "Arial-BoldItalicMT"],
|
|
70
|
+
["arialbolditalicmt", "Arial-BoldItalicMT"],
|
|
71
|
+
["timesnewroman", "TimesNewRomanPSMT"],
|
|
72
|
+
["timesnewromanpsmt", "TimesNewRomanPSMT"],
|
|
73
|
+
["timesnewromanbold", "TimesNewRomanPS-BoldMT"],
|
|
74
|
+
["timesnewromanpsboldmt", "TimesNewRomanPS-BoldMT"],
|
|
75
|
+
["timesnewromanitalic", "TimesNewRomanPS-ItalicMT"],
|
|
76
|
+
["timesnewromanpsitalicmt", "TimesNewRomanPS-ItalicMT"],
|
|
77
|
+
["timesnewromanbolditalic", "TimesNewRomanPS-BoldItalicMT"],
|
|
78
|
+
["timesnewromanpsbolditalicmt", "TimesNewRomanPS-BoldItalicMT"],
|
|
79
|
+
["couriernew", "CourierNewPSMT"],
|
|
80
|
+
["couriernewpsmt", "CourierNewPSMT"],
|
|
81
|
+
["couriernewbold", "CourierNewPS-BoldMT"],
|
|
82
|
+
["couriernewpsboldmt", "CourierNewPS-BoldMT"],
|
|
83
|
+
["couriernewitalic", "CourierNewPS-ItalicMT"],
|
|
84
|
+
["couriernewpsitalicmt", "CourierNewPS-ItalicMT"],
|
|
85
|
+
["couriernewbolditalic", "CourierNewPS-BoldItalicMT"],
|
|
86
|
+
["couriernewpsbolditalicmt", "CourierNewPS-BoldItalicMT"]
|
|
87
|
+
]);
|
|
88
|
+
const ZERO_CODE = 48;
|
|
89
|
+
const NINE_CODE = 57;
|
|
90
|
+
const FULL_STOP_CODE = 46;
|
|
91
|
+
function isDigitAt(name, index) {
|
|
92
|
+
const code = name.charCodeAt(index);
|
|
93
|
+
return code >= ZERO_CODE && code <= NINE_CODE;
|
|
94
|
+
}
|
|
95
|
+
function digitRunStart(name, end) {
|
|
96
|
+
let start = end;
|
|
97
|
+
while (start > 0 && isDigitAt(name, start - 1)) start -= 1;
|
|
98
|
+
return start;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* `name` without a trailing size or version number a producer appends to a font name ("Arial.Bold0133", "Arial086.75"): digits, optionally a fraction. Found by one scan back from the end, so the work is the length of the number and not of the name: the end-anchored pattern this replaces (a digit run followed by an optional fraction, then the end) is retried from every offset of a long digit run that a letter follows, which takes time quadratic in its length.
|
|
102
|
+
* @param name - A `/BaseFont` name.
|
|
103
|
+
* @returns The name with its trailing number cut off, or the name itself when it does not end in one.
|
|
104
|
+
*/
|
|
105
|
+
function withoutTrailingNumber(name) {
|
|
106
|
+
const fractionStart = digitRunStart(name, name.length);
|
|
107
|
+
if (fractionStart === name.length) return name;
|
|
108
|
+
const point = fractionStart - 1;
|
|
109
|
+
if (point > 0 && name.charCodeAt(point) === FULL_STOP_CODE) {
|
|
110
|
+
const wholeStart = digitRunStart(name, point);
|
|
111
|
+
if (wholeStart < point) return name.slice(0, wholeStart);
|
|
112
|
+
}
|
|
113
|
+
return name.slice(0, fractionStart);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The PostScript name of the core face a font name spells, or `undefined` when it spells none: `TimesNewRoman` and `Times.New.Roman.Bold092` are `TimesNewRomanPSMT` and `TimesNewRomanPS-BoldMT`. A spelling is only a candidate: the glyph order is accepted per glyph, against the font dictionary's own widths (`coreFaceTextOfNumberedGlyph`).
|
|
117
|
+
* @param fontName - A `/BaseFont` name, with or without its subset tag.
|
|
118
|
+
* @returns The face's PostScript name, or `undefined`.
|
|
119
|
+
*/
|
|
120
|
+
function coreFaceNameOfFontName(fontName) {
|
|
121
|
+
const spelled = withoutTrailingNumber(fontName.replace(/^[A-Z]{6}\+/u, "")).replace(/[^A-Za-z]/gu, "").toLowerCase();
|
|
122
|
+
return CORE_FACE_BY_SPELLING.get(spelled);
|
|
123
|
+
}
|
|
124
|
+
const NUMBERED_GLYPH_NAME = /^G(\d+)$/u;
|
|
125
|
+
const WIDTH_AGREEMENT_TOLERANCE = 25;
|
|
126
|
+
const THOUSANDTHS_PER_EM = 1e3;
|
|
127
|
+
const MIN_AGREEING_SHARE = .9;
|
|
128
|
+
const MIN_NUMBERED_GLYPHS = 5;
|
|
129
|
+
/**
|
|
130
|
+
* What a core face draws at the glyphs of a CFF subset that names each `G<id>`, accepted for the whole font only when the font dictionary's widths agree with the face's advances over at least `MIN_NUMBERED_GLYPHS` glyphs and `MIN_AGREEING_SHARE` of them, so a subset of any other face, or of another release of this one, is read as before.
|
|
131
|
+
* @param fontName - The font's `/BaseFont`.
|
|
132
|
+
* @param glyphs - Each glyph of the font a `/Differences` array names, with the width, in thousandths of an em, the dictionary gives its code.
|
|
133
|
+
* @returns The character each numbered glyph name draws, or `undefined` when the font is not taken for a core face.
|
|
134
|
+
*/
|
|
135
|
+
function coreFaceTextsOfNumberedGlyphs(fontName, glyphs) {
|
|
136
|
+
const faceName = coreFaceNameOfFontName(fontName);
|
|
137
|
+
const face = faceName === void 0 ? void 0 : readFaces().get(faceName);
|
|
138
|
+
if (face === void 0) return;
|
|
139
|
+
const texts = /* @__PURE__ */ new Map();
|
|
140
|
+
let numbered = 0;
|
|
141
|
+
let agreeing = 0;
|
|
142
|
+
for (const { name, width } of glyphs) {
|
|
143
|
+
const id = NUMBERED_GLYPH_NAME.exec(name)?.[1];
|
|
144
|
+
const advance = id === void 0 ? void 0 : face.advances[Number.parseInt(id, 10)];
|
|
145
|
+
if (id === void 0 || advance === void 0) continue;
|
|
146
|
+
numbered++;
|
|
147
|
+
const text = face.texts[Number.parseInt(id, 10)];
|
|
148
|
+
if (Math.abs(advance * THOUSANDTHS_PER_EM / face.unitsPerEm - width) <= WIDTH_AGREEMENT_TOLERANCE) {
|
|
149
|
+
agreeing++;
|
|
150
|
+
if (text !== void 0) texts.set(name, text);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return numbered >= MIN_NUMBERED_GLYPHS && agreeing >= numbered * MIN_AGREEING_SHARE ? texts : void 0;
|
|
154
|
+
}
|
|
61
155
|
//#endregion
|
|
62
156
|
exports.coreFaceGlyphOrder = coreFaceGlyphOrder;
|
|
157
|
+
exports.coreFaceNameOfFontName = coreFaceNameOfFontName;
|
|
158
|
+
exports.coreFaceTextsOfNumberedGlyphs = coreFaceTextsOfNumberedGlyphs;
|
|
159
|
+
exports.withoutTrailingNumber = withoutTrailingNumber;
|
|
@@ -6,5 +6,27 @@
|
|
|
6
6
|
* @returns a lookup from a glyph id and the program's own advance width for that glyph to the character the face draws there, answering undefined wherever the width disagrees with the face's or the face draws no character; or undefined when the name is not a core face or the grid is not the face's
|
|
7
7
|
*/
|
|
8
8
|
declare function coreFaceGlyphOrder(postScriptName: string, unitsPerEm: number): ((glyphId: number, advanceWidth: number) => string | undefined) | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* `name` without a trailing size or version number a producer appends to a font name ("Arial.Bold0133", "Arial086.75"): digits, optionally a fraction. Found by one scan back from the end, so the work is the length of the number and not of the name: the end-anchored pattern this replaces (a digit run followed by an optional fraction, then the end) is retried from every offset of a long digit run that a letter follows, which takes time quadratic in its length.
|
|
11
|
+
* @param name - A `/BaseFont` name.
|
|
12
|
+
* @returns The name with its trailing number cut off, or the name itself when it does not end in one.
|
|
13
|
+
*/
|
|
14
|
+
declare function withoutTrailingNumber(name: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* The PostScript name of the core face a font name spells, or `undefined` when it spells none: `TimesNewRoman` and `Times.New.Roman.Bold092` are `TimesNewRomanPSMT` and `TimesNewRomanPS-BoldMT`. A spelling is only a candidate: the glyph order is accepted per glyph, against the font dictionary's own widths (`coreFaceTextOfNumberedGlyph`).
|
|
17
|
+
* @param fontName - A `/BaseFont` name, with or without its subset tag.
|
|
18
|
+
* @returns The face's PostScript name, or `undefined`.
|
|
19
|
+
*/
|
|
20
|
+
declare function coreFaceNameOfFontName(fontName: string): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* What a core face draws at the glyphs of a CFF subset that names each `G<id>`, accepted for the whole font only when the font dictionary's widths agree with the face's advances over at least `MIN_NUMBERED_GLYPHS` glyphs and `MIN_AGREEING_SHARE` of them, so a subset of any other face, or of another release of this one, is read as before.
|
|
23
|
+
* @param fontName - The font's `/BaseFont`.
|
|
24
|
+
* @param glyphs - Each glyph of the font a `/Differences` array names, with the width, in thousandths of an em, the dictionary gives its code.
|
|
25
|
+
* @returns The character each numbered glyph name draws, or `undefined` when the font is not taken for a core face.
|
|
26
|
+
*/
|
|
27
|
+
declare function coreFaceTextsOfNumberedGlyphs(fontName: string, glyphs: readonly {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly width: number;
|
|
30
|
+
}[]): ReadonlyMap<string, string> | undefined;
|
|
9
31
|
//#endregion
|
|
10
|
-
export { coreFaceGlyphOrder };
|
|
32
|
+
export { coreFaceGlyphOrder, coreFaceNameOfFontName, coreFaceTextsOfNumberedGlyphs, withoutTrailingNumber };
|
|
@@ -6,5 +6,27 @@
|
|
|
6
6
|
* @returns a lookup from a glyph id and the program's own advance width for that glyph to the character the face draws there, answering undefined wherever the width disagrees with the face's or the face draws no character; or undefined when the name is not a core face or the grid is not the face's
|
|
7
7
|
*/
|
|
8
8
|
declare function coreFaceGlyphOrder(postScriptName: string, unitsPerEm: number): ((glyphId: number, advanceWidth: number) => string | undefined) | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* `name` without a trailing size or version number a producer appends to a font name ("Arial.Bold0133", "Arial086.75"): digits, optionally a fraction. Found by one scan back from the end, so the work is the length of the number and not of the name: the end-anchored pattern this replaces (a digit run followed by an optional fraction, then the end) is retried from every offset of a long digit run that a letter follows, which takes time quadratic in its length.
|
|
11
|
+
* @param name - A `/BaseFont` name.
|
|
12
|
+
* @returns The name with its trailing number cut off, or the name itself when it does not end in one.
|
|
13
|
+
*/
|
|
14
|
+
declare function withoutTrailingNumber(name: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* The PostScript name of the core face a font name spells, or `undefined` when it spells none: `TimesNewRoman` and `Times.New.Roman.Bold092` are `TimesNewRomanPSMT` and `TimesNewRomanPS-BoldMT`. A spelling is only a candidate: the glyph order is accepted per glyph, against the font dictionary's own widths (`coreFaceTextOfNumberedGlyph`).
|
|
17
|
+
* @param fontName - A `/BaseFont` name, with or without its subset tag.
|
|
18
|
+
* @returns The face's PostScript name, or `undefined`.
|
|
19
|
+
*/
|
|
20
|
+
declare function coreFaceNameOfFontName(fontName: string): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* What a core face draws at the glyphs of a CFF subset that names each `G<id>`, accepted for the whole font only when the font dictionary's widths agree with the face's advances over at least `MIN_NUMBERED_GLYPHS` glyphs and `MIN_AGREEING_SHARE` of them, so a subset of any other face, or of another release of this one, is read as before.
|
|
23
|
+
* @param fontName - The font's `/BaseFont`.
|
|
24
|
+
* @param glyphs - Each glyph of the font a `/Differences` array names, with the width, in thousandths of an em, the dictionary gives its code.
|
|
25
|
+
* @returns The character each numbered glyph name draws, or `undefined` when the font is not taken for a core face.
|
|
26
|
+
*/
|
|
27
|
+
declare function coreFaceTextsOfNumberedGlyphs(fontName: string, glyphs: readonly {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly width: number;
|
|
30
|
+
}[]): ReadonlyMap<string, string> | undefined;
|
|
9
31
|
//#endregion
|
|
10
|
-
export { coreFaceGlyphOrder };
|
|
32
|
+
export { coreFaceGlyphOrder, coreFaceNameOfFontName, coreFaceTextsOfNumberedGlyphs, withoutTrailingNumber };
|
|
@@ -57,5 +57,99 @@ function coreFaceGlyphOrder(postScriptName, unitsPerEm) {
|
|
|
57
57
|
if (face?.unitsPerEm !== unitsPerEm) return;
|
|
58
58
|
return (glyphId, advanceWidth) => face.advances[glyphId] === advanceWidth ? face.texts[glyphId] : void 0;
|
|
59
59
|
}
|
|
60
|
+
/** The PostScript names of the core faces, by the family and style a font name spells with its punctuation, subset tag and Distiller size suffix taken off. */
|
|
61
|
+
const CORE_FACE_BY_SPELLING = /* @__PURE__ */ new Map([
|
|
62
|
+
["arial", "ArialMT"],
|
|
63
|
+
["arialmt", "ArialMT"],
|
|
64
|
+
["arialbold", "Arial-BoldMT"],
|
|
65
|
+
["arialboldmt", "Arial-BoldMT"],
|
|
66
|
+
["arialitalic", "Arial-ItalicMT"],
|
|
67
|
+
["arialitalicmt", "Arial-ItalicMT"],
|
|
68
|
+
["arialbolditalic", "Arial-BoldItalicMT"],
|
|
69
|
+
["arialbolditalicmt", "Arial-BoldItalicMT"],
|
|
70
|
+
["timesnewroman", "TimesNewRomanPSMT"],
|
|
71
|
+
["timesnewromanpsmt", "TimesNewRomanPSMT"],
|
|
72
|
+
["timesnewromanbold", "TimesNewRomanPS-BoldMT"],
|
|
73
|
+
["timesnewromanpsboldmt", "TimesNewRomanPS-BoldMT"],
|
|
74
|
+
["timesnewromanitalic", "TimesNewRomanPS-ItalicMT"],
|
|
75
|
+
["timesnewromanpsitalicmt", "TimesNewRomanPS-ItalicMT"],
|
|
76
|
+
["timesnewromanbolditalic", "TimesNewRomanPS-BoldItalicMT"],
|
|
77
|
+
["timesnewromanpsbolditalicmt", "TimesNewRomanPS-BoldItalicMT"],
|
|
78
|
+
["couriernew", "CourierNewPSMT"],
|
|
79
|
+
["couriernewpsmt", "CourierNewPSMT"],
|
|
80
|
+
["couriernewbold", "CourierNewPS-BoldMT"],
|
|
81
|
+
["couriernewpsboldmt", "CourierNewPS-BoldMT"],
|
|
82
|
+
["couriernewitalic", "CourierNewPS-ItalicMT"],
|
|
83
|
+
["couriernewpsitalicmt", "CourierNewPS-ItalicMT"],
|
|
84
|
+
["couriernewbolditalic", "CourierNewPS-BoldItalicMT"],
|
|
85
|
+
["couriernewpsbolditalicmt", "CourierNewPS-BoldItalicMT"]
|
|
86
|
+
]);
|
|
87
|
+
const ZERO_CODE = 48;
|
|
88
|
+
const NINE_CODE = 57;
|
|
89
|
+
const FULL_STOP_CODE = 46;
|
|
90
|
+
function isDigitAt(name, index) {
|
|
91
|
+
const code = name.charCodeAt(index);
|
|
92
|
+
return code >= ZERO_CODE && code <= NINE_CODE;
|
|
93
|
+
}
|
|
94
|
+
function digitRunStart(name, end) {
|
|
95
|
+
let start = end;
|
|
96
|
+
while (start > 0 && isDigitAt(name, start - 1)) start -= 1;
|
|
97
|
+
return start;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* `name` without a trailing size or version number a producer appends to a font name ("Arial.Bold0133", "Arial086.75"): digits, optionally a fraction. Found by one scan back from the end, so the work is the length of the number and not of the name: the end-anchored pattern this replaces (a digit run followed by an optional fraction, then the end) is retried from every offset of a long digit run that a letter follows, which takes time quadratic in its length.
|
|
101
|
+
* @param name - A `/BaseFont` name.
|
|
102
|
+
* @returns The name with its trailing number cut off, or the name itself when it does not end in one.
|
|
103
|
+
*/
|
|
104
|
+
function withoutTrailingNumber(name) {
|
|
105
|
+
const fractionStart = digitRunStart(name, name.length);
|
|
106
|
+
if (fractionStart === name.length) return name;
|
|
107
|
+
const point = fractionStart - 1;
|
|
108
|
+
if (point > 0 && name.charCodeAt(point) === FULL_STOP_CODE) {
|
|
109
|
+
const wholeStart = digitRunStart(name, point);
|
|
110
|
+
if (wholeStart < point) return name.slice(0, wholeStart);
|
|
111
|
+
}
|
|
112
|
+
return name.slice(0, fractionStart);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The PostScript name of the core face a font name spells, or `undefined` when it spells none: `TimesNewRoman` and `Times.New.Roman.Bold092` are `TimesNewRomanPSMT` and `TimesNewRomanPS-BoldMT`. A spelling is only a candidate: the glyph order is accepted per glyph, against the font dictionary's own widths (`coreFaceTextOfNumberedGlyph`).
|
|
116
|
+
* @param fontName - A `/BaseFont` name, with or without its subset tag.
|
|
117
|
+
* @returns The face's PostScript name, or `undefined`.
|
|
118
|
+
*/
|
|
119
|
+
function coreFaceNameOfFontName(fontName) {
|
|
120
|
+
const spelled = withoutTrailingNumber(fontName.replace(/^[A-Z]{6}\+/u, "")).replace(/[^A-Za-z]/gu, "").toLowerCase();
|
|
121
|
+
return CORE_FACE_BY_SPELLING.get(spelled);
|
|
122
|
+
}
|
|
123
|
+
const NUMBERED_GLYPH_NAME = /^G(\d+)$/u;
|
|
124
|
+
const WIDTH_AGREEMENT_TOLERANCE = 25;
|
|
125
|
+
const THOUSANDTHS_PER_EM = 1e3;
|
|
126
|
+
const MIN_AGREEING_SHARE = .9;
|
|
127
|
+
const MIN_NUMBERED_GLYPHS = 5;
|
|
128
|
+
/**
|
|
129
|
+
* What a core face draws at the glyphs of a CFF subset that names each `G<id>`, accepted for the whole font only when the font dictionary's widths agree with the face's advances over at least `MIN_NUMBERED_GLYPHS` glyphs and `MIN_AGREEING_SHARE` of them, so a subset of any other face, or of another release of this one, is read as before.
|
|
130
|
+
* @param fontName - The font's `/BaseFont`.
|
|
131
|
+
* @param glyphs - Each glyph of the font a `/Differences` array names, with the width, in thousandths of an em, the dictionary gives its code.
|
|
132
|
+
* @returns The character each numbered glyph name draws, or `undefined` when the font is not taken for a core face.
|
|
133
|
+
*/
|
|
134
|
+
function coreFaceTextsOfNumberedGlyphs(fontName, glyphs) {
|
|
135
|
+
const faceName = coreFaceNameOfFontName(fontName);
|
|
136
|
+
const face = faceName === void 0 ? void 0 : readFaces().get(faceName);
|
|
137
|
+
if (face === void 0) return;
|
|
138
|
+
const texts = /* @__PURE__ */ new Map();
|
|
139
|
+
let numbered = 0;
|
|
140
|
+
let agreeing = 0;
|
|
141
|
+
for (const { name, width } of glyphs) {
|
|
142
|
+
const id = NUMBERED_GLYPH_NAME.exec(name)?.[1];
|
|
143
|
+
const advance = id === void 0 ? void 0 : face.advances[Number.parseInt(id, 10)];
|
|
144
|
+
if (id === void 0 || advance === void 0) continue;
|
|
145
|
+
numbered++;
|
|
146
|
+
const text = face.texts[Number.parseInt(id, 10)];
|
|
147
|
+
if (Math.abs(advance * THOUSANDTHS_PER_EM / face.unitsPerEm - width) <= WIDTH_AGREEMENT_TOLERANCE) {
|
|
148
|
+
agreeing++;
|
|
149
|
+
if (text !== void 0) texts.set(name, text);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return numbered >= MIN_NUMBERED_GLYPHS && agreeing >= numbered * MIN_AGREEING_SHARE ? texts : void 0;
|
|
153
|
+
}
|
|
60
154
|
//#endregion
|
|
61
|
-
export { coreFaceGlyphOrder };
|
|
155
|
+
export { coreFaceGlyphOrder, coreFaceNameOfFontName, coreFaceTextsOfNumberedGlyphs, withoutTrailingNumber };
|
package/dist/font-read.cjs
CHANGED
|
@@ -3,6 +3,7 @@ const require_encoding = require("./encoding.cjs");
|
|
|
3
3
|
const require_afm_widths = require("./afm-widths.cjs");
|
|
4
4
|
const require_objects = require("./objects.cjs");
|
|
5
5
|
const require_filters = require("./filters.cjs");
|
|
6
|
+
const require_core_font_glyph_orders = require("./core-font-glyph-orders.cjs");
|
|
6
7
|
const require_font_style = require("./font-style.cjs");
|
|
7
8
|
const require_glyph_names = require("./glyph-names.cjs");
|
|
8
9
|
const require_builtin_encoding = require("./builtin-encoding.cjs");
|
|
@@ -125,9 +126,19 @@ function buildSimpleFont(fontDict, context) {
|
|
|
125
126
|
});
|
|
126
127
|
const differencesMap = readDifferencesMap(encodingDict !== void 0 ? resolvedArray(require_objects.dictGet(encodingDict, "Differences"), context) : void 0);
|
|
127
128
|
const programEncoding = lazyFontProgram(descriptor, baseFont, context);
|
|
129
|
+
let numberedTexts;
|
|
130
|
+
const numberedCoreFaceTexts = () => {
|
|
131
|
+
numberedTexts ??= require_core_font_glyph_orders.coreFaceTextsOfNumberedGlyphs(baseFont, [...differencesMap].map(([code, name]) => ({
|
|
132
|
+
name,
|
|
133
|
+
width: widthOf(code)
|
|
134
|
+
}))) ?? /* @__PURE__ */ new Map();
|
|
135
|
+
return numberedTexts;
|
|
136
|
+
};
|
|
128
137
|
const unicodeForDifferencesName = (name) => {
|
|
129
138
|
const named = require_glyph_names.glyphNameToUnicode(name);
|
|
130
139
|
if (named !== void 0) return named;
|
|
140
|
+
const ofCoreFace = numberedCoreFaceTexts().get(name);
|
|
141
|
+
if (ofCoreFace !== void 0) return ofCoreFace;
|
|
131
142
|
const program = programEncoding();
|
|
132
143
|
const glyphId = program?.glyphIdOfName(name);
|
|
133
144
|
return glyphId === void 0 ? void 0 : program?.glyphIdToUnicode(glyphId);
|
package/dist/font-read.js
CHANGED
|
@@ -2,6 +2,7 @@ import { namedEncodingGlyphName, winAnsiGlyphName } from "./encoding.js";
|
|
|
2
2
|
import { afmWidthOfCode } from "./afm-widths.js";
|
|
3
3
|
import { asArray, asName, asNumber, dictGet, isName } from "./objects.js";
|
|
4
4
|
import { decodeStream } from "./filters.js";
|
|
5
|
+
import { coreFaceTextsOfNumberedGlyphs } from "./core-font-glyph-orders.js";
|
|
5
6
|
import { styleFromBaseFontName } from "./font-style.js";
|
|
6
7
|
import { glyphNameToUnicode } from "./glyph-names.js";
|
|
7
8
|
import { readFontProgramEncoding } from "./builtin-encoding.js";
|
|
@@ -124,9 +125,19 @@ function buildSimpleFont(fontDict, context) {
|
|
|
124
125
|
});
|
|
125
126
|
const differencesMap = readDifferencesMap(encodingDict !== void 0 ? resolvedArray(dictGet(encodingDict, "Differences"), context) : void 0);
|
|
126
127
|
const programEncoding = lazyFontProgram(descriptor, baseFont, context);
|
|
128
|
+
let numberedTexts;
|
|
129
|
+
const numberedCoreFaceTexts = () => {
|
|
130
|
+
numberedTexts ??= coreFaceTextsOfNumberedGlyphs(baseFont, [...differencesMap].map(([code, name]) => ({
|
|
131
|
+
name,
|
|
132
|
+
width: widthOf(code)
|
|
133
|
+
}))) ?? /* @__PURE__ */ new Map();
|
|
134
|
+
return numberedTexts;
|
|
135
|
+
};
|
|
127
136
|
const unicodeForDifferencesName = (name) => {
|
|
128
137
|
const named = glyphNameToUnicode(name);
|
|
129
138
|
if (named !== void 0) return named;
|
|
139
|
+
const ofCoreFace = numberedCoreFaceTexts().get(name);
|
|
140
|
+
if (ofCoreFace !== void 0) return ofCoreFace;
|
|
130
141
|
const program = programEncoding();
|
|
131
142
|
const glyphId = program?.glyphIdOfName(name);
|
|
132
143
|
return glyphId === void 0 ? void 0 : program?.glyphIdToUnicode(glyphId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-codec",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.2",
|
|
4
4
|
"description": "Hand-written, dependency-minimal PDF codec: parses arbitrary real-world PDFs and generates new ones, built on its own codec-owned LayoutDocument item model and Zod 4 codecs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|