@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
|
@@ -1,28 +1,73 @@
|
|
|
1
1
|
import OTProcessor from './OTProcessor';
|
|
2
2
|
import GlyphInfo from './GlyphInfo';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('../../types/fontkit').CoverageTable} CoverageTable */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').OTLookup} OTLookup */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').ExtensionSubtable} ExtensionSubtable */
|
|
7
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {StructValue & {
|
|
11
|
+
* coverage?: CoverageTable,
|
|
12
|
+
* version?: number,
|
|
13
|
+
* deltaGlyphID?: number,
|
|
14
|
+
* substitute?: import('../../types/fontkit').LazyList<number>,
|
|
15
|
+
* sequences?: import('../../types/fontkit').LazyList<number[]>,
|
|
16
|
+
* alternateSet?: import('../../types/fontkit').LazyList<number[]>,
|
|
17
|
+
* ligatureSets?: import('../../types/fontkit').LazyList<Array<{ glyph: number, components: number[] }>>,
|
|
18
|
+
* lookupType?: number,
|
|
19
|
+
* extension?: StructValue,
|
|
20
|
+
* backtrackGlyphCount?: number,
|
|
21
|
+
* backtrackCoverage?: CoverageTable[],
|
|
22
|
+
* lookaheadCoverage?: CoverageTable[],
|
|
23
|
+
* }} GSUBSubtable
|
|
24
|
+
*/
|
|
25
|
+
|
|
4
26
|
export default class GSUBProcessor extends OTProcessor {
|
|
27
|
+
/**
|
|
28
|
+
* @param {OTLookup} lookup
|
|
29
|
+
* @returns {number}
|
|
30
|
+
*/
|
|
5
31
|
lookupDirection(lookup) {
|
|
6
|
-
let
|
|
32
|
+
let first = lookup.subTables[0];
|
|
33
|
+
let type = lookup.lookupType === 7
|
|
34
|
+
? (first && typeof first === 'object' && 'lookupType' in first
|
|
35
|
+
? /** @type {number} */ (/** @type {{ lookupType?: number }} */ (first).lookupType)
|
|
36
|
+
: undefined)
|
|
37
|
+
: lookup.lookupType;
|
|
7
38
|
return type === 8 ? -1 : 1;
|
|
8
39
|
}
|
|
9
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @param {number} lookupType
|
|
43
|
+
* @param {StructValue} table
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
10
46
|
applyLookup(lookupType, table) {
|
|
47
|
+
let t = /** @type {GSUBSubtable} */ (table);
|
|
48
|
+
let glyphIterator = this.glyphIterator;
|
|
49
|
+
if (!glyphIterator) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
11
53
|
switch (lookupType) {
|
|
12
54
|
case 1: { // Single Substitution
|
|
13
|
-
|
|
55
|
+
if (!t.coverage) return false;
|
|
56
|
+
let index = this.coverageIndex(t.coverage);
|
|
14
57
|
if (index === -1) {
|
|
15
58
|
return false;
|
|
16
59
|
}
|
|
17
60
|
|
|
18
|
-
let glyph =
|
|
19
|
-
|
|
61
|
+
let glyph = glyphIterator.cur;
|
|
62
|
+
if (!glyph) return false;
|
|
63
|
+
|
|
64
|
+
switch (t.version) {
|
|
20
65
|
case 1:
|
|
21
|
-
glyph.id = (glyph.id +
|
|
66
|
+
glyph.id = (glyph.id + (t.deltaGlyphID ?? 0)) & 0xffff;
|
|
22
67
|
break;
|
|
23
68
|
|
|
24
69
|
case 2:
|
|
25
|
-
glyph.id =
|
|
70
|
+
if (t.substitute) glyph.id = t.substitute.get(index);
|
|
26
71
|
break;
|
|
27
72
|
}
|
|
28
73
|
|
|
@@ -30,23 +75,26 @@ export default class GSUBProcessor extends OTProcessor {
|
|
|
30
75
|
}
|
|
31
76
|
|
|
32
77
|
case 2: { // Multiple Substitution
|
|
33
|
-
|
|
78
|
+
if (!t.coverage || !t.sequences) return false;
|
|
79
|
+
let index = this.coverageIndex(t.coverage);
|
|
34
80
|
if (index !== -1) {
|
|
35
|
-
let sequence =
|
|
81
|
+
let sequence = t.sequences.get(index);
|
|
36
82
|
|
|
37
83
|
if (sequence.length === 0) {
|
|
38
84
|
// If the sequence length is zero, delete the glyph.
|
|
39
85
|
// The OpenType spec disallows this, but seems like Harfbuzz and Uniscribe allow it.
|
|
40
|
-
this.glyphs.splice(
|
|
86
|
+
this.glyphs.splice(glyphIterator.index, 1);
|
|
41
87
|
return true;
|
|
42
88
|
}
|
|
43
89
|
|
|
44
|
-
|
|
45
|
-
|
|
90
|
+
let curGlyph = glyphIterator.cur;
|
|
91
|
+
if (!curGlyph) return false;
|
|
46
92
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
93
|
+
curGlyph.id = sequence[0];
|
|
94
|
+
curGlyph.ligatureComponent = 0;
|
|
95
|
+
|
|
96
|
+
let features = curGlyph.features;
|
|
97
|
+
let replacement = sequence.slice(1).map(/** @param {number} gid @param {number} i */ (gid, i) => {
|
|
50
98
|
let glyph = new GlyphInfo(this.font, gid, undefined, features);
|
|
51
99
|
glyph.shaperInfo = curGlyph.shaperInfo;
|
|
52
100
|
glyph.isLigated = curGlyph.isLigated;
|
|
@@ -56,7 +104,7 @@ export default class GSUBProcessor extends OTProcessor {
|
|
|
56
104
|
return glyph;
|
|
57
105
|
});
|
|
58
106
|
|
|
59
|
-
this.glyphs.splice(
|
|
107
|
+
this.glyphs.splice(glyphIterator.index + 1, 0, ...replacement);
|
|
60
108
|
return true;
|
|
61
109
|
}
|
|
62
110
|
|
|
@@ -64,31 +112,37 @@ export default class GSUBProcessor extends OTProcessor {
|
|
|
64
112
|
}
|
|
65
113
|
|
|
66
114
|
case 3: { // Alternate Substitution
|
|
67
|
-
|
|
115
|
+
if (!t.coverage || !t.alternateSet) return false;
|
|
116
|
+
let index = this.coverageIndex(t.coverage);
|
|
68
117
|
if (index === -1) return false;
|
|
69
118
|
|
|
70
119
|
// 1-based index from user features (e.g. { aalt: 2 }); boolean / missing → first
|
|
71
|
-
let alternates =
|
|
72
|
-
let
|
|
73
|
-
|
|
74
|
-
|
|
120
|
+
let alternates = t.alternateSet.get(index);
|
|
121
|
+
let cur = glyphIterator.cur;
|
|
122
|
+
if (!cur) return false;
|
|
123
|
+
|
|
124
|
+
let alt = this.currentFeature && this.userFeatures ? this.userFeatures[this.currentFeature] : undefined;
|
|
125
|
+
let i = typeof alt === 'number' && Number.isInteger(alt) && alt > 0 ? alt - 1 : 0;
|
|
126
|
+
cur.id = alternates[i < alternates.length ? i : 0];
|
|
75
127
|
return true;
|
|
76
128
|
}
|
|
77
129
|
|
|
78
130
|
case 4: { // Ligature Substitution
|
|
79
|
-
|
|
131
|
+
if (!t.coverage || !t.ligatureSets) return false;
|
|
132
|
+
let index = this.coverageIndex(t.coverage);
|
|
80
133
|
if (index === -1) return false;
|
|
81
134
|
|
|
82
|
-
for (let ligature of
|
|
135
|
+
for (let ligature of t.ligatureSets.get(index)) {
|
|
83
136
|
let matched = this.sequenceMatchIndices(1, ligature.components);
|
|
84
137
|
if (!matched) continue;
|
|
85
138
|
|
|
86
|
-
let curGlyph =
|
|
139
|
+
let curGlyph = glyphIterator.cur;
|
|
140
|
+
if (!curGlyph) continue;
|
|
87
141
|
|
|
88
142
|
// Concatenate all of the characters the new ligature will represent
|
|
89
143
|
let characters = curGlyph.codePoints.slice();
|
|
90
|
-
for (let
|
|
91
|
-
characters.push(...this.glyphs[
|
|
144
|
+
for (let matchIdx of matched) {
|
|
145
|
+
characters.push(...this.glyphs[matchIdx].codePoints);
|
|
92
146
|
}
|
|
93
147
|
|
|
94
148
|
// Create the replacement ligature glyph
|
|
@@ -131,7 +185,7 @@ export default class GSUBProcessor extends OTProcessor {
|
|
|
131
185
|
let lastLigID = curGlyph.ligatureID;
|
|
132
186
|
let lastNumComps = curGlyph.codePoints.length;
|
|
133
187
|
let curComps = lastNumComps;
|
|
134
|
-
let idx =
|
|
188
|
+
let idx = glyphIterator.index + 1;
|
|
135
189
|
|
|
136
190
|
// Set ligatureID and ligatureComponent on glyphs that were skipped in the matched sequence.
|
|
137
191
|
// This allows GPOS to attach marks to the correct ligature components.
|
|
@@ -171,7 +225,7 @@ export default class GSUBProcessor extends OTProcessor {
|
|
|
171
225
|
this.glyphs.splice(matched[i], 1);
|
|
172
226
|
}
|
|
173
227
|
|
|
174
|
-
this.glyphs[
|
|
228
|
+
this.glyphs[glyphIterator.index] = ligatureGlyph;
|
|
175
229
|
return true;
|
|
176
230
|
}
|
|
177
231
|
|
|
@@ -184,20 +238,25 @@ export default class GSUBProcessor extends OTProcessor {
|
|
|
184
238
|
case 6: // Chaining Contextual Substitution
|
|
185
239
|
return this.applyChainingContext(table);
|
|
186
240
|
|
|
187
|
-
case 7: // Extension Substitution
|
|
188
|
-
|
|
241
|
+
case 7: { // Extension Substitution
|
|
242
|
+
let ext = /** @type {ExtensionSubtable} */ (table);
|
|
243
|
+
return this.applyLookup(ext.lookupType, ext.extension);
|
|
244
|
+
}
|
|
189
245
|
|
|
190
246
|
case 8: { // Reverse Chaining Contextual Single Substitution
|
|
191
|
-
|
|
247
|
+
if (!t.coverage || !t.substitute || !t.backtrackCoverage || !t.lookaheadCoverage) return false;
|
|
248
|
+
let index = this.coverageIndex(t.coverage);
|
|
192
249
|
if (index === -1) return false;
|
|
193
250
|
|
|
194
251
|
// Backtrack is stored closest-first; coverageSequenceMatches walks furthest-first.
|
|
195
|
-
if (!this.coverageSequenceMatches(-
|
|
196
|
-
|| !this.coverageSequenceMatches(1,
|
|
252
|
+
if (!this.coverageSequenceMatches(-(t.backtrackGlyphCount ?? t.backtrackCoverage.length), [...t.backtrackCoverage].reverse())
|
|
253
|
+
|| !this.coverageSequenceMatches(1, t.lookaheadCoverage)) {
|
|
197
254
|
return false;
|
|
198
255
|
}
|
|
199
256
|
|
|
200
|
-
|
|
257
|
+
let cur = glyphIterator.cur;
|
|
258
|
+
if (!cur) return false;
|
|
259
|
+
cur.id = t.substitute.get(index);
|
|
201
260
|
return true;
|
|
202
261
|
}
|
|
203
262
|
|
|
@@ -1,36 +1,79 @@
|
|
|
1
|
-
import {isMark} from '../packages/unicode-properties/index.js';
|
|
1
|
+
import { isMark } from '../packages/unicode-properties/index.js';
|
|
2
2
|
import OTProcessor from './OTProcessor';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').FeatureMap} FeatureMap */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').FeatureInput} FeatureInput */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').ClassDefTable} ClassDefTable */
|
|
8
|
+
|
|
4
9
|
export default class GlyphInfo {
|
|
10
|
+
/**
|
|
11
|
+
* @param {LayoutFont} font
|
|
12
|
+
* @param {number} id
|
|
13
|
+
* @param {number[]} [codePoints]
|
|
14
|
+
* @param {FeatureInput | null | undefined} [features]
|
|
15
|
+
*/
|
|
5
16
|
constructor(font, id, codePoints = [], features) {
|
|
17
|
+
/** @type {LayoutFont} */
|
|
6
18
|
this._font = font;
|
|
19
|
+
/** @type {number[]} */
|
|
7
20
|
this.codePoints = codePoints;
|
|
8
|
-
|
|
21
|
+
/** @type {number} */
|
|
22
|
+
this._id = 0;
|
|
9
23
|
|
|
24
|
+
/** @type {FeatureMap} */
|
|
10
25
|
this.features = {};
|
|
11
26
|
if (Array.isArray(features)) {
|
|
12
27
|
for (let i = 0; i < features.length; i++) {
|
|
13
28
|
let feature = features[i];
|
|
14
29
|
this.features[feature] = true;
|
|
15
30
|
}
|
|
16
|
-
} else if (typeof features === 'object') {
|
|
31
|
+
} else if (typeof features === 'object' && features !== null) {
|
|
17
32
|
Object.assign(this.features, features);
|
|
18
33
|
}
|
|
19
34
|
|
|
35
|
+
/** @type {number | null} */
|
|
20
36
|
this.ligatureID = null;
|
|
37
|
+
/** @type {number | null} */
|
|
21
38
|
this.ligatureComponent = null;
|
|
39
|
+
/** @type {boolean} */
|
|
22
40
|
this.isLigated = false;
|
|
41
|
+
/** @type {number | null} */
|
|
23
42
|
this.cursiveAttachment = null;
|
|
43
|
+
/** @type {number | null} */
|
|
24
44
|
this.markAttachment = null;
|
|
45
|
+
/** @type {import('../../types/fontkit').IndicShaperInfo | import('../../types/fontkit').USEShaperInfo | null} */
|
|
25
46
|
this.shaperInfo = null;
|
|
47
|
+
/** @type {boolean} */
|
|
26
48
|
this.substituted = false;
|
|
49
|
+
/** @type {boolean} */
|
|
27
50
|
this.isMultiplied = false;
|
|
51
|
+
|
|
52
|
+
/** @type {boolean} */
|
|
53
|
+
this.isBase = false;
|
|
54
|
+
/** @type {boolean} */
|
|
55
|
+
this.isLigature = false;
|
|
56
|
+
/** @type {boolean} */
|
|
57
|
+
this.isMark = false;
|
|
58
|
+
/** @type {number} */
|
|
59
|
+
this.markAttachmentType = 0;
|
|
60
|
+
|
|
61
|
+
// Apply GDEF / mark classification via the id setter, then clear
|
|
62
|
+
// `substituted` — construction is not a GSUB substitution.
|
|
63
|
+
this.id = id;
|
|
64
|
+
this.substituted = false;
|
|
28
65
|
}
|
|
29
66
|
|
|
67
|
+
/**
|
|
68
|
+
* @returns {number}
|
|
69
|
+
*/
|
|
30
70
|
get id() {
|
|
31
71
|
return this._id;
|
|
32
72
|
}
|
|
33
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @param {number} id
|
|
76
|
+
*/
|
|
34
77
|
set id(id) {
|
|
35
78
|
this._id = id;
|
|
36
79
|
this.substituted = true;
|
|
@@ -38,11 +81,13 @@ export default class GlyphInfo {
|
|
|
38
81
|
let GDEF = this._font.GDEF;
|
|
39
82
|
if (GDEF && GDEF.glyphClassDef) {
|
|
40
83
|
// TODO: clean this up
|
|
41
|
-
let classID = OTProcessor.prototype.getClassID(id, GDEF.glyphClassDef);
|
|
84
|
+
let classID = OTProcessor.prototype.getClassID(id, /** @type {ClassDefTable} */ (GDEF.glyphClassDef));
|
|
42
85
|
this.isBase = classID === 1;
|
|
43
86
|
this.isLigature = classID === 2;
|
|
44
87
|
this.isMark = classID === 3;
|
|
45
|
-
this.markAttachmentType = GDEF.markAttachClassDef
|
|
88
|
+
this.markAttachmentType = GDEF.markAttachClassDef
|
|
89
|
+
? OTProcessor.prototype.getClassID(id, /** @type {ClassDefTable} */ (GDEF.markAttachClassDef))
|
|
90
|
+
: 0;
|
|
46
91
|
} else {
|
|
47
92
|
this.isMark = this.codePoints.length > 0 && this.codePoints.every(isMark);
|
|
48
93
|
this.isBase = !this.isMark;
|
|
@@ -51,6 +96,9 @@ export default class GlyphInfo {
|
|
|
51
96
|
}
|
|
52
97
|
}
|
|
53
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @returns {GlyphInfo}
|
|
101
|
+
*/
|
|
54
102
|
copy() {
|
|
55
103
|
return new GlyphInfo(this._font, this.id, this.codePoints, this.features);
|
|
56
104
|
}
|
|
@@ -1,21 +1,48 @@
|
|
|
1
|
+
/** @typedef {import('../../types/fontkit').GlyphInfoLike} GlyphInfoLike */
|
|
2
|
+
/** @typedef {import('../../types/fontkit').GlyphIteratorOptions} GlyphIteratorOptions */
|
|
3
|
+
/** @typedef {import('../../types/fontkit').LookupFlags} LookupFlags */
|
|
4
|
+
/** @typedef {import('../../types/fontkit').MarkFilteringSet} MarkFilteringSet */
|
|
5
|
+
|
|
1
6
|
export default class GlyphIterator {
|
|
7
|
+
/**
|
|
8
|
+
* @param {GlyphInfoLike[]} glyphs
|
|
9
|
+
* @param {GlyphIteratorOptions} [options]
|
|
10
|
+
*/
|
|
2
11
|
constructor(glyphs, options) {
|
|
12
|
+
/** @type {GlyphInfoLike[]} */
|
|
3
13
|
this.glyphs = glyphs;
|
|
4
14
|
this.reset(options);
|
|
5
15
|
}
|
|
6
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @param {GlyphIteratorOptions} [options]
|
|
19
|
+
* @param {number} [index]
|
|
20
|
+
* @param {MarkFilteringSet | null} [markFilteringSet]
|
|
21
|
+
*/
|
|
7
22
|
reset(options = {}, index = 0, markFilteringSet = null) {
|
|
23
|
+
/** @type {GlyphIteratorOptions} */
|
|
8
24
|
this.options = options;
|
|
25
|
+
/** @type {LookupFlags} */
|
|
9
26
|
this.flags = options.flags || {};
|
|
27
|
+
/** @type {number} */
|
|
10
28
|
this.markAttachmentType = options.markAttachmentType || 0;
|
|
29
|
+
/** @type {MarkFilteringSet | null} */
|
|
11
30
|
this.markFilteringSet = markFilteringSet;
|
|
31
|
+
/** @type {number} */
|
|
12
32
|
this.index = index;
|
|
13
33
|
}
|
|
14
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @returns {GlyphInfoLike | null}
|
|
37
|
+
*/
|
|
15
38
|
get cur() {
|
|
16
39
|
return this.glyphs[this.index] || null;
|
|
17
40
|
}
|
|
18
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @param {GlyphInfoLike} glyph
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
19
46
|
shouldIgnore(glyph) {
|
|
20
47
|
// Mark skip hierarchy (OT chapter 2): ignoreMarks > mark filtering set > markAttachmentType.
|
|
21
48
|
if (glyph.isMark) {
|
|
@@ -30,10 +57,14 @@ export default class GlyphIterator {
|
|
|
30
57
|
}
|
|
31
58
|
}
|
|
32
59
|
|
|
33
|
-
return (this.flags.ignoreBaseGlyphs && glyph.isBase)
|
|
34
|
-
|
|
60
|
+
return !!(this.flags.ignoreBaseGlyphs && glyph.isBase)
|
|
61
|
+
|| !!(this.flags.ignoreLigatures && glyph.isLigature);
|
|
35
62
|
}
|
|
36
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @param {number} dir
|
|
66
|
+
* @returns {GlyphInfoLike | null}
|
|
67
|
+
*/
|
|
37
68
|
move(dir) {
|
|
38
69
|
this.index += dir;
|
|
39
70
|
while (0 <= this.index && this.index < this.glyphs.length && this.shouldIgnore(this.glyphs[this.index])) {
|
|
@@ -47,14 +78,24 @@ export default class GlyphIterator {
|
|
|
47
78
|
return this.glyphs[this.index];
|
|
48
79
|
}
|
|
49
80
|
|
|
81
|
+
/**
|
|
82
|
+
* @returns {GlyphInfoLike | null}
|
|
83
|
+
*/
|
|
50
84
|
next() {
|
|
51
85
|
return this.move(+1);
|
|
52
86
|
}
|
|
53
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @returns {GlyphInfoLike | null}
|
|
90
|
+
*/
|
|
54
91
|
prev() {
|
|
55
92
|
return this.move(-1);
|
|
56
93
|
}
|
|
57
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {number} [count]
|
|
97
|
+
* @returns {GlyphInfoLike | undefined}
|
|
98
|
+
*/
|
|
58
99
|
peek(count = 1) {
|
|
59
100
|
let idx = this.index;
|
|
60
101
|
let res = this.increment(count);
|
|
@@ -62,6 +103,10 @@ export default class GlyphIterator {
|
|
|
62
103
|
return res;
|
|
63
104
|
}
|
|
64
105
|
|
|
106
|
+
/**
|
|
107
|
+
* @param {number} [count]
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
65
110
|
peekIndex(count = 1) {
|
|
66
111
|
let idx = this.index;
|
|
67
112
|
this.increment(count);
|
|
@@ -70,6 +115,10 @@ export default class GlyphIterator {
|
|
|
70
115
|
return res;
|
|
71
116
|
}
|
|
72
117
|
|
|
118
|
+
/**
|
|
119
|
+
* @param {number} [count]
|
|
120
|
+
* @returns {GlyphInfoLike | undefined}
|
|
121
|
+
*/
|
|
73
122
|
increment(count = 1) {
|
|
74
123
|
let dir = count < 0 ? -1 : 1;
|
|
75
124
|
count = Math.abs(count);
|
|
@@ -4,14 +4,35 @@ import GlyphInfo from './GlyphInfo';
|
|
|
4
4
|
import GSUBProcessor from './GSUBProcessor';
|
|
5
5
|
import GPOSProcessor from './GPOSProcessor';
|
|
6
6
|
|
|
7
|
+
/** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
|
|
8
|
+
/** @typedef {import('../../types/fontkit').GlyphRunLike} GlyphRunLike */
|
|
9
|
+
/** @typedef {import('../../types/fontkit').GlyphInfoLike} GlyphInfoLike */
|
|
10
|
+
/** @typedef {import('../../types/fontkit').GlyphPositionLike} GlyphPositionLike */
|
|
11
|
+
/** @typedef {import('../../types/fontkit').FeatureMap} FeatureMap */
|
|
12
|
+
/** @typedef {import('../../types/fontkit').ScriptTag} ScriptTag */
|
|
13
|
+
/** @typedef {import('../../types/fontkit').LanguageTag} LanguageTag */
|
|
14
|
+
/** @typedef {import('../../types/fontkit').LayoutGlyph} LayoutGlyph */
|
|
15
|
+
/** @typedef {import('../../types/fontkit').ShaperLike} ShaperLike */
|
|
16
|
+
|
|
7
17
|
export default class OTLayoutEngine {
|
|
18
|
+
/**
|
|
19
|
+
* @param {LayoutFont} font
|
|
20
|
+
*/
|
|
8
21
|
constructor(font) {
|
|
22
|
+
/** @type {LayoutFont} */
|
|
9
23
|
this.font = font;
|
|
24
|
+
/** @type {GlyphInfo[] | null} */
|
|
10
25
|
this.glyphInfos = null;
|
|
26
|
+
/** @type {ShapingPlan | null} */
|
|
11
27
|
this.plan = null;
|
|
28
|
+
/** @type {GSUBProcessor | null} */
|
|
12
29
|
this.GSUBProcessor = null;
|
|
30
|
+
/** @type {GPOSProcessor | null} */
|
|
13
31
|
this.GPOSProcessor = null;
|
|
32
|
+
/** @type {boolean} */
|
|
14
33
|
this.fallbackPosition = true;
|
|
34
|
+
/** @type {ShaperLike | null} */
|
|
35
|
+
this.shaper = null;
|
|
15
36
|
|
|
16
37
|
if (font.GSUB) {
|
|
17
38
|
this.GSUBProcessor = new GSUBProcessor(font, font.GSUB);
|
|
@@ -22,12 +43,18 @@ export default class OTLayoutEngine {
|
|
|
22
43
|
}
|
|
23
44
|
}
|
|
24
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @param {GlyphRunLike} glyphRun
|
|
48
|
+
*/
|
|
25
49
|
setup(glyphRun) {
|
|
26
50
|
// Map glyphs to GlyphInfo objects so data can be passed between
|
|
27
51
|
// GSUB and GPOS without mutating the real (shared) Glyph objects.
|
|
28
|
-
this.glyphInfos = glyphRun.glyphs.map(
|
|
52
|
+
this.glyphInfos = /** @type {LayoutGlyph[]} */ (glyphRun.glyphs).map(
|
|
53
|
+
/** @param {LayoutGlyph} glyph */ glyph => new GlyphInfo(this.font, glyph.id, [...glyph.codePoints])
|
|
54
|
+
);
|
|
29
55
|
|
|
30
56
|
// Select an OpenType script for GSUB/GPOS features.
|
|
57
|
+
/** @type {string | null} */
|
|
31
58
|
let script = null;
|
|
32
59
|
if (this.GPOSProcessor) {
|
|
33
60
|
script = this.GPOSProcessor.selectScript(glyphRun.script, glyphRun.language, glyphRun.direction);
|
|
@@ -40,12 +67,13 @@ export default class OTLayoutEngine {
|
|
|
40
67
|
// Choose the complex shaper from the buffer Unicode script (HB model). The OT
|
|
41
68
|
// script may fall back to DFLT/latn even for Thai text; that must not select
|
|
42
69
|
// DefaultShaper or Thai SARA AM / PUA preprocessing is skipped.
|
|
43
|
-
this.shaper = Shapers.choose(glyphRun.script || script);
|
|
70
|
+
this.shaper = /** @type {ShaperLike} */ (Shapers.choose(glyphRun.script || script));
|
|
44
71
|
this.plan = new ShapingPlan(this.font, script || glyphRun.script, glyphRun.direction);
|
|
45
72
|
this.plan.bufferScript = glyphRun.script;
|
|
46
73
|
this.shaper.plan(this.plan, this.glyphInfos, glyphRun.features);
|
|
47
74
|
|
|
48
75
|
// Enabled features as true, then overlay user values (e.g. aalt: 2) without mutating input.
|
|
76
|
+
/** @type {FeatureMap} */
|
|
49
77
|
let features = {};
|
|
50
78
|
for (let key in this.plan.allFeatures) {
|
|
51
79
|
features[key] = true;
|
|
@@ -53,38 +81,62 @@ export default class OTLayoutEngine {
|
|
|
53
81
|
glyphRun.features = Object.assign(features, glyphRun.features);
|
|
54
82
|
}
|
|
55
83
|
|
|
84
|
+
/**
|
|
85
|
+
* @param {GlyphRunLike} glyphRun
|
|
86
|
+
*/
|
|
56
87
|
substitute(glyphRun) {
|
|
57
|
-
if (this.GSUBProcessor) {
|
|
88
|
+
if (this.GSUBProcessor && this.plan && this.glyphInfos) {
|
|
58
89
|
this.plan.process(this.GSUBProcessor, this.glyphInfos);
|
|
59
90
|
|
|
60
91
|
// Map glyph infos back to normal Glyph objects
|
|
61
|
-
|
|
92
|
+
/** @type {LayoutGlyph[]} */
|
|
93
|
+
let glyphs = [];
|
|
94
|
+
for (let glyphInfo of this.glyphInfos) {
|
|
95
|
+
let g = this.font.getGlyph(glyphInfo.id, glyphInfo.codePoints);
|
|
96
|
+
if (g) {
|
|
97
|
+
glyphs.push(g);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
glyphRun.glyphs = glyphs;
|
|
62
101
|
}
|
|
63
102
|
}
|
|
64
103
|
|
|
104
|
+
/**
|
|
105
|
+
* @param {GlyphRunLike} glyphRun
|
|
106
|
+
* @returns {FeatureMap | Record<string, import('../../types/fontkit').OTFeature> | null | undefined | false}
|
|
107
|
+
*/
|
|
65
108
|
position(glyphRun) {
|
|
66
|
-
if (this.shaper.zeroMarkWidths === 'BEFORE_GPOS') {
|
|
109
|
+
if (this.shaper && this.shaper.zeroMarkWidths === 'BEFORE_GPOS' && glyphRun.positions) {
|
|
67
110
|
this.zeroMarkAdvances(glyphRun.positions);
|
|
68
111
|
}
|
|
69
112
|
|
|
70
|
-
if (this.GPOSProcessor) {
|
|
113
|
+
if (this.GPOSProcessor && this.plan && this.glyphInfos && glyphRun.positions) {
|
|
71
114
|
this.plan.process(this.GPOSProcessor, this.glyphInfos, glyphRun.positions);
|
|
72
115
|
}
|
|
73
116
|
|
|
74
|
-
if (this.shaper.zeroMarkWidths === 'AFTER_GPOS') {
|
|
117
|
+
if (this.shaper && this.shaper.zeroMarkWidths === 'AFTER_GPOS' && glyphRun.positions) {
|
|
75
118
|
this.zeroMarkAdvances(glyphRun.positions);
|
|
76
119
|
}
|
|
77
120
|
|
|
78
121
|
// Reverse the glyphs and positions if the script is right-to-left
|
|
79
122
|
if (glyphRun.direction === 'rtl') {
|
|
80
123
|
glyphRun.glyphs.reverse();
|
|
81
|
-
glyphRun.positions
|
|
124
|
+
if (glyphRun.positions) {
|
|
125
|
+
glyphRun.positions.reverse();
|
|
126
|
+
}
|
|
82
127
|
}
|
|
83
128
|
|
|
84
129
|
return this.GPOSProcessor && this.GPOSProcessor.features;
|
|
85
130
|
}
|
|
86
131
|
|
|
132
|
+
/**
|
|
133
|
+
* @param {GlyphPositionLike[]} positions
|
|
134
|
+
*/
|
|
87
135
|
zeroMarkAdvances(positions) {
|
|
136
|
+
if (!this.glyphInfos) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
88
140
|
for (let i = 0; i < this.glyphInfos.length; i++) {
|
|
89
141
|
if (this.glyphInfos[i].isMark) {
|
|
90
142
|
positions[i].xAdvance = 0;
|
|
@@ -99,7 +151,13 @@ export default class OTLayoutEngine {
|
|
|
99
151
|
this.shaper = null;
|
|
100
152
|
}
|
|
101
153
|
|
|
154
|
+
/**
|
|
155
|
+
* @param {ScriptTag | string[] | null | undefined} script
|
|
156
|
+
* @param {LanguageTag | null | undefined} language
|
|
157
|
+
* @returns {string[]}
|
|
158
|
+
*/
|
|
102
159
|
getAvailableFeatures(script, language) {
|
|
160
|
+
/** @type {string[]} */
|
|
103
161
|
let features = [];
|
|
104
162
|
|
|
105
163
|
if (this.GSUBProcessor) {
|