@cantoo/fontkit 2.0.5 → 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 (140) hide show
  1. package/dist/browser-module.mjs +4423 -1117
  2. package/dist/browser-module.mjs.map +1 -1
  3. package/dist/browser.cjs +4424 -1117
  4. package/dist/browser.cjs.map +1 -1
  5. package/dist/main.cjs +4424 -1117
  6. package/dist/main.cjs.map +1 -1
  7. package/dist/module.mjs +4423 -1117
  8. package/dist/module.mjs.map +1 -1
  9. package/package.json +3 -1
  10. package/src/CmapProcessor.js +59 -20
  11. package/src/DFont.js +114 -14
  12. package/src/TTFFont.js +259 -57
  13. package/src/TrueTypeCollection.js +54 -10
  14. package/src/WOFF2Font.js +128 -16
  15. package/src/WOFFFont.js +45 -8
  16. package/src/aat/AATFeatureMap.js +62 -18
  17. package/src/aat/AATLayoutEngine.js +33 -1
  18. package/src/aat/AATLookupTable.js +22 -4
  19. package/src/aat/AATMorxProcessor.js +189 -52
  20. package/src/aat/AATStateMachine.js +32 -0
  21. package/src/base.js +19 -0
  22. package/src/cff/CFFCharsets.js +3 -0
  23. package/src/cff/CFFDict.js +79 -9
  24. package/src/cff/CFFEncodings.js +2 -0
  25. package/src/cff/CFFFont.js +119 -23
  26. package/src/cff/CFFIndex.js +62 -5
  27. package/src/cff/CFFOperand.js +52 -17
  28. package/src/cff/CFFPointer.js +41 -2
  29. package/src/cff/CFFPrivateDict.js +14 -4
  30. package/src/cff/CFFStandardStrings.js +1 -0
  31. package/src/cff/CFFTop.js +103 -10
  32. package/src/decorators.js +32 -13
  33. package/src/encodings.js +22 -5
  34. package/src/fs.js +10 -0
  35. package/src/glyph/BBox.js +14 -0
  36. package/src/glyph/CFFGlyph.js +290 -148
  37. package/src/glyph/COLRGlyph.js +38 -3
  38. package/src/glyph/Glyph.js +68 -9
  39. package/src/glyph/GlyphVariationProcessor.js +135 -34
  40. package/src/glyph/Path.js +107 -20
  41. package/src/glyph/SBIXGlyph.js +57 -8
  42. package/src/glyph/StandardNames.js +1 -0
  43. package/src/glyph/TTFGlyph.js +176 -29
  44. package/src/glyph/TTFGlyphEncoder.js +32 -0
  45. package/src/glyph/WOFF2Glyph.js +23 -4
  46. package/src/layout/GlyphPosition.js +6 -0
  47. package/src/layout/GlyphRun.js +25 -7
  48. package/src/layout/KernProcessor.js +62 -3
  49. package/src/layout/LayoutEngine.js +86 -12
  50. package/src/layout/Script.js +27 -1
  51. package/src/layout/UnicodeLayoutEngine.js +29 -4
  52. package/src/opentype/GPOSProcessor.js +156 -45
  53. package/src/opentype/GSUBProcessor.js +92 -33
  54. package/src/opentype/GlyphInfo.js +52 -4
  55. package/src/opentype/GlyphIterator.js +51 -2
  56. package/src/opentype/OTLayoutEngine.js +66 -8
  57. package/src/opentype/OTProcessor.js +283 -70
  58. package/src/opentype/ShapingPlan.js +51 -3
  59. package/src/opentype/shapers/ArabicShaper.js +19 -0
  60. package/src/opentype/shapers/DefaultShaper.js +51 -7
  61. package/src/opentype/shapers/HangulShaper.js +85 -7
  62. package/src/opentype/shapers/HebrewShaper.js +45 -11
  63. package/src/opentype/shapers/IndicShaper.js +141 -26
  64. package/src/opentype/shapers/ThaiShaper.js +35 -4
  65. package/src/opentype/shapers/UniversalShaper.js +83 -17
  66. package/src/opentype/shapers/index.js +15 -2
  67. package/src/packages/unicode-properties/data-trie.js +1 -0
  68. package/src/packages/unicode-properties/data.js +12 -0
  69. package/src/packages/unicode-properties/dist/index.cjs +22 -9
  70. package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
  71. package/src/packages/unicode-properties/dist/index.mjs +21 -9
  72. package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
  73. package/src/packages/unicode-properties/index.js +65 -0
  74. package/src/packages/unicode-properties/package.json +1 -1
  75. package/src/packages/unicode-trie/builder.js +128 -7
  76. package/src/packages/unicode-trie/dist/builder.cjs +129 -16
  77. package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
  78. package/src/packages/unicode-trie/dist/builder.mjs +128 -16
  79. package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
  80. package/src/packages/unicode-trie/dist/index.cjs +22 -9
  81. package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
  82. package/src/packages/unicode-trie/dist/index.mjs +21 -9
  83. package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
  84. package/src/packages/unicode-trie/index.js +62 -10
  85. package/src/packages/unicode-trie/package.json +1 -1
  86. package/src/packages/unicode-trie/swap.js +13 -0
  87. package/src/subset/CFFSubset.js +112 -22
  88. package/src/subset/Subset.js +21 -1
  89. package/src/subset/TTFSubset.js +79 -36
  90. package/src/tables/BASE.js +1 -0
  91. package/src/tables/COLR.js +1 -0
  92. package/src/tables/CPAL.js +1 -0
  93. package/src/tables/DSIG.js +1 -0
  94. package/src/tables/EBDT.js +10 -3
  95. package/src/tables/EBLC.js +67 -7
  96. package/src/tables/GDEF.js +1 -0
  97. package/src/tables/GPOS.js +79 -16
  98. package/src/tables/GSUB.js +14 -3
  99. package/src/tables/HVAR.js +51 -5
  100. package/src/tables/JSTF.js +3 -2
  101. package/src/tables/LTSH.js +1 -0
  102. package/src/tables/OS2.js +1 -0
  103. package/src/tables/PCLT.js +1 -0
  104. package/src/tables/STAT.js +46 -6
  105. package/src/tables/VDMX.js +1 -0
  106. package/src/tables/VORG.js +1 -0
  107. package/src/tables/WOFF2Directory.js +56 -8
  108. package/src/tables/WOFFDirectory.js +14 -2
  109. package/src/tables/aat.js +160 -35
  110. package/src/tables/avar.js +1 -0
  111. package/src/tables/bsln.js +3 -2
  112. package/src/tables/cmap.js +26 -6
  113. package/src/tables/cvt.js +1 -0
  114. package/src/tables/directory.js +44 -16
  115. package/src/tables/feat.js +24 -2
  116. package/src/tables/fpgm.js +1 -0
  117. package/src/tables/fvar.js +32 -4
  118. package/src/tables/gasp.js +1 -0
  119. package/src/tables/glyf.js +1 -0
  120. package/src/tables/gvar.js +29 -2
  121. package/src/tables/hdmx.js +12 -1
  122. package/src/tables/head.js +1 -0
  123. package/src/tables/hhea.js +1 -0
  124. package/src/tables/hmtx.js +18 -2
  125. package/src/tables/index.js +1 -0
  126. package/src/tables/just.js +15 -4
  127. package/src/tables/kern.js +43 -6
  128. package/src/tables/loca.js +21 -8
  129. package/src/tables/maxp.js +1 -0
  130. package/src/tables/morx.js +14 -7
  131. package/src/tables/name.js +104 -23
  132. package/src/tables/opbd.js +2 -1
  133. package/src/tables/opentype.js +39 -4
  134. package/src/tables/post.js +8 -1
  135. package/src/tables/prep.js +1 -0
  136. package/src/tables/sbix.js +12 -1
  137. package/src/tables/variations.js +23 -3
  138. package/src/tables/vhea.js +1 -0
  139. package/src/tables/vmtx.js +18 -2
  140. package/src/utils.js +15 -0
