@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
package/src/TTFFont.js CHANGED
@@ -1,6 +1,6 @@
1
- import * as r from 'restructure';
2
1
  import { cache } from './decorators';
3
2
  import * as fontkit from './base';
3
+ import * as r from 'restructure';
4
4
  import Directory from './tables/directory';
5
5
  import tables from './tables';
6
6
  import CmapProcessor from './CmapProcessor';
@@ -16,18 +16,114 @@ import CFFSubset from './subset/CFFSubset';
16
16
  import BBox from './glyph/BBox';
17
17
  import { asciiDecoder } from './utils';
18
18
 
19
+ /** @typedef {import('restructure').DecodeStream} DecodeStream */
20
+ /** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
21
+ /** @typedef {import('../types/fontkit').FontDirectory} FontDirectory */
22
+ /** @typedef {import('../types/fontkit').FontLike} FontLike */
23
+ /** @typedef {import('../types/fontkit').FontMetrics} FontMetrics */
24
+ /** @typedef {import('../types/fontkit').TableEntry} TableEntry */
25
+ /** @typedef {import('../types/fontkit').TableCodec} TableCodec */
26
+ /** @typedef {import('../types/fontkit').VariationCoords} VariationCoords */
27
+ /** @typedef {import('../types/fontkit').NameString} NameString */
28
+ /** @typedef {import('../types/fontkit').HeadTable} HeadTable */
29
+ /** @typedef {import('../types/fontkit').HheaTable} HheaTable */
30
+ /** @typedef {import('../types/fontkit').MaxpTable} MaxpTable */
31
+ /** @typedef {import('../types/fontkit').OS2Table} OS2Table */
32
+ /** @typedef {import('../types/fontkit').NameTable} NameTable */
33
+ /** @typedef {import('../types/fontkit').PostTable} PostTable */
34
+ /** @typedef {import('../types/fontkit').CmapTable} CmapTable */
35
+ /** @typedef {import('../types/fontkit').MetricsTable} MetricsTable */
36
+ /** @typedef {import('../types/fontkit').FvarTable} FvarTable */
37
+ /** @typedef {import('../types/fontkit').FvarAxis} FvarAxis */
38
+ /** @typedef {import('../types/fontkit').CFFFontLike} CFFFontLike */
39
+ /** @typedef {import('../types/fontkit').FeatureInput} FeatureInput */
40
+ /** @typedef {import('../types/fontkit').ScriptTag} ScriptTag */
41
+ /** @typedef {import('../types/fontkit').LanguageTag} LanguageTag */
42
+ /** @typedef {import('../types/fontkit').TextDirection} TextDirection */
43
+ /** @typedef {import('./glyph/Glyph').default} Glyph */
44
+ /** @typedef {import('./layout/GlyphRun').default} GlyphRun */
45
+ /** @typedef {import('./subset/Subset').default} Subset */
46
+
47
+ /**
48
+ * Axis description from {@link TTFFont#variationAxes}.
49
+ * @typedef {{ name: NameString | undefined, min: number, default: number, max: number }} VariationAxisInfo
50
+ */
51
+
19
52
  /**
20
53
  * This is the base class for all SFNT-based font formats in fontkit.
21
54
  * It supports TrueType, and PostScript glyphs, and several color glyph formats.
55
+ *
56
+ * Dynamic SFNT table accessors (`cmap`, `head`, `hhea`, …) are installed by
57
+ * `_installTableGetters` and typed here so call sites see real table shapes.
58
+ *
59
+ * @implements {FontLike}
22
60
  */
