@cantoo/fontkit 2.0.5 → 2.0.7

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 (153) hide show
  1. package/dist/browser-module.d.ts +239 -0
  2. package/dist/browser-module.mjs +4423 -1117
  3. package/dist/browser-module.mjs.map +1 -1
  4. package/dist/browser.cjs +4424 -1117
  5. package/dist/browser.cjs.map +1 -1
  6. package/dist/browser.d.ts +239 -0
  7. package/dist/fontkit.umd.d.ts +239 -0
  8. package/dist/fontkit.umd.js +22076 -0
  9. package/dist/fontkit.umd.js.map +7 -0
  10. package/dist/index.d.ts +239 -0
  11. package/dist/main.cjs +4424 -1117
  12. package/dist/main.cjs.map +1 -1
  13. package/dist/main.d.ts +239 -0
  14. package/dist/module.d.ts +239 -0
  15. package/dist/module.mjs +4423 -1117
  16. package/dist/module.mjs.map +1 -1
  17. package/package.json +22 -5
  18. package/src/CmapProcessor.js +59 -20
  19. package/src/DFont.js +114 -14
  20. package/src/TTFFont.js +259 -57
  21. package/src/TrueTypeCollection.js +54 -10
  22. package/src/WOFF2Font.js +128 -16
  23. package/src/WOFFFont.js +45 -8
  24. package/src/aat/AATFeatureMap.js +62 -18
  25. package/src/aat/AATLayoutEngine.js +33 -1
  26. package/src/aat/AATLookupTable.js +22 -4
  27. package/src/aat/AATMorxProcessor.js +189 -52
  28. package/src/aat/AATStateMachine.js +32 -0
  29. package/src/base.js +19 -0
  30. package/src/cff/CFFCharsets.js +3 -0
  31. package/src/cff/CFFDict.js +79 -9
  32. package/src/cff/CFFEncodings.js +2 -0
  33. package/src/cff/CFFFont.js +119 -23
  34. package/src/cff/CFFIndex.js +62 -5
  35. package/src/cff/CFFOperand.js +52 -17
  36. package/src/cff/CFFPointer.js +41 -2
  37. package/src/cff/CFFPrivateDict.js +14 -4
  38. package/src/cff/CFFStandardStrings.js +1 -0
  39. package/src/cff/CFFTop.js +103 -10
  40. package/src/decorators.js +32 -13
  41. package/src/encodings.js +22 -5
  42. package/src/fs.js +10 -0
  43. package/src/glyph/BBox.js +14 -0
  44. package/src/glyph/CFFGlyph.js +290 -148
  45. package/src/glyph/COLRGlyph.js +38 -3
  46. package/src/glyph/Glyph.js +68 -9
  47. package/src/glyph/GlyphVariationProcessor.js +135 -34
  48. package/src/glyph/Path.js +107 -20
  49. package/src/glyph/SBIXGlyph.js +57 -8
  50. package/src/glyph/StandardNames.js +1 -0
  51. package/src/glyph/TTFGlyph.js +176 -29
  52. package/src/glyph/TTFGlyphEncoder.js +32 -0
  53. package/src/glyph/WOFF2Glyph.js +23 -4
  54. package/src/layout/GlyphPosition.js +6 -0
  55. package/src/layout/GlyphRun.js +25 -7
  56. package/src/layout/KernProcessor.js +62 -3
  57. package/src/layout/LayoutEngine.js +86 -12
  58. package/src/layout/Script.js +27 -1
  59. package/src/layout/UnicodeLayoutEngine.js +29 -4
  60. package/src/opentype/GPOSProcessor.js +156 -45
  61. package/src/opentype/GSUBProcessor.js +92 -33
  62. package/src/opentype/GlyphInfo.js +52 -4
  63. package/src/opentype/GlyphIterator.js +51 -2
  64. package/src/opentype/OTLayoutEngine.js +66 -8
  65. package/src/opentype/OTProcessor.js +283 -70
  66. package/src/opentype/ShapingPlan.js +51 -3
  67. package/src/opentype/shapers/ArabicShaper.js +19 -0
  68. package/src/opentype/shapers/DefaultShaper.js +51 -7
  69. package/src/opentype/shapers/HangulShaper.js +85 -7
  70. package/src/opentype/shapers/HebrewShaper.js +45 -11
  71. package/src/opentype/shapers/IndicShaper.js +141 -26
  72. package/src/opentype/shapers/ThaiShaper.js +35 -4
  73. package/src/opentype/shapers/UniversalShaper.js +83 -17
  74. package/src/opentype/shapers/index.js +15 -2
  75. package/src/packages/unicode-properties/data-trie.js +1 -0
  76. package/src/packages/unicode-properties/data.js +12 -0
  77. package/src/packages/unicode-properties/dist/index.cjs +22 -9
  78. package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
  79. package/src/packages/unicode-properties/dist/index.mjs +21 -9
  80. package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
  81. package/src/packages/unicode-properties/index.js +65 -0
  82. package/src/packages/unicode-properties/package.json +1 -1
  83. package/src/packages/unicode-trie/builder.js +128 -7
  84. package/src/packages/unicode-trie/dist/builder.cjs +129 -16
  85. package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
  86. package/src/packages/unicode-trie/dist/builder.mjs +128 -16
  87. package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
  88. package/src/packages/unicode-trie/dist/index.cjs +22 -9
  89. package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
  90. package/src/packages/unicode-trie/dist/index.mjs +21 -9
  91. package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
  92. package/src/packages/unicode-trie/index.js +62 -10
  93. package/src/packages/unicode-trie/package.json +1 -1
  94. package/src/packages/unicode-trie/swap.js +13 -0
  95. package/src/subset/CFFSubset.js +112 -22
  96. package/src/subset/Subset.js +21 -1
  97. package/src/subset/TTFSubset.js +79 -36
  98. package/src/tables/BASE.js +1 -0
  99. package/src/tables/COLR.js +1 -0
  100. package/src/tables/CPAL.js +1 -0
  101. package/src/tables/DSIG.js +1 -0
  102. package/src/tables/EBDT.js +10 -3
  103. package/src/tables/EBLC.js +67 -7
  104. package/src/tables/GDEF.js +1 -0
  105. package/src/tables/GPOS.js +79 -16
  106. package/src/tables/GSUB.js +14 -3
  107. package/src/tables/HVAR.js +51 -5
  108. package/src/tables/JSTF.js +3 -2
  109. package/src/tables/LTSH.js +1 -0
  110. package/src/tables/OS2.js +1 -0
  111. package/src/tables/PCLT.js +1 -0
  112. package/src/tables/STAT.js +46 -6
  113. package/src/tables/VDMX.js +1 -0
  114. package/src/tables/VORG.js +1 -0
  115. package/src/tables/WOFF2Directory.js +56 -8
  116. package/src/tables/WOFFDirectory.js +14 -2
  117. package/src/tables/aat.js +160 -35
  118. package/src/tables/avar.js +1 -0
  119. package/src/tables/bsln.js +3 -2
  120. package/src/tables/cmap.js +26 -6
  121. package/src/tables/cvt.js +1 -0
  122. package/src/tables/directory.js +44 -16
  123. package/src/tables/feat.js +24 -2
  124. package/src/tables/fpgm.js +1 -0
  125. package/src/tables/fvar.js +32 -4
  126. package/src/tables/gasp.js +1 -0
  127. package/src/tables/glyf.js +1 -0
  128. package/src/tables/gvar.js +29 -2
  129. package/src/tables/hdmx.js +12 -1
  130. package/src/tables/head.js +1 -0
  131. package/src/tables/hhea.js +1 -0
  132. package/src/tables/hmtx.js +18 -2
  133. package/src/tables/index.js +1 -0
  134. package/src/tables/just.js +15 -4
  135. package/src/tables/kern.js +43 -6
  136. package/src/tables/loca.js +21 -8
  137. package/src/tables/maxp.js +1 -0
  138. package/src/tables/morx.js +14 -7
  139. package/src/tables/name.js +104 -23
  140. package/src/tables/opbd.js +2 -1
  141. package/src/tables/opentype.js +39 -4
  142. package/src/tables/post.js +8 -1
  143. package/src/tables/prep.js +1 -0
  144. package/src/tables/sbix.js +12 -1
  145. package/src/tables/variations.js +23 -3
  146. package/src/tables/vhea.js +1 -0
  147. package/src/tables/vmtx.js +18 -2
  148. package/src/utils.js +15 -0
  149. package/types/assets.d.ts +13 -0
  150. package/types/deps.d.ts +83 -0
  151. package/types/fontkit.d.ts +1324 -0
  152. package/types/index.d.ts +239 -0
  153. package/types/restructure.d.ts +354 -0
