@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.
Files changed (142) hide show
  1. package/dist/browser-module.mjs +4448 -1142
  2. package/dist/browser-module.mjs.map +1 -1
  3. package/dist/browser.cjs +4449 -1142
  4. package/dist/browser.cjs.map +1 -1
  5. package/dist/main.cjs +4449 -1142
  6. package/dist/main.cjs.map +1 -1
  7. package/dist/module.mjs +4448 -1142
  8. package/dist/module.mjs.map +1 -1
  9. package/package.json +4 -1
  10. package/src/CmapProcessor.js +60 -21
  11. package/src/DFont.js +114 -14
  12. package/src/TTFFont.js +260 -62
  13. package/src/TrueTypeCollection.js +59 -15
  14. package/src/WOFF2Font.js +128 -22
  15. package/src/WOFFFont.js +45 -8
  16. package/src/aat/AATFeatureMap.js +83 -40
  17. package/src/aat/AATLayoutEngine.js +34 -2
  18. package/src/aat/AATLookupTable.js +24 -6
  19. package/src/aat/AATMorxProcessor.js +202 -65
  20. package/src/aat/AATStateMachine.js +35 -3
  21. package/src/base.js +20 -1
  22. package/src/cff/CFFCharsets.js +3 -0
  23. package/src/cff/CFFDict.js +81 -11
  24. package/src/cff/CFFEncodings.js +2 -0
  25. package/src/cff/CFFFont.js +120 -24
  26. package/src/cff/CFFIndex.js +66 -9
  27. package/src/cff/CFFOperand.js +53 -28
  28. package/src/cff/CFFPointer.js +41 -2
  29. package/src/cff/CFFPrivateDict.js +34 -24
  30. package/src/cff/CFFStandardStrings.js +68 -67
  31. package/src/cff/CFFTop.js +161 -68
  32. package/src/decorators.js +34 -15
  33. package/src/encodings.js +133 -116
  34. package/src/fs.js +10 -0
  35. package/src/glyph/BBox.js +14 -0
  36. package/src/glyph/CFFGlyph.js +293 -152
  37. package/src/glyph/COLRGlyph.js +40 -5
  38. package/src/glyph/Glyph.js +72 -13
  39. package/src/glyph/GlyphVariationProcessor.js +158 -62
  40. package/src/glyph/Path.js +119 -32
  41. package/src/glyph/SBIXGlyph.js +57 -8
  42. package/src/glyph/StandardNames.js +1 -0
  43. package/src/glyph/TTFGlyph.js +203 -63
  44. package/src/glyph/TTFGlyphEncoder.js +58 -26
  45. package/src/glyph/WOFF2Glyph.js +25 -6
  46. package/src/layout/GlyphPosition.js +6 -0
  47. package/src/layout/GlyphRun.js +28 -10
  48. package/src/layout/KernProcessor.js +63 -4
  49. package/src/layout/LayoutEngine.js +99 -26
  50. package/src/layout/Script.js +52 -26
  51. package/src/layout/UnicodeLayoutEngine.js +34 -10
  52. package/src/opentype/GPOSProcessor.js +156 -45
  53. package/src/opentype/GSUBProcessor.js +92 -33
  54. package/src/opentype/GlyphInfo.js +53 -5
  55. package/src/opentype/GlyphIterator.js +51 -2
  56. package/src/opentype/OTLayoutEngine.js +66 -8
  57. package/src/opentype/OTProcessor.js +284 -71
  58. package/src/opentype/ShapingPlan.js +52 -5
  59. package/src/opentype/shapers/ArabicShaper.js +27 -8
  60. package/src/opentype/shapers/DefaultShaper.js +54 -10
  61. package/src/opentype/shapers/HangulShaper.js +120 -42
  62. package/src/opentype/shapers/HebrewShaper.js +50 -16
  63. package/src/opentype/shapers/IndicShaper.js +159 -44
  64. package/src/opentype/shapers/ThaiShaper.js +50 -19
  65. package/src/opentype/shapers/UniversalShaper.js +83 -17
  66. package/src/opentype/shapers/index.js +48 -35
  67. package/src/opentype/shapers/indic-data.js +2 -2
  68. package/src/packages/unicode-properties/data-trie.js +2 -1
  69. package/src/packages/unicode-properties/data.js +13 -1
  70. package/src/packages/unicode-properties/dist/index.cjs +23 -10
  71. package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
  72. package/src/packages/unicode-properties/dist/index.mjs +22 -10
  73. package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
  74. package/src/packages/unicode-properties/generate.js +3 -4
  75. package/src/packages/unicode-properties/index.js +95 -30
  76. package/src/packages/unicode-properties/package.json +1 -1
  77. package/src/packages/unicode-trie/builder.js +146 -29
  78. package/src/packages/unicode-trie/dist/builder.cjs +135 -25
  79. package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
  80. package/src/packages/unicode-trie/dist/builder.mjs +134 -25
  81. package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
  82. package/src/packages/unicode-trie/dist/index.cjs +22 -9
  83. package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
  84. package/src/packages/unicode-trie/dist/index.mjs +21 -9
  85. package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
  86. package/src/packages/unicode-trie/index.js +63 -12
  87. package/src/packages/unicode-trie/package.json +1 -1
  88. package/src/packages/unicode-trie/swap.js +15 -2
  89. package/src/subset/CFFSubset.js +112 -22
  90. package/src/subset/Subset.js +23 -3
  91. package/src/subset/TTFSubset.js +84 -41
  92. package/src/tables/BASE.js +28 -27
  93. package/src/tables/COLR.js +10 -9
  94. package/src/tables/CPAL.js +2 -1
  95. package/src/tables/DSIG.js +8 -7
  96. package/src/tables/EBDT.js +10 -4
  97. package/src/tables/EBLC.js +70 -10
  98. package/src/tables/GDEF.js +18 -17
  99. package/src/tables/GPOS.js +153 -90
  100. package/src/tables/GSUB.js +50 -39
  101. package/src/tables/HVAR.js +51 -5
  102. package/src/tables/JSTF.js +17 -16
  103. package/src/tables/LTSH.js +4 -3
  104. package/src/tables/OS2.js +50 -49
  105. package/src/tables/PCLT.js +16 -15
  106. package/src/tables/STAT.js +46 -6
  107. package/src/tables/VDMX.js +18 -17
  108. package/src/tables/VORG.js +8 -7
  109. package/src/tables/WOFF2Directory.js +57 -9
  110. package/src/tables/WOFFDirectory.js +32 -20
  111. package/src/tables/aat.js +160 -35
  112. package/src/tables/avar.js +1 -0
  113. package/src/tables/bsln.js +3 -2
  114. package/src/tables/cmap.js +83 -63
  115. package/src/tables/cvt.js +1 -0
  116. package/src/tables/directory.js +56 -28
  117. package/src/tables/feat.js +24 -2
  118. package/src/tables/fpgm.js +1 -0
  119. package/src/tables/fvar.js +32 -4
  120. package/src/tables/gasp.js +6 -5
  121. package/src/tables/glyf.js +2 -1
  122. package/src/tables/gvar.js +29 -2
  123. package/src/tables/hdmx.js +18 -7
  124. package/src/tables/head.js +17 -16
  125. package/src/tables/hhea.js +15 -14
  126. package/src/tables/hmtx.js +18 -2
  127. package/src/tables/index.js +1 -5
  128. package/src/tables/just.js +15 -4
  129. package/src/tables/kern.js +84 -47
  130. package/src/tables/loca.js +23 -10
  131. package/src/tables/maxp.js +16 -15
  132. package/src/tables/morx.js +27 -20
  133. package/src/tables/name.js +120 -39
  134. package/src/tables/opbd.js +2 -1
  135. package/src/tables/opentype.js +126 -91
  136. package/src/tables/post.js +18 -11
  137. package/src/tables/prep.js +1 -0
  138. package/src/tables/sbix.js +12 -1
  139. package/src/tables/variations.js +29 -9
  140. package/src/tables/vhea.js +15 -14
  141. package/src/tables/vmtx.js +20 -4
  142. 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
