@cantoo/fontkit 2.0.4 → 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 +4448 -1142
- package/dist/browser-module.mjs.map +1 -1
- package/dist/browser.cjs +4449 -1142
- package/dist/browser.cjs.map +1 -1
- package/dist/main.cjs +4449 -1142
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +4448 -1142
- package/dist/module.mjs.map +1 -1
- package/package.json +4 -1
- package/src/CmapProcessor.js +60 -21
- package/src/DFont.js +114 -14
- package/src/TTFFont.js +260 -62
- package/src/TrueTypeCollection.js +59 -15
- package/src/WOFF2Font.js +128 -22
- package/src/WOFFFont.js +45 -8
- package/src/aat/AATFeatureMap.js +83 -40
- package/src/aat/AATLayoutEngine.js +34 -2
- package/src/aat/AATLookupTable.js +24 -6
- package/src/aat/AATMorxProcessor.js +202 -65
- package/src/aat/AATStateMachine.js +35 -3
- package/src/base.js +20 -1
- package/src/cff/CFFCharsets.js +3 -0
- package/src/cff/CFFDict.js +81 -11
- package/src/cff/CFFEncodings.js +2 -0
- package/src/cff/CFFFont.js +120 -24
- package/src/cff/CFFIndex.js +66 -9
- package/src/cff/CFFOperand.js +53 -28
- package/src/cff/CFFPointer.js +41 -2
- package/src/cff/CFFPrivateDict.js +34 -24
- package/src/cff/CFFStandardStrings.js +68 -67
- package/src/cff/CFFTop.js +161 -68
- package/src/decorators.js +34 -15
- package/src/encodings.js +133 -116
- package/src/fs.js +10 -0
- package/src/glyph/BBox.js +14 -0
- package/src/glyph/CFFGlyph.js +293 -152
- package/src/glyph/COLRGlyph.js +40 -5
- package/src/glyph/Glyph.js +72 -13
- package/src/glyph/GlyphVariationProcessor.js +158 -62
- package/src/glyph/Path.js +119 -32
- package/src/glyph/SBIXGlyph.js +57 -8
- package/src/glyph/StandardNames.js +1 -0
- package/src/glyph/TTFGlyph.js +203 -63
- package/src/glyph/TTFGlyphEncoder.js +58 -26
- package/src/glyph/WOFF2Glyph.js +25 -6
- package/src/layout/GlyphPosition.js +6 -0
- package/src/layout/GlyphRun.js +28 -10
- package/src/layout/KernProcessor.js +63 -4
- package/src/layout/LayoutEngine.js +99 -26
- package/src/layout/Script.js +52 -26
- package/src/layout/UnicodeLayoutEngine.js +34 -10
- package/src/opentype/GPOSProcessor.js +156 -45
- package/src/opentype/GSUBProcessor.js +92 -33
- package/src/opentype/GlyphInfo.js +53 -5
- package/src/opentype/GlyphIterator.js +51 -2
- package/src/opentype/OTLayoutEngine.js +66 -8
- package/src/opentype/OTProcessor.js +284 -71
- package/src/opentype/ShapingPlan.js +52 -5
- package/src/opentype/shapers/ArabicShaper.js +27 -8
- package/src/opentype/shapers/DefaultShaper.js +54 -10
- package/src/opentype/shapers/HangulShaper.js +120 -42
- package/src/opentype/shapers/HebrewShaper.js +50 -16
- package/src/opentype/shapers/IndicShaper.js +159 -44
- package/src/opentype/shapers/ThaiShaper.js +50 -19
- package/src/opentype/shapers/UniversalShaper.js +83 -17
- package/src/opentype/shapers/index.js +48 -35
- package/src/opentype/shapers/indic-data.js +2 -2
- package/src/packages/unicode-properties/data-trie.js +2 -1
- package/src/packages/unicode-properties/data.js +13 -1
- package/src/packages/unicode-properties/dist/index.cjs +23 -10
- package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
- package/src/packages/unicode-properties/dist/index.mjs +22 -10
- package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
- package/src/packages/unicode-properties/generate.js +3 -4
- package/src/packages/unicode-properties/index.js +95 -30
- package/src/packages/unicode-properties/package.json +1 -1
- package/src/packages/unicode-trie/builder.js +146 -29
- package/src/packages/unicode-trie/dist/builder.cjs +135 -25
- package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/builder.mjs +134 -25
- 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 +63 -12
- package/src/packages/unicode-trie/package.json +1 -1
- package/src/packages/unicode-trie/swap.js +15 -2
- package/src/subset/CFFSubset.js +112 -22
- package/src/subset/Subset.js +23 -3
- package/src/subset/TTFSubset.js +84 -41
- package/src/tables/BASE.js +28 -27
- package/src/tables/COLR.js +10 -9
- package/src/tables/CPAL.js +2 -1
- package/src/tables/DSIG.js +8 -7
- package/src/tables/EBDT.js +10 -4
- package/src/tables/EBLC.js +70 -10
- package/src/tables/GDEF.js +18 -17
- package/src/tables/GPOS.js +153 -90
- package/src/tables/GSUB.js +50 -39
- package/src/tables/HVAR.js +51 -5
- package/src/tables/JSTF.js +17 -16
- package/src/tables/LTSH.js +4 -3
- package/src/tables/OS2.js +50 -49
- package/src/tables/PCLT.js +16 -15
- package/src/tables/STAT.js +46 -6
- package/src/tables/VDMX.js +18 -17
- package/src/tables/VORG.js +8 -7
- package/src/tables/WOFF2Directory.js +57 -9
- package/src/tables/WOFFDirectory.js +32 -20
- 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 +83 -63
- package/src/tables/cvt.js +1 -0
- package/src/tables/directory.js +56 -28
- 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 +6 -5
- package/src/tables/glyf.js +2 -1
- package/src/tables/gvar.js +29 -2
- package/src/tables/hdmx.js +18 -7
- package/src/tables/head.js +17 -16
- package/src/tables/hhea.js +15 -14
- package/src/tables/hmtx.js +18 -2
- package/src/tables/index.js +1 -5
- package/src/tables/just.js +15 -4
- package/src/tables/kern.js +84 -47
- package/src/tables/loca.js +23 -10
- package/src/tables/maxp.js +16 -15
- package/src/tables/morx.js +27 -20
- package/src/tables/name.js +120 -39
- package/src/tables/opbd.js +2 -1
- package/src/tables/opentype.js +126 -91
- package/src/tables/post.js +18 -11
- package/src/tables/prep.js +1 -0
- package/src/tables/sbix.js +12 -1
- package/src/tables/variations.js +29 -9
- package/src/tables/vhea.js +15 -14
- package/src/tables/vmtx.js +20 -4
- package/src/utils.js +15 -0
|
@@ -2,49 +2,89 @@ import * as r from 'restructure';
|
|
|
2
2
|
import TTFFont from './TTFFont';
|
|
3
3
|
import { asciiDecoder } from './utils';
|
|
4
4
|
|
|
5
|
+
/** @typedef {import('restructure').DecodeStream} DecodeStream */
|
|
6
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
7
|
+
/** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
|
|
8
|
+
/** @typedef {import('../types/fontkit').NameString} NameString */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {NameString | null | undefined} a
|
|
12
|
+
* @param {string | Uint8Array} b
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function postscriptNamesEqual(a, b) {
|
|
16
|
+
if (a === b) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (a instanceof Uint8Array && b instanceof Uint8Array) {
|
|
20
|
+
return a.length === b.length && a.every((v, i) => b[i] === v);
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
let TTCHeader = new r.VersionedStruct(r.uint32, {
|
|
6
26
|
0x00010000: {
|
|
7
|
-
numFonts:
|
|
8
|
-
offsets:
|
|
27
|
+
numFonts: r.uint32,
|
|
28
|
+
offsets: new r.Array(r.uint32, 'numFonts')
|
|
9
29
|
},
|
|
10
30
|
0x00020000: {
|
|
11
|
-
numFonts:
|
|
12
|
-
offsets:
|
|
13
|
-
dsigTag:
|
|
31
|
+
numFonts: r.uint32,
|
|
32
|
+
offsets: new r.Array(r.uint32, 'numFonts'),
|
|
33
|
+
dsigTag: r.uint32,
|
|
14
34
|
dsigLength: r.uint32,
|
|
15
35
|
dsigOffset: r.uint32
|
|
16
36
|
}
|
|
17
37
|
});
|
|
18
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {StructValue & { offsets: number[] }} TTCHeaderValue
|
|
41
|
+
*/
|
|
42
|
+
|
|
19
43
|
export default class TrueTypeCollection {
|
|
44
|
+
/** @type {string} */
|
|
20
45
|
type = 'TTC';
|
|
21
46
|
|
|
47
|
+
/** @type {DecodeStream} */
|
|
48
|
+
stream;
|
|
49
|
+
/** @type {TTCHeaderValue} */
|
|
50
|
+
header;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {ArrayBufferView} buffer
|
|
54
|
+
* @returns {boolean}
|
|
55
|
+
*/
|
|
22
56
|
static probe(buffer) {
|
|
23
|
-
|
|
57
|
+
let bytes
|
|
58
|
+
= buffer instanceof Uint8Array
|
|
59
|
+
? buffer
|
|
60
|
+
: new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
61
|
+
return asciiDecoder.decode(bytes.subarray(0, 4)) === 'ttcf';
|
|
24
62
|
}
|
|
25
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @param {DecodeStream} stream
|
|
66
|
+
*/
|
|
26
67
|
constructor(stream) {
|
|
27
68
|
this.stream = stream;
|
|
28
69
|
if (stream.readString(4) !== 'ttcf') {
|
|
29
70
|
throw new Error('Not a TrueType collection');
|
|
30
71
|
}
|
|
31
72
|
|
|
32
|
-
this.header =
|
|
73
|
+
this.header = /** @type {TTCHeaderValue} */ (
|
|
74
|
+
/** @type {unknown} */ (TTCHeader.decode(stream))
|
|
75
|
+
);
|
|
33
76
|
}
|
|
34
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @param {string | Uint8Array} name
|
|
80
|
+
* @returns {TTFFont | null}
|
|
81
|
+
*/
|
|
35
82
|
getFont(name) {
|
|
36
83
|
for (let offset of this.header.offsets) {
|
|
37
84
|
let stream = new r.DecodeStream(this.stream.buffer);
|
|
38
85
|
stream.pos = offset;
|
|
39
86
|
let font = new TTFFont(stream);
|
|
40
|
-
if (
|
|
41
|
-
font.postscriptName === name ||
|
|
42
|
-
(
|
|
43
|
-
font.postscriptName instanceof Uint8Array &&
|
|
44
|
-
name instanceof Uint8Array &&
|
|
45
|
-
font.postscriptName.every((v, i) => name[i] === v)
|
|
46
|
-
)
|
|
47
|
-
) {
|
|
87
|
+
if (postscriptNamesEqual(font.postscriptName, name)) {
|
|
48
88
|
return font;
|
|
49
89
|
}
|
|
50
90
|
}
|
|
@@ -52,7 +92,11 @@ export default class TrueTypeCollection {
|
|
|
52
92
|
return null;
|
|
53
93
|
}
|
|
54
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @type {TTFFont[]}
|
|
97
|
+
*/
|
|
55
98
|
get fonts() {
|
|
99
|
+
/** @type {TTFFont[]} */
|
|
56
100
|
let fonts = [];
|
|
57
101
|
for (let offset of this.header.offsets) {
|
|
58
102
|
let stream = new r.DecodeStream(this.stream.buffer);
|
package/src/WOFF2Font.js
CHANGED
|
@@ -6,45 +6,98 @@ import WOFF2Glyph from './glyph/WOFF2Glyph';
|
|
|
6
6
|
import WOFF2Directory from './tables/WOFF2Directory';
|
|
7
7
|
import { asciiDecoder } from './utils';
|
|
8
8
|
|
|
9
|
+
/** @typedef {import('restructure').DecodeStream} DecodeStream */
|
|
10
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
11
|
+
/** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
|
|
12
|
+
/** @typedef {import('restructure').BaseType} BaseType */
|
|
13
|
+
/** @typedef {import('../types/fontkit').FontDirectory} FontDirectory */
|
|
14
|
+
/** @typedef {import('../types/fontkit').TableEntry} TableEntry */
|
|
15
|
+
/** @typedef {import('../types/fontkit').TransformedGlyf} TransformedGlyf */
|
|
16
|
+
/** @typedef {import('../types/fontkit').DecodedGlyf} DecodedGlyf */
|
|
17
|
+
/** @typedef {import('./glyph/Glyph').default} Glyph */
|
|
18
|
+
|
|
9
19
|
/**
|
|
10
20
|
* Subclass of TTFFont that represents a TTF/OTF font compressed by WOFF2
|
|
11
21
|
* See spec here: http://www.w3.org/TR/WOFF2/
|
|
22
|
+
*
|
|
23
|
+
* @property {number} [_dataPos] Byte offset of the compressed table data (set in `_decodeDirectory`).
|
|
12
24
|
*/
|
|
13
25
|
export default class WOFF2Font extends TTFFont {
|
|
26
|
+
/** @type {string} */
|
|
14
27
|
type = 'WOFF2';
|
|
15
28
|
|
|
29
|
+
// NOTE: Do not declare `_dataPos` as a class field. It is assigned in
|
|
30
|
+
// `_decodeDirectory()` during `super()`, and subclass field initializers
|
|
31
|
+
// run *after* `super()` returns — which would wipe the real offset.
|
|
32
|
+
|
|
33
|
+
/** @type {boolean | undefined} */
|
|
34
|
+
_decompressed;
|
|
35
|
+
/** @type {(TransformedGlyf | undefined)[] | undefined} */
|
|
36
|
+
_transformedGlyphs;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {ArrayBufferView} buffer
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
16
42
|
static probe(buffer) {
|
|
17
|
-
|
|
43
|
+
let bytes
|
|
44
|
+
= buffer instanceof Uint8Array
|
|
45
|
+
? buffer
|
|
46
|
+
: new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
47
|
+
return asciiDecoder.decode(bytes.subarray(0, 4)) === 'wOF2';
|
|
18
48
|
}
|
|
19
49
|
|
|
50
|
+
/** @returns {void} */
|
|
20
51
|
_decodeDirectory() {
|
|
21
|
-
this.directory =
|
|
52
|
+
this.directory = /** @type {FontDirectory} */ (
|
|
53
|
+
/** @type {unknown} */ (WOFF2Directory.decode(this.stream))
|
|
54
|
+
);
|
|
22
55
|
this._dataPos = this.stream.pos;
|
|
23
56
|
}
|
|
24
57
|
|
|
58
|
+
/** @returns {void} */
|
|
25
59
|
_decompress() {
|
|
26
60
|
// decompress data and setup table offsets if we haven't already
|
|
27
61
|
if (!this._decompressed) {
|
|
28
|
-
|
|
29
|
-
|
|
62
|
+
let dataPos = this._dataPos;
|
|
63
|
+
if (dataPos == null) {
|
|
64
|
+
throw new Error('Missing WOFF2 data offset; directory was not decoded');
|
|
65
|
+
}
|
|
66
|
+
this.stream.pos = dataPos;
|
|
67
|
+
let totalCompressedSize = this.directory.totalCompressedSize;
|
|
68
|
+
if (totalCompressedSize == null) {
|
|
69
|
+
throw new Error('Missing totalCompressedSize in WOFF2 directory');
|
|
70
|
+
}
|
|
71
|
+
let buffer = this.stream.readBuffer(totalCompressedSize);
|
|
30
72
|
|
|
31
73
|
let decompressedSize = 0;
|
|
32
|
-
|
|
33
|
-
|
|
74
|
+
let tableMap = this.directory.tables;
|
|
75
|
+
for (let tag in tableMap) {
|
|
76
|
+
let entry = tableMap[tag];
|
|
77
|
+
if (!entry) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
34
80
|
entry.offset = decompressedSize;
|
|
35
81
|
decompressedSize += (entry.transformLength != null) ? entry.transformLength : entry.length;
|
|
36
82
|
}
|
|
37
83
|
|
|
38
|
-
let decompressed = brotli(
|
|
84
|
+
let decompressed = brotli(
|
|
85
|
+
buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength),
|
|
86
|
+
decompressedSize
|
|
87
|
+
);
|
|
39
88
|
if (!decompressed) {
|
|
40
89
|
throw new Error('Error decoding compressed data in WOFF2');
|
|
41
90
|
}
|
|
42
91
|
|
|
43
|
-
this.
|
|
92
|
+
this._setStream(new r.DecodeStream(decompressed));
|
|
44
93
|
this._decompressed = true;
|
|
45
94
|
}
|
|
46
95
|
}
|
|
47
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @param {TableEntry} table
|
|
99
|
+
* @returns {unknown}
|
|
100
|
+
*/
|
|
48
101
|
_decodeTable(table) {
|
|
49
102
|
this._decompress();
|
|
50
103
|
return super._decodeTable(table);
|
|
@@ -52,13 +105,19 @@ export default class WOFF2Font extends TTFFont {
|
|
|
52
105
|
|
|
53
106
|
// Override this method to get a glyph and return our
|
|
54
107
|
// custom subclass if there is a glyf table.
|
|
108
|
+
/**
|
|
109
|
+
* @param {number} glyph
|
|
110
|
+
* @param {number[]} [characters]
|
|
111
|
+
* @returns {Glyph | null | undefined}
|
|
112
|
+
*/
|
|
55
113
|
_getBaseGlyph(glyph, characters = []) {
|
|
56
114
|
let cached = this._glyphs[glyph];
|
|
57
|
-
if (cached
|
|
115
|
+
if (cached && typeof (/** @type {{ _getContours?: unknown }} */ (cached))._getContours === 'function') {
|
|
58
116
|
return cached;
|
|
59
117
|
}
|
|
60
118
|
|
|
61
|
-
|
|
119
|
+
let glyfEntry = this.directory.tables.glyf;
|
|
120
|
+
if (glyfEntry && glyfEntry.transformed) {
|
|
62
121
|
if (!this._transformedGlyphs) { this._transformGlyfTable(); }
|
|
63
122
|
let outline = new WOFF2Glyph(glyph, characters, this);
|
|
64
123
|
if (!cached) {
|
|
@@ -70,18 +129,27 @@ export default class WOFF2Font extends TTFFont {
|
|
|
70
129
|
return super._getBaseGlyph(glyph, characters);
|
|
71
130
|
}
|
|
72
131
|
|
|
132
|
+
/** @returns {void} */
|
|
73
133
|
_transformGlyfTable() {
|
|
74
134
|
this._decompress();
|
|
75
|
-
|
|
76
|
-
|
|
135
|
+
let glyfEntry = this.directory.tables.glyf;
|
|
136
|
+
if (!glyfEntry) {
|
|
137
|
+
this._transformedGlyphs = [];
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.stream.pos = glyfEntry.offset;
|
|
141
|
+
let table = /** @type {GlyfTableDecoded} */ (GlyfTable.decode(this.stream));
|
|
142
|
+
/** @type {TransformedGlyf[]} */
|
|
77
143
|
let glyphs = [];
|
|
78
144
|
|
|
79
145
|
for (let index = 0; index < table.numGlyphs; index++) {
|
|
80
|
-
|
|
146
|
+
/** @type {TransformedGlyf} */
|
|
147
|
+
let glyph = { numberOfContours: 0 };
|
|
81
148
|
let nContours = table.nContours.readInt16BE();
|
|
82
149
|
glyph.numberOfContours = nContours;
|
|
83
150
|
|
|
84
151
|
if (nContours > 0) { // simple glyph
|
|
152
|
+
/** @type {number[]} */
|
|
85
153
|
let nPoints = [];
|
|
86
154
|
let totalPoints = 0;
|
|
87
155
|
|
|
@@ -93,13 +161,19 @@ export default class WOFF2Font extends TTFFont {
|
|
|
93
161
|
|
|
94
162
|
glyph.points = decodeTriplet(table.flags, table.glyphs, totalPoints);
|
|
95
163
|
for (let i = 0; i < nContours; i++) {
|
|
96
|
-
|
|
164
|
+
let pts = glyph.points;
|
|
165
|
+
if (pts) {
|
|
166
|
+
pts[nPoints[i] - 1].endContour = true;
|
|
167
|
+
}
|
|
97
168
|
}
|
|
98
169
|
|
|
99
170
|
read255UInt16(table.glyphs);
|
|
100
|
-
|
|
101
171
|
} else if (nContours < 0) { // composite glyph
|
|
102
|
-
let haveInstructions = TTFGlyph.prototype._decodeComposite.call(
|
|
172
|
+
let haveInstructions = TTFGlyph.prototype._decodeComposite.call(
|
|
173
|
+
/** @type {TTFGlyph} */ (/** @type {unknown} */ ({ _font: this })),
|
|
174
|
+
/** @type {DecodedGlyf} */ (glyph),
|
|
175
|
+
table.composites
|
|
176
|
+
);
|
|
103
177
|
if (haveInstructions) {
|
|
104
178
|
read255UInt16(table.glyphs);
|
|
105
179
|
}
|
|
@@ -112,13 +186,33 @@ export default class WOFF2Font extends TTFFont {
|
|
|
112
186
|
}
|
|
113
187
|
}
|
|
114
188
|
|
|
189
|
+
/**
|
|
190
|
+
* @typedef {{
|
|
191
|
+
* version: number,
|
|
192
|
+
* numGlyphs: number,
|
|
193
|
+
* nContours: DecodeStream,
|
|
194
|
+
* nPoints: DecodeStream,
|
|
195
|
+
* flags: DecodeStream,
|
|
196
|
+
* glyphs: DecodeStream,
|
|
197
|
+
* composites: DecodeStream
|
|
198
|
+
* }} GlyfTableDecoded
|
|
199
|
+
*/
|
|
200
|
+
|
|
115
201
|
// Special class that accepts a length and returns a sub-stream for that data
|
|
116
202
|
class Substream {
|
|
203
|
+
/**
|
|
204
|
+
* @param {number | string} length
|
|
205
|
+
*/
|
|
117
206
|
constructor(length) {
|
|
118
207
|
this.length = length;
|
|
119
208
|
this._buf = new r.Buffer(length);
|
|
120
209
|
}
|
|
121
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @param {DecodeStream} stream
|
|
213
|
+
* @param {StructValue} parent
|
|
214
|
+
* @returns {DecodeStream}
|
|
215
|
+
*/
|
|
122
216
|
decode(stream, parent) {
|
|
123
217
|
return new r.DecodeStream(this._buf.decode(stream, parent));
|
|
124
218
|
}
|
|
@@ -150,6 +244,10 @@ const ONE_MORE_BYTE_CODE2 = 254;
|
|
|
150
244
|
const ONE_MORE_BYTE_CODE1 = 255;
|
|
151
245
|
const LOWEST_U_CODE = 253;
|
|
152
246
|
|
|
247
|
+
/**
|
|
248
|
+
* @param {DecodeStream} stream
|
|
249
|
+
* @returns {number}
|
|
250
|
+
*/
|
|
153
251
|
function read255UInt16(stream) {
|
|
154
252
|
let code = stream.readUInt8();
|
|
155
253
|
|
|
@@ -168,17 +266,30 @@ function read255UInt16(stream) {
|
|
|
168
266
|
return code;
|
|
169
267
|
}
|
|
170
268
|
|
|
269
|
+
/**
|
|
270
|
+
* @param {number} flag
|
|
271
|
+
* @param {number} baseval
|
|
272
|
+
* @returns {number}
|
|
273
|
+
*/
|
|
171
274
|
function withSign(flag, baseval) {
|
|
172
275
|
return flag & 1 ? baseval : -baseval;
|
|
173
276
|
}
|
|
174
277
|
|
|
278
|
+
/**
|
|
279
|
+
* @param {DecodeStream} flags
|
|
280
|
+
* @param {DecodeStream} glyphs
|
|
281
|
+
* @param {number} nPoints
|
|
282
|
+
* @returns {Point[]}
|
|
283
|
+
*/
|
|
175
284
|
function decodeTriplet(flags, glyphs, nPoints) {
|
|
176
285
|
let y;
|
|
177
286
|
let x = y = 0;
|
|
287
|
+
/** @type {Point[]} */
|
|
178
288
|
let res = [];
|
|
179
289
|
|
|
180
290
|
for (let i = 0; i < nPoints; i++) {
|
|
181
|
-
let dx
|
|
291
|
+
let dx = 0;
|
|
292
|
+
let dy = 0;
|
|
182
293
|
let flag = flags.readUInt8();
|
|
183
294
|
let onCurve = !(flag >> 7);
|
|
184
295
|
flag &= 0x7f;
|
|
@@ -186,28 +297,23 @@ function decodeTriplet(flags, glyphs, nPoints) {
|
|
|
186
297
|
if (flag < 10) {
|
|
187
298
|
dx = 0;
|
|
188
299
|
dy = withSign(flag, ((flag & 14) << 7) + glyphs.readUInt8());
|
|
189
|
-
|
|
190
300
|
} else if (flag < 20) {
|
|
191
301
|
dx = withSign(flag, (((flag - 10) & 14) << 7) + glyphs.readUInt8());
|
|
192
302
|
dy = 0;
|
|
193
|
-
|
|
194
303
|
} else if (flag < 84) {
|
|
195
304
|
let b0 = flag - 20;
|
|
196
305
|
let b1 = glyphs.readUInt8();
|
|
197
306
|
dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
|
|
198
307
|
dy = withSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
|
|
199
|
-
|
|
200
308
|
} else if (flag < 120) {
|
|
201
309
|
let b0 = flag - 84;
|
|
202
310
|
dx = withSign(flag, 1 + ((b0 / 12) << 8) + glyphs.readUInt8());
|
|
203
311
|
dy = withSign(flag >> 1, 1 + (((b0 % 12) >> 2) << 8) + glyphs.readUInt8());
|
|
204
|
-
|
|
205
312
|
} else if (flag < 124) {
|
|
206
313
|
let b1 = glyphs.readUInt8();
|
|
207
314
|
let b2 = glyphs.readUInt8();
|
|
208
315
|
dx = withSign(flag, (b1 << 4) + (b2 >> 4));
|
|
209
316
|
dy = withSign(flag >> 1, ((b2 & 0x0f) << 8) + glyphs.readUInt8());
|
|
210
|
-
|
|
211
317
|
} else {
|
|
212
318
|
dx = withSign(flag, glyphs.readUInt16BE());
|
|
213
319
|
dy = withSign(flag >> 1, glyphs.readUInt16BE());
|
package/src/WOFFFont.js
CHANGED
|
@@ -4,17 +4,41 @@ import { unzlibSync } from 'fflate';
|
|
|
4
4
|
import * as r from 'restructure';
|
|
5
5
|
import { asciiDecoder } from './utils';
|
|
6
6
|
|
|
7
|
+
/** @typedef {import('restructure').DecodeStream} DecodeStream */
|
|
8
|
+
/** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
|
|
9
|
+
/** @typedef {import('../types/fontkit').FontDirectory} FontDirectory */
|
|
10
|
+
/** @typedef {import('../types/fontkit').TableEntry} TableEntry */
|
|
11
|
+
|
|
7
12
|
export default class WOFFFont extends TTFFont {
|
|
13
|
+
/** @type {string} */
|
|
8
14
|
type = 'WOFF';
|
|
9
15
|
|
|
16
|
+
/** @type {boolean | undefined} */
|
|
17
|
+
_decompressed;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {ArrayBufferView} buffer
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
10
23
|
static probe(buffer) {
|
|
11
|
-
|
|
24
|
+
let bytes
|
|
25
|
+
= buffer instanceof Uint8Array
|
|
26
|
+
? buffer
|
|
27
|
+
: new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
28
|
+
return asciiDecoder.decode(bytes.subarray(0, 4)) === 'wOFF';
|
|
12
29
|
}
|
|
13
30
|
|
|
31
|
+
/** @returns {void} */
|
|
14
32
|
_decodeDirectory() {
|
|
15
|
-
this.directory =
|
|
33
|
+
this.directory = /** @type {FontDirectory} */ (
|
|
34
|
+
/** @type {unknown} */ (WOFFDirectory.decode(this.stream, { _startOffset: 0 }))
|
|
35
|
+
);
|
|
16
36
|
}
|
|
17
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {TableEntry} table
|
|
40
|
+
* @returns {unknown}
|
|
41
|
+
*/
|
|
18
42
|
_decodeTable(table) {
|
|
19
43
|
this._decompress();
|
|
20
44
|
return super._decodeTable(table);
|
|
@@ -22,15 +46,21 @@ export default class WOFFFont extends TTFFont {
|
|
|
22
46
|
|
|
23
47
|
// Inflate all tables into one stream so internal offsets (e.g. gvar) stay
|
|
24
48
|
// valid against this.stream — same reason WOFF2Font decompresses up front.
|
|
49
|
+
/** @returns {void} */
|
|
25
50
|
_decompress() {
|
|
26
51
|
if (this._decompressed) {
|
|
27
52
|
return;
|
|
28
53
|
}
|
|
29
54
|
|
|
30
55
|
let totalSize = 0;
|
|
56
|
+
/** @type {Array<{ entry: TableEntry, newOffset: number }>} */
|
|
31
57
|
let layout = [];
|
|
32
|
-
|
|
33
|
-
|
|
58
|
+
let tableMap = this.directory.tables;
|
|
59
|
+
for (let tag in tableMap) {
|
|
60
|
+
let entry = tableMap[tag];
|
|
61
|
+
if (!entry) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
34
64
|
layout.push({ entry, newOffset: totalSize });
|
|
35
65
|
totalSize = (totalSize + entry.length + 3) & ~3;
|
|
36
66
|
}
|
|
@@ -38,19 +68,26 @@ export default class WOFFFont extends TTFFont {
|
|
|
38
68
|
let buffer = new Uint8Array(totalSize);
|
|
39
69
|
for (let { entry, newOffset } of layout) {
|
|
40
70
|
this.stream.pos = entry.offset;
|
|
71
|
+
/** @type {BinaryBuffer | Uint8Array} */
|
|
41
72
|
let data;
|
|
42
|
-
|
|
43
|
-
|
|
73
|
+
let compLength = entry.compLength != null ? entry.compLength : entry.length;
|
|
74
|
+
if (compLength < entry.length) {
|
|
75
|
+
let compressed = this.stream.readBuffer(compLength);
|
|
76
|
+
let compressedBytes
|
|
77
|
+
= compressed instanceof Uint8Array
|
|
78
|
+
? compressed
|
|
79
|
+
: new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
|
|
80
|
+
data = unzlibSync(compressedBytes, {
|
|
44
81
|
out: new Uint8Array(entry.length)
|
|
45
82
|
});
|
|
46
83
|
} else {
|
|
47
84
|
data = this.stream.readBuffer(entry.length);
|
|
48
85
|
}
|
|
49
|
-
buffer.set(data, newOffset);
|
|
86
|
+
buffer.set(data instanceof Uint8Array ? data : new Uint8Array(data.buffer, data.byteOffset, data.byteLength), newOffset);
|
|
50
87
|
entry.offset = newOffset;
|
|
51
88
|
}
|
|
52
89
|
|
|
53
|
-
this.
|
|
90
|
+
this._setStream(new r.DecodeStream(buffer));
|
|
54
91
|
this._decompressed = true;
|
|
55
92
|
}
|
|
56
93
|
}
|