23
61
  export default class TTFFont {
62
+ /** @type {string} */
24
63
  type = 'TTF';
25
64
 
65
+ /** @type {DecodeStream} */
66
+ stream = new r.DecodeStream(new Uint8Array(0));
67
+ /** @type {FontDirectory} */
68
+ directory = /** @type {FontDirectory} */ ({ tables: {} });
69
+ /** @type {string | null} */
70
+ defaultLanguage = null;
71
+ /** @type {number[] | null} */
72
+ variationCoords = null;
73
+ /** @type {number} */
74
+ _directoryPos = 0;
75
+ /** @type {Record<string, unknown>} */
76
+ _tables = {};
77
+ /** @type {Record<number, Glyph | undefined>} */
78
+ _glyphs = {};
79
+ /** @type {FontMetrics | undefined} */
80
+ _metrics;
81
+
82
+ // SFNT tables installed as configurable getters by `_installTableGetters`.
83
+ /** @type {CmapTable | undefined} */
84
+ cmap;
85
+ /** @type {HeadTable} */
86
+ head = /** @type {HeadTable} */ ({ unitsPerEm: 0, xMin: 0, yMin: 0, xMax: 0, yMax: 0 });
87
+ /** @type {HheaTable | undefined} */
88
+ hhea;
89
+ /** @type {MetricsTable} */
90
+ hmtx = /** @type {MetricsTable} */ ({
91
+ metrics: { length: 0, get() { return undefined; } },
92
+ bearings: { length: 0, get() { return undefined; } }
93
+ });
94
+
95
+ /** @type {MaxpTable} */
96
+ maxp = { numGlyphs: 0 };
97
+ /** @type {NameTable | undefined} */
98
+ name;
99
+ /** @type {OS2Table | undefined} */
100
+ 'OS/2';
101
+ /** @type {PostTable} */
102
+ post = { version: 0, italicAngle: 0, underlinePosition: 0, underlineThickness: 0 };
103
+ /** @type {FvarTable | undefined} */
104
+ fvar;
105
+ /** @type {CFFFontLike | undefined} */
106
+ CFF2;
107
+ /** @type {CFFFontLike | undefined} */
108
+ 'CFF ';
109
+
110
+ /**
111
+ * @param {ArrayBufferView} buffer
112
+ * @returns {boolean}
113
+ */
26
114
  static probe(buffer) {
27
- let format = asciiDecoder.decode(buffer.slice(0, 4));
115
+ let bytes
116
+ = buffer instanceof Uint8Array
117
+ ? buffer
118
+ : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
119
+ let format = asciiDecoder.decode(bytes.subarray(0, 4));
28
120
  return format === 'true' || format === 'OTTO' || format === String.fromCharCode(0, 1, 0, 0);
29
121
  }
30
122
 
123
+ /**
124
+ * @param {DecodeStream} stream
125
+ * @param {number[] | null} [variationCoords]
126
+ */
31
127
  constructor(stream, variationCoords = null) {
32
128
  this.defaultLanguage = null;
33
129
  this.stream = stream;
@@ -36,14 +132,28 @@ export default class TTFFont {
36
132
  this._directoryPos = this.stream.pos;
37
133
  this._tables = {};
38
134
  this._glyphs = {};
135
+ this._metrics = undefined;
136
+ this.directory = /** @type {FontDirectory} */ ({ tables: {} });
137
+
39
138
  this._decodeDirectory();
40
139
  this._installTableGetters();
41
140
  }
42
141
 
142
+ /**
143
+ * @param {DecodeStream} stream
144
+ * @returns {void}
145
+ */
146
+ _setStream(stream) {
147
+ this.stream = stream;
148
+ }
149
+
150
+ /** @returns {void} */
43
151
  _installTableGetters() {
44
- for (let tag in this.directory.tables) {
45
- let table = this.directory.tables[tag];
46
- if (tables[tag] && table.length > 0) {
152
+ let tableMap = this.directory.tables;
153
+ for (let tag in tableMap) {
154
+ let table = tableMap[tag];
155
+ let codec = tables[tag];
156
+ if (codec && table && table.length > 0) {
47
157
  Object.defineProperty(this, tag, {
48
158
  get: this._getTable.bind(this, table),
49
159
  configurable: true
@@ -52,10 +162,18 @@ export default class TTFFont {
52
162
  }
53
163
  }
54
164
 
165
+ /**
166
+ * @param {string | null} [lang]
167
+ * @returns {void}
168
+ */
55
169
  setDefaultLanguage(lang = null) {
56
170
  this.defaultLanguage = lang;
57
171
  }
58
172
 
173
+ /**
174
+ * @param {TableEntry} table
175
+ * @returns {unknown}
176
+ */
59
177
  _getTable(table) {
60
178
  if (!(table.tag in this._tables)) {
61
179
  try {
@@ -63,7 +181,8 @@ export default class TTFFont {
63
181
  } catch (e) {
64
182
  if (fontkit.logErrors) {
65
183
  console.error(`Error decoding table ${table.tag}`);
66
- console.error(e.stack);
184
+ let err = /** @type {{ stack?: string }} */ (e);
185
+ console.error(err.stack);
67
186
  }
68
187
  }
69
188
  }
@@ -71,6 +190,10 @@ export default class TTFFont {
71
190
  return this._tables[table.tag];
72
191
  }
73
192
 
193
+ /**
194
+ * @param {string} tag
195
+ * @returns {DecodeStream | null}
196
+ */
74
197
  _getTableStream(tag) {
75
198
  let table = this.directory.tables[tag];
76
199
  if (table) {
@@ -81,20 +204,36 @@ export default class TTFFont {
81
204
  return null;
82
205
  }
83
206
 
207
+ /** @returns {void} */
84
208
  _decodeDirectory() {
85
- return this.directory = Directory.decode(this.stream, {_startOffset: 0});
209
+ this.directory = /** @type {FontDirectory} */ (
210
+ /** @type {unknown} */ (Directory.decode(this.stream, { _startOffset: 0 }))
211
+ );
86
212
  }
87
213
 
214
+ /**
215
+ * @param {TableEntry} table
216
+ * @returns {unknown}
217
+ */
88
218
  _decodeTable(table) {
89
219
  let pos = this.stream.pos;
90
220
 
91
221
  let stream = this._getTableStream(table.tag);
92
- let result = tables[table.tag].decode(stream, this, table.length);
222
+ let codec = /** @type {TableCodec | undefined} */ (tables[table.tag]);
223
+ if (!stream || !codec || !codec.decode) {
224
+ this.stream.pos = pos;
225
+ return undefined;
226
+ }
227
+
228
+ let result = codec.decode(stream, this, table.length);
93
229
 
94
230
  this.stream.pos = pos;
95
231
  return result;
96
232
  }
97
233
 
234
+ /**
235
+ * @returns {FontMetrics}
236
+ */
98
237
  _getMetrics() {
99
238
  if (this._metrics) {
100
239
  return this._metrics;
@@ -104,21 +243,34 @@ export default class TTFFont {
104
243
  // https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/src/sfnt/sfobjs.c
105
244
  // typo*/win* fields exist only in OS/2 version >= 1
106
245
  let os2 = this['OS/2'];
107
- let hasTypo = os2 && os2.version > 0;
108
- let ascent, descent, lineGap;
109
-
110
- if (hasTypo && os2.fsSelection.useTypoMetrics) {
111
- ({typoAscender: ascent, typoDescender: descent, typoLineGap: lineGap} = os2);
246
+ let hasTypo = !!(os2 && os2.version > 0);
247
+ /** @type {number} */
248
+ let ascent;
249
+ /** @type {number} */
250
+ let descent;
251
+ /** @type {number} */
252
+ let lineGap;
253
+
254
+ if (hasTypo && os2 && os2.fsSelection.useTypoMetrics) {
255
+ ascent = /** @type {number} */ (os2.typoAscender);
256
+ descent = /** @type {number} */ (os2.typoDescender);
257
+ lineGap = /** @type {number} */ (os2.typoLineGap);
112
258
  } else {
113
- ({ascent, descent, lineGap} = this.hhea);
259
+ let hhea = this.hhea;
260
+ if (!hhea) {
261
+ throw new Error('Missing hhea table');
262
+ }
263
+ ({ ascent, descent, lineGap } = hhea);
114
264
 
115
265
  // Only when both hhea values are zero — FreeType uses !(ascender || descender)
116
- if (!(ascent || descent) && hasTypo) {
266
+ if (!(ascent || descent) && hasTypo && os2) {
117
267
  if (os2.typoAscender || os2.typoDescender) {
118
- ({typoAscender: ascent, typoDescender: descent, typoLineGap: lineGap} = os2);
268
+ ascent = /** @type {number} */ (os2.typoAscender);
269
+ descent = /** @type {number} */ (os2.typoDescender);
270
+ lineGap = /** @type {number} */ (os2.typoLineGap);
119
271
  } else {
120
- ascent = os2.winAscent;
121
- descent = -os2.winDescent;
272
+ ascent = /** @type {number} */ (os2.winAscent);
273
+ descent = -(/** @type {number} */ (os2.winDescent));
122
274
  lineGap = 0;
123
275
  }
124
276
  }
@@ -135,19 +287,23 @@ export default class TTFFont {
135
287
  /**
136
288
  * Gets a string from the font's `name` table
137
289
  * `lang` is a BCP-47 language code.
138
- * @return {string}
290
+ * @param {string} key
291
+ * @param {string | null} [lang]
292
+ * @returns {NameString | null}
139
293
  */
140
294
  getName(key, lang = this.defaultLanguage || fontkit.defaultLanguage) {
141
295
  let record = this.name && this.name.records[key];
142
- if (record) {
296
+ if (record && typeof record === 'object') {
143
297
  // Attempt to retrieve the entry, depending on which translation is available:
298
+ /** @type {import('../types/fontkit').NameLocaleMap} */
299
+ let locales = /** @type {import('../types/fontkit').NameLocaleMap} */ (record);
144
300
  return (
145
- record[lang]
146
- || record[this.defaultLanguage]
147
- || record[fontkit.defaultLanguage]
148
- || record['en']
149
- || record[Object.keys(record)[0]] // Seriously, ANY language would be fine
150
- || null
301
+ (lang ? locales[lang] : undefined)
302
+ || (this.defaultLanguage ? locales[this.defaultLanguage] : undefined)
303
+ || locales[fontkit.defaultLanguage]
304
+ || locales['en']
305
+ || locales[Object.keys(locales)[0]] // Seriously, ANY language would be fine
306
+ || null
151
307
  );
152
308
  }
153
309
 
@@ -156,7 +312,7 @@ export default class TTFFont {
156
312
 
157
313
  /**
158
314
  * The unique PostScript name for this font, e.g. "Helvetica-Bold"
159
- * @type {string}
315
+ * @type {NameString | null}
160
316
  */
161
317
  get postscriptName() {
162
318
  return this.getName('postscriptName');
@@ -164,7 +320,7 @@ export default class TTFFont {
164
320
 
165
321
  /**
166
322
  * The font's full name, e.g. "Helvetica Bold"
167
- * @type {string}
323
+ * @type {NameString | null}
168
324
  */
169
325
  get fullName() {
170
326
  return this.getName('fullName');
@@ -172,7 +328,7 @@ export default class TTFFont {
172
328
 
173
329
  /**
174
330
  * The font's family name, e.g. "Helvetica"
175
- * @type {string}
331
+ * @type {NameString | null}
176
332
  */
177
333
  get familyName() {
178
334
  return this.getName('fontFamily');
@@ -180,7 +336,7 @@ export default class TTFFont {
180
336
 
181
337
  /**
182
338
  * The font's sub-family, e.g. "Bold".
183
- * @type {string}
339
+ * @type {NameString | null}
184
340
  */
185
341
  get subfamilyName() {
186
342
  return this.getName('fontSubfamily');
@@ -188,7 +344,7 @@ export default class TTFFont {
188
344
 
189
345
  /**
190
346
  * The font's copyright information
191
- * @type {string}
347
+ * @type {NameString | null}
192
348
  */
193
349
  get copyright() {
194
350
  return this.getName('copyright');
@@ -196,7 +352,7 @@ export default class TTFFont {
196
352
 
197
353
  /**
198
354
  * The font's version number
199
- * @type {string}
355
+ * @type {NameString | null}
200
356
  */
201
357
  get version() {
202
358
  return this.getName('version');
@@ -266,7 +422,7 @@ export default class TTFFont {
266
422
  */
267
423
  get capHeight() {
268
424
  let os2 = this['OS/2'];
269
- return os2 ? os2.capHeight : this.ascent;
425
+ return os2 && os2.capHeight != null ? os2.capHeight : this.ascent;
270
426
  }
271
427
 
272
428
  /**
@@ -276,7 +432,7 @@ export default class TTFFont {
276
432
  */
277
433
  get xHeight() {
278
434
  let os2 = this['OS/2'];
279
- return os2 ? os2.xHeight : 0;
435
+ return os2 && os2.xHeight != null ? os2.xHeight : 0;
280
436
  }
281
437
 
282
438
  /**
@@ -304,9 +460,12 @@ export default class TTFFont {
304
460
  return Object.freeze(new BBox(this.head.xMin, this.head.yMin, this.head.xMax, this.head.yMax));
305
461
  }
306
462
 
463
+ /**
464
+ * @type {CmapProcessor}
465
+ */
307
466
  @cache
308
467
  get _cmapProcessor() {
309
- return new CmapProcessor(this.cmap);
468
+ return new CmapProcessor(/** @type {CmapTable} */ (this.cmap));
310
469
  }
311
470
 
312
471
  /**
@@ -333,7 +492,7 @@ export default class TTFFont {
333
492
  * Does not perform any advanced substitutions (there is no context to do so).
334
493
  *
335
494
  * @param {number} codePoint
336
- * @return {Glyph}
495
+ * @return {Glyph | null}
337
496
  */
338
497
  glyphForCodePoint(codePoint) {
339
498
  return this.getGlyph(this._cmapProcessor.lookup(codePoint), [codePoint]);
@@ -349,6 +508,7 @@ export default class TTFFont {
349
508
  * @return {Glyph[]}
350
509
  */
351
510
  glyphsForString(string) {
511
+ /** @type {Glyph[]} */
352
512
  let glyphs = [];
353
513
  let len = string.length;
354
514
  let idx = 0;
@@ -378,10 +538,16 @@ export default class TTFFont {
378
538
 
379
539
  if (state === 0 && nextState === 1) {
380
540
  // Variation selector following normal codepoint.
381
- glyphs.push(this.getGlyph(this._cmapProcessor.lookup(last, code), [last, code]));
541
+ let g = this.getGlyph(this._cmapProcessor.lookup(last, code), [last, code]);
542
+ if (g) {
543
+ glyphs.push(g);
544
+ }
382
545
  } else if (state === 0 && nextState === 0) {
383
546
  // Normal codepoint following normal codepoint.
384
- glyphs.push(this.glyphForCodePoint(last));
547
+ let g = this.glyphForCodePoint(last);
548
+ if (g) {
549
+ glyphs.push(g);
550
+ }
385
551
  }
386
552
 
387
553
  last = code;
@@ -391,6 +557,9 @@ export default class TTFFont {
391
557
  return glyphs;
392
558
  }
393
559
 
560
+ /**
561
+ * @type {LayoutEngine}
562
+ */
394
563
  @cache
395
564
  get _layoutEngine() {
396
565
  return new LayoutEngine(this);
@@ -400,10 +569,10 @@ export default class TTFFont {
400
569
  * Returns a GlyphRun object, which includes an array of Glyphs and GlyphPositions for the given string.
401
570
  *
402
571
  * @param {string} string
403
- * @param {string[]} [userFeatures]
404
- * @param {string} [script]
405
- * @param {string} [language]
406
- * @param {string} [direction]
572
+ * @param {FeatureInput} [userFeatures]
573
+ * @param {ScriptTag} [script]
574
+ * @param {LanguageTag} [language]
575
+ * @param {TextDirection} [direction]
407
576
  * @return {GlyphRun}
408
577
  */
409
578
  layout(string, userFeatures, script, language, direction) {
@@ -413,6 +582,7 @@ export default class TTFFont {
413
582
  /**
414
583
  * Returns an array of strings that map to the given glyph id.
415
584
  * @param {number} gid - glyph id
585
+ * @returns {string[]}
416
586
  */
417
587
  stringsForGlyph(gid) {
418
588
  return this._layoutEngine.stringsForGlyph(gid);
@@ -430,16 +600,27 @@ export default class TTFFont {
430
600
  return this._layoutEngine.getAvailableFeatures();
431
601
  }
432
602
 
603
+ /**
604
+ * @param {ScriptTag} [script]
605
+ * @param {LanguageTag} [language]
606
+ * @returns {string[]}
607
+ */
433
608
  getAvailableFeatures(script, language) {
434
609
  return this._layoutEngine.getAvailableFeatures(script, language);
435
610
  }
436
611
 
612
+ /**
613
+ * @param {number} glyph
614
+ * @param {number[]} [characters]
615
+ * @returns {Glyph | null | undefined}
616
+ */
437
617
  _getBaseGlyph(glyph, characters = []) {
438
618
  let cached = this._glyphs[glyph];
439
- if (cached?._getContours) {
619
+ if (cached && typeof (/** @type {{ _getContours?: unknown }} */ (cached))._getContours === 'function') {
440
620
  return cached;
441
621
  }
442
622
 
623
+ /** @type {Glyph | null} */
443
624
  let outline = null;
444
625
  if (this.directory.tables.glyf) {
445
626
  outline = new TTFGlyph(glyph, characters, this);
@@ -460,20 +641,17 @@ export default class TTFFont {
460
641
  * your use later, and it will be stored in the glyph object.
461
642
  *
462
643
  * @param {number} glyph
463
- * @param {number[]} characters
464
- * @return {Glyph}
644
+ * @param {number[]} [characters]
645
+ * @return {Glyph | null}
465
646
  */
466
647
  getGlyph(glyph, characters = []) {
467
648
  if (!this._glyphs[glyph]) {
468
649
  if (this.directory.tables.sbix) {
469
650
  this._glyphs[glyph] = new SBIXGlyph(glyph, characters, this);
470
-
471
651
  } else if ((this.directory.tables.COLR) && (this.directory.tables.CPAL)) {
472
652
  this._glyphs[glyph] = new COLRGlyph(glyph, characters, this);
473
-
474
653
  } else if (this.directory.tables.CBLC || this.directory.tables.EBLC) {
475
654
  this._glyphs[glyph] = new CBDTGlyph(glyph, characters, this);
476
-
477
655
  } else {
478
656
  this._getBaseGlyph(glyph, characters);
479
657
  }
@@ -499,10 +677,11 @@ export default class TTFFont {
499
677
  * that this font supports. Keys are setting tags, and values
500
678
  * contain the axis name, range, and default value.
501
679
  *
502
- * @type {object}
680
+ * @type {Record<string, VariationAxisInfo>}
503
681
  */
504
682
  @cache
505
683
  get variationAxes() {
684
+ /** @type {Record<string, VariationAxisInfo>} */
506
685
  let res = {};
507
686
  if (!this.fvar) {
508
687
  return res;
@@ -510,7 +689,7 @@ export default class TTFFont {
510
689
 
511
690
  for (let axis of this.fvar.axis) {
512
691
  res[axis.axisTag.trim()] = {
513
- name: axis.name.en,
692
+ name: axis.name && axis.name.en,
514
693
  min: axis.minValue,
515
694
  default: axis.defaultValue,
516
695
  max: axis.maxValue
@@ -525,23 +704,28 @@ export default class TTFFont {
525
704
  * that the font designer has specified. Keys are variation names
526
705
  * and values are the variation settings for this instance.
527
706
  *
528
- * @type {object}
707
+ * @type {Record<string, Record<string, number>>}
529
708
  */
530
709
  @cache
531
710
  get namedVariations() {
711
+ /** @type {Record<string, Record<string, number>>} */
532
712
  let res = {};
533
713
  if (!this.fvar) {
534
714
  return res;
535
715
  }
536
716
 
537
717
  for (let instance of this.fvar.instance) {
718
+ /** @type {Record<string, number>} */
538
719
  let settings = {};
539
720
  for (let i = 0; i < this.fvar.axis.length; i++) {
540
721
  let axis = this.fvar.axis[i];
541
722
  settings[axis.axisTag.trim()] = instance.coord[i];
542
723
  }
543
724
 
544
- res[instance.name.en] = settings;
725
+ let key = instance.name && instance.name.en;
726
+ if (typeof key === 'string') {
727
+ res[key] = settings;
728
+ }
545
729
  }
546
730
 
547
731
  return res;
@@ -552,7 +736,7 @@ export default class TTFFont {
552
736
  * Settings can either be an instance name, or an object containing
553
737
  * variation tags as specified by the `variationAxes` property.
554
738
  *
555
- * @param {object} settings
739
+ * @param {string | Record<string, number>} settings
556
740
  * @return {TTFFont}
557
741
  */
558
742
  getVariation(settings) {
@@ -560,27 +744,34 @@ export default class TTFFont {
560
744
  throw new Error('Variations require a font with the fvar, gvar and glyf, or CFF2 tables.');
561
745
  }
562
746
 
747
+ /** @type {Record<string, number> | undefined} */
748
+ let resolved;
563
749
  if (typeof settings === 'string') {
564
- settings = this.namedVariations[settings];
750
+ resolved = this.namedVariations[settings];
751
+ } else {
752
+ resolved = settings;
565
753
  }
566
754
 
567
- if (typeof settings !== 'object') {
755
+ if (typeof resolved !== 'object' || resolved == null) {
568
756
  throw new Error('Variation settings must be either a variation name or settings object.');
569
757
  }
570
758
 
759
+ let fvar = /** @type {FvarTable} */ (this.fvar);
760
+
571
761
  // normalize the coordinates
572
- let coords = this.fvar.axis.map(axis => {
762
+ let coords = fvar.axis.map((/** @type {FvarAxis} */ axis) => {
573
763
  let axisTag = axis.axisTag.trim();
574
- if (axisTag in settings) {
575
- return Math.max(axis.minValue, Math.min(axis.maxValue, settings[axisTag]));
764
+ if (axisTag in resolved) {
765
+ return Math.max(axis.minValue, Math.min(axis.maxValue, resolved[axisTag]));
576
766
  } else {
577
767
  return axis.defaultValue;
578
768
  }
579
769
  });
580
770
 
581
771
  // Decompress WOFF/WOFF2 on the source once so the clone shares resolved tables.
582
- if (typeof this._decompress === 'function') {
583
- this._decompress();
772
+ let maybeDecompress = /** @type {{ _decompress?: () => void }} */ (this)._decompress;
773
+ if (typeof maybeDecompress === 'function') {
774
+ maybeDecompress.call(this);
584
775
  }
585
776
 
586
777
  // Preserve subclass (WOFF/WOFF2) and share decoded state. @cache values and
@@ -594,6 +785,9 @@ export default class TTFFont {
594
785
  return font;
595
786
  }
596
787
 
788
+ /**
789
+ * @type {GlyphVariationProcessor | null}
790
+ */
597
791
  @cache
598
792
  get _variationProcessor() {
599
793
  if (!this.fvar) {
@@ -608,14 +802,18 @@ export default class TTFFont {
608
802
  }
609
803
 
610
804
  if (!variationCoords) {
611
- variationCoords = this.fvar.axis.map(axis => axis.defaultValue);
805
+ variationCoords = this.fvar.axis.map((/** @type {FvarAxis} */ axis) => axis.defaultValue);
612
806
  }
613
807
 
614
808
  return new GlyphVariationProcessor(this, variationCoords);
615
809
  }
616
810
 
617
811
  // Standardized format plugin API
812
+ /**
813
+ * @param {string | Uint8Array | Record<string, number>} name
814
+ * @returns {TTFFont}
815
+ */
618
816
  getFont(name) {
619
- return this.getVariation(name);
817
+ return this.getVariation(/** @type {string | Record<string, number>} */ (name));
620
818
  }
621
819
  }