@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,49 +1,107 @@
|
|
|
1
1
|
import GlyphIterator from './GlyphIterator';
|
|
2
2
|
import * as Script from '../layout/Script';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').OTLayoutTable} OTLayoutTable */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').OTScriptRecord} OTScriptRecord */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').OTScript} OTScript */
|
|
8
|
+
/** @typedef {import('../../types/fontkit').OTLangSys} OTLangSys */
|
|
9
|
+
/** @typedef {import('../../types/fontkit').OTFeature} OTFeature */
|
|
10
|
+
/** @typedef {import('../../types/fontkit').OTLookup} OTLookup */
|
|
11
|
+
/** @typedef {import('../../types/fontkit').FeatureLookup} FeatureLookup */
|
|
12
|
+
/** @typedef {import('../../types/fontkit').CoverageTable} CoverageTable */
|
|
13
|
+
/** @typedef {import('../../types/fontkit').ClassDefTable} ClassDefTable */
|
|
14
|
+
/** @typedef {import('../../types/fontkit').ContextSubtable} ContextSubtable */
|
|
15
|
+
/** @typedef {import('../../types/fontkit').ChainContextSubtable} ChainContextSubtable */
|
|
16
|
+
/** @typedef {import('../../types/fontkit').LookupRecord} LookupRecord */
|
|
17
|
+
/** @typedef {import('../../types/fontkit').FeatureVariationCondition} FeatureVariationCondition */
|
|
18
|
+
/** @typedef {import('../../types/fontkit').GlyphInfoLike} GlyphInfoLike */
|
|
19
|
+
/** @typedef {import('../../types/fontkit').GlyphPositionLike} GlyphPositionLike */
|
|
20
|
+
/** @typedef {import('../../types/fontkit').ScriptTag} ScriptTag */
|
|
21
|
+
/** @typedef {import('../../types/fontkit').LanguageTag} LanguageTag */
|
|
22
|
+
/** @typedef {import('../../types/fontkit').TextDirection} TextDirection */
|
|
23
|
+
/** @typedef {import('../../types/fontkit').FeatureMap} FeatureMap */
|
|
24
|
+
/** @typedef {import('../../types/fontkit').MarkFilteringSet} MarkFilteringSet */
|
|
25
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
26
|
+
|
|
4
27
|
const DEFAULT_SCRIPTS = ['DFLT', 'dflt', 'latn'];
|
|
5
28
|
|
|
6
29
|
export default class OTProcessor {
|
|
30
|
+
/**
|
|
31
|
+
* @param {LayoutFont} font
|
|
32
|
+
* @param {OTLayoutTable} table
|
|
33
|
+
*/
|
|
7
34
|
constructor(font, table) {
|
|
35
|
+
/** @type {LayoutFont} */
|
|
8
36
|
this.font = font;
|
|
37
|
+
/** @type {OTLayoutTable} */
|
|
9
38
|
this.table = table;
|
|
10
39
|
|
|
40
|
+
/** @type {OTScript | null} */
|
|
11
41
|
this.script = null;
|
|
42
|
+
/** @type {string | null} */
|
|
12
43
|
this.scriptTag = null;
|
|
13
44
|
|
|
45
|
+
/** @type {OTLangSys | null} */
|
|
14
46
|
this.language = null;
|
|
47
|
+
/** @type {string | null} */
|
|
15
48
|
this.languageTag = null;
|
|
16
49
|
|
|
50
|
+
/** @type {TextDirection | undefined} */
|
|
51
|
+
this.direction = undefined;
|
|
52
|
+
|
|
53
|
+
/** @type {Record<string, OTFeature>} */
|
|
17
54
|
this.features = {};
|
|
55
|
+
/** @type {Record<string, unknown>} */
|
|
18
56
|
this.lookups = {};
|
|
19
57
|
|
|
58
|
+
/** @type {FeatureMap | null} */
|
|
59
|
+
this.userFeatures = null;
|
|
60
|
+
|
|
20
61
|
// FeatureVariations apply at the current location; default to normalized
|
|
21
62
|
// origin when no variation is set (HarfBuzz behavior). Without this,
|
|
22
63
|
// variable fonts skip FeatureVariations until getVariation() is called.
|
|
23
64
|
let coords = font._variationProcessor?.normalizedCoords
|
|
24
65
|
?? (font.fvar ? new Array(font.fvar.axis.length).fill(0) : null);
|
|
66
|
+
/** @type {number} */
|
|
25
67
|
this.variationsIndex = coords ? this.findVariationsIndex(coords) : -1;
|
|
26
68
|
|
|
27
69
|
// initialize to default script + language
|
|
28
70
|
this.selectScript();
|
|
29
71
|
|
|
30
72
|
// current context (set by applyFeatures)
|
|
73
|
+
/** @type {GlyphInfoLike[]} */
|
|
31
74
|
this.glyphs = [];
|
|
75
|
+
/** @type {GlyphPositionLike[] | null | undefined} */
|
|
32
76
|
this.positions = []; // only used by GPOS
|
|
77
|
+
/** @type {number} */
|
|
33
78
|
this.ligatureID = 1;
|
|
79
|
+
/** @type {string | null} */
|
|
34
80
|
this.currentFeature = null;
|
|
81
|
+
/** @type {GlyphIterator | undefined} */
|
|
82
|
+
this.glyphIterator = undefined;
|
|
83
|
+
/** @type {Map<CoverageTable, MarkFilteringSet> | undefined} */
|
|
84
|
+
this._markFilteringCache = undefined;
|
|
35
85
|
}
|
|
36
86
|
|
|
87
|
+
/**
|
|
88
|
+
* @param {ScriptTag | string[] | null | undefined} script
|
|
89
|
+
* @returns {OTScriptRecord | null}
|
|
90
|
+
*/
|
|
37
91
|
findScript(script) {
|
|
38
92
|
if (this.table.scriptList == null) {
|
|
39
93
|
return null;
|
|
40
94
|
}
|
|
41
95
|
|
|
96
|
+
/** @type {string[]} */
|
|
97
|
+
let scripts;
|
|
42
98
|
if (!Array.isArray(script)) {
|
|
43
|
-
|
|
99
|
+
scripts = script == null ? [] : [script];
|
|
100
|
+
} else {
|
|
101
|
+
scripts = script;
|
|
44
102
|
}
|
|
45
103
|
|
|
46
|
-
for (let s of
|
|
104
|
+
for (let s of scripts) {
|
|
47
105
|
for (let entry of this.table.scriptList) {
|
|
48
106
|
if (entry.tag === s) {
|
|
49
107
|
return entry;
|
|
@@ -54,8 +112,15 @@ export default class OTProcessor {
|
|
|
54
112
|
return null;
|
|
55
113
|
}
|
|
56
114
|
|
|
115
|
+
/**
|
|
116
|
+
* @param {ScriptTag | string[] | null | undefined} [script]
|
|
117
|
+
* @param {LanguageTag | null | undefined} [language]
|
|
118
|
+
* @param {TextDirection | null | undefined} [direction]
|
|
119
|
+
* @returns {string | null}
|
|
120
|
+
*/
|
|
57
121
|
selectScript(script, language, direction) {
|
|
58
122
|
let changed = false;
|
|
123
|
+
/** @type {OTScriptRecord | null | undefined} */
|
|
59
124
|
let entry;
|
|
60
125
|
if (!this.script || script !== this.scriptTag) {
|
|
61
126
|
entry = this.findScript(script);
|
|
@@ -85,17 +150,19 @@ export default class OTProcessor {
|
|
|
85
150
|
if (!language || language !== this.languageTag) {
|
|
86
151
|
this.language = null;
|
|
87
152
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
153
|
+
if (this.script) {
|
|
154
|
+
for (let lang of this.script.langSysRecords) {
|
|
155
|
+
if (lang.tag === language) {
|
|
156
|
+
this.language = lang.langSys;
|
|
157
|
+
this.languageTag = lang.tag;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
93
160
|
}
|
|
94
|
-
}
|
|
95
161
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
162
|
+
if (!this.language) {
|
|
163
|
+
this.language = this.script.defaultLangSys;
|
|
164
|
+
this.languageTag = null;
|
|
165
|
+
}
|
|
99
166
|
}
|
|
100
167
|
|
|
101
168
|
changed = true;
|
|
@@ -116,7 +183,13 @@ export default class OTProcessor {
|
|
|
116
183
|
return this.scriptTag;
|
|
117
184
|
}
|
|
118
185
|
|
|
186
|
+
/**
|
|
187
|
+
* @param {string[]} [userFeatures]
|
|
188
|
+
* @param {number[] | null | undefined} [exclude]
|
|
189
|
+
* @returns {FeatureLookup[]}
|
|
190
|
+
*/
|
|
119
191
|
lookupsForFeatures(userFeatures = [], exclude) {
|
|
192
|
+
/** @type {FeatureLookup[]} */
|
|
120
193
|
let lookups = [];
|
|
121
194
|
for (let tag of userFeatures) {
|
|
122
195
|
let feature = this.features[tag];
|
|
@@ -141,12 +214,21 @@ export default class OTProcessor {
|
|
|
141
214
|
return lookups;
|
|
142
215
|
}
|
|
143
216
|
|
|
217
|
+
/**
|
|
218
|
+
* @param {number} featureIndex
|
|
219
|
+
* @returns {OTFeature | null}
|
|
220
|
+
*/
|
|
144
221
|
substituteFeatureForVariations(featureIndex) {
|
|
145
222
|
if (this.variationsIndex === -1) {
|
|
146
223
|
return null;
|
|
147
224
|
}
|
|
148
225
|
|
|
149
|
-
let
|
|
226
|
+
let variations = this.table.featureVariations;
|
|
227
|
+
if (!variations) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let record = variations.featureVariationRecords[this.variationsIndex];
|
|
150
232
|
let substitutions = record.featureTableSubstitution.substitutions;
|
|
151
233
|
for (let substitution of substitutions) {
|
|
152
234
|
if (substitution.featureIndex === featureIndex) {
|
|
@@ -157,6 +239,10 @@ export default class OTProcessor {
|
|
|
157
239
|
return null;
|
|
158
240
|
}
|
|
159
241
|
|
|
242
|
+
/**
|
|
243
|
+
* @param {number[]} coords
|
|
244
|
+
* @returns {number}
|
|
245
|
+
*/
|
|
160
246
|
findVariationsIndex(coords) {
|
|
161
247
|
let variations = this.table.featureVariations;
|
|
162
248
|
if (!variations) {
|
|
@@ -174,18 +260,33 @@ export default class OTProcessor {
|
|
|
174
260
|
return -1;
|
|
175
261
|
}
|
|
176
262
|
|
|
263
|
+
/**
|
|
264
|
+
* @param {FeatureVariationCondition[]} conditions
|
|
265
|
+
* @param {number[]} coords
|
|
266
|
+
* @returns {boolean}
|
|
267
|
+
*/
|
|
177
268
|
variationConditionsMatch(conditions, coords) {
|
|
178
|
-
return conditions.every((condition) => {
|
|
269
|
+
return conditions.every(/** @param {FeatureVariationCondition} condition */ (condition) => {
|
|
179
270
|
let coord = condition.axisIndex < coords.length ? coords[condition.axisIndex] : 0;
|
|
180
271
|
return condition.filterRangeMinValue <= coord && coord <= condition.filterRangeMaxValue;
|
|
181
272
|
});
|
|
182
273
|
}
|
|
183
274
|
|
|
275
|
+
/**
|
|
276
|
+
* @param {string[]} userFeatures
|
|
277
|
+
* @param {GlyphInfoLike[]} glyphs
|
|
278
|
+
* @param {GlyphPositionLike[] | null | undefined} [advances]
|
|
279
|
+
*/
|
|
184
280
|
applyFeatures(userFeatures, glyphs, advances) {
|
|
185
281
|
let lookups = this.lookupsForFeatures(userFeatures);
|
|
186
282
|
this.applyLookups(lookups, glyphs, advances);
|
|
187
283
|
}
|
|
188
284
|
|
|
285
|
+
/**
|
|
286
|
+
* @param {FeatureLookup[]} lookups
|
|
287
|
+
* @param {GlyphInfoLike[]} glyphs
|
|
288
|
+
* @param {GlyphPositionLike[] | null | undefined} [positions]
|
|
289
|
+
*/
|
|
189
290
|
applyLookups(lookups, glyphs, positions) {
|
|
190
291
|
this.glyphs = glyphs;
|
|
191
292
|
this.positions = positions;
|
|
@@ -203,7 +304,8 @@ export default class OTProcessor {
|
|
|
203
304
|
);
|
|
204
305
|
|
|
205
306
|
while (this.glyphIterator.index >= 0 && this.glyphIterator.index < glyphs.length) {
|
|
206
|
-
|
|
307
|
+
let cur = this.glyphIterator.cur;
|
|
308
|
+
if (cur && feature in cur.features) {
|
|
207
309
|
for (let table of lookup.subTables) {
|
|
208
310
|
if (this.applyLookup(lookup.lookupType, table)) {
|
|
209
311
|
break;
|
|
@@ -215,28 +317,46 @@ export default class OTProcessor {
|
|
|
215
317
|
}
|
|
216
318
|
}
|
|
217
319
|
|
|
218
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Subclasses may reverse iteration for specific lookup types (e.g. GSUB Type 8).
|
|
322
|
+
* @param {OTLookup} _lookup
|
|
323
|
+
* @returns {number}
|
|
324
|
+
*/
|
|
219
325
|
lookupDirection(_lookup) {
|
|
220
326
|
return 1;
|
|
221
327
|
}
|
|
222
328
|
|
|
329
|
+
/**
|
|
330
|
+
* @param {number} _lookup
|
|
331
|
+
* @param {StructValue} _table
|
|
332
|
+
* @returns {boolean}
|
|
333
|
+
*/
|
|
223
334
|
applyLookup(_lookup, _table) {
|
|
224
335
|
throw new Error('applyLookup must be implemented by subclasses');
|
|
225
336
|
}
|
|
226
337
|
|
|
338
|
+
/**
|
|
339
|
+
* @param {LookupRecord[]} lookupRecords
|
|
340
|
+
* @returns {boolean}
|
|
341
|
+
*/
|
|
227
342
|
applyLookupList(lookupRecords) {
|
|
228
|
-
let
|
|
229
|
-
|
|
230
|
-
|
|
343
|
+
let glyphIterator = this.glyphIterator;
|
|
344
|
+
if (!glyphIterator) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let options = glyphIterator.options;
|
|
349
|
+
let markFilteringSet = glyphIterator.markFilteringSet;
|
|
350
|
+
let glyphIndex = glyphIterator.index;
|
|
231
351
|
|
|
232
352
|
for (let lookupRecord of lookupRecords) {
|
|
233
353
|
// Reset flags and find glyph index for this lookup record
|
|
234
|
-
|
|
235
|
-
|
|
354
|
+
glyphIterator.reset(options, glyphIndex, markFilteringSet);
|
|
355
|
+
glyphIterator.increment(lookupRecord.sequenceIndex);
|
|
236
356
|
|
|
237
357
|
// Get the lookup and setup flags for subtables
|
|
238
358
|
let lookup = this.table.lookupList.get(lookupRecord.lookupListIndex);
|
|
239
|
-
|
|
359
|
+
glyphIterator.reset(lookup.flags, glyphIterator.index, this.getMarkFilteringSet(lookup));
|
|
240
360
|
|
|
241
361
|
// Apply lookup subtables until one matches
|
|
242
362
|
for (let table of lookup.subTables) {
|
|
@@ -246,16 +366,20 @@ export default class OTProcessor {
|
|
|
246
366
|
}
|
|
247
367
|
}
|
|
248
368
|
|
|
249
|
-
|
|
369
|
+
glyphIterator.reset(options, glyphIndex, markFilteringSet);
|
|
250
370
|
return true;
|
|
251
371
|
}
|
|
252
372
|
|
|
373
|
+
/**
|
|
374
|
+
* @param {OTLookup} lookup
|
|
375
|
+
* @returns {MarkFilteringSet | null}
|
|
376
|
+
*/
|
|
253
377
|
getMarkFilteringSet(lookup) {
|
|
254
|
-
if (!lookup.flags.flags
|
|
378
|
+
if (!lookup.flags.flags?.useMarkFilteringSet) {
|
|
255
379
|
return null;
|
|
256
380
|
}
|
|
257
381
|
|
|
258
|
-
let coverage = this.font.GDEF?.markGlyphSetsDef?.coverage?.[lookup.markFilteringSet];
|
|
382
|
+
let coverage = this.font.GDEF?.markGlyphSetsDef?.coverage?.[lookup.markFilteringSet ?? -1];
|
|
259
383
|
if (!coverage) {
|
|
260
384
|
return null;
|
|
261
385
|
}
|
|
@@ -264,25 +388,36 @@ export default class OTProcessor {
|
|
|
264
388
|
let cache = (this._markFilteringCache ??= new Map());
|
|
265
389
|
let filter = cache.get(coverage);
|
|
266
390
|
if (!filter) {
|
|
267
|
-
filter = { has: id => this.coverageIndex(coverage, id) >= 0 };
|
|
391
|
+
filter = { has: /** @param {number} id */ id => this.coverageIndex(coverage, id) >= 0 };
|
|
268
392
|
cache.set(coverage, filter);
|
|
269
393
|
}
|
|
270
394
|
return filter;
|
|
271
395
|
}
|
|
272
396
|
|
|
397
|
+
/**
|
|
398
|
+
* @param {CoverageTable} coverage
|
|
399
|
+
* @param {number | null | undefined} [glyph]
|
|
400
|
+
* @returns {number}
|
|
401
|
+
*/
|
|
273
402
|
coverageIndex(coverage, glyph) {
|
|
274
403
|
if (glyph == null) {
|
|
275
|
-
|
|
404
|
+
let cur = this.glyphIterator?.cur;
|
|
405
|
+
if (!cur) {
|
|
406
|
+
return -1;
|
|
407
|
+
}
|
|
408
|
+
glyph = cur.id;
|
|
276
409
|
}
|
|
277
410
|
|
|
278
411
|
switch (coverage.version) {
|
|
279
412
|
case 1:
|
|
280
|
-
return coverage.glyphs.indexOf(glyph);
|
|
413
|
+
return coverage.glyphs ? coverage.glyphs.indexOf(glyph) : -1;
|
|
281
414
|
|
|
282
415
|
case 2:
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
416
|
+
if (coverage.rangeRecords) {
|
|
417
|
+
for (let range of coverage.rangeRecords) {
|
|
418
|
+
if (range.start <= glyph && glyph <= range.end) {
|
|
419
|
+
return range.startCoverageIndex + glyph - range.start;
|
|
420
|
+
}
|
|
286
421
|
}
|
|
287
422
|
}
|
|
288
423
|
|
|
@@ -292,21 +427,35 @@ export default class OTProcessor {
|
|
|
292
427
|
return -1;
|
|
293
428
|
}
|
|
294
429
|
|
|
430
|
+
/**
|
|
431
|
+
* @template T
|
|
432
|
+
* @param {number} sequenceIndex
|
|
433
|
+
* @param {T[]} sequence
|
|
434
|
+
* @param {(component: T, glyph: GlyphInfoLike) => boolean} fn
|
|
435
|
+
* @param {number[] | null | undefined} [matched]
|
|
436
|
+
* @returns {boolean | number[]}
|
|
437
|
+
*/
|
|
295
438
|
match(sequenceIndex, sequence, fn, matched) {
|
|
296
|
-
let
|
|
297
|
-
|
|
439
|
+
let glyphIterator = this.glyphIterator;
|
|
440
|
+
if (!glyphIterator) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
let pos = glyphIterator.index;
|
|
445
|
+
/** @type {GlyphInfoLike | null | undefined} */
|
|
446
|
+
let glyph = glyphIterator.increment(sequenceIndex);
|
|
298
447
|
let idx = 0;
|
|
299
448
|
|
|
300
449
|
while (idx < sequence.length && glyph && fn(sequence[idx], glyph)) {
|
|
301
450
|
if (matched) {
|
|
302
|
-
matched.push(
|
|
451
|
+
matched.push(glyphIterator.index);
|
|
303
452
|
}
|
|
304
453
|
|
|
305
454
|
idx++;
|
|
306
|
-
glyph =
|
|
455
|
+
glyph = glyphIterator.next();
|
|
307
456
|
}
|
|
308
457
|
|
|
309
|
-
|
|
458
|
+
glyphIterator.index = pos;
|
|
310
459
|
if (idx < sequence.length) {
|
|
311
460
|
return false;
|
|
312
461
|
}
|
|
@@ -314,32 +463,58 @@ export default class OTProcessor {
|
|
|
314
463
|
return matched || true;
|
|
315
464
|
}
|
|
316
465
|
|
|
466
|
+
/**
|
|
467
|
+
* @param {number} sequenceIndex
|
|
468
|
+
* @param {number[]} sequence
|
|
469
|
+
* @returns {boolean | number[]}
|
|
470
|
+
*/
|
|
317
471
|
sequenceMatches(sequenceIndex, sequence) {
|
|
318
|
-
return this.match(sequenceIndex, sequence, (component, glyph) => component === glyph.id);
|
|
472
|
+
return this.match(sequenceIndex, sequence, /** @param {number} component @param {GlyphInfoLike} glyph */ (component, glyph) => component === glyph.id);
|
|
319
473
|
}
|
|
320
474
|
|
|
475
|
+
/**
|
|
476
|
+
* @param {number} sequenceIndex
|
|
477
|
+
* @param {number[]} sequence
|
|
478
|
+
* @returns {false | number[]}
|
|
479
|
+
*/
|
|
321
480
|
sequenceMatchIndices(sequenceIndex, sequence) {
|
|
322
|
-
|
|
481
|
+
let result = this.match(sequenceIndex, sequence, /** @param {number} component @param {GlyphInfoLike} glyph */ (component, glyph) => {
|
|
323
482
|
// If the current feature doesn't apply to this glyph,
|
|
324
|
-
if (!(this.currentFeature in glyph.features)) {
|
|
483
|
+
if (!(this.currentFeature && this.currentFeature in glyph.features)) {
|
|
325
484
|
return false;
|
|
326
485
|
}
|
|
327
486
|
|
|
328
487
|
return component === glyph.id;
|
|
329
488
|
}, []);
|
|
489
|
+
return result === false ? false : /** @type {number[]} */ (result);
|
|
330
490
|
}
|
|
331
491
|
|
|
492
|
+
/**
|
|
493
|
+
* @param {number} sequenceIndex
|
|
494
|
+
* @param {CoverageTable[]} sequence
|
|
495
|
+
* @returns {boolean | number[]}
|
|
496
|
+
*/
|
|
332
497
|
coverageSequenceMatches(sequenceIndex, sequence) {
|
|
333
|
-
return this.match(sequenceIndex, sequence, (coverage, glyph) =>
|
|
498
|
+
return this.match(sequenceIndex, sequence, /** @param {CoverageTable} coverage @param {GlyphInfoLike} glyph */ (coverage, glyph) =>
|
|
334
499
|
this.coverageIndex(coverage, glyph.id) >= 0
|
|
335
500
|
);
|
|
336
501
|
}
|
|
337
502
|
|
|
503
|
+
/**
|
|
504
|
+
* @param {number} glyph
|
|
505
|
+
* @param {ClassDefTable | null | undefined} classDef
|
|
506
|
+
* @returns {number}
|
|
507
|
+
*/
|
|
338
508
|
getClassID(glyph, classDef) {
|
|
509
|
+
// Offset 0 ClassDefs decode as null; treat as class 0 (OT default).
|
|
510
|
+
if (!classDef) {
|
|
511
|
+
return 0;
|
|
512
|
+
}
|
|
513
|
+
|
|
339
514
|
switch (classDef.version) {
|
|
340
515
|
case 1: { // Class array
|
|
341
|
-
let i = glyph - classDef.startGlyph;
|
|
342
|
-
if (i >= 0 && i < classDef.classValueArray.length) {
|
|
516
|
+
let i = glyph - (classDef.startGlyph ?? 0);
|
|
517
|
+
if (classDef.classValueArray && i >= 0 && i < classDef.classValueArray.length) {
|
|
343
518
|
return classDef.classValueArray[i];
|
|
344
519
|
}
|
|
345
520
|
|
|
@@ -347,9 +522,11 @@ export default class OTProcessor {
|
|
|
347
522
|
}
|
|
348
523
|
|
|
349
524
|
case 2:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
525
|
+
if (classDef.classRangeRecord) {
|
|
526
|
+
for (let range of classDef.classRangeRecord) {
|
|
527
|
+
if (range.start <= glyph && glyph <= range.end) {
|
|
528
|
+
return range.class;
|
|
529
|
+
}
|
|
353
530
|
}
|
|
354
531
|
}
|
|
355
532
|
|
|
@@ -359,22 +536,38 @@ export default class OTProcessor {
|
|
|
359
536
|
return 0;
|
|
360
537
|
}
|
|
361
538
|
|
|
539
|
+
/**
|
|
540
|
+
* @param {number} sequenceIndex
|
|
541
|
+
* @param {number[]} sequence
|
|
542
|
+
* @param {ClassDefTable | null | undefined} classDef
|
|
543
|
+
* @returns {boolean | number[]}
|
|
544
|
+
*/
|
|
362
545
|
classSequenceMatches(sequenceIndex, sequence, classDef) {
|
|
363
|
-
return this.match(sequenceIndex, sequence, (classID, glyph) =>
|
|
546
|
+
return this.match(sequenceIndex, sequence, /** @param {number} classID @param {GlyphInfoLike} glyph */ (classID, glyph) =>
|
|
364
547
|
classID === this.getClassID(glyph.id, classDef)
|
|
365
548
|
);
|
|
366
549
|
}
|
|
367
550
|
|
|
551
|
+
/**
|
|
552
|
+
* @param {ContextSubtable | StructValue} table
|
|
553
|
+
* @returns {boolean}
|
|
554
|
+
*/
|
|
368
555
|
applyContext(table) {
|
|
369
|
-
let
|
|
370
|
-
|
|
556
|
+
let ctx = /** @type {ContextSubtable} */ (table);
|
|
557
|
+
let index;
|
|
558
|
+
/** @type {import('../../types/fontkit').ContextRule[] | null | undefined} */
|
|
559
|
+
let set;
|
|
560
|
+
switch (ctx.version) {
|
|
371
561
|
case 1:
|
|
372
|
-
index = this.coverageIndex(
|
|
562
|
+
index = this.coverageIndex(ctx.coverage);
|
|
373
563
|
if (index === -1) {
|
|
374
564
|
return false;
|
|
375
565
|
}
|
|
376
566
|
|
|
377
|
-
set =
|
|
567
|
+
set = ctx.ruleSets?.[index];
|
|
568
|
+
if (!set) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
378
571
|
for (let rule of set) {
|
|
379
572
|
if (this.sequenceMatches(1, rule.input)) {
|
|
380
573
|
return this.applyLookupList(rule.lookupRecords);
|
|
@@ -383,28 +576,33 @@ export default class OTProcessor {
|
|
|
383
576
|
|
|
384
577
|
break;
|
|
385
578
|
|
|
386
|
-
case 2:
|
|
387
|
-
if (this.coverageIndex(
|
|
579
|
+
case 2: {
|
|
580
|
+
if (this.coverageIndex(ctx.coverage) === -1) {
|
|
388
581
|
return false;
|
|
389
582
|
}
|
|
390
583
|
|
|
391
|
-
|
|
392
|
-
if (
|
|
584
|
+
let cur = this.glyphIterator?.cur;
|
|
585
|
+
if (!cur || !ctx.classDef) {
|
|
393
586
|
return false;
|
|
394
587
|
}
|
|
395
588
|
|
|
396
|
-
|
|
589
|
+
index = this.getClassID(cur.id, ctx.classDef);
|
|
590
|
+
set = ctx.classSet?.[index];
|
|
591
|
+
if (!set) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
397
594
|
for (let rule of set) {
|
|
398
|
-
if (this.classSequenceMatches(1, rule.classes,
|
|
595
|
+
if (this.classSequenceMatches(1, rule.classes ?? [], ctx.classDef)) {
|
|
399
596
|
return this.applyLookupList(rule.lookupRecords);
|
|
400
597
|
}
|
|
401
598
|
}
|
|
402
599
|
|
|
403
600
|
break;
|
|
601
|
+
}
|
|
404
602
|
|
|
405
603
|
case 3:
|
|
406
|
-
if (this.coverageSequenceMatches(0,
|
|
407
|
-
return this.applyLookupList(
|
|
604
|
+
if (ctx.coverages && ctx.lookupRecords && this.coverageSequenceMatches(0, ctx.coverages)) {
|
|
605
|
+
return this.applyLookupList(ctx.lookupRecords);
|
|
408
606
|
}
|
|
409
607
|
|
|
410
608
|
break;
|
|
@@ -413,16 +611,24 @@ export default class OTProcessor {
|
|
|
413
611
|
return false;
|
|
414
612
|
}
|
|
415
613
|
|
|
614
|
+
/**
|
|
615
|
+
* @param {ChainContextSubtable | StructValue} table
|
|
616
|
+
* @returns {boolean}
|
|
617
|
+
*/
|
|
416
618
|
applyChainingContext(table) {
|
|
619
|
+
let ctx = /** @type {ChainContextSubtable} */ (table);
|
|
417
620
|
let index;
|
|
418
|
-
switch (
|
|
621
|
+
switch (ctx.version) {
|
|
419
622
|
case 1: {
|
|
420
|
-
index = this.coverageIndex(
|
|
623
|
+
index = this.coverageIndex(ctx.coverage);
|
|
421
624
|
if (index === -1) {
|
|
422
625
|
return false;
|
|
423
626
|
}
|
|
424
627
|
|
|
425
|
-
let set =
|
|
628
|
+
let set = ctx.chainRuleSets?.[index];
|
|
629
|
+
if (!set) {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
426
632
|
for (let rule of set) {
|
|
427
633
|
// Backtrack is stored closest-first; sequenceMatches walks furthest-first.
|
|
428
634
|
if (this.sequenceMatches(-rule.backtrack.length, [...rule.backtrack].reverse())
|
|
@@ -436,20 +642,26 @@ export default class OTProcessor {
|
|
|
436
642
|
}
|
|
437
643
|
|
|
438
644
|
case 2: {
|
|
439
|
-
if (this.coverageIndex(
|
|
645
|
+
if (this.coverageIndex(ctx.coverage) === -1) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
let cur = this.glyphIterator?.cur;
|
|
650
|
+
if (!cur || !ctx.inputClassDef) {
|
|
440
651
|
return false;
|
|
441
652
|
}
|
|
442
653
|
|
|
443
|
-
index = this.getClassID(
|
|
444
|
-
let rules =
|
|
654
|
+
index = this.getClassID(cur.id, ctx.inputClassDef);
|
|
655
|
+
let rules = ctx.chainClassSet?.[index];
|
|
445
656
|
if (!rules) {
|
|
446
657
|
return false;
|
|
447
658
|
}
|
|
448
659
|
|
|
449
660
|
for (let rule of rules) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
&& this.classSequenceMatches(1
|
|
661
|
+
// Backtrack/lookahead ClassDefs may be NULL (offset 0) when unused.
|
|
662
|
+
if (this.classSequenceMatches(-rule.backtrack.length, [...rule.backtrack].reverse(), ctx.backtrackClassDef)
|
|
663
|
+
&& this.classSequenceMatches(1, rule.input, ctx.inputClassDef)
|
|
664
|
+
&& this.classSequenceMatches(1 + rule.input.length, rule.lookahead, ctx.lookaheadClassDef)) {
|
|
453
665
|
return this.applyLookupList(rule.lookupRecords);
|
|
454
666
|
}
|
|
455
667
|
}
|
|
@@ -458,10 +670,11 @@ export default class OTProcessor {
|
|
|
458
670
|
}
|
|
459
671
|
|
|
460
672
|
case 3:
|
|
461
|
-
if (
|
|
462
|
-
&& this.coverageSequenceMatches(
|
|
463
|
-
&& this.coverageSequenceMatches(
|
|
464
|
-
|
|
673
|
+
if (ctx.backtrackCoverage && ctx.inputCoverage && ctx.lookaheadCoverage && ctx.lookupRecords
|
|
674
|
+
&& this.coverageSequenceMatches(-(ctx.backtrackGlyphCount ?? ctx.backtrackCoverage.length), [...ctx.backtrackCoverage].reverse())
|
|
675
|
+
&& this.coverageSequenceMatches(0, ctx.inputCoverage)
|
|
676
|
+
&& this.coverageSequenceMatches(ctx.inputGlyphCount ?? ctx.inputCoverage.length, ctx.lookaheadCoverage)) {
|
|
677
|
+
return this.applyLookupList(ctx.lookupRecords);
|
|
465
678
|
}
|
|
466
679
|
|
|
467
680
|
break;
|