@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
|
@@ -2,6 +2,26 @@ 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
27
|
numFonts: r.uint32,
|
|
@@ -16,35 +36,55 @@ let TTCHeader = new r.VersionedStruct(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,12 +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
171
|
} else if (nContours < 0) { // composite glyph
|
|
101
|
-
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
|
+
);
|
|
102
177
|
if (haveInstructions) {
|
|
103
178
|
read255UInt16(table.glyphs);
|
|
104
179
|
}
|
|
@@ -111,13 +186,33 @@ export default class WOFF2Font extends TTFFont {
|
|
|
111
186
|
}
|
|
112
187
|
}
|
|
113
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
|
+
|
|
114
201
|
// Special class that accepts a length and returns a sub-stream for that data
|
|
115
202
|
class Substream {
|
|
203
|
+
/**
|
|
204
|
+
* @param {number | string} length
|
|
205
|
+
*/
|
|
116
206
|
constructor(length) {
|
|
117
207
|
this.length = length;
|
|
118
208
|
this._buf = new r.Buffer(length);
|
|
119
209
|
}
|
|
120
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @param {DecodeStream} stream
|
|
213
|
+
* @param {StructValue} parent
|
|
214
|
+
* @returns {DecodeStream}
|
|
215
|
+
*/
|
|
121
216
|
decode(stream, parent) {
|
|
122
217
|
return new r.DecodeStream(this._buf.decode(stream, parent));
|
|
123
218
|
}
|
|
@@ -149,6 +244,10 @@ const ONE_MORE_BYTE_CODE2 = 254;
|
|
|
149
244
|
const ONE_MORE_BYTE_CODE1 = 255;
|
|
150
245
|
const LOWEST_U_CODE = 253;
|
|
151
246
|
|
|
247
|
+
/**
|
|
248
|
+
* @param {DecodeStream} stream
|
|
249
|
+
* @returns {number}
|
|
250
|
+
*/
|
|
152
251
|
function read255UInt16(stream) {
|
|
153
252
|
let code = stream.readUInt8();
|
|
154
253
|
|
|
@@ -167,17 +266,30 @@ function read255UInt16(stream) {
|
|
|
167
266
|
return code;
|
|
168
267
|
}
|
|
169
268
|
|
|
269
|
+
/**
|
|
270
|
+
* @param {number} flag
|
|
271
|
+
* @param {number} baseval
|
|
272
|
+
* @returns {number}
|
|
273
|
+
*/
|
|
170
274
|
function withSign(flag, baseval) {
|
|
171
275
|
return flag & 1 ? baseval : -baseval;
|
|
172
276
|
}
|
|
173
277
|
|
|
278
|
+
/**
|
|
279
|
+
* @param {DecodeStream} flags
|
|
280
|
+
* @param {DecodeStream} glyphs
|
|
281
|
+
* @param {number} nPoints
|
|
282
|
+
* @returns {Point[]}
|
|
283
|
+
*/
|
|
174
284
|
function decodeTriplet(flags, glyphs, nPoints) {
|
|
175
285
|
let y;
|
|
176
286
|
let x = y = 0;
|
|
287
|
+
/** @type {Point[]} */
|
|
177
288
|
let res = [];
|
|
178
289
|
|
|
179
290
|
for (let i = 0; i < nPoints; i++) {
|
|
180
|
-
let dx
|
|
291
|
+
let dx = 0;
|
|
292
|
+
let dy = 0;
|
|
181
293
|
let flag = flags.readUInt8();
|
|
182
294
|
let onCurve = !(flag >> 7);
|
|
183
295
|
flag &= 0x7f;
|
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
|
}
|
package/src/aat/AATFeatureMap.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
// see https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html
|
|
2
2
|
// and /System/Library/Frameworks/CoreText.framework/Versions/A/Headers/SFNTLayoutTypes.h on a Mac
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../../types/fontkit').FeatureMap} FeatureMap */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').FeatureValue} FeatureValue */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').AATFeatureSettings} AATFeatureSettings */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {{ code: number, exclusive: boolean, [selector: string]: number | boolean }} AATFeatureDef
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {Record<string, AATFeatureDef>} */
|
|
3
13
|
const features = {
|
|
4
14
|
allTypographicFeatures: {
|
|
5
15
|
code: 0,
|
|
@@ -346,8 +356,17 @@ const features = {
|
|
|
346
356
|
}
|
|
347
357
|
};
|
|
348
358
|
|
|
349
|
-
|
|
359
|
+
/**
|
|
360
|
+
* @param {string} name
|
|
361
|
+
* @param {string} selector
|
|
362
|
+
* @returns {[number, number]}
|
|
363
|
+
*/
|
|
364
|
+
const feature = (name, selector) => {
|
|
365
|
+
let f = features[name];
|
|
366
|
+
return [(f.code), /** @type {number} */ (f[selector])];
|
|
367
|
+
};
|
|
350
368
|
|
|
369
|
+
/** @type {Record<string, [number, number]>} */
|
|
351
370
|
const OTMapping = {
|
|
352
371
|
rlig: feature('ligatures', 'requiredLigatures'),
|
|
353
372
|
clig: feature('ligatures', 'contextualLigatures'),
|
|
@@ -463,7 +482,7 @@ for (let i = 1; i <= 99; i++) {
|
|
|
463
482
|
OTMapping[`cv${`00${i}`.slice(-2)}`] = [features.characterAlternatives.code, i];
|
|
464
483
|
}
|
|
465
484
|
|
|
466
|
-
|
|
485
|
+
/** @type {Record<number, Record<number, string>>} */
|
|
467
486
|
let AATMapping = {};
|
|
468
487
|
for (let ot in OTMapping) {
|
|
469
488
|
let aat = OTMapping[ot];
|
|
@@ -474,9 +493,14 @@ for (let ot in OTMapping) {
|
|
|
474
493
|
AATMapping[aat[0]][aat[1]] = ot;
|
|
475
494
|
}
|
|
476
495
|
|
|
477
|
-
|
|
478
|
-
|
|
496
|
+
/**
|
|
497
|
+
* Maps an OpenType feature map to AAT features
|
|
498
|
+
* in the form of {featureType:{featureSetting:true}}
|
|
499
|
+
* @param {FeatureMap | Record<string, FeatureValue>} features
|
|
500
|
+
* @returns {AATFeatureSettings}
|
|
501
|
+
*/
|
|
479
502
|
export function mapOTToAAT(features) {
|
|
503
|
+
/** @type {AATFeatureSettings} */
|
|
480
504
|
let res = {};
|
|
481
505
|
for (let k in features) {
|
|
482
506
|
let r;
|
|
@@ -492,38 +516,58 @@ export function mapOTToAAT(features) {
|
|
|
492
516
|
return res;
|
|
493
517
|
}
|
|
494
518
|
|
|
495
|
-
|
|
496
|
-
|
|
519
|
+
/**
|
|
520
|
+
* @param {string | number} value
|
|
521
|
+
* @returns {value is string}
|
|
522
|
+
*/
|
|
523
|
+
function isFeatureName(value) {
|
|
524
|
+
return typeof value === 'string' && Number.isNaN(Number(value));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Maps strings in a [featureType, featureSetting]
|
|
529
|
+
* to their equivalent number codes
|
|
530
|
+
* @param {[string | number, string | number]} f
|
|
531
|
+
* @returns {[number | undefined, number | undefined]}
|
|
532
|
+
*/
|
|
497
533
|
function mapFeatureStrings(f) {
|
|
498
534
|
let [type, setting] = f;
|
|
535
|
+
/** @type {number | undefined} */
|
|
499
536
|
let typeCode;
|
|
500
|
-
if (
|
|
501
|
-
typeCode = features[type] && features[type].code;
|
|
537
|
+
if (isFeatureName(type)) {
|
|
538
|
+
typeCode = features[type] && /** @type {number} */ (features[type].code);
|
|
502
539
|
} else {
|
|
503
|
-
typeCode = type;
|
|
540
|
+
typeCode = Number(type);
|
|
504
541
|
}
|
|
505
542
|
|
|
543
|
+
/** @type {number | undefined} */
|
|
506
544
|
let settingCode;
|
|
507
|
-
if (
|
|
508
|
-
|
|
545
|
+
if (isFeatureName(setting)) {
|
|
546
|
+
let feat = isFeatureName(type) ? features[type] : undefined;
|
|
547
|
+
settingCode = feat ? /** @type {number} */ (feat[setting]) : undefined;
|
|
509
548
|
} else {
|
|
510
|
-
settingCode = setting;
|
|
549
|
+
settingCode = Number(setting);
|
|
511
550
|
}
|
|
512
551
|
|
|
513
552
|
return [typeCode, settingCode];
|
|
514
553
|
}
|
|
515
554
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
555
|
+
/**
|
|
556
|
+
* Maps AAT features to an array of OpenType features.
|
|
557
|
+
* Supports both arrays in the form of [[featureType, featureSetting]]
|
|
558
|
+
* and objects in the form of {featureType:{featureSetting:true}}.
|
|
559
|
+
* featureTypes and featureSettings can be either strings or number codes.
|
|
560
|
+
* @param {Array<[string | number, string | number]> | Record<string, Record<string, FeatureValue>>} features
|
|
561
|
+
* @returns {string[]}
|
|
562
|
+
*/
|
|
520
563
|
export function mapAATToOT(features) {
|
|
564
|
+
/** @type {Record<string, true>} */
|
|
521
565
|
let res = {};
|
|
522
566
|
if (Array.isArray(features)) {
|
|
523
567
|
for (let k = 0; k < features.length; k++) {
|
|
524
568
|
let r;
|
|
525
569
|
let f = mapFeatureStrings(features[k]);
|
|
526
|
-
if ((r = AATMapping[f[0]] && AATMapping[f[0]][f[1]])) {
|
|
570
|
+
if (f[0] != null && f[1] != null && (r = AATMapping[f[0]] && AATMapping[f[0]][f[1]])) {
|
|
527
571
|
res[r] = true;
|
|
528
572
|
}
|
|
529
573
|
}
|
|
@@ -533,7 +577,7 @@ export function mapAATToOT(features) {
|
|
|
533
577
|
for (let setting in feature) {
|
|
534
578
|
let r;
|
|
535
579
|
let f = mapFeatureStrings([type, setting]);
|
|
536
|
-
if (feature[setting] && (r = AATMapping[f[0]] && AATMapping[f[0]][f[1]])) {
|
|
580
|
+
if (feature[setting] && f[0] != null && f[1] != null && (r = AATMapping[f[0]] && AATMapping[f[0]][f[1]])) {
|
|
537
581
|
res[r] = true;
|
|
538
582
|
}
|
|
539
583
|
}
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import * as AATFeatureMap from './AATFeatureMap';
|
|
2
2
|
import AATMorxProcessor from './AATMorxProcessor';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').GlyphRunLike} GlyphRunLike */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').ScriptTag} ScriptTag */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').LanguageTag} LanguageTag */
|
|
8
|
+
|
|
4
9
|
export default class AATLayoutEngine {
|
|
10
|
+
/**
|
|
11
|
+
* @param {LayoutFont} font
|
|
12
|
+
*/
|
|
5
13
|
constructor(font) {
|
|
14
|
+
/** @type {LayoutFont} */
|
|
6
15
|
this.font = font;
|
|
16
|
+
/** @type {AATMorxProcessor} */
|
|
7
17
|
this.morxProcessor = new AATMorxProcessor(font);
|
|
18
|
+
/** @type {boolean} */
|
|
8
19
|
this.fallbackPosition = false;
|
|
9
20
|
}
|
|
10
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @param {GlyphRunLike} glyphRun
|
|
24
|
+
*/
|
|
11
25
|
substitute(glyphRun) {
|
|
12
26
|
// AAT expects the glyphs to be in visual order prior to morx processing,
|
|
13
27
|
// so reverse the glyphs if the script is right-to-left.
|
|
@@ -15,13 +29,25 @@ export default class AATLayoutEngine {
|
|
|
15
29
|
glyphRun.glyphs.reverse();
|
|
16
30
|
}
|
|
17
31
|
|
|
18
|
-
this.morxProcessor.process(
|
|
32
|
+
this.morxProcessor.process(
|
|
33
|
+
/** @type {import('../../types/fontkit').LayoutGlyph[]} */ (glyphRun.glyphs),
|
|
34
|
+
AATFeatureMap.mapOTToAAT(glyphRun.features)
|
|
35
|
+
);
|
|
19
36
|
}
|
|
20
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {ScriptTag | string[] | null | undefined} [_script]
|
|
40
|
+
* @param {LanguageTag | null | undefined} [_language]
|
|
41
|
+
* @returns {string[]}
|
|
42
|
+
*/
|
|
21
43
|
getAvailableFeatures(_script, _language) {
|
|
22
44
|
return AATFeatureMap.mapAATToOT(this.morxProcessor.getSupportedFeatures());
|
|
23
45
|
}
|
|
24
46
|
|
|
47
|
+
/**
|
|
48
|
+
* @param {number} gid
|
|
49
|
+
* @returns {Set<string>}
|
|
50
|
+
*/
|
|
25
51
|
stringsForGlyph(gid) {
|
|
26
52
|
let glyphStrings = this.morxProcessor.generateInputs(gid);
|
|
27
53
|
let result = new Set();
|
|
@@ -33,6 +59,12 @@ export default class AATLayoutEngine {
|
|
|
33
59
|
return result;
|
|
34
60
|
}
|
|
35
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @param {number[]} glyphs
|
|
64
|
+
* @param {number} index
|
|
65
|
+
* @param {Set<string>} strings
|
|
66
|
+
* @param {string} string
|
|
67
|
+
*/
|
|
36
68
|
_addStrings(glyphs, index, strings, string) {
|
|
37
69
|
let codePoints = this.font._cmapProcessor.codePointsForGlyph(glyphs[index]);
|
|
38
70
|
|