- // Arabic and Syriac
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
- // Thai
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
- // Lao
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
- // Tibetan
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 position = this.positions[this.glyphIterator.peekIndex(sequenceIndex)];
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
- let index = this.coverageIndex(table.coverage);
98
+ if (!t.coverage) return false;
99
+ let index = this.coverageIndex(t.coverage);
39
100
  if (index === -1) return false;
40
101
 
41
- switch (table.version) {
102
+ switch (t.version) {
42
103
  case 1:
43
- this.applyPositionValue(0, table.value);
104
+ if (t.value) this.applyPositionValue(0, t.value);
44
105
  break;
45
106
 
46
107
  case 2:
47
- this.applyPositionValue(0, table.values.get(index));
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 = this.glyphIterator.peek();
116
+ let nextGlyph = glyphIterator.peek();
56
117
  if (!nextGlyph) return false;
57
118
 
58
- let index = this.coverageIndex(table.coverage);
119
+ if (!t.coverage) return false;
120
+ let index = this.coverageIndex(t.coverage);
59
121
  if (index === -1) return false;
60
122
 
61
- switch (table.version) {
123
+ switch (t.version) {
62
124
  case 1: { // Adjustments for glyph pairs
63
- let set = table.pairSets.get(index);
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 class1 = this.getClassID(this.glyphIterator.cur.id, table.classDef1);
78
- let class2 = this.getClassID(nextGlyph.id, table.classDef2);
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 = table.classRecords.get(class1).get(class2);
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
- let nextIndex = this.glyphIterator.peekIndex();
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 = table.entryExitRecords[this.coverageIndex(table.coverage)];
164
+ let curRecord = t.entryExitRecords[this.coverageIndex(t.coverage)];
97
165
  if (!curRecord || !curRecord.exitAnchor) return false;
98
166
 
99
- let nextRecord = table.entryExitRecords[this.coverageIndex(table.coverage, nextGlyph.id)];
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[this.glyphIterator.index];
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
- if (this.glyphIterator.flags.rightToLeft) {
127
- this.glyphIterator.cur.cursiveAttachment = nextIndex;
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 = this.glyphIterator.index;
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
- let markIndex = this.coverageIndex(table.markCoverage);
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 = this.glyphIterator.index;
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(table.baseCoverage, this.glyphs[baseGlyphIndex].id);
221
+ let baseIndex = this.coverageIndex(t.baseCoverage, this.glyphs[baseGlyphIndex].id);
148
222
  if (baseIndex === -1) return false;
149
223
 
150
- let markRecord = table.markArray[markIndex];
151
- let baseAnchor = table.baseArray[baseIndex][markRecord.class];
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
- let markIndex = this.coverageIndex(table.markCoverage);
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 = this.glyphIterator.index;
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(table.ligatureCoverage, this.glyphs[baseGlyphIndex].id);
241
+ let ligIndex = this.coverageIndex(t.ligatureCoverage, this.glyphs[baseGlyphIndex].id);
166
242
  if (ligIndex === -1) return false;
167
243
 
168
- let ligAttach = table.ligatureArray[ligIndex];
169
- let markGlyph = this.glyphIterator.cur;
244
+ let ligAttach = t.ligatureArray[ligIndex];
245
+ let markGlyph = glyphIterator.cur;
170
246
  let ligGlyph = this.glyphs[baseGlyphIndex];
171
- let compIndex = ligGlyph.ligatureID && ligGlyph.ligatureID === markGlyph.ligatureID && (markGlyph.ligatureComponent > 0)
172
- ? Math.min(markGlyph.ligatureComponent, ligGlyph.codePoints.length) - 1
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 = table.markArray[markIndex];
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
- let mark1Index = this.coverageIndex(table.mark1Coverage);
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 = this.glyphIterator.peekIndex(-1);
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 = this.glyphIterator.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(table.mark2Coverage, prev.id);
290
+ let mark2Index = this.coverageIndex(t.mark2Coverage, prev.id);
210
291
  if (mark2Index === -1) return false;
211
292
 
212
- let markRecord = table.mark1Array[mark1Index];
213
- let baseAnchor = table.mark2Array[mark2Index][markRecord.class];
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
- return this.applyLookup(table.lookupType, table.extension);
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[this.glyphIterator.index];
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
- this.glyphIterator.cur.markAttachment = baseGlyphIndex;
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 (var i = 0; i < this.glyphs.length; i++) {
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) {