@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
@@ -3,6 +3,13 @@ import Path from './Path';
3
3
  import BBox from './BBox';
4
4
  import * as r from 'restructure';
5
5
 
6
+ /** @typedef {import('restructure').DecodeStream} DecodeStream */
7
+ /** @typedef {import('restructure').StructValue} StructValue */
8
+ /** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
9
+ /** @typedef {import('../../types/fontkit').GlyphComponent} GlyphComponent */
10
+ /** @typedef {import('../../types/fontkit').GlyphMetrics} GlyphMetrics */
11
+ /** @typedef {import('../../types/fontkit').GlyphPoint} GlyphPoint */
12
+
6
13
  // The header for both simple and composite glyphs
7
14
  let GlyfHeader = new r.Struct({
8
15
  numberOfContours: r.int16, // if negative, this is a composite glyph
@@ -34,15 +41,67 @@ const _OVERLAP_COMPOUND = 1 << 10;
34
41
  const _SCALED_COMPONENT_OFFSET = 1 << 11;
35
42
  const _UNSCALED_COMPONENT_OFFSET = 1 << 12;
36
43
 
44
+ /**
45
+ * @param {StructValue} raw
46
+ * @returns {DecodedGlyf}
47
+ */
48
+ function decodeGlyfHeader(raw) {
49
+ let numberOfContours = raw.numberOfContours;
50
+ let xMin = raw.xMin;
51
+ let yMin = raw.yMin;
52
+ let xMax = raw.xMax;
53
+ let yMax = raw.yMax;
54
+ if (
55
+ typeof numberOfContours !== 'number'
56
+ || typeof xMin !== 'number'
57
+ || typeof yMin !== 'number'
58
+ || typeof xMax !== 'number'
59
+ || typeof yMax !== 'number'
60
+ ) {
61
+ throw new Error('Invalid glyf header');
62
+ }
63
+ return { numberOfContours, xMin, yMin, xMax, yMax };
64
+ }
65
+
66
+ /**
67
+ * @param {unknown[]} values
68
+ * @returns {number[]}
69
+ */
70
+ function asNumberArray(values) {
71
+ /** @type {number[]} */
72
+ let out = [];
73
+ for (let i = 0; i < values.length; i++) {
74
+ let v = values[i];
75
+ if (typeof v !== 'number') {
76
+ throw new Error('Expected number array from glyf decode');
77
+ }
78
+ out.push(v);
79
+ }
80
+ return out;
81
+ }
82
+
37
83
  // Represents a point in a simple glyph
38
84
  export class Point {
85
+ /**
86
+ * @param {boolean} onCurve
87
+ * @param {boolean} endContour
88
+ * @param {number} [x]
89
+ * @param {number} [y]
90
+ */
39
91
  constructor(onCurve, endContour, x = 0, y = 0) {
92
+ /** @type {boolean} */
40
93
  this.onCurve = onCurve;
94
+ /** @type {boolean} */
41
95
  this.endContour = endContour;
96
+ /** @type {number} */
42
97
  this.x = x;
98
+ /** @type {number} */
43
99
  this.y = y;
44
100
  }
45
101
 
102
+ /**
103
+ * @returns {Point}
104
+ */
46
105
  copy() {
47
106
  return new Point(this.onCurve, this.endContour, this.x, this.y);
48
107
  }
@@ -50,13 +109,28 @@ export class Point {
50
109
 
51
110
  // Represents a component in a composite glyph
52
111
  class Component {
112
+ /**
113
+ * @param {number} glyphID
114
+ * @param {number} dx
115
+ * @param {number} dy
116
+ */
53
117
  constructor(glyphID, dx, dy) {
118
+ /** @type {number} */
54
119
  this.glyphID = glyphID;
120
+ /** @type {number} */
55
121
  this.dx = dx;
122
+ /** @type {number} */
56
123
  this.dy = dy;
124
+ /** @type {number} */
57
125
  this.pos = 0;
58
- this.scaleX = this.scaleY = 1;
59
- this.scale01 = this.scale10 = 0;
126
+ /** @type {number} */
127
+ this.scaleX = 1;
128
+ /** @type {number} */
129
+ this.scaleY = 1;
130
+ /** @type {number} */
131
+ this.scale01 = 0;
132
+ /** @type {number} */
133
+ this.scale10 = 0;
60
134
  }
61
135
  }
62
136
 
@@ -66,7 +140,11 @@ class Component {
66
140
  export default class TTFGlyph extends Glyph {
67
141
  type = 'TTF';
68
142
 
69
- // Parses just the glyph header and returns the bounding box
143
+ /**
144
+ * Parses just the glyph header and returns the bounding box
145
+ * @param {boolean} [internal]
146
+ * @returns {BBox}
147
+ */
70
148
  _getCBox(internal) {
71
149
  // We need to decode the glyph if variation processing is requested,
72
150
  // so it's easier just to recompute the path's cbox after decoding.
@@ -74,19 +152,34 @@ export default class TTFGlyph extends Glyph {
74
152
  return this.path.cbox;
75
153
  }
76
154
 
77
- let glyfPos = this._font.loca.offsets[this.id];
155
+ let loca = this._font.loca;
156
+ if (!loca) {
157
+ return Object.freeze(new BBox(0, 0, 0, 0));
158
+ }
159
+
160
+ let glyfPos = loca.offsets[this.id];
78
161
  // No outline data (e.g. space): avoid decoding the next glyph's header
79
- if (glyfPos === this._font.loca.offsets[this.id + 1]) {
162
+ if (glyfPos === loca.offsets[this.id + 1]) {
80
163
  return Object.freeze(new BBox(0, 0, 0, 0));
81
164
  }
82
165
 
83
166
  let stream = this._font._getTableStream('glyf');
167
+ if (!stream) {
168
+ return Object.freeze(new BBox(0, 0, 0, 0));
169
+ }
84
170
  stream.pos += glyfPos;
85
- let glyph = GlyfHeader.decode(stream);
171
+ let glyph = decodeGlyfHeader(GlyfHeader.decode(stream));
86
172
  return Object.freeze(new BBox(glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax));
87
173
  }
88
174
 
89
- // Parses a single glyph coordinate
175
+ /**
176
+ * Parses a single glyph coordinate
177
+ * @param {DecodeStream} stream
178
+ * @param {number} prev
179
+ * @param {number} short
180
+ * @param {number} same
181
+ * @returns {number}
182
+ */
90
183
  _parseGlyphCoord(stream, prev, short, same) {
91
184
  let val;
92
185
  if (short) {
@@ -107,20 +200,31 @@ export default class TTFGlyph extends Glyph {
107
200
  return val;
108
201
  }
109
202
 
110
- // Decodes the glyph data into points for simple glyphs,
111
- // or components for composite glyphs
203
+ /**
204
+ * Decodes the glyph data into points for simple glyphs,
205
+ * or components for composite glyphs
206
+ * @returns {DecodedGlyf | null}
207
+ */
112
208
  _decode() {
113
- let glyfPos = this._font.loca.offsets[this.id];
114
- let nextPos = this._font.loca.offsets[this.id + 1];
209
+ let loca = this._font.loca;
210
+ if (!loca) {
211
+ return null;
212
+ }
213
+
214
+ let glyfPos = loca.offsets[this.id];
215
+ let nextPos = loca.offsets[this.id + 1];
115
216
 
116
217
  // Nothing to do if there is no data for this glyph
117
218
  if (glyfPos === nextPos) { return null; }
118
219
 
119
220
  let stream = this._font._getTableStream('glyf');
221
+ if (!stream) {
222
+ return null;
223
+ }
120
224
  stream.pos += glyfPos;
121
225
  let startPos = stream.pos;
122
226
 
123
- let glyph = GlyfHeader.decode(stream);
227
+ let glyph = decodeGlyfHeader(GlyfHeader.decode(stream));
124
228
 
125
229
  if (glyph.numberOfContours > 0) {
126
230
  this._decodeSimple(glyph, stream);
@@ -131,13 +235,19 @@ export default class TTFGlyph extends Glyph {
131
235
  return glyph;
132
236
  }
133
237
 
238
+ /**
239
+ * @param {DecodedGlyf} glyph
240
+ * @param {DecodeStream} stream
241
+ * @returns {void}
242
+ */
134
243
  _decodeSimple(glyph, stream) {
135
244
  // this is a simple glyph
136
245
  glyph.points = [];
137
246
 
138
- let endPtsOfContours = new r.Array(r.uint16, glyph.numberOfContours).decode(stream);
139
- glyph.instructions = new r.Array(r.uint8, r.uint16).decode(stream);
247
+ let endPtsOfContours = asNumberArray(new r.Array(r.uint16, glyph.numberOfContours).decode(stream));
248
+ glyph.instructions = asNumberArray(new r.Array(r.uint8, r.uint16).decode(stream));
140
249
 
250
+ /** @type {number[]} */
141
251
  let flags = [];
142
252
  let numCoords = endPtsOfContours[endPtsOfContours.length - 1] + 1;
143
253
 
@@ -163,13 +273,15 @@ export default class TTFGlyph extends Glyph {
163
273
  let px = 0;
164
274
  for (let i = 0; i < flags.length; i++) {
165
275
  let flag = flags[i];
166
- glyph.points[i].x = px = this._parseGlyphCoord(stream, px, flag & X_SHORT_VECTOR, flag & SAME_X);
276
+ let point = glyph.points[i];
277
+ point.x = px = this._parseGlyphCoord(stream, px, flag & X_SHORT_VECTOR, flag & SAME_X);
167
278
  }
168
279
 
169
280
  let py = 0;
170
281
  for (let i = 0; i < flags.length; i++) {
171
282
  let flag = flags[i];
172
- glyph.points[i].y = py = this._parseGlyphCoord(stream, py, flag & Y_SHORT_VECTOR, flag & SAME_Y);
283
+ let point = glyph.points[i];
284
+ point.y = py = this._parseGlyphCoord(stream, py, flag & Y_SHORT_VECTOR, flag & SAME_Y);
173
285
  }
174
286
 
175
287
  if (this._font._variationProcessor) {
@@ -183,6 +295,12 @@ export default class TTFGlyph extends Glyph {
183
295
  return;
184
296
  }
185
297
 
298
+ /**
299
+ * @param {DecodedGlyf} glyph
300
+ * @param {DecodeStream} stream
301
+ * @param {number} [offset]
302
+ * @returns {boolean}
303
+ */
186
304
  _decodeComposite(glyph, stream, offset = 0) {
187
305
  // this is a composite glyph
188
306
  glyph.components = [];
@@ -227,6 +345,7 @@ export default class TTFGlyph extends Glyph {
227
345
  }
228
346
 
229
347
  if (this._font._variationProcessor) {
348
+ /** @type {Point[]} */
230
349
  let points = [];
231
350
  for (let j = 0; j < glyph.components.length; j++) {
232
351
  let component = glyph.components[j];
@@ -248,6 +367,10 @@ export default class TTFGlyph extends Glyph {
248
367
  return haveInstructions;
249
368
  }
250
369
 
370
+ /**
371
+ * @param {DecodedGlyf} glyph
372
+ * @returns {Point[]}
373
+ */
251
374
  _getPhantomPoints(glyph) {
252
375
  let cbox = this._getCBox(true);
253
376
  if (this._metrics == null) {
@@ -264,20 +387,25 @@ export default class TTFGlyph extends Glyph {
264
387
  ];
265
388
  }
266
389
 
267
- // Decodes font data, resolves composite glyphs, and returns an array of contours
390
+ /**
391
+ * Decodes font data, resolves composite glyphs, and returns an array of contours
392
+ * @returns {Point[][]}
393
+ */
268
394
  _getContours() {
269
395
  let glyph = this._decode();
270
396
  if (!glyph) {
271
397
  return [];
272
398
  }
273
399
 
400
+ /** @type {Point[]} */
274
401
  let points = [];
275
402
 
276
403
  if (glyph.numberOfContours < 0) {
277
404
  // resolve composite glyphs
278
- for (let component of glyph.components) {
405
+ let components = glyph.components || [];
406
+ for (let component of components) {
279
407
  let componentGlyph = this._font._getBaseGlyph(component.glyphID);
280
- if (!componentGlyph) {
408
+ if (!componentGlyph || typeof componentGlyph._getContours !== 'function') {
281
409
  continue;
282
410
  }
283
411
 
@@ -299,18 +427,22 @@ export default class TTFGlyph extends Glyph {
299
427
  // Recompute and cache metrics if we performed variation processing, and don't have an HVAR table
300
428
  if (glyph.phantomPoints && !this._font.directory.tables.HVAR) {
301
429
  let m = this._metrics;
302
- m.advanceWidth = glyph.phantomPoints[1].x - glyph.phantomPoints[0].x;
303
- m.advanceHeight = glyph.phantomPoints[3].y - glyph.phantomPoints[2].y;
304
- m.leftBearing = glyph.xMin - glyph.phantomPoints[0].x;
305
- m.topBearing = glyph.phantomPoints[2].y - glyph.yMax;
306
- m.rightBearing = m.advanceWidth - m.leftBearing - m.width;
307
- m.bottomBearing = m.advanceHeight - m.topBearing - m.height;
430
+ if (m) {
431
+ m.advanceWidth = glyph.phantomPoints[1].x - glyph.phantomPoints[0].x;
432
+ m.advanceHeight = glyph.phantomPoints[3].y - glyph.phantomPoints[2].y;
433
+ m.leftBearing = glyph.xMin - glyph.phantomPoints[0].x;
434
+ m.topBearing = glyph.phantomPoints[2].y - glyph.yMax;
435
+ m.rightBearing = m.advanceWidth - m.leftBearing - m.width;
436
+ m.bottomBearing = m.advanceHeight - m.topBearing - m.height;
437
+ }
308
438
  }
309
439
 
440
+ /** @type {Point[][]} */
310
441
  let contours = [];
442
+ /** @type {Point[]} */
311
443
  let cur = [];
312
444
  for (let k = 0; k < points.length; k++) {
313
- var point = points[k];
445
+ let point = points[k];
314
446
  cur.push(point);
315
447
  if (point.endContour) {
316
448
  contours.push(cur);
@@ -321,6 +453,9 @@ export default class TTFGlyph extends Glyph {
321
453
  return contours;
322
454
  }
323
455
 
456
+ /**
457
+ * @returns {GlyphMetrics}
458
+ */
324
459
  _getMetrics() {
325
460
  if (this._metrics) {
326
461
  return this._metrics;
@@ -334,10 +469,18 @@ export default class TTFGlyph extends Glyph {
334
469
  this.path;
335
470
  }
336
471
 
337
- return this._metrics;
472
+ // super._getMetrics always assigns this._metrics
473
+ let metrics = this._metrics;
474
+ if (!metrics) {
475
+ throw new Error('Failed to compute glyph metrics');
476
+ }
477
+ return metrics;
338
478
  }
339
479
 
340
- // Converts contours to a Path object that can be rendered
480
+ /**
481
+ * Converts contours to a Path object that can be rendered
482
+ * @returns {Path}
483
+ */
341
484
  _getPath() {
342
485
  let contours = this._getContours();
343
486
  let path = new Path();
@@ -347,7 +490,8 @@ export default class TTFGlyph extends Glyph {
347
490
  let firstPt = contour[0];
348
491
  let lastPt = contour[contour.length - 1];
349
492
  let start = 0;
350
- let curvePt;
493
+ /** @type {Point | null} */
494
+ let curvePt = null;
351
495
 
352
496
  if (firstPt.onCurve) {
353
497
  // The first point will be consumed by the moveTo command, so skip in the loop
@@ -381,6 +525,9 @@ export default class TTFGlyph extends Glyph {
381
525
  path.quadraticCurveTo(prevPt.x, prevPt.y, midX, midY);
382
526
  curvePt = pt;
383
527
  } else if (!prevPt.onCurve && pt.onCurve) {
528
+ if (!curvePt) {
529
+ throw new Error('Unknown TTF path state');
530
+ }
384
531
  path.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
385
532
  curvePt = null;
386
533
  } else {
@@ -1,5 +1,9 @@
1
1
  import * as r from 'restructure';
2
2
 
3
+ /** @typedef {import('restructure').EncodeStream} EncodeStream */
4
+ /** @typedef {import('restructure').StructValue} StructValue */
5
+ /** @typedef {import('./Path').default} Path */
6
+
3
7
  // Flags for simple glyphs
4
8
  const ON_CURVE = 1 << 0;
5
9
  const X_SHORT_VECTOR = 1 << 1;
@@ -9,10 +13,19 @@ const SAME_X = 1 << 4;
9
13
  const SAME_Y = 1 << 5;
10
14
 
11
15
  class Point {
16
+ /**
17
+ * @param {number} val
18
+ * @returns {number}
19
+ */
12
20
  static size(val) {
13
21
  return val >= 0 && val <= 255 ? 1 : 2;
14
22
  }
15
23
 
24
+ /**
25
+ * @param {EncodeStream} stream
26
+ * @param {number} value
27
+ * @returns {void}
28
+ */
16
29
  static encode(stream, value) {
17
30
  if (value >= 0 && value <= 255) {
18
31
  stream.writeUInt8(value);
@@ -39,10 +52,19 @@ let Glyf = new r.Struct({
39
52
  * Encodes TrueType glyph outlines
40
53
  */
41
54
  export default class TTFGlyphEncoder {
55
+ /**
56
+ * @param {Path} path
57
+ * @param {number[]} [instructions]
58
+ * @returns {import('restructure').BinaryBuffer}
59
+ */
42
60
  encodeSimple(path, instructions = []) {
61
+ /** @type {number[]} */
43
62
  let endPtsOfContours = [];
63
+ /** @type {number[]} */
44
64
  let xPoints = [];
65
+ /** @type {number[]} */
45
66
  let yPoints = [];
67
+ /** @type {number[]} */
46
68
  let flags = [];
47
69
  let same = 0;
48
70
  let lastX = 0, lastY = 0, lastFlag = 0;
@@ -108,6 +130,7 @@ export default class TTFGlyphEncoder {
108
130
  }
109
131
 
110
132
  let bbox = path.bbox;
133
+ /** @type {StructValue} */
111
134
  let glyf = {
112
135
  numberOfContours: endPtsOfContours.length,
113
136
  xMin: bbox.minX,
@@ -135,6 +158,15 @@ export default class TTFGlyphEncoder {
135
158
  return stream.buffer;
136
159
  }
137
160
 
161
+ /**
162
+ * @param {number} value
163
+ * @param {number} last
164
+ * @param {number[]} points
165
+ * @param {number} flag
166
+ * @param {number} shortFlag
167
+ * @param {number} sameFlag
168
+ * @returns {number}
169
+ */
138
170
  _encodePoint(value, last, points, flag, shortFlag, sameFlag) {
139
171
  let diff = value - last;
140
172
 
@@ -1,26 +1,33 @@
1
1
  import TTFGlyph, { Point } from './TTFGlyph';
2
2
  import BBox from './BBox';
3
3
 
4
+ /** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
5
+ /** @typedef {import('../../types/fontkit').GlyphComponent} GlyphComponent */
6
+
4
7
  /**
5
8
  * Represents a TrueType glyph in the WOFF2 format, which compresses glyphs differently.
6
9
  */
7
10
  export default class WOFF2Glyph extends TTFGlyph {
8
11
  type = 'WOFF2';
9
12
 
13
+ /**
14
+ * @returns {DecodedGlyf | null}
15
+ */
10
16
  _decode() {
11
- let cached = this._font._transformedGlyphs[this.id];
17
+ let cached = this._font._transformedGlyphs?.[this.id];
12
18
  if (!cached) {
13
19
  return null;
14
20
  }
15
21
 
16
22
  if (!this._font._variationProcessor) {
17
23
  // Non-variable path: return the pre-decoded glyph without copying.
18
- return cached;
24
+ return /** @type {DecodedGlyf} */ (cached);
19
25
  }
20
26
 
21
27
  // Clone so per-instance deltas do not mutate the shared transformed cache.
22
28
  // Transformed glyf has no per-glyph bbox; zeros keep phantom-point math
23
29
  // aligned with hmtx (same approach as a missing bbox bitmap entry).
30
+ /** @type {DecodedGlyf} */
24
31
  let glyph = {
25
32
  numberOfContours: cached.numberOfContours,
26
33
  xMin: 0,
@@ -30,7 +37,12 @@ export default class WOFF2Glyph extends TTFGlyph {
30
37
  };
31
38
 
32
39
  if (cached.points) {
33
- glyph.points = cached.points.map(p => p.copy());
40
+ glyph.points = cached.points.map((p) => {
41
+ if (typeof p.copy === 'function') {
42
+ return p.copy();
43
+ }
44
+ return new Point(p.onCurve, p.endContour, p.x, p.y);
45
+ });
34
46
  let points = glyph.points.concat(this._getPhantomPoints(glyph));
35
47
  this._font._variationProcessor.transformPoints(this.id, points);
36
48
  glyph.phantomPoints = points.slice(-4);
@@ -38,9 +50,12 @@ export default class WOFF2Glyph extends TTFGlyph {
38
50
 
39
51
  if (cached.components) {
40
52
  glyph.components = cached.components.map((c) => {
41
- return Object.assign(Object.create(Object.getPrototypeOf(c)), c);
53
+ /** @type {GlyphComponent} */
54
+ let clone = Object.assign(Object.create(Object.getPrototypeOf(c)), c);
55
+ return clone;
42
56
  });
43
57
 
58
+ /** @type {Point[]} */
44
59
  let points = [];
45
60
  for (let component of glyph.components) {
46
61
  points.push(new Point(true, true, component.dx, component.dy));
@@ -59,6 +74,10 @@ export default class WOFF2Glyph extends TTFGlyph {
59
74
  return glyph;
60
75
  }
61
76
 
77
+ /**
78
+ * @param {boolean} [internal]
79
+ * @returns {BBox}
80
+ */
62
81
  _getCBox(internal) {
63
82
  // Avoid path recursion while building phantom points during variation decode.
64
83
  if (internal) {
@@ -2,6 +2,12 @@
2
2
  * Represents positioning information for a glyph in a GlyphRun.
3
3
  */
4
4
  export default class GlyphPosition {
5
+ /**
6
+ * @param {number} [xAdvance]
7
+ * @param {number} [yAdvance]
8
+ * @param {number} [xOffset]
9
+ * @param {number} [yOffset]
10
+ */
5
11
  constructor(xAdvance = 0, yAdvance = 0, xOffset = 0, yOffset = 0) {
6
12
  /**
7
13
  * The amount to move the virtual pen in the X direction after rendering this glyph.
@@ -6,43 +6,50 @@ import * as Script from '../layout/Script';
6
6
  * Returned by the font layout method.
7
7
  */
8
8
  export default class GlyphRun {
9
+ /**
10
+ * @param {import('../glyph/Glyph').default[]} glyphs
11
+ * @param {import('../../types/fontkit').FeatureInput | null | undefined} features
12
+ * @param {import('../../types/fontkit').ScriptTag | string[] | null | undefined} script
13
+ * @param {import('../../types/fontkit').LanguageTag | null | undefined} language
14
+ * @param {import('../../types/fontkit').TextDirection | null | undefined} direction
15
+ */
9
16
  constructor(glyphs, features, script, language, direction) {
10
17
  /**
11
18
  * An array of Glyph objects in the run
12
- * @type {Glyph[]}
19
+ * @type {import('../glyph/Glyph').default[]}
13
20
  */
14
21
  this.glyphs = glyphs;
15
22
 
16
23
  /**
17
24
  * An array of GlyphPosition objects for each glyph in the run
18
- * @type {GlyphPosition[]}
25
+ * @type {import('./GlyphPosition').default[] | null}
19
26
  */
20
27
  this.positions = null;
21
28
 
22
29
  /**
23
30
  * The script that was requested for shaping. This was either passed in or detected automatically.
24
- * @type {string}
31
+ * @type {import('../../types/fontkit').ScriptTag | string[] | null | undefined}
25
32
  */
26
33
  this.script = script;
27
34
 
28
35
  /**
29
36
  * The language requested for shaping, as passed in. If `null`, the default language for the
30
37
  * script was used.
31
- * @type {string}
38
+ * @type {import('../../types/fontkit').LanguageTag | null}
32
39
  */
33
40
  this.language = language || null;
34
41
 
35
42
  /**
36
43
  * The direction requested for shaping, as passed in (either ltr or rtl).
37
44
  * If `null`, the default direction of the script is used.
38
- * @type {string}
45
+ * @type {import('../../types/fontkit').TextDirection}
39
46
  */
40
47
  this.direction = direction || Script.direction(script);
41
48
 
42
49
  /**
43
50
  * The features requested during shaping. This is a combination of user
44
51
  * specified features and features chosen by the shaper.
45
- * @type {object}
52
+ * @type {import('../../types/fontkit').FeatureMap}
46
53
  */
47
54
  this.features = {};
48
55
 
@@ -51,7 +58,7 @@ export default class GlyphRun {
51
58
  for (let tag of features) {
52
59
  this.features[tag] = true;
53
60
  }
54
- } else if (typeof features === 'object') {
61
+ } else if (typeof features === 'object' && features !== null) {
55
62
  this.features = features;
56
63
  }
57
64
  }
@@ -62,6 +69,10 @@ export default class GlyphRun {
62
69
  */
63
70
  get advanceWidth() {
64
71
  let width = 0;
72
+ if (!this.positions) {
73
+ return width;
74
+ }
75
+
65
76
  for (let position of this.positions) {
66
77
  width += position.xAdvance;
67
78
  }
@@ -75,6 +86,10 @@ export default class GlyphRun {
75
86
  */
76
87
  get advanceHeight() {
77
88
  let height = 0;
89
+ if (!this.positions) {
90
+ return height;
91
+ }
92
+
78
93
  for (let position of this.positions) {
79
94
  height += position.yAdvance;
80
95
  }
@@ -88,6 +103,9 @@ export default class GlyphRun {
88
103
  */
89
104
  get bbox() {
90
105
  let bbox = new BBox();
106
+ if (!this.positions) {
107
+ return bbox;
108
+ }
91
109
 
92
110
  let x = 0;
93
111
  let y = 0;