package/src/TTFFont.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { cache } from './decorators';
2
2
  import * as fontkit from './base';
3
+ import * as r from 'restructure';
3
4
  import Directory from './tables/directory';
4
5
  import tables from './tables';
5
6
  import CmapProcessor from './CmapProcessor';
@@ -15,18 +16,114 @@ import CFFSubset from './subset/CFFSubset';
15
16
  import BBox from './glyph/BBox';
16
17
  import { asciiDecoder } from './utils';
17
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
+
18
52
  /**
19
53
  * This is the base class for all SFNT-based font formats in fontkit.
20
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}
21
60
  */
22
61
  export default class TTFFont {
62
+ /** @type {string} */
23
63
  type = 'TTF';
24
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
+ */
25
114
  static probe(buffer) {
26
- 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));
27
120
  return format === 'true' || format === 'OTTO' || format === String.fromCharCode(0, 1, 0, 0);
28
121
  }
29
122
 
123
+ /**
124
+ * @param {DecodeStream} stream
125
+ * @param {number[] | null} [variationCoords]
126
+ */
30
127
  constructor(stream, variationCoords = null) {
31
128
  this.defaultLanguage = null;
32
129
  this.stream = stream;
@@ -35,14 +132,28 @@ export default class TTFFont {
35
132
  this._directoryPos = this.stream.pos;
36
133
  this._tables = {};
37
134
  this._glyphs = {};
135
+ this._metrics = undefined;
136
+ this.directory = /** @type {FontDirectory} */ ({ tables: {} });
137
+
38
138
  this._decodeDirectory();
39
139
  this._installTableGetters();
40
140
  }
41
141
 
142
+ /**
143
+ * @param {DecodeStream} stream
144
+ * @returns {void}
145
+ */
146
+ _setStream(stream) {
147
+ this.stream = stream;
148
+ }
149
+
150
+ /** @returns {void} */
42
151
  _installTableGetters() {
43
- for (let tag in this.directory.tables) {
44
- let table = this.directory.tables[tag];
45
- 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) {
46
157
  Object.defineProperty(this, tag, {
47
158
  get: this._getTable.bind(this, table),
48
159
  configurable: true
@@ -51,10 +162,18 @@ export default class TTFFont {
51
162
  }
52
163
  }
53
164
 
165
+ /**
166
+ * @param {string | null} [lang]
167
+ * @returns {void}
168
+ */
54
169
  setDefaultLanguage(lang = null) {
55
170
  this.defaultLanguage = lang;
56
171
  }
57
172
 
173
+ /**
174
+ * @param {TableEntry} table
175
+ * @returns {unknown}
176
+ */
58
177
  _getTable(table) {
59
178
  if (!(table.tag in this._tables)) {
60
179
  try {
@@ -62,7 +181,8 @@ export default class TTFFont {
62
181
  } catch (e) {
63
182
  if (fontkit.logErrors) {
64
183
  console.error(`Error decoding table ${table.tag}`);
65
- console.error(e.stack);
184
+ let err = /** @type {{ stack?: string }} */ (e);
185
+ console.error(err.stack);
66
186
  }
67
187
  }
68
188
  }
@@ -70,6 +190,10 @@ export default class TTFFont {
70
190
  return this._tables[table.tag];
71
191
  }
72
192
 
193
+ /**
194
+ * @param {string} tag
195
+ * @returns {DecodeStream | null}
196
+ */
73
197
  _getTableStream(tag) {
74
198
  let table = this.directory.tables[tag];
75
199
  if (table) {
@@ -80,20 +204,36 @@ export default class TTFFont {
80
204
  return null;
81
205
  }
82
206
 
207
+ /** @returns {void} */
83
208
  _decodeDirectory() {
84
- return this.directory = Directory.decode(this.stream, { _startOffset: 0 });
209
+ this.directory = /** @type {FontDirectory} */ (
210
+ /** @type {unknown} */ (Directory.decode(this.stream, { _startOffset: 0 }))
211
+ );
85
212
  }
86
213
 
214
+ /**
215
+ * @param {TableEntry} table
216
+ * @returns {unknown}
217
+ */
87
218
  _decodeTable(table) {
88
219
  let pos = this.stream.pos;
89
220
 
90
221
  let stream = this._getTableStream(table.tag);
91
- 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);
92
229
 
93
230
  this.stream.pos = pos;
94
231
  return result;
95
232
  }
96
233
 
234
+ /**
235
+ * @returns {FontMetrics}
236
+ */
97
237
  _getMetrics() {
98
238
  if (this._metrics) {
99
239
  return this._metrics;
@@ -103,21 +243,34 @@ export default class TTFFont {
103
243
  // https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/src/sfnt/sfobjs.c
104
244
  // typo*/win* fields exist only in OS/2 version >= 1
105
245
  let os2 = this['OS/2'];
106
- let hasTypo = os2 && os2.version > 0;
107
- let ascent, descent, lineGap;
108
-
109
- if (hasTypo && os2.fsSelection.useTypoMetrics) {
110
- ({ 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);
111
258
  } else {
112
- ({ 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);
113
264
 
114
265
  // Only when both hhea values are zero — FreeType uses !(ascender || descender)
115
- if (!(ascent || descent) && hasTypo) {
266
+ if (!(ascent || descent) && hasTypo && os2) {
116
267
  if (os2.typoAscender || os2.typoDescender) {
117
- ({ 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);
118
271
  } else {
119
- ascent = os2.winAscent;
120
- descent = -os2.winDescent;
272
+ ascent = /** @type {number} */ (os2.winAscent);
273
+ descent = -(/** @type {number} */ (os2.winDescent));
121
274
  lineGap = 0;
122
275
  }
123
276
  }
@@ -134,18 +287,22 @@ export default class TTFFont {
134
287
  /**
135
288
  * Gets a string from the font's `name` table
136
289
  * `lang` is a BCP-47 language code.
137
- * @return {string}
290
+ * @param {string} key
291
+ * @param {string | null} [lang]
292
+ * @returns {NameString | null}
138
293
  */
139
294
  getName(key, lang = this.defaultLanguage || fontkit.defaultLanguage) {
140
295
  let record = this.name && this.name.records[key];
141
- if (record) {
296
+ if (record && typeof record === 'object') {
142
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);
143
300
  return (
144
- record[lang]
145
- || record[this.defaultLanguage]
146
- || record[fontkit.defaultLanguage]
147
- || record['en']
148
- || record[Object.keys(record)[0]] // Seriously, ANY language would be fine
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
149
306
  || null
150
307
  );
151
308
  }
@@ -155,7 +312,7 @@ export default class TTFFont {
155
312
 
156
313
  /**
157
314
  * The unique PostScript name for this font, e.g. "Helvetica-Bold"
158
- * @type {string}
315
+ * @type {NameString | null}
159
316
  */
160
317
  get postscriptName() {
161
318
  return this.getName('postscriptName');
@@ -163,7 +320,7 @@ export default class TTFFont {
163
320
 
164
321
  /**
165
322
  * The font's full name, e.g. "Helvetica Bold"
166
- * @type {string}
323
+ * @type {NameString | null}
167
324
  */
168
325
  get fullName() {
169
326
  return this.getName('fullName');
@@ -171,7 +328,7 @@ export default class TTFFont {
171
328
 
172
329
  /**
173
330
  * The font's family name, e.g. "Helvetica"
174
- * @type {string}
331
+ * @type {NameString | null}
175
332
  */
176
333
  get familyName() {
177
334
  return this.getName('fontFamily');
@@ -179,7 +336,7 @@ export default class TTFFont {
179
336
 
180
337
  /**
181
338
  * The font's sub-family, e.g. "Bold".
182
- * @type {string}
339
+ * @type {NameString | null}
183
340
  */
184
341
  get subfamilyName() {
185
342
  return this.getName('fontSubfamily');
@@ -187,7 +344,7 @@ export default class TTFFont {
187
344
 
188
345
  /**
189
346
  * The font's copyright information
190
- * @type {string}
347
+ * @type {NameString | null}
191
348
  */
192
349
  get copyright() {
193
350
  return this.getName('copyright');
@@ -195,7 +352,7 @@ export default class TTFFont {
195
352
 
196
353
  /**
197
354
  * The font's version number
198
- * @type {string}
355
+ * @type {NameString | null}
199
356
  */
200
357
  get version() {
201
358
  return this.getName('version');
@@ -265,7 +422,7 @@ export default class TTFFont {
265
422
  */
266
423
  get capHeight() {
267
424
  let os2 = this['OS/2'];
268
- return os2 ? os2.capHeight : this.ascent;
425
+ return os2 && os2.capHeight != null ? os2.capHeight : this.ascent;
269
426
  }
270
427
 
271
428
  /**
@@ -275,7 +432,7 @@ export default class TTFFont {
275
432
  */
276
433
  get xHeight() {
277
434
  let os2 = this['OS/2'];
278
- return os2 ? os2.xHeight : 0;
435
+ return os2 && os2.xHeight != null ? os2.xHeight : 0;
279
436
  }
280
437
 
281
438
  /**
@@ -303,9 +460,12 @@ export default class TTFFont {
303
460
  return Object.freeze(new BBox(this.head.xMin, this.head.yMin, this.head.xMax, this.head.yMax));
304
461
  }
305
462
 
463
+ /**
464
+ * @type {CmapProcessor}
465
+ */
306
466
  @cache
307
467
  get _cmapProcessor() {
308
- return new CmapProcessor(this.cmap);
468
+ return new CmapProcessor(/** @type {CmapTable} */ (this.cmap));
309
469
  }
310
470
 
311
471
  /**
@@ -332,7 +492,7 @@ export default class TTFFont {
332
492
  * Does not perform any advanced substitutions (there is no context to do so).
333
493
  *
334
494
  * @param {number} codePoint
335
- * @return {Glyph}
495
+ * @return {Glyph | null}
336
496
  */
337
497
  glyphForCodePoint(codePoint) {
338
498
  return this.getGlyph(this._cmapProcessor.lookup(codePoint), [codePoint]);
@@ -348,6 +508,7 @@ export default class TTFFont {
348
508
  * @return {Glyph[]}
349
509
  */
350
510
  glyphsForString(string) {
511
+ /** @type {Glyph[]} */
351
512
  let glyphs = [];
352
513
  let len = string.length;
353
514
  let idx = 0;
@@ -377,10 +538,16 @@ export default class TTFFont {
377
538
 
378
539
  if (state === 0 && nextState === 1) {
379
540
  // Variation selector following normal codepoint.
380
- 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
+ }
381
545
  } else if (state === 0 && nextState === 0) {
382
546
  // Normal codepoint following normal codepoint.
383
- glyphs.push(this.glyphForCodePoint(last));
547
+ let g = this.glyphForCodePoint(last);
548
+ if (g) {
549
+ glyphs.push(g);
550
+ }
384
551
  }
385
552
 
386
553
  last = code;
@@ -390,6 +557,9 @@ export default class TTFFont {
390
557
  return glyphs;
391
558
  }
392
559
 
560
+ /**
561
+ * @type {LayoutEngine}
562
+ */
393
563
  @cache
394
564
  get _layoutEngine() {
395
565
  return new LayoutEngine(this);
@@ -399,10 +569,10 @@ export default class TTFFont {
399
569
  * Returns a GlyphRun object, which includes an array of Glyphs and GlyphPositions for the given string.
400
570
  *
401
571
  * @param {string} string
402
- * @param {string[]} [userFeatures]
403
- * @param {string} [script]
404
- * @param {string} [language]
405
- * @param {string} [direction]
572
+ * @param {FeatureInput} [userFeatures]
573
+ * @param {ScriptTag} [script]
574
+ * @param {LanguageTag} [language]
575
+ * @param {TextDirection} [direction]
406
576
  * @return {GlyphRun}
407
577
  */
408
578
  layout(string, userFeatures, script, language, direction) {
@@ -412,6 +582,7 @@ export default class TTFFont {
412
582
  /**
413
583
  * Returns an array of strings that map to the given glyph id.
414
584
  * @param {number} gid - glyph id
585
+ * @returns {string[]}
415
586
  */
416
587
  stringsForGlyph(gid) {
417
588
  return this._layoutEngine.stringsForGlyph(gid);
@@ -429,16 +600,27 @@ export default class TTFFont {
429
600
  return this._layoutEngine.getAvailableFeatures();
430
601
  }
431
602
 
603
+ /**
604
+ * @param {ScriptTag} [script]
605
+ * @param {LanguageTag} [language]
606
+ * @returns {string[]}
607
+ */
432
608
  getAvailableFeatures(script, language) {
433
609
  return this._layoutEngine.getAvailableFeatures(script, language);
434
610
  }
435
611
 
612
+ /**
613
+ * @param {number} glyph
614
+ * @param {number[]} [characters]
615
+ * @returns {Glyph | null | undefined}
616
+ */
436
617
  _getBaseGlyph(glyph, characters = []) {
437
618
  let cached = this._glyphs[glyph];
438
- if (cached?._getContours) {
619
+ if (cached && typeof (/** @type {{ _getContours?: unknown }} */ (cached))._getContours === 'function') {
439
620
  return cached;
440
621
  }
441
622
 
623
+ /** @type {Glyph | null} */
442
624
  let outline = null;
443
625
  if (this.directory.tables.glyf) {
444
626
  outline = new TTFGlyph(glyph, characters, this);
@@ -459,8 +641,8 @@ export default class TTFFont {
459
641
  * your use later, and it will be stored in the glyph object.
460
642
  *
461
643
  * @param {number} glyph
462
- * @param {number[]} characters
463
- * @return {Glyph}
644
+ * @param {number[]} [characters]
645
+ * @return {Glyph | null}
464
646
  */
465
647
  getGlyph(glyph, characters = []) {
466
648
  if (!this._glyphs[glyph]) {
@@ -495,10 +677,11 @@ export default class TTFFont {
495
677
  * that this font supports. Keys are setting tags, and values
496
678
  * contain the axis name, range, and default value.
497
679
  *
498
- * @type {object}
680
+ * @type {Record<string, VariationAxisInfo>}
499
681
  */
500
682
  @cache
501
683
  get variationAxes() {
684
+ /** @type {Record<string, VariationAxisInfo>} */
502
685
  let res = {};
503
686
  if (!this.fvar) {
504
687
  return res;
@@ -506,7 +689,7 @@ export default class TTFFont {
506
689
 
507
690
  for (let axis of this.fvar.axis) {
508
691
  res[axis.axisTag.trim()] = {
509
- name: axis.name.en,
692
+ name: axis.name && axis.name.en,
510
693
  min: axis.minValue,
511
694
  default: axis.defaultValue,
512
695
  max: axis.maxValue
@@ -521,23 +704,28 @@ export default class TTFFont {
521
704
  * that the font designer has specified. Keys are variation names
522
705
  * and values are the variation settings for this instance.
523
706
  *
524
- * @type {object}
707
+ * @type {Record<string, Record<string, number>>}
525
708
  */
526
709
  @cache
527
710
  get namedVariations() {
711
+ /** @type {Record<string, Record<string, number>>} */
528
712
  let res = {};
529
713
  if (!this.fvar) {
530
714
  return res;
531
715
  }
532
716
 
533
717
  for (let instance of this.fvar.instance) {
718
+ /** @type {Record<string, number>} */
534
719
  let settings = {};
535
720
  for (let i = 0; i < this.fvar.axis.length; i++) {
536
721
  let axis = this.fvar.axis[i];
537
722
  settings[axis.axisTag.trim()] = instance.coord[i];
538
723
  }
539
724
 
540
- res[instance.name.en] = settings;
725
+ let key = instance.name && instance.name.en;
726
+ if (typeof key === 'string') {
727
+ res[key] = settings;
728
+ }
541
729
  }
542
730
 
543
731
  return res;
@@ -548,7 +736,7 @@ export default class TTFFont {
548
736
  * Settings can either be an instance name, or an object containing
549
737
  * variation tags as specified by the `variationAxes` property.
550
738
  *
551
- * @param {object} settings
739
+ * @param {string | Record<string, number>} settings
552
740
  * @return {TTFFont}
553
741
  */
554
742
  getVariation(settings) {
@@ -556,27 +744,34 @@ export default class TTFFont {
556
744
  throw new Error('Variations require a font with the fvar, gvar and glyf, or CFF2 tables.');
557
745
  }
558
746
 
747
+ /** @type {Record<string, number> | undefined} */
748
+ let resolved;
559
749
  if (typeof settings === 'string') {
560
- settings = this.namedVariations[settings];
750
+ resolved = this.namedVariations[settings];
751
+ } else {
752
+ resolved = settings;
561
753
  }
562
754
 
563
- if (typeof settings !== 'object') {
755
+ if (typeof resolved !== 'object' || resolved == null) {
564
756
  throw new Error('Variation settings must be either a variation name or settings object.');
565
757
  }
566
758
 
759
+ let fvar = /** @type {FvarTable} */ (this.fvar);
760
+
567
761
  // normalize the coordinates
568
- let coords = this.fvar.axis.map((axis) => {
762
+ let coords = fvar.axis.map((/** @type {FvarAxis} */ axis) => {
569
763
  let axisTag = axis.axisTag.trim();
570
- if (axisTag in settings) {
571
- 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]));
572
766
  } else {
573
767
  return axis.defaultValue;
574
768
  }
575
769
  });
576
770
 
577
771
  // Decompress WOFF/WOFF2 on the source once so the clone shares resolved tables.
578
- if (typeof this._decompress === 'function') {
579
- this._decompress();
772
+ let maybeDecompress = /** @type {{ _decompress?: () => void }} */ (this)._decompress;
773
+ if (typeof maybeDecompress === 'function') {
774
+ maybeDecompress.call(this);
580
775
  }
581
776
 
582
777
  // Preserve subclass (WOFF/WOFF2) and share decoded state. @cache values and
@@ -590,6 +785,9 @@ export default class TTFFont {
590
785
  return font;
591
786
  }
592
787
 
788
+ /**
789
+ * @type {GlyphVariationProcessor | null}
790
+ */
593
791
  @cache
594
792
  get _variationProcessor() {
595
793
  if (!this.fvar) {
@@ -604,14 +802,18 @@ export default class TTFFont {
604
802
  }
605
803
 
606
804
  if (!variationCoords) {
607
- variationCoords = this.fvar.axis.map(axis => axis.defaultValue);
805
+ variationCoords = this.fvar.axis.map((/** @type {FvarAxis} */ axis) => axis.defaultValue);
608
806
  }
609
807
 
610
808
  return new GlyphVariationProcessor(this, variationCoords);
611
809
  }
612
810
 
613
811
  // Standardized format plugin API
812
+ /**
813
+ * @param {string | Uint8Array | Record<string, number>} name
814
+ * @returns {TTFFont}
815
+ */
614
816
  getFont(name) {
615
- return this.getVariation(name);
817
+ return this.getVariation(/** @type {string | Record<string, number>} */ (name));
616
818
  }
617
819
  }