@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,4 +1,9 @@
|
|
|
1
|
-
import {getCombiningClass} from '../packages/unicode-properties/index.js';
|
|
1
|
+
import { getCombiningClass } from '../packages/unicode-properties/index.js';
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
|
|
4
|
+
/** @typedef {import('../../types/fontkit').LayoutGlyph} LayoutGlyph */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').GlyphInfoLike} GlyphInfoLike */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').GlyphPositionLike} GlyphPositionLike */
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* This class is used when GPOS does not define 'mark' or 'mkmk' features
|
|
@@ -9,10 +14,19 @@ import {getCombiningClass} from '../packages/unicode-properties/index.js';
|
|
|
9
14
|
* https://github.com/behdad/harfbuzz/blob/master/src/hb-ot-shape-fallback.cc
|
|
10
15
|
*/
|
|
11
16
|
export default class UnicodeLayoutEngine {
|
|
17
|
+
/**
|
|
18
|
+
* @param {LayoutFont} font
|
|
19
|
+
*/
|
|
12
20
|
constructor(font) {
|
|
21
|
+
/** @type {LayoutFont} */
|
|
13
22
|
this.font = font;
|
|
14
23
|
}
|
|
15
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @param {Array<LayoutGlyph | GlyphInfoLike & { cbox?: import('../../types/fontkit').BBoxLike }>} glyphs
|
|
27
|
+
* @param {GlyphPositionLike[]} positions
|
|
28
|
+
* @returns {GlyphPositionLike[]}
|
|
29
|
+
*/
|
|
16
30
|
positionGlyphs(glyphs, positions) {
|
|
17
31
|
// find each base + mark cluster, and position the marks relative to the base
|
|
18
32
|
let clusterStart = 0;
|
|
@@ -37,9 +51,16 @@ export default class UnicodeLayoutEngine {
|
|
|
37
51
|
return positions;
|
|
38
52
|
}
|
|
39
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @param {Array<LayoutGlyph | (GlyphInfoLike & { cbox?: import('../../types/fontkit').BBoxLike, advanceWidth?: number })>} glyphs
|
|
56
|
+
* @param {GlyphPositionLike[]} positions
|
|
57
|
+
* @param {number} clusterStart
|
|
58
|
+
* @param {number} clusterEnd
|
|
59
|
+
* @returns {void}
|
|
60
|
+
*/
|
|
40
61
|
positionCluster(glyphs, positions, clusterStart, clusterEnd) {
|
|
41
|
-
let base = glyphs[clusterStart];
|
|
42
|
-
let baseBox = base.cbox.copy();
|
|
62
|
+
let base = /** @type {LayoutGlyph} */ (glyphs[clusterStart]);
|
|
63
|
+
let baseBox = /** @type {import('../../types/fontkit').BBoxLike} */ (base.cbox).copy();
|
|
43
64
|
|
|
44
65
|
// adjust bounding box for ligature glyphs
|
|
45
66
|
if (base.codePoints.length > 1) {
|
|
@@ -53,8 +74,8 @@ export default class UnicodeLayoutEngine {
|
|
|
53
74
|
|
|
54
75
|
// position each of the mark glyphs relative to the base glyph
|
|
55
76
|
for (let index = clusterStart + 1; index <= clusterEnd; index++) {
|
|
56
|
-
let mark = glyphs[index];
|
|
57
|
-
let markBox = mark.cbox;
|
|
77
|
+
let mark = /** @type {LayoutGlyph} */ (glyphs[index]);
|
|
78
|
+
let markBox = /** @type {import('../../types/fontkit').BBoxLike} */ (mark.cbox);
|
|
58
79
|
let position = positions[index];
|
|
59
80
|
|
|
60
81
|
let combiningClass = this.getCombiningClass(mark.codePoints[0]);
|
|
@@ -125,7 +146,6 @@ export default class UnicodeLayoutEngine {
|
|
|
125
146
|
position.xAdvance = position.yAdvance = 0;
|
|
126
147
|
position.xOffset += xOffset;
|
|
127
148
|
position.yOffset += yOffset;
|
|
128
|
-
|
|
129
149
|
} else {
|
|
130
150
|
xOffset -= position.xAdvance;
|
|
131
151
|
yOffset -= position.yAdvance;
|
|
@@ -135,6 +155,10 @@ export default class UnicodeLayoutEngine {
|
|
|
135
155
|
return;
|
|
136
156
|
}
|
|
137
157
|
|
|
158
|
+
/**
|
|
159
|
+
* @param {number} codePoint
|
|
160
|
+
* @returns {string}
|
|
161
|
+
*/
|
|
138
162
|
getCombiningClass(codePoint) {
|
|
139
163
|
let combiningClass = getCombiningClass(codePoint);
|
|
140
164
|
|
|
@@ -203,7 +227,7 @@ export default class UnicodeLayoutEngine {
|
|
|
203
227
|
case 'CCC21': // dagesh
|
|
204
228
|
break;
|
|
205
229
|
|
|
206
|
-
|
|
230
|
+
// Arabic and Syriac
|
|
207
231
|
|
|
208
232
|
case 'CCC27': // fathatan
|
|
209
233
|
case 'CCC28': // dammatan
|
|
@@ -219,7 +243,7 @@ export default class UnicodeLayoutEngine {
|
|
|
219
243
|
case 'CCC32': // kasra
|
|
220
244
|
return 'Below';
|
|
221
245
|
|
|
222
|
-
|
|
246
|
+
// Thai
|
|
223
247
|
|
|
224
248
|
case 'CCC103': // sara u / sara uu
|
|
225
249
|
return 'Below_Right';
|
|
@@ -227,7 +251,7 @@ export default class UnicodeLayoutEngine {
|
|
|
227
251
|
case 'CCC107': // mai
|
|
228
252
|
return 'Above_Right';
|
|
229
253
|
|
|
230
|
-
|
|
254
|
+
// Lao
|
|
231
255
|
|
|
232
256
|
case 'CCC118': // sign u / sign uu
|
|
233
257
|
return 'Below';
|
|
@@ -235,7 +259,7 @@ export default class UnicodeLayoutEngine {
|
|
|
235
259
|
case 'CCC122': // mai
|
|
236
260
|
return 'Above';
|
|
237
261
|
|
|
238
|
-
|
|
262
|
+
// Tibetan
|
|
239
263
|
|
|
240
264
|
case 'CCC129': // sign aa
|
|
241
265
|
case 'CCC132': // sign u
|
|
@@ -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) {
|