@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,8 +1,57 @@
|
|
|
1
1
|
import OTProcessor from './OTProcessor';
|
|
2
2
|
|
|
3
|
+
/** @typedef {import('../../types/fontkit').PositionValue} PositionValue */
|
|
4
|
+
/** @typedef {import('../../types/fontkit').AnchorPoint} AnchorPoint */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').MarkRecord} MarkRecord */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').CoverageTable} CoverageTable */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').ClassDefTable} ClassDefTable */
|
|
8
|
+
/** @typedef {import('../../types/fontkit').GlyphInfoLike} GlyphInfoLike */
|
|
9
|
+
/** @typedef {import('../../types/fontkit').GlyphPositionLike} GlyphPositionLike */
|
|
10
|
+
/** @typedef {import('../../types/fontkit').ExtensionSubtable} ExtensionSubtable */
|
|
11
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {StructValue & {
|
|
15
|
+
* coverage?: CoverageTable,
|
|
16
|
+
* version?: number,
|
|
17
|
+
* value?: PositionValue,
|
|
18
|
+
* values?: import('../../types/fontkit').LazyList<PositionValue>,
|
|
19
|
+
* pairSets?: import('../../types/fontkit').LazyList<Array<{ secondGlyph: number, value1: PositionValue, value2: PositionValue }>>,
|
|
20
|
+
* classDef1?: ClassDefTable,
|
|
21
|
+
* classDef2?: ClassDefTable,
|
|
22
|
+
* classRecords?: import('../../types/fontkit').LazyList<import('../../types/fontkit').LazyList<{ value1: PositionValue, value2: PositionValue }>>,
|
|
23
|
+
* entryExitRecords?: Array<{ entryAnchor?: AnchorPoint | null, exitAnchor?: AnchorPoint | null }>,
|
|
24
|
+
* markCoverage?: CoverageTable,
|
|
25
|
+
* baseCoverage?: CoverageTable,
|
|
26
|
+
* markArray?: MarkRecord[],
|
|
27
|
+
* baseArray?: AnchorPoint[][],
|
|
28
|
+
* ligatureCoverage?: CoverageTable,
|
|
29
|
+
* ligatureArray?: AnchorPoint[][][],
|
|
30
|
+
* mark1Coverage?: CoverageTable,
|
|
31
|
+
* mark2Coverage?: CoverageTable,
|
|
32
|
+
* mark1Array?: MarkRecord[],
|
|
33
|
+
* mark2Array?: AnchorPoint[][],
|
|
34
|
+
* lookupType?: number,
|
|
35
|
+
* extension?: StructValue,
|
|
36
|
+
* }} GPOSSubtable
|
|
37
|
+
*/
|
|
38
|
+
|
|
3
39
|
export default class GPOSProcessor extends OTProcessor {
|
|
40
|
+
/**
|
|
41
|
+
* @param {number} sequenceIndex
|
|
42
|
+
* @param {PositionValue} value
|
|
43
|
+
*/
|
|
4
44
|
applyPositionValue(sequenceIndex, value) {
|
|
5
|
-
let
|
|
45
|
+
let glyphIterator = this.glyphIterator;
|
|
46
|
+
if (!glyphIterator || !this.positions) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let position = this.positions[glyphIterator.peekIndex(sequenceIndex)];
|
|
51
|
+
if (!position) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
6
55
|
if (value.xAdvance != null) position.xAdvance += value.xAdvance;
|
|
7
56
|
if (value.yAdvance != null) position.yAdvance += value.yAdvance;
|
|
8
57
|
if (value.xPlacement != null) position.xOffset += value.xPlacement;
|
|
@@ -32,19 +81,31 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
32
81
|
// TODO: device tables
|
|
33
82
|
}
|
|
34
83
|
|
|
84
|
+
/**
|
|
85
|
+
* @param {number} lookupType
|
|
86
|
+
* @param {StructValue} table
|
|
87
|
+
* @returns {boolean}
|
|
88
|
+
*/
|
|
35
89
|
applyLookup(lookupType, table) {
|
|
90
|
+
let t = /** @type {GPOSSubtable} */ (table);
|
|
91
|
+
let glyphIterator = this.glyphIterator;
|
|
92
|
+
if (!glyphIterator) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
36
96
|
switch (lookupType) {
|
|
37
97
|
case 1: { // Single positioning value
|
|
38
|
-
|
|
98
|
+
if (!t.coverage) return false;
|
|
99
|
+
let index = this.coverageIndex(t.coverage);
|
|
39
100
|
if (index === -1) return false;
|
|
40
101
|
|
|
41
|
-
switch (
|
|
102
|
+
switch (t.version) {
|
|
42
103
|
case 1:
|
|
43
|
-
this.applyPositionValue(0,
|
|
104
|
+
if (t.value) this.applyPositionValue(0, t.value);
|
|
44
105
|
break;
|
|
45
106
|
|
|
46
107
|
case 2:
|
|
47
|
-
this.applyPositionValue(0,
|
|
108
|
+
if (t.values) this.applyPositionValue(0, t.values.get(index));
|
|
48
109
|
break;
|
|
49
110
|
}
|
|
50
111
|
|
|
@@ -52,15 +113,17 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
52
113
|
}
|
|
53
114
|
|
|
54
115
|
case 2: { // Pair Adjustment Positioning
|
|
55
|
-
let nextGlyph =
|
|
116
|
+
let nextGlyph = glyphIterator.peek();
|
|
56
117
|
if (!nextGlyph) return false;
|
|
57
118
|
|
|
58
|
-
|
|
119
|
+
if (!t.coverage) return false;
|
|
120
|
+
let index = this.coverageIndex(t.coverage);
|
|
59
121
|
if (index === -1) return false;
|
|
60
122
|
|
|
61
|
-
switch (
|
|
123
|
+
switch (t.version) {
|
|
62
124
|
case 1: { // Adjustments for glyph pairs
|
|
63
|
-
|
|
125
|
+
if (!t.pairSets) return false;
|
|
126
|
+
let set = t.pairSets.get(index);
|
|
64
127
|
|
|
65
128
|
for (let pair of set) {
|
|
66
129
|
if (pair.secondGlyph === nextGlyph.id) {
|
|
@@ -74,11 +137,14 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
74
137
|
}
|
|
75
138
|
|
|
76
139
|
case 2: { // Class pair adjustment
|
|
77
|
-
let
|
|
78
|
-
|
|
140
|
+
let cur = glyphIterator.cur;
|
|
141
|
+
if (!cur || !t.classDef1 || !t.classDef2 || !t.classRecords) return false;
|
|
142
|
+
|
|
143
|
+
let class1 = this.getClassID(cur.id, t.classDef1);
|
|
144
|
+
let class2 = this.getClassID(nextGlyph.id, t.classDef2);
|
|
79
145
|
if (class1 === -1 || class2 === -1) return false;
|
|
80
146
|
|
|
81
|
-
let pair =
|
|
147
|
+
let pair = t.classRecords.get(class1).get(class2);
|
|
82
148
|
this.applyPositionValue(0, pair.value1);
|
|
83
149
|
this.applyPositionValue(1, pair.value2);
|
|
84
150
|
return true;
|
|
@@ -89,21 +155,24 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
89
155
|
}
|
|
90
156
|
|
|
91
157
|
case 3: { // Cursive Attachment Positioning
|
|
92
|
-
|
|
158
|
+
if (!t.coverage || !t.entryExitRecords || !this.positions) return false;
|
|
159
|
+
|
|
160
|
+
let nextIndex = glyphIterator.peekIndex();
|
|
93
161
|
let nextGlyph = this.glyphs[nextIndex];
|
|
94
162
|
if (!nextGlyph) return false;
|
|
95
163
|
|
|
96
|
-
let curRecord =
|
|
164
|
+
let curRecord = t.entryExitRecords[this.coverageIndex(t.coverage)];
|
|
97
165
|
if (!curRecord || !curRecord.exitAnchor) return false;
|
|
98
166
|
|
|
99
|
-
let nextRecord =
|
|
167
|
+
let nextRecord = t.entryExitRecords[this.coverageIndex(t.coverage, nextGlyph.id)];
|
|
100
168
|
if (!nextRecord || !nextRecord.entryAnchor) return false;
|
|
101
169
|
|
|
102
170
|
let entry = this.getAnchor(nextRecord.entryAnchor);
|
|
103
171
|
let exit = this.getAnchor(curRecord.exitAnchor);
|
|
104
172
|
|
|
105
|
-
let cur = this.positions[
|
|
173
|
+
let cur = this.positions[glyphIterator.index];
|
|
106
174
|
let next = this.positions[nextIndex];
|
|
175
|
+
if (!cur || !next) return false;
|
|
107
176
|
let d;
|
|
108
177
|
|
|
109
178
|
switch (this.direction) {
|
|
@@ -123,11 +192,14 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
123
192
|
break;
|
|
124
193
|
}
|
|
125
194
|
|
|
126
|
-
|
|
127
|
-
|
|
195
|
+
let curGlyph = glyphIterator.cur;
|
|
196
|
+
if (!curGlyph) return false;
|
|
197
|
+
|
|
198
|
+
if (glyphIterator.flags.rightToLeft) {
|
|
199
|
+
curGlyph.cursiveAttachment = nextIndex;
|
|
128
200
|
cur.yOffset = entry.y - exit.y;
|
|
129
201
|
} else {
|
|
130
|
-
nextGlyph.cursiveAttachment =
|
|
202
|
+
nextGlyph.cursiveAttachment = glyphIterator.index;
|
|
131
203
|
cur.yOffset = exit.y - entry.y;
|
|
132
204
|
}
|
|
133
205
|
|
|
@@ -135,58 +207,67 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
135
207
|
}
|
|
136
208
|
|
|
137
209
|
case 4: { // Mark to base positioning
|
|
138
|
-
|
|
210
|
+
if (!t.markCoverage || !t.baseCoverage || !t.markArray || !t.baseArray) return false;
|
|
211
|
+
|
|
212
|
+
let markIndex = this.coverageIndex(t.markCoverage);
|
|
139
213
|
if (markIndex === -1) return false;
|
|
140
214
|
|
|
141
215
|
// search backward for a base glyph
|
|
142
|
-
let baseGlyphIndex =
|
|
143
|
-
while (--baseGlyphIndex >= 0 && (this.glyphs[baseGlyphIndex].isMark || this.glyphs[baseGlyphIndex].ligatureComponent > 0));
|
|
216
|
+
let baseGlyphIndex = glyphIterator.index;
|
|
217
|
+
while (--baseGlyphIndex >= 0 && (this.glyphs[baseGlyphIndex].isMark || (this.glyphs[baseGlyphIndex].ligatureComponent ?? 0) > 0));
|
|
144
218
|
|
|
145
219
|
if (baseGlyphIndex < 0) return false;
|
|
146
220
|
|
|
147
|
-
let baseIndex = this.coverageIndex(
|
|
221
|
+
let baseIndex = this.coverageIndex(t.baseCoverage, this.glyphs[baseGlyphIndex].id);
|
|
148
222
|
if (baseIndex === -1) return false;
|
|
149
223
|
|
|
150
|
-
let markRecord =
|
|
151
|
-
let baseAnchor =
|
|
224
|
+
let markRecord = t.markArray[markIndex];
|
|
225
|
+
let baseAnchor = t.baseArray[baseIndex][markRecord.class];
|
|
152
226
|
return this.applyAnchor(markRecord, baseAnchor, baseGlyphIndex);
|
|
153
227
|
}
|
|
154
228
|
|
|
155
229
|
case 5: { // Mark to ligature positioning
|
|
156
|
-
|
|
230
|
+
if (!t.markCoverage || !t.ligatureCoverage || !t.markArray || !t.ligatureArray) return false;
|
|
231
|
+
|
|
232
|
+
let markIndex = this.coverageIndex(t.markCoverage);
|
|
157
233
|
if (markIndex === -1) return false;
|
|
158
234
|
|
|
159
235
|
// search backward for a base glyph
|
|
160
|
-
let baseGlyphIndex =
|
|
236
|
+
let baseGlyphIndex = glyphIterator.index;
|
|
161
237
|
while (--baseGlyphIndex >= 0 && this.glyphs[baseGlyphIndex].isMark);
|
|
162
238
|
|
|
163
239
|
if (baseGlyphIndex < 0) return false;
|
|
164
240
|
|
|
165
|
-
let ligIndex = this.coverageIndex(
|
|
241
|
+
let ligIndex = this.coverageIndex(t.ligatureCoverage, this.glyphs[baseGlyphIndex].id);
|
|
166
242
|
if (ligIndex === -1) return false;
|
|
167
243
|
|
|
168
|
-
let ligAttach =
|
|
169
|
-
let markGlyph =
|
|
244
|
+
let ligAttach = t.ligatureArray[ligIndex];
|
|
245
|
+
let markGlyph = glyphIterator.cur;
|
|
170
246
|
let ligGlyph = this.glyphs[baseGlyphIndex];
|
|
171
|
-
|
|
172
|
-
|
|
247
|
+
if (!markGlyph) return false;
|
|
248
|
+
|
|
249
|
+
let compIndex = ligGlyph.ligatureID && ligGlyph.ligatureID === markGlyph.ligatureID && (markGlyph.ligatureComponent ?? 0) > 0
|
|
250
|
+
? Math.min(markGlyph.ligatureComponent ?? 0, ligGlyph.codePoints.length) - 1
|
|
173
251
|
: ligGlyph.codePoints.length - 1;
|
|
174
252
|
|
|
175
|
-
let markRecord =
|
|
253
|
+
let markRecord = t.markArray[markIndex];
|
|
176
254
|
let baseAnchor = ligAttach[compIndex][markRecord.class];
|
|
177
255
|
return this.applyAnchor(markRecord, baseAnchor, baseGlyphIndex);
|
|
178
256
|
}
|
|
179
257
|
|
|
180
258
|
case 6: { // Mark to mark positioning
|
|
181
|
-
|
|
259
|
+
if (!t.mark1Coverage || !t.mark2Coverage || !t.mark1Array || !t.mark2Array) return false;
|
|
260
|
+
|
|
261
|
+
let mark1Index = this.coverageIndex(t.mark1Coverage);
|
|
182
262
|
if (mark1Index === -1) return false;
|
|
183
263
|
|
|
184
264
|
// get the previous mark to attach to
|
|
185
|
-
let prevIndex =
|
|
265
|
+
let prevIndex = glyphIterator.peekIndex(-1);
|
|
186
266
|
let prev = this.glyphs[prevIndex];
|
|
187
267
|
if (!prev || !prev.isMark) return false;
|
|
188
268
|
|
|
189
|
-
let cur =
|
|
269
|
+
let cur = glyphIterator.cur;
|
|
270
|
+
if (!cur) return false;
|
|
190
271
|
|
|
191
272
|
// The following logic was borrowed from Harfbuzz
|
|
192
273
|
let good = false;
|
|
@@ -206,11 +287,11 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
206
287
|
|
|
207
288
|
if (!good) return false;
|
|
208
289
|
|
|
209
|
-
let mark2Index = this.coverageIndex(
|
|
290
|
+
let mark2Index = this.coverageIndex(t.mark2Coverage, prev.id);
|
|
210
291
|
if (mark2Index === -1) return false;
|
|
211
292
|
|
|
212
|
-
let markRecord =
|
|
213
|
-
let baseAnchor =
|
|
293
|
+
let markRecord = t.mark1Array[mark1Index];
|
|
294
|
+
let baseAnchor = t.mark2Array[mark2Index][markRecord.class];
|
|
214
295
|
return this.applyAnchor(markRecord, baseAnchor, prevIndex);
|
|
215
296
|
}
|
|
216
297
|
|
|
@@ -220,28 +301,46 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
220
301
|
case 8: // Chaining contextual positioning
|
|
221
302
|
return this.applyChainingContext(table);
|
|
222
303
|
|
|
223
|
-
case 9: // Extension positioning
|
|
224
|
-
|
|
304
|
+
case 9: { // Extension positioning
|
|
305
|
+
let ext = /** @type {ExtensionSubtable} */ (table);
|
|
306
|
+
return this.applyLookup(ext.lookupType, ext.extension);
|
|
307
|
+
}
|
|
225
308
|
|
|
226
309
|
default:
|
|
227
310
|
throw new Error(`Unsupported GPOS table: ${lookupType}`);
|
|
228
311
|
}
|
|
229
312
|
}
|
|
230
313
|
|
|
314
|
+
/**
|
|
315
|
+
* @param {MarkRecord} markRecord
|
|
316
|
+
* @param {AnchorPoint | null | undefined} baseAnchor
|
|
317
|
+
* @param {number} baseGlyphIndex
|
|
318
|
+
* @returns {boolean}
|
|
319
|
+
*/
|
|
231
320
|
applyAnchor(markRecord, baseAnchor, baseGlyphIndex) {
|
|
232
321
|
// NULL offset = no anchor for this class; false so later subtables can try (HarfBuzz).
|
|
233
322
|
if (!baseAnchor || !markRecord.markAnchor) return false;
|
|
234
323
|
|
|
324
|
+
let glyphIterator = this.glyphIterator;
|
|
325
|
+
if (!glyphIterator || !this.positions) return false;
|
|
326
|
+
|
|
235
327
|
let baseCoords = this.getAnchor(baseAnchor);
|
|
236
328
|
let markCoords = this.getAnchor(markRecord.markAnchor);
|
|
237
|
-
let markPos = this.positions[
|
|
329
|
+
let markPos = this.positions[glyphIterator.index];
|
|
330
|
+
if (!markPos) return false;
|
|
238
331
|
|
|
239
332
|
markPos.xOffset = baseCoords.x - markCoords.x;
|
|
240
333
|
markPos.yOffset = baseCoords.y - markCoords.y;
|
|
241
|
-
|
|
334
|
+
let cur = glyphIterator.cur;
|
|
335
|
+
if (!cur) return false;
|
|
336
|
+
cur.markAttachment = baseGlyphIndex;
|
|
242
337
|
return true;
|
|
243
338
|
}
|
|
244
339
|
|
|
340
|
+
/**
|
|
341
|
+
* @param {AnchorPoint} anchor
|
|
342
|
+
* @returns {{ x: number, y: number }}
|
|
343
|
+
*/
|
|
245
344
|
getAnchor(anchor) {
|
|
246
345
|
// TODO: contour point, device tables
|
|
247
346
|
let x = anchor.xCoordinate;
|
|
@@ -263,19 +362,27 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
263
362
|
return { x, y };
|
|
264
363
|
}
|
|
265
364
|
|
|
365
|
+
/**
|
|
366
|
+
* @param {string[]} userFeatures
|
|
367
|
+
* @param {GlyphInfoLike[]} glyphs
|
|
368
|
+
* @param {GlyphPositionLike[] | null | undefined} [advances]
|
|
369
|
+
*/
|
|
266
370
|
applyFeatures(userFeatures, glyphs, advances) {
|
|
267
371
|
super.applyFeatures(userFeatures, glyphs, advances);
|
|
268
372
|
|
|
269
|
-
for (
|
|
373
|
+
for (let i = 0; i < this.glyphs.length; i++) {
|
|
270
374
|
this.fixCursiveAttachment(i);
|
|
271
375
|
}
|
|
272
376
|
|
|
273
377
|
this.fixMarkAttachment();
|
|
274
378
|
}
|
|
275
379
|
|
|
380
|
+
/**
|
|
381
|
+
* @param {number} i
|
|
382
|
+
*/
|
|
276
383
|
fixCursiveAttachment(i) {
|
|
277
384
|
let glyph = this.glyphs[i];
|
|
278
|
-
if (glyph.cursiveAttachment != null) {
|
|
385
|
+
if (glyph.cursiveAttachment != null && this.positions) {
|
|
279
386
|
let j = glyph.cursiveAttachment;
|
|
280
387
|
|
|
281
388
|
glyph.cursiveAttachment = null;
|
|
@@ -286,6 +393,10 @@ export default class GPOSProcessor extends OTProcessor {
|
|
|
286
393
|
}
|
|
287
394
|
|
|
288
395
|
fixMarkAttachment() {
|
|
396
|
+
if (!this.positions) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
289
400
|
for (let i = 0; i < this.glyphs.length; i++) {
|
|
290
401
|
let glyph = this.glyphs[i];
|
|
291
402
|
if (glyph.markAttachment != null) {
|
|
@@ -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
|
|