@@ -2,20 +2,61 @@ import Glyph from './Glyph';
2
2
  import Path from './Path';
3
3
  import { StandardEncoding } from '../cff/CFFEncodings';
4
4
 
5
+ /** @typedef {import('../../types/fontkit').CFFCharString} CFFCharString */
6
+ /** @typedef {import('../../types/fontkit').CFFFontLike} CFFFontLike */
7
+ /** @typedef {import('../../types/fontkit').GlyphLike} GlyphLike */
8
+ /** @typedef {import('../../types/fontkit').ItemVariationStore} ItemVariationStore */
9
+
10
+ /**
11
+ * @param {unknown} value
12
+ * @returns {value is CFFCharString}
13
+ */
14
+ function isCFFCharString(value) {
15
+ if (typeof value !== 'object' || value === null) {
16
+ return false;
17
+ }
18
+ let offset = Reflect.get(value, 'offset');
19
+ let length = Reflect.get(value, 'length');
20
+ return typeof offset === 'number' && typeof length === 'number';
21
+ }
22
+
5
23
  /**
6
24
  * Represents an OpenType PostScript glyph, in the Compact Font Format.
7
25
  */
8
26
  export default class CFFGlyph extends Glyph {
9
27
  type = 'CFF';
10
28
 
29
+ /**
30
+ * Subroutines used while decoding; set during `_getPath`.
31
+ * @type {Record<number, boolean> | undefined}
32
+ */
33
+ _usedGsubrs;
34
+
35
+ /**
36
+ * Local subroutines used while decoding; set during `_getPath`.
37
+ * @type {Record<number, boolean> | undefined}
38
+ */
39
+ _usedSubrs;
40
+
41
+ /**
42
+ * @returns {string | null | undefined}
43
+ */
11
44
  _getName() {
12
45
  if (this._font.CFF2) {
13
46
  return super._getName();
14
47
  }
15
48
 
16
- return this._font['CFF '].getGlyphName(this.id);
49
+ let cff = this._font['CFF '];
50
+ if (!cff || typeof cff.getGlyphName !== 'function') {
51
+ return null;
52
+ }
53
+ return cff.getGlyphName(this.id);
17
54
  }
18
55
 
56
+ /**
57
+ * @param {{ length: number }} s
58
+ * @returns {number}
59
+ */
19
60
  bias(s) {
20
61
  if (s.length < 1240) {
21
62
  return 107;
@@ -26,45 +67,103 @@ export default class CFFGlyph extends Glyph {
26
67
  }
27
68
  }
28
69
 
70
+ /**
71
+ * @returns {Path}
72
+ */
29
73
  _getPath() {
30
74
  let cff = this._font.CFF2 || this._font['CFF '];
31
- let { stream } = cff;
32
- let str = cff.topDict.CharStrings[this.id];
75
+ if (!cff) {
76
+ return new Path();
77
+ }
78
+ /** @type {CFFFontLike} */
79
+ let cffFont = cff;
80
+ let { stream } = cffFont;
81
+ let rawStr = cffFont.topDict.CharStrings[this.id];
82
+ if (!isCFFCharString(rawStr)) {
83
+ return new Path();
84
+ }
85
+ let str = rawStr;
33
86
  let end = str.offset + str.length;
34
87
  stream.pos = str.offset;
35
88
 
36
89
  let path = new Path();
90
+ /** @type {number[]} */
37
91
  let stack = [];
92
+ /** @type {number[]} */
38
93
  let trans = [];
39
94
 
95
+ /** @type {number | null} */
40
96
  let width = null;
41
97
  let nStems = 0;
42
98
  let x = 0, y = 0;
99
+ /** @type {Record<number, boolean>} */
43
100
  let usedGsubrs;
101
+ /** @type {Record<number, boolean>} */
44
102
  let usedSubrs;
45
103
  let open = false;
46
104
 
47
105
  this._usedGsubrs = usedGsubrs = {};
48
106
  this._usedSubrs = usedSubrs = {};
49
107
 
50
- let gsubrs = cff.globalSubrIndex || [];
108
+ let gsubrs = cffFont.globalSubrIndex || [];
51
109
  let gsubrsBias = this.bias(gsubrs);
52
110
 
53
- let privateDict = cff.privateDictForGlyph(this.id) || {};
54
- let subrs = privateDict.Subrs || [];
111
+ let fetchedPrivate = cffFont.privateDictForGlyph(this.id);
112
+ /** @type {import('../../types/fontkit').CFFPrivateDict} */
113
+ let privateDict = fetchedPrivate ? fetchedPrivate : {};
114
+ /** @type {CFFCharString[]} */
115
+ let subrs = [];
116
+ if (Array.isArray(privateDict.Subrs)) {
117
+ for (let entry of privateDict.Subrs) {
118
+ if (isCFFCharString(entry)) {
119
+ subrs.push(entry);
120
+ }
121
+ }
122
+ }
55
123
  let subrsBias = this.bias(subrs);
124
+ let nominalWidthX = typeof privateDict.nominalWidthX === 'number' ? privateDict.nominalWidthX : 0;
56
125
 
57
- let vstore = cff.topDict.vstore && cff.topDict.vstore.itemVariationStore;
58
- let vsindex = privateDict.vsindex;
126
+ /** @type {ItemVariationStore | undefined} */
127
+ let vstore = (cffFont.topDict.vstore && cffFont.topDict.vstore.itemVariationStore) || undefined;
128
+ /** @type {number} */
129
+ let vsindex = typeof privateDict.vsindex === 'number' ? privateDict.vsindex : 0;
59
130
  let variationProcessor = this._font._variationProcessor;
60
131
  let font = this._font;
61
132
 
133
+ /**
134
+ * @returns {number}
135
+ */
136
+ function shiftStack() {
137
+ let v = stack.shift();
138
+ if (v === undefined) {
139
+ throw new Error('CFF stack underflow');
140
+ }
141
+ return v;
142
+ }
143
+
144
+ /**
145
+ * @returns {number}
146
+ */
147
+ function popStack() {
148
+ let v = stack.pop();
149
+ if (v === undefined) {
150
+ throw new Error('CFF stack underflow');
151
+ }
152
+ return v;
153
+ }
154
+
155
+ /**
156
+ * @returns {void}
157
+ */
62
158
  function checkWidth() {
63
159
  if (width == null) {
64
- width = stack.shift() + privateDict.nominalWidthX;
160
+ width = shiftStack() + nominalWidthX;
65
161
  }
66
162
  }
67
163
 
164
+ /**
165
+ * @returns {number}
166
+ */
68
167
  function parseStems() {
69
168
  if (stack.length % 2 !== 0) {
70
169
  checkWidth();
@@ -74,33 +173,61 @@ export default class CFFGlyph extends Glyph {
74
173
  return stack.length = 0;
75
174
  }
76
175
 
77
- function moveTo(x, y) {
176
+ /**
177
+ * @param {number} mx
178
+ * @param {number} my
179
+ * @returns {void}
180
+ */
181
+ function moveTo(mx, my) {
78
182
  if (open) {
79
183
  path.closePath();
80
184
  }
81
185
 
82
- path.moveTo(x, y);
186
+ path.moveTo(mx, my);
83
187
  open = true;
84
188
  }
85
189
 
190
+ /**
191
+ * @param {string | null | undefined} name
192
+ * @returns {GlyphLike}
193
+ */
86
194
  function glyphForName(name) {
87
195
  if (name) {
88
196
  for (let i = 0; i < font.numGlyphs; i++) {
89
- if (cff.getGlyphName(i) === name) {
90
- return font.getGlyph(i);
197
+ if (typeof cffFont.getGlyphName === 'function' && cffFont.getGlyphName(i) === name) {
198
+ let g = font.getGlyph(i);
199
+ if (!g) {
200
+ throw new Error(`CFF glyph ${i} (name "${name}") is missing; font may be corrupt or incomplete`);
201
+ }
202
+ return g;
91
203
  }
92
204
  }
93
205
  }
94
- return font.getGlyph(0);
206
+ let fallback = font.getGlyph(0);
207
+ if (!fallback) {
208
+ throw new Error('CFF glyph 0 (.notdef) is missing; font may be corrupt or incomplete');
209
+ }
210
+ return fallback;
95
211
  }
96
212
 
213
+ /**
214
+ * @returns {void}
215
+ */
97
216
  let parse = function () {
98
217
  while (stream.pos < end) {
99
218
  let op = stream.readUInt8();
100
219
  if (op < 32) {
101
- let index, subr, phase;
220
+ /** @type {number} */
221
+ let index;
222
+ /** @type {CFFCharString | undefined} */
223
+ let subr;
224
+ /** @type {boolean} */
225
+ let phase;
226
+ /** @type {number} */
102
227
  let c1x, c1y, c2x, c2y, c3x, c3y;
228
+ /** @type {number} */
103
229
  let c4x, c4y, c5x, c5y, c6x, c6y;
230
+ /** @type {number[]} */
104
231
  let pts;
105
232
 
106
233
  switch (op) {
@@ -116,14 +243,14 @@ export default class CFFGlyph extends Glyph {
116
243
  checkWidth();
117
244
  }
118
245
 
119
- y += stack.shift();
246
+ y += shiftStack();
120
247
  moveTo(x, y);
121
248
  break;
122
249
 
123
250
  case 5: // rlineto
124
251
  while (stack.length >= 2) {
125
- x += stack.shift();
126
- y += stack.shift();
252
+ x += shiftStack();
253
+ y += shiftStack();
127
254
  path.lineTo(x, y);
128
255
  }
129
256
  break;
@@ -133,9 +260,9 @@ export default class CFFGlyph extends Glyph {
133
260
  phase = op === 6;
134
261
  while (stack.length >= 1) {
135
262
  if (phase) {
136
- x += stack.shift();
263
+ x += shiftStack();
137
264
  } else {
138
- y += stack.shift();
265
+ y += shiftStack();
139
266
  }
140
267
 
141
268
  path.lineTo(x, y);
@@ -145,18 +272,18 @@ export default class CFFGlyph extends Glyph {
145
272
 
146
273
  case 8: // rrcurveto
147
274
  while (stack.length > 0) {
148
- c1x = x + stack.shift();
149
- c1y = y + stack.shift();
150
- c2x = c1x + stack.shift();
151
- c2y = c1y + stack.shift();
152
- x = c2x + stack.shift();
153
- y = c2y + stack.shift();
275
+ c1x = x + shiftStack();
276
+ c1y = y + shiftStack();
277
+ c2x = c1x + shiftStack();
278
+ c2y = c1y + shiftStack();
279
+ x = c2x + shiftStack();
280
+ y = c2y + shiftStack();
154
281
  path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
155
282
  }
156
283
  break;
157
284
 
158
285
  case 10: // callsubr
159
- index = stack.pop() + subrsBias;
286
+ index = popStack() + subrsBias;
160
287
  subr = subrs[index];
161
288
  if (subr) {
162
289
  usedSubrs[index] = true;
@@ -171,13 +298,13 @@ export default class CFFGlyph extends Glyph {
171
298
  break;
172
299
 
173
300
  case 11: // return
174
- if (cff.version >= 2) {
301
+ if ((cffFont.version ?? 0) >= 2) {
175
302
  break;
176
303
  }
177
304
  return;
178
305
 
179
306
  case 14: // endchar
180
- if (cff.version >= 2) {
307
+ if ((cffFont.version ?? 0) >= 2) {
181
308
  break;
182
309
  }
183
310
 
@@ -186,10 +313,10 @@ export default class CFFGlyph extends Glyph {
186
313
  if (stack.length > 4) {
187
314
  checkWidth();
188
315
  }
189
- let a = glyphForName(StandardEncoding[stack.pop()]);
190
- let b = glyphForName(StandardEncoding[stack.pop()]);
191
- let ady = stack.pop();
192
- let adx = stack.pop();
316
+ let a = glyphForName(StandardEncoding[popStack()]);
317
+ let b = glyphForName(StandardEncoding[popStack()]);
318
+ let ady = popStack();
319
+ let adx = popStack();
193
320
  let pos = stream.pos;
194
321
  path.commands = b.path.commands.concat(a.path.translate(adx, ady).commands);
195
322
  stream.pos = pos;
@@ -205,16 +332,16 @@ export default class CFFGlyph extends Glyph {
205
332
  break;
206
333
 
207
334
  case 15: { // vsindex
208
- if (cff.version < 2) {
335
+ if ((cffFont.version ?? 0) < 2) {
209
336
  throw new Error('vsindex operator not supported in CFF v1');
210
337
  }
211
338
 
212
- vsindex = stack.pop();
339
+ vsindex = popStack();
213
340
  break;
214
341
  }
215
342
 
216
343
  case 16: { // blend
217
- if (cff.version < 2) {
344
+ if ((cffFont.version ?? 0) < 2) {
218
345
  throw new Error('blend operator not supported in CFF v1');
219
346
  }
220
347
 
@@ -222,13 +349,20 @@ export default class CFFGlyph extends Glyph {
222
349
  throw new Error('blend operator in non-variation font');
223
350
  }
224
351
 
352
+ if (!vstore) {
353
+ throw new Error('blend operator without variation store');
354
+ }
355
+
225
356
  let blendVector = variationProcessor.getBlendVector(vstore, vsindex);
226
- let numBlends = stack.pop();
357
+ let numBlends = popStack();
227
358
  let numOperands = numBlends * blendVector.length;
359
+ /** @type {number} */
228
360
  let delta = stack.length - numOperands;
361
+ /** @type {number} */
229
362
  let base = delta - numBlends;
230
363
 
231
364
  for (let i = 0; i < numBlends; i++) {
365
+ /** @type {number} */
232
366
  let sum = stack[base + i];
233
367
  for (let j = 0; j < blendVector.length; j++) {
234
368
  sum += blendVector[j] * stack[delta++];
@@ -255,8 +389,8 @@ export default class CFFGlyph extends Glyph {
255
389
  checkWidth();
256
390
  }
257
391
 
258
- x += stack.shift();
259
- y += stack.shift();
392
+ x += shiftStack();
393
+ y += shiftStack();
260
394
  moveTo(x, y);
261
395
  break;
262
396
 
@@ -265,69 +399,69 @@ export default class CFFGlyph extends Glyph {
265
399
  checkWidth();
266
400
  }
267
401
 
268
- x += stack.shift();
402
+ x += shiftStack();
269
403
  moveTo(x, y);
270
404
  break;
271
405
 
272
406
  case 24: // rcurveline
273
407
  while (stack.length >= 8) {
274
- c1x = x + stack.shift();
275
- c1y = y + stack.shift();
276
- c2x = c1x + stack.shift();
277
- c2y = c1y + stack.shift();
278
- x = c2x + stack.shift();
279
- y = c2y + stack.shift();
408
+ c1x = x + shiftStack();
409
+ c1y = y + shiftStack();
410
+ c2x = c1x + shiftStack();
411
+ c2y = c1y + shiftStack();
412
+ x = c2x + shiftStack();
413
+ y = c2y + shiftStack();
280
414
  path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
281
415
  }
282
416
 
283
- x += stack.shift();
284
- y += stack.shift();
417
+ x += shiftStack();
418
+ y += shiftStack();
285
419
  path.lineTo(x, y);
286
420
  break;
287
421
 
288
422
  case 25: // rlinecurve
289
423
  while (stack.length >= 8) {
290
- x += stack.shift();
291
- y += stack.shift();
424
+ x += shiftStack();
425
+ y += shiftStack();
292
426
  path.lineTo(x, y);
293
427
  }
294
428
 
295
- c1x = x + stack.shift();
296
- c1y = y + stack.shift();
297
- c2x = c1x + stack.shift();
298
- c2y = c1y + stack.shift();
299
- x = c2x + stack.shift();
300
- y = c2y + stack.shift();
429
+ c1x = x + shiftStack();
430
+ c1y = y + shiftStack();
431
+ c2x = c1x + shiftStack();
432
+ c2y = c1y + shiftStack();
433
+ x = c2x + shiftStack();
434
+ y = c2y + shiftStack();
301
435
  path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
302
436
  break;
303
437
 
304
438
  case 26: // vvcurveto
305
439
  if (stack.length % 2) {
306
- x += stack.shift();
440
+ x += shiftStack();
307
441
  }
308
442
 
309
443
  while (stack.length >= 4) {
310
444
  c1x = x;
311
- c1y = y + stack.shift();
312
- c2x = c1x + stack.shift();
313
- c2y = c1y + stack.shift();
445
+ c1y = y + shiftStack();
446
+ c2x = c1x + shiftStack();
447
+ c2y = c1y + shiftStack();
314
448
  x = c2x;
315
- y = c2y + stack.shift();
449
+ y = c2y + shiftStack();
316
450
  path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
317
451
  }
318
452
  break;
319
453
 
320
454
  case 27: // hhcurveto
321
455
  if (stack.length % 2) {
322
- y += stack.shift();
456
+ y += shiftStack();
323
457
  }
324
458
 
325
459
  while (stack.length >= 4) {
326
- c1x = x + stack.shift();
460
+ c1x = x + shiftStack();
327
461
  c1y = y;
328
- c2x = c1x + stack.shift();
329
- c2y = c1y + stack.shift();
330
- x = c2x + stack.shift();
462
+ c2x = c1x + shiftStack();
463
+ c2y = c1y + shiftStack();
464
+ x = c2x + shiftStack();
331
465
  y = c2y;
332
466
  path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
333
467
  }
@@ -338,17 +472,20 @@ export default class CFFGlyph extends Glyph {
338
472
  break;
339
473
 
340
474
  case 29: // callgsubr
341
- index = stack.pop() + gsubrsBias;
342
- subr = gsubrs[index];
343
- if (subr) {
344
- usedGsubrs[index] = true;
345
- let p = stream.pos;
346
- let e = end;
347
- stream.pos = subr.offset;
348
- end = subr.offset + subr.length;
349
- parse();
350
- stream.pos = p;
351
- end = e;
475
+ index = popStack() + gsubrsBias;
476
+ {
477
+ let candidate = Array.isArray(gsubrs) ? gsubrs[index] : gsubrs[index];
478
+ if (isCFFCharString(candidate)) {
479
+ subr = candidate;
480
+ usedGsubrs[index] = true;
481
+ let p = stream.pos;
482
+ let e = end;
483
+ stream.pos = subr.offset;
484
+ end = subr.offset + subr.length;
485
+ parse();
486
+ stream.pos = p;
487
+ end = e;
488
+ }
352
489
  }
353
490
  break;
354
491
 
@@ -357,19 +494,19 @@ export default class CFFGlyph extends Glyph {
357
494
  phase = op === 31;
358
495
  while (stack.length >= 4) {
359
496
  if (phase) {
360
- c1x = x + stack.shift();
497
+ c1x = x + shiftStack();
361
498
  c1y = y;
362
- c2x = c1x + stack.shift();
363
- c2y = c1y + stack.shift();
364
- y = c2y + stack.shift();
365
- x = c2x + (stack.length === 1 ? stack.shift() : 0);
499
+ c2x = c1x + shiftStack();
500
+ c2y = c1y + shiftStack();
501
+ y = c2y + shiftStack();
502
+ x = c2x + (stack.length === 1 ? shiftStack() : 0);
366
503
  } else {
367
504
  c1x = x;
368
- c1y = y + stack.shift();
369
- c2x = c1x + stack.shift();
370
- c2y = c1y + stack.shift();
371
- x = c2x + stack.shift();
372
- y = c2y + (stack.length === 1 ? stack.shift() : 0);
505
+ c1y = y + shiftStack();
506
+ c2x = c1x + shiftStack();
507
+ c2y = c1y + shiftStack();
508
+ x = c2x + shiftStack();
509
+ y = c2y + (stack.length === 1 ? shiftStack() : 0);
373
510
  }
374
511
 
375
512
  path.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
@@ -379,79 +516,80 @@ export default class CFFGlyph extends Glyph {
379
516
 
380
517
  case 12: {
381
518
  op = stream.readUInt8();
519
+ /** @type {number} */
382
520
  let a, b, val, idx;
383
521
  switch (op) {
384
522
  case 3: // and
385
- a = stack.pop();
386
- b = stack.pop();
523
+ a = popStack();
524
+ b = popStack();
387
525
  stack.push(a && b ? 1 : 0);
388
526
  break;
389
527
 
390
528
  case 4: // or
391
- a = stack.pop();
392
- b = stack.pop();
529
+ a = popStack();
530
+ b = popStack();
393
531
  stack.push(a || b ? 1 : 0);
394
532
  break;
395
533
 
396
534
  case 5: // not
397
- a = stack.pop();
535
+ a = popStack();
398
536
  stack.push(a ? 0 : 1);
399
537
  break;
400
538
 
401
539
  case 9: // abs
402
- a = stack.pop();
540
+ a = popStack();
403
541
  stack.push(Math.abs(a));
404
542
  break;
405
543
 
406
544
  case 10: // add
407
- a = stack.pop();
408
- b = stack.pop();
545
+ a = popStack();
546
+ b = popStack();
409
547
  stack.push(a + b);
410
548
  break;
411
549
 
412
550
  case 11: // sub
413
- a = stack.pop();
414
- b = stack.pop();
551
+ a = popStack();
552
+ b = popStack();
415
553
  stack.push(a - b);
416
554
  break;
417
555
 
418
556
  case 12: // div
419
- a = stack.pop();
420
- b = stack.pop();
557
+ a = popStack();
558
+ b = popStack();
421
559
  stack.push(a / b);
422
560
  break;
423
561
 
424
562
  case 14: // neg
425
- a = stack.pop();
563
+ a = popStack();
426
564
  stack.push(-a);
427
565
  break;
428
566
 
429
567
  case 15: // eq
430
- a = stack.pop();
431
- b = stack.pop();
568
+ a = popStack();
569
+ b = popStack();
432
570
  stack.push(a === b ? 1 : 0);
433
571
  break;
434
572
 
435
573
  case 18: // drop
436
- stack.pop();
574
+ popStack();
437
575
  break;
438
576
 
439
577
  case 20: // put
440
- val = stack.pop();
441
- idx = stack.pop();
578
+ val = popStack();
579
+ idx = popStack();
442
580
  trans[idx] = val;
443
581
  break;
444
582
 
445
583
  case 21: // get
446
- idx = stack.pop();
584
+ idx = popStack();
447
585
  stack.push(trans[idx] || 0);
448
586
  break;
449
587
 
450
588
  case 22: { // ifelse
451
- let s1 = stack.pop();
452
- let s2 = stack.pop();
453
- let v1 = stack.pop();
454
- let v2 = stack.pop();
589
+ let s1 = popStack();
590
+ let s2 = popStack();
591
+ let v1 = popStack();
592
+ let v2 = popStack();
455
593
  stack.push(v1 <= v2 ? s1 : s2);
456
594
  break;
457
595
  }
@@ -461,29 +599,29 @@ export default class CFFGlyph extends Glyph {
461
599
  break;
462
600
 
463
601
  case 24: // mul
464
- a = stack.pop();
465
- b = stack.pop();
602
+ a = popStack();
603
+ b = popStack();
466
604
  stack.push(a * b);
467
605
  break;
468
606
 
469
607
  case 26: // sqrt
470
- a = stack.pop();
608
+ a = popStack();
471
609
  stack.push(Math.sqrt(a));
472
610
  break;
473
611
 
474
612
  case 27: // dup
475
- a = stack.pop();
613
+ a = popStack();
476
614
  stack.push(a, a);
477
615
  break;
478
616
 
479
617
  case 28: // exch
480
- a = stack.pop();
481
- b = stack.pop();
618
+ a = popStack();
619
+ b = popStack();
482
620
  stack.push(b, a);
483
621
  break;
484
622
 
485
623
  case 29: // index
486
- idx = stack.pop();
624
+ idx = popStack();
487
625
  if (idx < 0) {
488
626
  idx = 0;
489
627
  } else if (idx > stack.length - 1) {
@@ -494,13 +632,16 @@ export default class CFFGlyph extends Glyph {
494
632
  break;
495
633
 
496
634
  case 30: { // roll
497
- let n = stack.pop();
498
- let j = stack.pop();
635
+ /** @type {number} */
636
+ let n = popStack();
637
+ /** @type {number} */
638
+ let j = popStack();
499
639
 
500
640
  if (j >= 0) {
501
641
  while (j > 0) {
642
+ /** @type {number} */
502
643
  let t = stack[n - 1];
503
- for (let i = n - 2; i >= 0; i--) {
644
+ for (/** @type {number} */ let i = n - 2; i >= 0; i--) {
504
645
  stack[i + 1] = stack[i];
505
646
  }
506
647
 
@@ -509,8 +650,9 @@ export default class CFFGlyph extends Glyph {
509
650
  }
510
651
  } else {
511
652
  while (j < 0) {
653
+ /** @type {number} */
512
654
  let t = stack[0];
513
- for (let i = 0; i <= n; i++) {
655
+ for (/** @type {number} */ let i = 0; i <= n; i++) {
514
656
  stack[i] = stack[i + 1];
515
657
  }
516
658
 
@@ -522,17 +664,17 @@ export default class CFFGlyph extends Glyph {
522
664
  }
523
665
 
524
666
  case 34: // hflex
525
- c1x = x + stack.shift();
667
+ c1x = x + shiftStack();
526
668
  c1y = y;
527
- c2x = c1x + stack.shift();
528
- c2y = c1y + stack.shift();
529
- c3x = c2x + stack.shift();
669
+ c2x = c1x + shiftStack();
670
+ c2y = c1y + shiftStack();
671
+ c3x = c2x + shiftStack();
530
672
  c3y = c2y;
531
- c4x = c3x + stack.shift();
673
+ c4x = c3x + shiftStack();
532
674
  c4y = c3y;
533
- c5x = c4x + stack.shift();
675
+ c5x = c4x + shiftStack();
534
676
  c5y = c4y;
535
- c6x = c5x + stack.shift();
677
+ c6x = c5x + shiftStack();
536
678
  c6y = c5y;
537
679
  x = c6x;
538
680
  y = c6y;
@@ -545,28 +687,28 @@ export default class CFFGlyph extends Glyph {
545
687
  pts = [];
546
688
 
547
689
  for (let i = 0; i <= 5; i++) {
548
- x += stack.shift();
549
- y += stack.shift();
690
+ x += shiftStack();
691
+ y += shiftStack();
550
692
  pts.push(x, y);
551
693
  }
552
694
 
553
- path.bezierCurveTo(...pts.slice(0, 6));
554
- path.bezierCurveTo(...pts.slice(6));
555
- stack.shift(); // fd
695
+ path.bezierCurveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]);
696
+ path.bezierCurveTo(pts[6], pts[7], pts[8], pts[9], pts[10], pts[11]);
697
+ shiftStack(); // fd
556
698
  break;
557
699
 
558
700
  case 36: // hflex1
559
- c1x = x + stack.shift();
560
- c1y = y + stack.shift();
561
- c2x = c1x + stack.shift();
562
- c2y = c1y + stack.shift();
563
- c3x = c2x + stack.shift();
701
+ c1x = x + shiftStack();
702
+ c1y = y + shiftStack();
703
+ c2x = c1x + shiftStack();
704
+ c2y = c1y + shiftStack();
705
+ c3x = c2x + shiftStack();
564
706
  c3y = c2y;
565
- c4x = c3x + stack.shift();
707
+ c4x = c3x + shiftStack();
566
708
  c4y = c3y;
567
- c5x = c4x + stack.shift();
568
- c5y = c4y + stack.shift();
569
- c6x = c5x + stack.shift();
709
+ c5x = c4x + shiftStack();
710
+ c5y = c4y + shiftStack();
711
+ c6x = c5x + shiftStack();
570
712
  c6y = c5y;
571
713
  x = c6x;
572
714
  y = c6y;
@@ -581,22 +723,22 @@ export default class CFFGlyph extends Glyph {
581
723
 
582
724
  pts = [];
583
725
  for (let i = 0; i <= 4; i++) {
584
- x += stack.shift();
585
- y += stack.shift();
726
+ x += shiftStack();
727
+ y += shiftStack();
586
728
  pts.push(x, y);
587
729
  }
588
730
 
589
731
  if (Math.abs(x - startx) > Math.abs(y - starty)) { // horizontal
590
- x += stack.shift();
732
+ x += shiftStack();
591
733
  y = starty;
592
734
  } else {
593
735
  x = startx;
594
- y += stack.shift();
736
+ y += shiftStack();
595
737
  }
596
738
 
597
739
  pts.push(x, y);
598
- path.bezierCurveTo(...pts.slice(0, 6));
599
- path.bezierCurveTo(...pts.slice(6));
740
+ path.bezierCurveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]);
741
+ path.bezierCurveTo(pts[6], pts[7], pts[8], pts[9], pts[10], pts[11]);
600
742
  break;
601
743
  }
602
744