@cantoo/fontkit 2.0.5 → 2.0.6
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/dist/browser-module.mjs +4423 -1117
- package/dist/browser-module.mjs.map +1 -1
- package/dist/browser.cjs +4424 -1117
- package/dist/browser.cjs.map +1 -1
- package/dist/main.cjs +4424 -1117
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +4423 -1117
- package/dist/module.mjs.map +1 -1
- package/package.json +3 -1
- package/src/CmapProcessor.js +59 -20
- package/src/DFont.js +114 -14
- package/src/TTFFont.js +259 -57
- package/src/TrueTypeCollection.js +54 -10
- package/src/WOFF2Font.js +128 -16
- package/src/WOFFFont.js +45 -8
- package/src/aat/AATFeatureMap.js +62 -18
- package/src/aat/AATLayoutEngine.js +33 -1
- package/src/aat/AATLookupTable.js +22 -4
- package/src/aat/AATMorxProcessor.js +189 -52
- package/src/aat/AATStateMachine.js +32 -0
- package/src/base.js +19 -0
- package/src/cff/CFFCharsets.js +3 -0
- package/src/cff/CFFDict.js +79 -9
- package/src/cff/CFFEncodings.js +2 -0
- package/src/cff/CFFFont.js +119 -23
- package/src/cff/CFFIndex.js +62 -5
- package/src/cff/CFFOperand.js +52 -17
- package/src/cff/CFFPointer.js +41 -2
- package/src/cff/CFFPrivateDict.js +14 -4
- package/src/cff/CFFStandardStrings.js +1 -0
- package/src/cff/CFFTop.js +103 -10
- package/src/decorators.js +32 -13
- package/src/encodings.js +22 -5
- package/src/fs.js +10 -0
- package/src/glyph/BBox.js +14 -0
- package/src/glyph/CFFGlyph.js +290 -148
- package/src/glyph/COLRGlyph.js +38 -3
- package/src/glyph/Glyph.js +68 -9
- package/src/glyph/GlyphVariationProcessor.js +135 -34
- package/src/glyph/Path.js +107 -20
- package/src/glyph/SBIXGlyph.js +57 -8
- package/src/glyph/StandardNames.js +1 -0
- package/src/glyph/TTFGlyph.js +176 -29
- package/src/glyph/TTFGlyphEncoder.js +32 -0
- package/src/glyph/WOFF2Glyph.js +23 -4
- package/src/layout/GlyphPosition.js +6 -0
- package/src/layout/GlyphRun.js +25 -7
- package/src/layout/KernProcessor.js +62 -3
- package/src/layout/LayoutEngine.js +86 -12
- package/src/layout/Script.js +27 -1
- package/src/layout/UnicodeLayoutEngine.js +29 -4
- package/src/opentype/GPOSProcessor.js +156 -45
- package/src/opentype/GSUBProcessor.js +92 -33
- package/src/opentype/GlyphInfo.js +52 -4
- package/src/opentype/GlyphIterator.js +51 -2
- package/src/opentype/OTLayoutEngine.js +66 -8
- package/src/opentype/OTProcessor.js +283 -70
- package/src/opentype/ShapingPlan.js +51 -3
- package/src/opentype/shapers/ArabicShaper.js +19 -0
- package/src/opentype/shapers/DefaultShaper.js +51 -7
- package/src/opentype/shapers/HangulShaper.js +85 -7
- package/src/opentype/shapers/HebrewShaper.js +45 -11
- package/src/opentype/shapers/IndicShaper.js +141 -26
- package/src/opentype/shapers/ThaiShaper.js +35 -4
- package/src/opentype/shapers/UniversalShaper.js +83 -17
- package/src/opentype/shapers/index.js +15 -2
- package/src/packages/unicode-properties/data-trie.js +1 -0
- package/src/packages/unicode-properties/data.js +12 -0
- package/src/packages/unicode-properties/dist/index.cjs +22 -9
- package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
- package/src/packages/unicode-properties/dist/index.mjs +21 -9
- package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
- package/src/packages/unicode-properties/index.js +65 -0
- package/src/packages/unicode-properties/package.json +1 -1
- package/src/packages/unicode-trie/builder.js +128 -7
- package/src/packages/unicode-trie/dist/builder.cjs +129 -16
- package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/builder.mjs +128 -16
- package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
- package/src/packages/unicode-trie/dist/index.cjs +22 -9
- package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/index.mjs +21 -9
- package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
- package/src/packages/unicode-trie/index.js +62 -10
- package/src/packages/unicode-trie/package.json +1 -1
- package/src/packages/unicode-trie/swap.js +13 -0
- package/src/subset/CFFSubset.js +112 -22
- package/src/subset/Subset.js +21 -1
- package/src/subset/TTFSubset.js +79 -36
- package/src/tables/BASE.js +1 -0
- package/src/tables/COLR.js +1 -0
- package/src/tables/CPAL.js +1 -0
- package/src/tables/DSIG.js +1 -0
- package/src/tables/EBDT.js +10 -3
- package/src/tables/EBLC.js +67 -7
- package/src/tables/GDEF.js +1 -0
- package/src/tables/GPOS.js +79 -16
- package/src/tables/GSUB.js +14 -3
- package/src/tables/HVAR.js +51 -5
- package/src/tables/JSTF.js +3 -2
- package/src/tables/LTSH.js +1 -0
- package/src/tables/OS2.js +1 -0
- package/src/tables/PCLT.js +1 -0
- package/src/tables/STAT.js +46 -6
- package/src/tables/VDMX.js +1 -0
- package/src/tables/VORG.js +1 -0
- package/src/tables/WOFF2Directory.js +56 -8
- package/src/tables/WOFFDirectory.js +14 -2
- package/src/tables/aat.js +160 -35
- package/src/tables/avar.js +1 -0
- package/src/tables/bsln.js +3 -2
- package/src/tables/cmap.js +26 -6
- package/src/tables/cvt.js +1 -0
- package/src/tables/directory.js +44 -16
- package/src/tables/feat.js +24 -2
- package/src/tables/fpgm.js +1 -0
- package/src/tables/fvar.js +32 -4
- package/src/tables/gasp.js +1 -0
- package/src/tables/glyf.js +1 -0
- package/src/tables/gvar.js +29 -2
- package/src/tables/hdmx.js +12 -1
- package/src/tables/head.js +1 -0
- package/src/tables/hhea.js +1 -0
- package/src/tables/hmtx.js +18 -2
- package/src/tables/index.js +1 -0
- package/src/tables/just.js +15 -4
- package/src/tables/kern.js +43 -6
- package/src/tables/loca.js +21 -8
- package/src/tables/maxp.js +1 -0
- package/src/tables/morx.js +14 -7
- package/src/tables/name.js +104 -23
- package/src/tables/opbd.js +2 -1
- package/src/tables/opentype.js +39 -4
- package/src/tables/post.js +8 -1
- package/src/tables/prep.js +1 -0
- package/src/tables/sbix.js +12 -1
- package/src/tables/variations.js +23 -3
- package/src/tables/vhea.js +1 -0
- package/src/tables/vmtx.js +18 -2
- package/src/utils.js +15 -0
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import { inflateSync } from 'fflate';
|
|
2
2
|
import { swap32LE } from './swap.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Pre-parsed trie payload (e.g. from UnicodeTrieBuilder.freeze).
|
|
6
|
+
* @typedef {object} UnicodeTrieInit
|
|
7
|
+
* @property {Int32Array | Uint32Array} data
|
|
8
|
+
* @property {number} highStart
|
|
9
|
+
* @property {number} errorValue
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Duck-type Node Buffer (has LE/BE readers + slice), matching prior runtime checks.
|
|
14
|
+
* @param {unknown} value
|
|
15
|
+
* @returns {value is Buffer}
|
|
16
|
+
*/
|
|
17
|
+
function isNodeBuffer(value) {
|
|
18
|
+
return (
|
|
19
|
+
typeof value === 'object'
|
|
20
|
+
&& value !== null
|
|
21
|
+
&& 'readUInt32BE' in value
|
|
22
|
+
&& 'readUInt32LE' in value
|
|
23
|
+
&& 'slice' in value
|
|
24
|
+
&& typeof value.readUInt32BE === 'function'
|
|
25
|
+
&& typeof value.readUInt32LE === 'function'
|
|
26
|
+
&& typeof value.slice === 'function'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
4
30
|
// Shift size for getting the index-1 table offset.
|
|
5
31
|
const SHIFT_1 = 6 + 5;
|
|
6
32
|
|
|
@@ -64,38 +90,64 @@ const INDEX_1_OFFSET = UTF8_2B_INDEX_2_OFFSET + UTF8_2B_INDEX_2_LENGTH;
|
|
|
64
90
|
const DATA_GRANULARITY = 1 << INDEX_SHIFT;
|
|
65
91
|
|
|
66
92
|
class UnicodeTrie {
|
|
93
|
+
/**
|
|
94
|
+
* @param {Uint8Array | Buffer | UnicodeTrieInit} data
|
|
95
|
+
*/
|
|
67
96
|
constructor(data) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (isBuffer || data instanceof Uint8Array) {
|
|
97
|
+
if (isNodeBuffer(data) || data instanceof Uint8Array) {
|
|
71
98
|
// read binary format
|
|
99
|
+
/** @type {Uint8Array} */
|
|
100
|
+
let bytes;
|
|
72
101
|
let uncompressedLength;
|
|
73
|
-
if (
|
|
102
|
+
if (isNodeBuffer(data)) {
|
|
74
103
|
this.highStart = data.readUInt32LE(0);
|
|
75
104
|
this.errorValue = data.readUInt32LE(4);
|
|
76
105
|
uncompressedLength = data.readUInt32LE(8);
|
|
77
|
-
|
|
106
|
+
// View past the 12-byte header (avoids Buffer.slice → Uint8Array assignability issues).
|
|
107
|
+
bytes = new Uint8Array(data.buffer, data.byteOffset + 12, data.byteLength - 12);
|
|
78
108
|
} else {
|
|
79
109
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
80
110
|
this.highStart = view.getUint32(0, true);
|
|
81
111
|
this.errorValue = view.getUint32(4, true);
|
|
82
112
|
uncompressedLength = view.getUint32(8, true);
|
|
83
|
-
|
|
113
|
+
bytes = data.subarray(12);
|
|
84
114
|
}
|
|
85
115
|
|
|
86
116
|
// inflate the actual trie data (raw deflate, matching builder)
|
|
87
|
-
|
|
117
|
+
bytes = inflateSync(bytes, { out: new Uint8Array(uncompressedLength) });
|
|
88
118
|
|
|
89
119
|
// swap bytes from little-endian
|
|
90
|
-
swap32LE(
|
|
120
|
+
swap32LE(bytes);
|
|
91
121
|
|
|
92
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Compacted index + data table.
|
|
124
|
+
* @type {Uint32Array | Int32Array}
|
|
125
|
+
*/
|
|
126
|
+
this.data = new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength >> 2);
|
|
93
127
|
} else {
|
|
94
128
|
// pre-parsed data
|
|
95
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Compacted index + data table.
|
|
131
|
+
* @type {Uint32Array | Int32Array}
|
|
132
|
+
*/
|
|
133
|
+
this.data = data.data;
|
|
134
|
+
/**
|
|
135
|
+
* First code point of the final single-value range.
|
|
136
|
+
* @type {number}
|
|
137
|
+
*/
|
|
138
|
+
this.highStart = data.highStart;
|
|
139
|
+
/**
|
|
140
|
+
* Value returned for out-of-range code points.
|
|
141
|
+
* @type {number}
|
|
142
|
+
*/
|
|
143
|
+
this.errorValue = data.errorValue;
|
|
96
144
|
}
|
|
97
145
|
}
|
|
98
146
|
|
|
147
|
+
/**
|
|
148
|
+
* @param {number} codePoint
|
|
149
|
+
* @returns {number}
|
|
150
|
+
*/
|
|
99
151
|
get(codePoint) {
|
|
100
152
|
let index;
|
|
101
153
|
if ((codePoint < 0) || (codePoint > 0x10ffff)) {
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
const isBigEndian = (new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12);
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @param {Uint8Array} b
|
|
5
|
+
* @param {number} n
|
|
6
|
+
* @param {number} m
|
|
7
|
+
*/
|
|
3
8
|
const swap = (b, n, m) => {
|
|
4
9
|
let i = b[n];
|
|
5
10
|
b[n] = b[m];
|
|
6
11
|
b[m] = i;
|
|
7
12
|
};
|
|
8
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @param {Uint8Array} array
|
|
16
|
+
*/
|
|
9
17
|
const swap32 = (array) => {
|
|
10
18
|
const len = array.length;
|
|
11
19
|
for (let i = 0; i < len; i += 4) {
|
|
@@ -14,6 +22,11 @@ const swap32 = (array) => {
|
|
|
14
22
|
}
|
|
15
23
|
};
|
|
16
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Swap byte order of each 32-bit word in-place when running on a big-endian host,
|
|
27
|
+
* so the trie data is little-endian as expected by the runtime.
|
|
28
|
+
* @param {Uint8Array} array
|
|
29
|
+
*/
|
|
17
30
|
export const swap32LE = (array) => {
|
|
18
31
|
if (isBigEndian) {
|
|
19
32
|
swap32(array);
|
package/src/subset/CFFSubset.js
CHANGED
|
@@ -2,39 +2,84 @@ import Subset from './Subset';
|
|
|
2
2
|
import CFFTop from '../cff/CFFTop';
|
|
3
3
|
import standardStrings from '../cff/CFFStandardStrings';
|
|
4
4
|
|
|
5
|
+
/** @typedef {import('../../types/fontkit').SubsetFont} SubsetFont */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').CFFFontLike} CFFFontLike */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').CFFTopDict} CFFTopDict */
|
|
8
|
+
/** @typedef {import('../../types/fontkit').CFFCharString} CFFCharString */
|
|
9
|
+
/** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
|
|
10
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
11
|
+
|
|
5
12
|
export default class CFFSubset extends Subset {
|
|
13
|
+
/** @type {CFFFontLike} */
|
|
14
|
+
cff;
|
|
15
|
+
/** @type {BinaryBuffer[] | null} */
|
|
16
|
+
charstrings = null;
|
|
17
|
+
/** @type {BinaryBuffer[] | null} */
|
|
18
|
+
gsubrs = null;
|
|
19
|
+
/** @type {string[] | null} */
|
|
20
|
+
strings = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {SubsetFont} font
|
|
24
|
+
*/
|
|
6
25
|
constructor(font) {
|
|
7
26
|
super(font);
|
|
8
27
|
|
|
9
|
-
|
|
10
|
-
if (!
|
|
28
|
+
let cff = this.font['CFF '];
|
|
29
|
+
if (!cff) {
|
|
11
30
|
throw new Error('Not a CFF Font');
|
|
12
31
|
}
|
|
32
|
+
this.cff = cff;
|
|
13
33
|
}
|
|
14
34
|
|
|
15
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} [_tag]
|
|
37
|
+
* @returns {never}
|
|
38
|
+
*/
|
|
39
|
+
includeTable(_tag) {
|
|
16
40
|
throw new Error('includeTable is only supported for TrueType (glyf) subsets, not CFF.');
|
|
17
41
|
}
|
|
18
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
19
46
|
subsetCharstrings() {
|
|
20
47
|
this.charstrings = [];
|
|
48
|
+
/** @type {Record<string, boolean>} */
|
|
21
49
|
let gsubrs = {};
|
|
22
50
|
|
|
23
51
|
for (let gid of this.glyphs) {
|
|
52
|
+
if (!this.cff.getCharString) {
|
|
53
|
+
throw new Error('CFF font missing getCharString()');
|
|
54
|
+
}
|
|
24
55
|
this.charstrings.push(this.cff.getCharString(gid));
|
|
25
56
|
|
|
26
57
|
let glyph = this.font.getGlyph(gid);
|
|
58
|
+
if (!glyph) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
27
61
|
void glyph.path; // this causes the glyph to be parsed
|
|
28
62
|
|
|
29
|
-
|
|
30
|
-
|
|
63
|
+
if (glyph._usedGsubrs) {
|
|
64
|
+
for (let subr in glyph._usedGsubrs) {
|
|
65
|
+
gsubrs[subr] = true;
|
|
66
|
+
}
|
|
31
67
|
}
|
|
32
68
|
}
|
|
33
69
|
|
|
34
|
-
this.gsubrs = this.subsetSubrs(
|
|
70
|
+
this.gsubrs = this.subsetSubrs(
|
|
71
|
+
/** @type {Array<{ offset: number, length: number }>} */ (this.cff.globalSubrIndex || []),
|
|
72
|
+
gsubrs
|
|
73
|
+
);
|
|
35
74
|
}
|
|
36
75
|
|
|
76
|
+
/**
|
|
77
|
+
* @param {Array<{ offset: number, length: number }>} subrs
|
|
78
|
+
* @param {Record<string | number, boolean>} used
|
|
79
|
+
* @returns {BinaryBuffer[]}
|
|
80
|
+
*/
|
|
37
81
|
subsetSubrs(subrs, used) {
|
|
82
|
+
/** @type {BinaryBuffer[]} */
|
|
38
83
|
let res = [];
|
|
39
84
|
for (let i = 0; i < subrs.length; i++) {
|
|
40
85
|
let subr = subrs[i];
|
|
@@ -49,6 +94,10 @@ export default class CFFSubset extends Subset {
|
|
|
49
94
|
return res;
|
|
50
95
|
}
|
|
51
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @param {CFFTopDict} topDict
|
|
99
|
+
* @returns {void}
|
|
100
|
+
*/
|
|
52
101
|
subsetFontdict(topDict) {
|
|
53
102
|
topDict.FDArray = [];
|
|
54
103
|
topDict.FDSelect = {
|
|
@@ -56,28 +105,42 @@ export default class CFFSubset extends Subset {
|
|
|
56
105
|
fds: []
|
|
57
106
|
};
|
|
58
107
|
|
|
108
|
+
/** @type {Record<number, boolean>} */
|
|
59
109
|
let used_fds = {};
|
|
110
|
+
/** @type {Array<Record<string, boolean>>} */
|
|
60
111
|
let used_subrs = [];
|
|
112
|
+
/** @type {Record<number, number>} */
|
|
61
113
|
let fd_select = {};
|
|
62
114
|
for (let gid of this.glyphs) {
|
|
115
|
+
if (!this.cff.fdForGlyph) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
63
118
|
let fd = this.cff.fdForGlyph(gid);
|
|
64
119
|
if (fd == null) {
|
|
65
120
|
continue;
|
|
66
121
|
}
|
|
67
122
|
|
|
68
123
|
if (!used_fds[fd]) {
|
|
69
|
-
topDict.FDArray
|
|
124
|
+
let src = this.cff.topDict.FDArray && this.cff.topDict.FDArray[fd];
|
|
125
|
+
topDict.FDArray.push(Object.assign({}, src));
|
|
70
126
|
used_subrs.push({});
|
|
71
127
|
fd_select[fd] = topDict.FDArray.length - 1;
|
|
72
128
|
}
|
|
73
129
|
|
|
74
130
|
used_fds[fd] = true;
|
|
75
|
-
topDict.FDSelect.fds
|
|
131
|
+
if (topDict.FDSelect.fds) {
|
|
132
|
+
topDict.FDSelect.fds.push(fd_select[fd]);
|
|
133
|
+
}
|
|
76
134
|
|
|
77
135
|
let glyph = this.font.getGlyph(gid);
|
|
136
|
+
if (!glyph) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
78
139
|
void glyph.path; // this causes the glyph to be parsed
|
|
79
|
-
|
|
80
|
-
|
|
140
|
+
if (glyph._usedSubrs) {
|
|
141
|
+
for (let subr in glyph._usedSubrs) {
|
|
142
|
+
used_subrs[fd_select[fd]][subr] = true;
|
|
143
|
+
}
|
|
81
144
|
}
|
|
82
145
|
}
|
|
83
146
|
|
|
@@ -86,38 +149,59 @@ export default class CFFSubset extends Subset {
|
|
|
86
149
|
delete dict.FontName;
|
|
87
150
|
if (dict.Private && dict.Private.Subrs) {
|
|
88
151
|
dict.Private = Object.assign({}, dict.Private);
|
|
89
|
-
dict.Private.Subrs = this.subsetSubrs(
|
|
152
|
+
dict.Private.Subrs = this.subsetSubrs(
|
|
153
|
+
/** @type {Array<{ offset: number, length: number }>} */ (dict.Private.Subrs),
|
|
154
|
+
used_subrs[i]
|
|
155
|
+
);
|
|
90
156
|
}
|
|
91
157
|
}
|
|
92
158
|
|
|
93
159
|
return;
|
|
94
160
|
}
|
|
95
161
|
|
|
162
|
+
/**
|
|
163
|
+
* @param {CFFTopDict} topDict
|
|
164
|
+
* @returns {StructValue}
|
|
165
|
+
*/
|
|
96
166
|
createCIDFontdict(topDict) {
|
|
167
|
+
/** @type {Record<string, boolean>} */
|
|
97
168
|
let used_subrs = {};
|
|
98
169
|
for (let gid of this.glyphs) {
|
|
99
170
|
let glyph = this.font.getGlyph(gid);
|
|
171
|
+
if (!glyph) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
100
174
|
void glyph.path; // this causes the glyph to be parsed
|
|
101
175
|
|
|
102
|
-
|
|
103
|
-
|
|
176
|
+
if (glyph._usedSubrs) {
|
|
177
|
+
for (let subr in glyph._usedSubrs) {
|
|
178
|
+
used_subrs[subr] = true;
|
|
179
|
+
}
|
|
104
180
|
}
|
|
105
181
|
}
|
|
106
182
|
|
|
107
183
|
let privateDict = Object.assign({}, this.cff.topDict.Private);
|
|
108
184
|
if (this.cff.topDict.Private && this.cff.topDict.Private.Subrs) {
|
|
109
|
-
privateDict.Subrs = this.subsetSubrs(
|
|
185
|
+
privateDict.Subrs = this.subsetSubrs(
|
|
186
|
+
/** @type {Array<{ offset: number, length: number }>} */ (this.cff.topDict.Private.Subrs),
|
|
187
|
+
used_subrs
|
|
188
|
+
);
|
|
110
189
|
}
|
|
111
190
|
|
|
112
191
|
topDict.FDArray = [{ Private: privateDict }];
|
|
192
|
+
let charstrings = this.charstrings || [];
|
|
113
193
|
return topDict.FDSelect = {
|
|
114
194
|
version: 3,
|
|
115
195
|
nRanges: 1,
|
|
116
196
|
ranges: [{ first: 0, fd: 0 }],
|
|
117
|
-
sentinel:
|
|
197
|
+
sentinel: charstrings.length
|
|
118
198
|
};
|
|
119
199
|
}
|
|
120
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @param {string | null | undefined} string
|
|
203
|
+
* @returns {number | null}
|
|
204
|
+
*/
|
|
121
205
|
addString(string) {
|
|
122
206
|
if (!string) {
|
|
123
207
|
return null;
|
|
@@ -131,28 +215,34 @@ export default class CFFSubset extends Subset {
|
|
|
131
215
|
return standardStrings.length + this.strings.length - 1;
|
|
132
216
|
}
|
|
133
217
|
|
|
218
|
+
/**
|
|
219
|
+
* @returns {import('restructure').BinaryBuffer}
|
|
220
|
+
*/
|
|
134
221
|
encode() {
|
|
135
222
|
this.subsetCharstrings();
|
|
136
223
|
|
|
224
|
+
let charstrings = this.charstrings || [];
|
|
225
|
+
|
|
137
226
|
let charset = {
|
|
138
|
-
version:
|
|
139
|
-
ranges:
|
|
140
|
-
? [{ first: 1, nLeft:
|
|
227
|
+
version: charstrings.length > 255 ? 2 : 1,
|
|
228
|
+
ranges: charstrings.length > 1
|
|
229
|
+
? [{ first: 1, nLeft: charstrings.length - 2 }]
|
|
141
230
|
: []
|
|
142
231
|
};
|
|
143
232
|
|
|
144
|
-
let topDict = Object.assign({}, this.cff.topDict);
|
|
233
|
+
let topDict = /** @type {CFFTopDict} */ (Object.assign({}, this.cff.topDict));
|
|
145
234
|
topDict.Private = null;
|
|
146
235
|
topDict.charset = charset;
|
|
147
236
|
topDict.Encoding = null;
|
|
148
|
-
topDict.CharStrings =
|
|
237
|
+
topDict.CharStrings = charstrings;
|
|
149
238
|
|
|
150
239
|
for (let key of ['version', 'Notice', 'Copyright', 'FullName', 'FamilyName', 'Weight', 'PostScript', 'BaseFontName', 'FontName']) {
|
|
151
|
-
|
|
240
|
+
let sid = /** @type {number | null | undefined} */ (topDict[key]);
|
|
241
|
+
topDict[key] = this.cff.string ? this.addString(this.cff.string(sid)) : null;
|
|
152
242
|
}
|
|
153
243
|
|
|
154
244
|
topDict.ROS = [this.addString('Adobe'), this.addString('Identity'), 0];
|
|
155
|
-
topDict.CIDCount =
|
|
245
|
+
topDict.CIDCount = charstrings.length;
|
|
156
246
|
|
|
157
247
|
if (this.cff.isCIDFont) {
|
|
158
248
|
this.subsetFontdict(topDict);
|
package/src/subset/Subset.js
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
import Tables from '../tables';
|
|
2
2
|
|
|
3
|
+
/** @typedef {import('../../types/fontkit').SubsetFont} SubsetFont */
|
|
4
|
+
/** @typedef {import('../../types/fontkit').SubsetGlyph} SubsetGlyph */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').TableCodec} TableCodec */
|
|
6
|
+
|
|
3
7
|
// Tables written by TTFSubset.encode; must not be overwritten via includeTable.
|
|
4
8
|
const MANAGED_TTF_TABLES = [
|
|
5
9
|
'head', 'hhea', 'loca', 'maxp', 'cvt ', 'prep', 'glyf', 'hmtx', 'fpgm'
|
|
6
10
|
];
|
|
7
11
|
|
|
8
12
|
export default class Subset {
|
|
13
|
+
/**
|
|
14
|
+
* @param {SubsetFont} font
|
|
15
|
+
*/
|
|
9
16
|
constructor(font) {
|
|
17
|
+
/** @type {SubsetFont} */
|
|
10
18
|
this.font = font;
|
|
19
|
+
/** @type {number[]} */
|
|
11
20
|
this.glyphs = [];
|
|
21
|
+
/** @type {Record<number, number>} */
|
|
12
22
|
this.mapping = {};
|
|
13
23
|
// Extra OT table tags to copy into a TTF subset (e.g. 'OS/2', 'name', 'post').
|
|
24
|
+
/** @type {string[]} */
|
|
14
25
|
this.includedTables = [];
|
|
15
26
|
|
|
16
27
|
// always include the missing glyph
|
|
17
28
|
this.includeGlyph(0);
|
|
18
29
|
}
|
|
19
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @param {number | SubsetGlyph} glyph
|
|
33
|
+
* @returns {number}
|
|
34
|
+
*/
|
|
20
35
|
includeGlyph(glyph) {
|
|
21
36
|
if (typeof glyph === 'object') {
|
|
22
37
|
glyph = glyph.id;
|
|
@@ -32,6 +47,10 @@ export default class Subset {
|
|
|
32
47
|
|
|
33
48
|
// Copy an OT table into the TTF subset (tag is a 4-byte string, e.g. 'OS/2').
|
|
34
49
|
// Only honored by TTFSubset.encode; CFFSubset overrides this to throw.
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} [tag]
|
|
52
|
+
* @returns {this}
|
|
53
|
+
*/
|
|
35
54
|
includeTable(tag) {
|
|
36
55
|
if (typeof tag !== 'string' || tag.length !== 4) {
|
|
37
56
|
throw new Error(
|
|
@@ -50,7 +69,8 @@ export default class Subset {
|
|
|
50
69
|
throw new Error(`Table "${tag}" is not present in this font.`);
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
|
|
72
|
+
let encoders = /** @type {Record<string, TableCodec | undefined>} */ (/** @type {unknown} */ (Tables));
|
|
73
|
+
if (!encoders[tag]) {
|
|
54
74
|
throw new Error(`Table "${tag}" cannot be encoded (no encoder registered in fontkit).`);
|
|
55
75
|
}
|
|
56
76
|
|
package/src/subset/TTFSubset.js
CHANGED
|
@@ -3,36 +3,88 @@ import Subset from './Subset';
|
|
|
3
3
|
import Directory from '../tables/directory';
|
|
4
4
|
import TTFGlyphEncoder from '../glyph/TTFGlyphEncoder';
|
|
5
5
|
|
|
6
|
+
/** @typedef {import('../../types/fontkit').SubsetFont} SubsetFont */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
|
|
8
|
+
/** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
|
|
9
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {{ advance: number, bearing: number }} HmtxMetric
|
|
13
|
+
*/
|
|
14
|
+
|
|
6
15
|
export default class TTFSubset extends Subset {
|
|
16
|
+
/** @type {TTFGlyphEncoder} */
|
|
17
|
+
glyphEncoder;
|
|
18
|
+
/** @type {Uint8Array[] | null} */
|
|
19
|
+
glyf = null;
|
|
20
|
+
/** @type {number} */
|
|
21
|
+
offset = 0;
|
|
22
|
+
/** @type {{ offsets: number[], version: number } | null} */
|
|
23
|
+
loca = null;
|
|
24
|
+
/** @type {{ metrics: HmtxMetric[], bearings: number[] } | null} */
|
|
25
|
+
hmtx = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {SubsetFont} font
|
|
29
|
+
*/
|
|
7
30
|
constructor(font) {
|
|
8
31
|
super(font);
|
|
9
32
|
this.glyphEncoder = new TTFGlyphEncoder();
|
|
10
33
|
}
|
|
11
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @param {number} gid
|
|
37
|
+
* @returns {number}
|
|
38
|
+
*/
|
|
12
39
|
_addGlyph(gid) {
|
|
13
40
|
let glyph = this.font.getGlyph(gid);
|
|
41
|
+
if (!glyph || !glyph._decode) {
|
|
42
|
+
throw new Error(`Cannot decode glyph ${gid}`);
|
|
43
|
+
}
|
|
14
44
|
let glyf = glyph._decode();
|
|
15
45
|
|
|
46
|
+
if (!this.font.loca) {
|
|
47
|
+
throw new Error('Font missing loca table');
|
|
48
|
+
}
|
|
49
|
+
|
|
16
50
|
// get the offset to the glyph from the loca table
|
|
17
51
|
let curOffset = this.font.loca.offsets[gid];
|
|
18
52
|
let nextOffset = this.font.loca.offsets[gid + 1];
|
|
19
53
|
|
|
20
54
|
let stream = this.font._getTableStream('glyf');
|
|
55
|
+
if (!stream) {
|
|
56
|
+
throw new Error('Font missing glyf table');
|
|
57
|
+
}
|
|
21
58
|
stream.pos += curOffset;
|
|
22
59
|
|
|
23
|
-
let
|
|
60
|
+
let raw = stream.readBuffer(nextOffset - curOffset);
|
|
61
|
+
/** @type {Uint8Array} */
|
|
62
|
+
let buffer = raw instanceof Uint8Array
|
|
63
|
+
? raw
|
|
64
|
+
: Uint8Array.from(/** @type {Iterable<number>} */ (/** @type {unknown} */ (raw)));
|
|
24
65
|
|
|
25
66
|
// if it is a compound glyph, include its components
|
|
26
67
|
if (glyf && glyf.numberOfContours < 0) {
|
|
27
68
|
buffer = new Uint8Array(buffer);
|
|
28
|
-
let
|
|
29
|
-
|
|
69
|
+
let ab = buffer.buffer instanceof ArrayBuffer ? buffer.buffer : new Uint8Array(buffer).buffer;
|
|
70
|
+
let view = new DataView(ab, buffer.byteOffset, buffer.byteLength);
|
|
71
|
+
for (let component of glyf.components || []) {
|
|
30
72
|
gid = this.includeGlyph(component.glyphID);
|
|
31
73
|
view.setUint16(component.pos, gid);
|
|
32
74
|
}
|
|
33
75
|
} else if (glyf && this.font._variationProcessor) {
|
|
34
76
|
// If this is a TrueType variation glyph, re-encode the path
|
|
35
|
-
|
|
77
|
+
let encoded = this.glyphEncoder.encodeSimple(
|
|
78
|
+
/** @type {import('../glyph/Path').default} */ (/** @type {unknown} */ (glyph.path)),
|
|
79
|
+
glyf.instructions
|
|
80
|
+
);
|
|
81
|
+
buffer = encoded instanceof Uint8Array
|
|
82
|
+
? encoded
|
|
83
|
+
: Uint8Array.from(/** @type {Iterable<number>} */ (/** @type {unknown} */ (encoded)));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!this.glyf || !this.loca || !this.hmtx) {
|
|
87
|
+
throw new Error('TTFSubset.encode must initialize glyf/loca/hmtx before _addGlyph');
|
|
36
88
|
}
|
|
37
89
|
|
|
38
90
|
this.glyf.push(buffer);
|
|
@@ -43,10 +95,13 @@ export default class TTFSubset extends Subset {
|
|
|
43
95
|
bearing: glyph._getMetrics().leftBearing
|
|
44
96
|
});
|
|
45
97
|
|
|
46
|
-
this.offset += buffer.
|
|
98
|
+
this.offset += buffer.byteLength;
|
|
47
99
|
return this.glyf.length - 1;
|
|
48
100
|
}
|
|
49
101
|
|
|
102
|
+
/**
|
|
103
|
+
* @returns {BinaryBuffer}
|
|
104
|
+
*/
|
|
50
105
|
encode() {
|
|
51
106
|
// tables required by PDF spec:
|
|
52
107
|
// head, hhea, loca, maxp, cvt , prep, glyf, hmtx, fpgm
|
|
@@ -54,16 +109,17 @@ export default class TTFSubset extends Subset {
|
|
|
54
109
|
// additional tables required for standalone fonts:
|
|
55
110
|
// name, cmap, OS/2, post
|
|
56
111
|
|
|
112
|
+
/** @type {Uint8Array[]} */
|
|
57
113
|
this.glyf = [];
|
|
58
114
|
this.offset = 0;
|
|
59
115
|
this.loca = {
|
|
60
|
-
offsets: [],
|
|
61
|
-
version: this.font.loca.version
|
|
116
|
+
offsets: /** @type {number[]} */ ([]),
|
|
117
|
+
version: this.font.loca ? (this.font.loca.version ?? 0) : 0
|
|
62
118
|
};
|
|
63
119
|
|
|
64
120
|
this.hmtx = {
|
|
65
|
-
metrics: [],
|
|
66
|
-
bearings: []
|
|
121
|
+
metrics: /** @type {HmtxMetric[]} */ ([]),
|
|
122
|
+
bearings: /** @type {number[]} */ ([])
|
|
67
123
|
};
|
|
68
124
|
|
|
69
125
|
// include all the glyphs
|
|
@@ -75,40 +131,26 @@ export default class TTFSubset extends Subset {
|
|
|
75
131
|
}
|
|
76
132
|
|
|
77
133
|
let maxp = cloneDeep(this.font.maxp);
|
|
78
|
-
maxp
|
|
134
|
+
if (maxp && typeof maxp === 'object') {
|
|
135
|
+
/** @type {StructValue} */ (maxp).numGlyphs = this.glyf.length;
|
|
136
|
+
}
|
|
79
137
|
|
|
80
|
-
this.loca
|
|
138
|
+
let loca = this.loca;
|
|
139
|
+
loca.offsets.push(this.offset);
|
|
81
140
|
|
|
82
141
|
let head = cloneDeep(this.font.head);
|
|
83
|
-
head
|
|
142
|
+
if (head && typeof head === 'object') {
|
|
143
|
+
/** @type {StructValue} */ (head).indexToLocFormat = loca.version;
|
|
144
|
+
}
|
|
84
145
|
|
|
85
146
|
let hhea = cloneDeep(this.font.hhea);
|
|
86
|
-
hhea
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// for index in [0...256]
|
|
90
|
-
// if index < @numGlyphs
|
|
91
|
-
// map[index] = index
|
|
92
|
-
// else
|
|
93
|
-
// map[index] = 0
|
|
94
|
-
//
|
|
95
|
-
// cmapTable =
|
|
96
|
-
// version: 0
|
|
97
|
-
// length: 262
|
|
98
|
-
// language: 0
|
|
99
|
-
// codeMap: map
|
|
100
|
-
//
|
|
101
|
-
// cmap =
|
|
102
|
-
// version: 0
|
|
103
|
-
// numSubtables: 1
|
|
104
|
-
// tables: [
|
|
105
|
-
// platformID: 1
|
|
106
|
-
// encodingID: 0
|
|
107
|
-
// table: cmapTable
|
|
108
|
-
// ]
|
|
147
|
+
if (hhea && typeof hhea === 'object') {
|
|
148
|
+
/** @type {StructValue} */ (hhea).numberOfMetrics = this.hmtx.metrics.length;
|
|
149
|
+
}
|
|
109
150
|
|
|
110
151
|
// TODO: subset prep, cvt, fpgm?
|
|
111
152
|
// The following is the minimum set of tables.
|
|
153
|
+
/** @type {Record<string, unknown>} */
|
|
112
154
|
let t = {
|
|
113
155
|
head,
|
|
114
156
|
hhea,
|
|
@@ -121,8 +163,9 @@ export default class TTFSubset extends Subset {
|
|
|
121
163
|
fpgm: this.font.fpgm
|
|
122
164
|
};
|
|
123
165
|
|
|
166
|
+
let fontTables = /** @type {Record<string, unknown>} */ (/** @type {unknown} */ (this.font));
|
|
124
167
|
for (const tag of this.includedTables) {
|
|
125
|
-
t[tag] = cloneDeep(
|
|
168
|
+
t[tag] = cloneDeep(fontTables[tag]);
|
|
126
169
|
}
|
|
127
170
|
|
|
128
171
|
return Directory.toBuffer({ tables: t });
|
package/src/tables/BASE.js
CHANGED
|
@@ -65,6 +65,7 @@ let Axis = new r.Struct({
|
|
|
65
65
|
baseScriptList: new r.Pointer(r.uint16, BaseScriptList)
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
/** @type {import('restructure').VersionedStruct} */
|
|
68
69
|
export default new r.VersionedStruct(r.uint32, {
|
|
69
70
|
header: {
|
|
70
71
|
horizAxis: new r.Pointer(r.uint16, Axis), // May be NULL
|
package/src/tables/COLR.js
CHANGED
package/src/tables/CPAL.js
CHANGED
package/src/tables/DSIG.js
CHANGED
|
@@ -12,6 +12,7 @@ let SignatureBlock = new r.Struct({
|
|
|
12
12
|
signature: new r.Buffer('cbSignature')
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
+
/** @type {import('restructure').Struct} */
|
|
15
16
|
export default new r.Struct({
|
|
16
17
|
ulVersion: r.uint32, // Version number of the DSIG table (0x00000001)
|
|
17
18
|
usNumSigs: r.uint16, // Number of signatures in the table
|