@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
@@ -2,49 +2,89 @@ import * as r from 'restructure';
2
2
  import TTFFont from './TTFFont';
3
3
  import { asciiDecoder } from './utils';
4
4
 
5
+ /** @typedef {import('restructure').DecodeStream} DecodeStream */
6
+ /** @typedef {import('restructure').StructValue} StructValue */
7
+ /** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
8
+ /** @typedef {import('../types/fontkit').NameString} NameString */
9
+
10
+ /**
11
+ * @param {NameString | null | undefined} a
12
+ * @param {string | Uint8Array} b
13
+ * @returns {boolean}
14
+ */
15
+ function postscriptNamesEqual(a, b) {
16
+ if (a === b) {
17
+ return true;
18
+ }
19
+ if (a instanceof Uint8Array && b instanceof Uint8Array) {
20
+ return a.length === b.length && a.every((v, i) => b[i] === v);
21
+ }
22
+ return false;
23
+ }
24
+
5
25
  let TTCHeader = new r.VersionedStruct(r.uint32, {
6
26
  0x00010000: {
7
- numFonts: r.uint32,
8
- offsets: new r.Array(r.uint32, 'numFonts')
27
+ numFonts: r.uint32,
28
+ offsets: new r.Array(r.uint32, 'numFonts')
9
29
  },
10
30
  0x00020000: {
11
- numFonts: r.uint32,
12
- offsets: new r.Array(r.uint32, 'numFonts'),
13
- dsigTag: r.uint32,
31
+ numFonts: r.uint32,
32
+ offsets: new r.Array(r.uint32, 'numFonts'),
33
+ dsigTag: r.uint32,
14
34
  dsigLength: r.uint32,
15
35
  dsigOffset: r.uint32
16
36
  }
17
37
  });
18
38
 
39
+ /**
40
+ * @typedef {StructValue & { offsets: number[] }} TTCHeaderValue
41
+ */
42
+
19
43
  export default class TrueTypeCollection {
44
+ /** @type {string} */
20
45
  type = 'TTC';
21
46
 
47
+ /** @type {DecodeStream} */
48
+ stream;
49
+ /** @type {TTCHeaderValue} */
50
+ header;
51
+
52
+ /**
53
+ * @param {ArrayBufferView} buffer
54
+ * @returns {boolean}
55
+ */
22
56
  static probe(buffer) {
23
- return asciiDecoder.decode(buffer.slice(0, 4)) === 'ttcf';
57
+ let bytes
58
+ = buffer instanceof Uint8Array
59
+ ? buffer
60
+ : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
61
+ return asciiDecoder.decode(bytes.subarray(0, 4)) === 'ttcf';
24
62
  }
25
63
 
64
+ /**
65
+ * @param {DecodeStream} stream
66
+ */
26
67
  constructor(stream) {
27
68
  this.stream = stream;
28
69
  if (stream.readString(4) !== 'ttcf') {
29
70
  throw new Error('Not a TrueType collection');
30
71
  }
31
72
 
32
- this.header = TTCHeader.decode(stream);
73
+ this.header = /** @type {TTCHeaderValue} */ (
74
+ /** @type {unknown} */ (TTCHeader.decode(stream))
75
+ );
33
76
  }
34
77
 
78
+ /**
79
+ * @param {string | Uint8Array} name
80
+ * @returns {TTFFont | null}
81
+ */
35
82
  getFont(name) {
36
83
  for (let offset of this.header.offsets) {
37
84
  let stream = new r.DecodeStream(this.stream.buffer);
38
85
  stream.pos = offset;
39
86
  let font = new TTFFont(stream);
40
- if (
41
- font.postscriptName === name ||
42
- (
43
- font.postscriptName instanceof Uint8Array &&
44
- name instanceof Uint8Array &&
45
- font.postscriptName.every((v, i) => name[i] === v)
46
- )
47
- ) {
87
+ if (postscriptNamesEqual(font.postscriptName, name)) {
48
88
  return font;
49
89
  }
50
90
  }
@@ -52,7 +92,11 @@ export default class TrueTypeCollection {
52
92
  return null;
53
93
  }
54
94
 
95
+ /**
96
+ * @type {TTFFont[]}
97
+ */
55
98
  get fonts() {
99
+ /** @type {TTFFont[]} */
56
100
  let fonts = [];
57
101
  for (let offset of this.header.offsets) {
58
102
  let stream = new r.DecodeStream(this.stream.buffer);
package/src/WOFF2Font.js CHANGED
@@ -6,45 +6,98 @@ import WOFF2Glyph from './glyph/WOFF2Glyph';
6
6
  import WOFF2Directory from './tables/WOFF2Directory';
7
7
  import { asciiDecoder } from './utils';
8
8
 
9
+ /** @typedef {import('restructure').DecodeStream} DecodeStream */
10
+ /** @typedef {import('restructure').StructValue} StructValue */
11
+ /** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
12
+ /** @typedef {import('restructure').BaseType} BaseType */
13
+ /** @typedef {import('../types/fontkit').FontDirectory} FontDirectory */
14
+ /** @typedef {import('../types/fontkit').TableEntry} TableEntry */
15
+ /** @typedef {import('../types/fontkit').TransformedGlyf} TransformedGlyf */
16
+ /** @typedef {import('../types/fontkit').DecodedGlyf} DecodedGlyf */
17
+ /** @typedef {import('./glyph/Glyph').default} Glyph */
18
+
9
19
  /**
10
20
  * Subclass of TTFFont that represents a TTF/OTF font compressed by WOFF2
11
21
  * See spec here: http://www.w3.org/TR/WOFF2/
22
+ *
23
+ * @property {number} [_dataPos] Byte offset of the compressed table data (set in `_decodeDirectory`).
12
24
  */
13
25
  export default class WOFF2Font extends TTFFont {
26
+ /** @type {string} */
14
27
  type = 'WOFF2';
15
28
 
29
+ // NOTE: Do not declare `_dataPos` as a class field. It is assigned in
30
+ // `_decodeDirectory()` during `super()`, and subclass field initializers
31
+ // run *after* `super()` returns — which would wipe the real offset.
32
+
33
+ /** @type {boolean | undefined} */
34
+ _decompressed;
35
+ /** @type {(TransformedGlyf | undefined)[] | undefined} */
36
+ _transformedGlyphs;
37
+
38
+ /**
39
+ * @param {ArrayBufferView} buffer
40
+ * @returns {boolean}
41
+ */
16
42
  static probe(buffer) {
17
- return asciiDecoder.decode(buffer.slice(0, 4)) === 'wOF2';
43
+ let bytes
44
+ = buffer instanceof Uint8Array
45
+ ? buffer
46
+ : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
47
+ return asciiDecoder.decode(bytes.subarray(0, 4)) === 'wOF2';
18
48
  }
19
49
 
50
+ /** @returns {void} */
20
51
  _decodeDirectory() {
21
- this.directory = WOFF2Directory.decode(this.stream);
52
+ this.directory = /** @type {FontDirectory} */ (
53
+ /** @type {unknown} */ (WOFF2Directory.decode(this.stream))
54
+ );
22
55
  this._dataPos = this.stream.pos;
23
56
  }
24
57
 
58
+ /** @returns {void} */
25
59
  _decompress() {
26
60
  // decompress data and setup table offsets if we haven't already
27
61
  if (!this._decompressed) {
28
- this.stream.pos = this._dataPos;
29
- let buffer = this.stream.readBuffer(this.directory.totalCompressedSize);
62
+ let dataPos = this._dataPos;
63
+ if (dataPos == null) {
64
+ throw new Error('Missing WOFF2 data offset; directory was not decoded');
65
+ }
66
+ this.stream.pos = dataPos;
67
+ let totalCompressedSize = this.directory.totalCompressedSize;
68
+ if (totalCompressedSize == null) {
69
+ throw new Error('Missing totalCompressedSize in WOFF2 directory');
70
+ }
71
+ let buffer = this.stream.readBuffer(totalCompressedSize);
30
72
 
31
73
  let decompressedSize = 0;
32
- for (let tag in this.directory.tables) {
33
- let entry = this.directory.tables[tag];
74
+ let tableMap = this.directory.tables;
75
+ for (let tag in tableMap) {
76
+ let entry = tableMap[tag];
77
+ if (!entry) {
78
+ continue;
79
+ }
34
80
  entry.offset = decompressedSize;
35
81
  decompressedSize += (entry.transformLength != null) ? entry.transformLength : entry.length;
36
82
  }
37
83
 
38
- let decompressed = brotli(buffer, decompressedSize);
84
+ let decompressed = brotli(
85
+ buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength),
86
+ decompressedSize
87
+ );
39
88
  if (!decompressed) {
40
89
  throw new Error('Error decoding compressed data in WOFF2');
41
90
  }
42
91
 
43
- this.stream = new r.DecodeStream(decompressed);
92
+ this._setStream(new r.DecodeStream(decompressed));
44
93
  this._decompressed = true;
45
94
  }
46
95
  }
47
96
 
97
+ /**
98
+ * @param {TableEntry} table
99
+ * @returns {unknown}
100
+ */
48
101
  _decodeTable(table) {
49
102
  this._decompress();
50
103
  return super._decodeTable(table);
@@ -52,13 +105,19 @@ export default class WOFF2Font extends TTFFont {
52
105
 
53
106
  // Override this method to get a glyph and return our
54
107
  // custom subclass if there is a glyf table.
108
+ /**
109
+ * @param {number} glyph
110
+ * @param {number[]} [characters]
111
+ * @returns {Glyph | null | undefined}
112
+ */
55
113
  _getBaseGlyph(glyph, characters = []) {
56
114
  let cached = this._glyphs[glyph];
57
- if (cached?._getContours) {
115
+ if (cached && typeof (/** @type {{ _getContours?: unknown }} */ (cached))._getContours === 'function') {
58
116
  return cached;
59
117
  }
60
118
 
61
- if (this.directory.tables.glyf && this.directory.tables.glyf.transformed) {
119
+ let glyfEntry = this.directory.tables.glyf;
120
+ if (glyfEntry && glyfEntry.transformed) {
62
121
  if (!this._transformedGlyphs) { this._transformGlyfTable(); }
63
122
  let outline = new WOFF2Glyph(glyph, characters, this);
64
123
  if (!cached) {
@@ -70,18 +129,27 @@ export default class WOFF2Font extends TTFFont {
70
129
  return super._getBaseGlyph(glyph, characters);
71
130
  }
72
131
 
132
+ /** @returns {void} */
73
133
  _transformGlyfTable() {
74
134
  this._decompress();
75
- this.stream.pos = this.directory.tables.glyf.offset;
76
- let table = GlyfTable.decode(this.stream);
135
+ let glyfEntry = this.directory.tables.glyf;
136
+ if (!glyfEntry) {
137
+ this._transformedGlyphs = [];
138
+ return;
139
+ }
140
+ this.stream.pos = glyfEntry.offset;
141
+ let table = /** @type {GlyfTableDecoded} */ (GlyfTable.decode(this.stream));
142
+ /** @type {TransformedGlyf[]} */
77
143
  let glyphs = [];
78
144
 
79
145
  for (let index = 0; index < table.numGlyphs; index++) {
80
- let glyph = {};
146
+ /** @type {TransformedGlyf} */
147
+ let glyph = { numberOfContours: 0 };
81
148
  let nContours = table.nContours.readInt16BE();
82
149
  glyph.numberOfContours = nContours;
83
150
 
84
151
  if (nContours > 0) { // simple glyph
152
+ /** @type {number[]} */
85
153
  let nPoints = [];
86
154
  let totalPoints = 0;
87
155
 
@@ -93,13 +161,19 @@ export default class WOFF2Font extends TTFFont {
93
161
 
94
162
  glyph.points = decodeTriplet(table.flags, table.glyphs, totalPoints);
95
163
  for (let i = 0; i < nContours; i++) {
96
- glyph.points[nPoints[i] - 1].endContour = true;
164
+ let pts = glyph.points;
165
+ if (pts) {
166
+ pts[nPoints[i] - 1].endContour = true;
167
+ }
97
168
  }
98
169
 
99
170
  read255UInt16(table.glyphs);
100
-
101
171
  } else if (nContours < 0) { // composite glyph
102
- let haveInstructions = TTFGlyph.prototype._decodeComposite.call({ _font: this }, glyph, table.composites);
172
+ let haveInstructions = TTFGlyph.prototype._decodeComposite.call(
173
+ /** @type {TTFGlyph} */ (/** @type {unknown} */ ({ _font: this })),
174
+ /** @type {DecodedGlyf} */ (glyph),
175
+ table.composites
176
+ );
103
177
  if (haveInstructions) {
104
178
  read255UInt16(table.glyphs);
105
179
  }
@@ -112,13 +186,33 @@ export default class WOFF2Font extends TTFFont {
112
186
  }
113
187
  }
114
188
 
189
+ /**
190
+ * @typedef {{
191
+ * version: number,
192
+ * numGlyphs: number,
193
+ * nContours: DecodeStream,
194
+ * nPoints: DecodeStream,
195
+ * flags: DecodeStream,
196
+ * glyphs: DecodeStream,
197
+ * composites: DecodeStream
198
+ * }} GlyfTableDecoded
199
+ */
200
+
115
201
  // Special class that accepts a length and returns a sub-stream for that data
116
202
  class Substream {
203
+ /**
204
+ * @param {number | string} length
205
+ */
117
206
  constructor(length) {
118
207
  this.length = length;
119
208
  this._buf = new r.Buffer(length);
120
209
  }
121
210
 
211
+ /**
212
+ * @param {DecodeStream} stream
213
+ * @param {StructValue} parent
214
+ * @returns {DecodeStream}
215
+ */
122
216
  decode(stream, parent) {
123
217
  return new r.DecodeStream(this._buf.decode(stream, parent));
124
218
  }
@@ -150,6 +244,10 @@ const ONE_MORE_BYTE_CODE2 = 254;
150
244
  const ONE_MORE_BYTE_CODE1 = 255;
151
245
  const LOWEST_U_CODE = 253;
152
246
 
247
+ /**
248
+ * @param {DecodeStream} stream
249
+ * @returns {number}
250
+ */
153
251
  function read255UInt16(stream) {
154
252
  let code = stream.readUInt8();
155
253
 
@@ -168,17 +266,30 @@ function read255UInt16(stream) {
168
266
  return code;
169
267
  }
170
268
 
269
+ /**
270
+ * @param {number} flag
271
+ * @param {number} baseval
272
+ * @returns {number}
273
+ */
171
274
  function withSign(flag, baseval) {
172
275
  return flag & 1 ? baseval : -baseval;
173
276
  }
174
277
 
278
+ /**
279
+ * @param {DecodeStream} flags
280
+ * @param {DecodeStream} glyphs
281
+ * @param {number} nPoints
282
+ * @returns {Point[]}
283
+ */
175
284
  function decodeTriplet(flags, glyphs, nPoints) {
176
285
  let y;
177
286
  let x = y = 0;
287
+ /** @type {Point[]} */
178
288
  let res = [];
179
289
 
180
290
  for (let i = 0; i < nPoints; i++) {
181
- let dx, dy;
291
+ let dx = 0;
292
+ let dy = 0;
182
293
  let flag = flags.readUInt8();
183
294
  let onCurve = !(flag >> 7);
184
295
  flag &= 0x7f;
@@ -186,28 +297,23 @@ function decodeTriplet(flags, glyphs, nPoints) {
186
297
  if (flag < 10) {
187
298
  dx = 0;
188
299
  dy = withSign(flag, ((flag & 14) << 7) + glyphs.readUInt8());
189
-
190
300
  } else if (flag < 20) {
191
301
  dx = withSign(flag, (((flag - 10) & 14) << 7) + glyphs.readUInt8());
192
302
  dy = 0;
193
-
194
303
  } else if (flag < 84) {
195
304
  let b0 = flag - 20;
196
305
  let b1 = glyphs.readUInt8();
197
306
  dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
198
307
  dy = withSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
199
-
200
308
  } else if (flag < 120) {
201
309
  let b0 = flag - 84;
202
310
  dx = withSign(flag, 1 + ((b0 / 12) << 8) + glyphs.readUInt8());
203
311
  dy = withSign(flag >> 1, 1 + (((b0 % 12) >> 2) << 8) + glyphs.readUInt8());
204
-
205
312
  } else if (flag < 124) {
206
313
  let b1 = glyphs.readUInt8();
207
314
  let b2 = glyphs.readUInt8();
208
315
  dx = withSign(flag, (b1 << 4) + (b2 >> 4));
209
316
  dy = withSign(flag >> 1, ((b2 & 0x0f) << 8) + glyphs.readUInt8());
210
-
211
317
  } else {
212
318
  dx = withSign(flag, glyphs.readUInt16BE());
213
319
  dy = withSign(flag >> 1, glyphs.readUInt16BE());
package/src/WOFFFont.js CHANGED
@@ -4,17 +4,41 @@ import { unzlibSync } from 'fflate';
4
4
  import * as r from 'restructure';
5
5
  import { asciiDecoder } from './utils';
6
6
 
7
+ /** @typedef {import('restructure').DecodeStream} DecodeStream */
8
+ /** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
9
+ /** @typedef {import('../types/fontkit').FontDirectory} FontDirectory */
10
+ /** @typedef {import('../types/fontkit').TableEntry} TableEntry */
11
+
7
12
  export default class WOFFFont extends TTFFont {
13
+ /** @type {string} */
8
14
  type = 'WOFF';
9
15
 
16
+ /** @type {boolean | undefined} */
17
+ _decompressed;
18
+
19
+ /**
20
+ * @param {ArrayBufferView} buffer
21
+ * @returns {boolean}
22
+ */
10
23
  static probe(buffer) {
11
- return asciiDecoder.decode(buffer.slice(0, 4)) === 'wOFF';
24
+ let bytes
25
+ = buffer instanceof Uint8Array
26
+ ? buffer
27
+ : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
28
+ return asciiDecoder.decode(bytes.subarray(0, 4)) === 'wOFF';
12
29
  }
13
30
 
31
+ /** @returns {void} */
14
32
  _decodeDirectory() {
15
- this.directory = WOFFDirectory.decode(this.stream, { _startOffset: 0 });
33
+ this.directory = /** @type {FontDirectory} */ (
34
+ /** @type {unknown} */ (WOFFDirectory.decode(this.stream, { _startOffset: 0 }))
35
+ );
16
36
  }
17
37
 
38
+ /**
39
+ * @param {TableEntry} table
40
+ * @returns {unknown}
41
+ */
18
42
  _decodeTable(table) {
19
43
  this._decompress();
20
44
  return super._decodeTable(table);
@@ -22,15 +46,21 @@ export default class WOFFFont extends TTFFont {
22
46
 
23
47
  // Inflate all tables into one stream so internal offsets (e.g. gvar) stay
24
48
  // valid against this.stream — same reason WOFF2Font decompresses up front.
49
+ /** @returns {void} */
25
50
  _decompress() {
26
51
  if (this._decompressed) {
27
52
  return;
28
53
  }
29
54
 
30
55
  let totalSize = 0;
56
+ /** @type {Array<{ entry: TableEntry, newOffset: number }>} */
31
57
  let layout = [];
32
- for (let tag in this.directory.tables) {
33
- let entry = this.directory.tables[tag];
58
+ let tableMap = this.directory.tables;
59
+ for (let tag in tableMap) {
60
+ let entry = tableMap[tag];
61
+ if (!entry) {
62
+ continue;
63
+ }
34
64
  layout.push({ entry, newOffset: totalSize });
35
65
  totalSize = (totalSize + entry.length + 3) & ~3;
36
66
  }
@@ -38,19 +68,26 @@ export default class WOFFFont extends TTFFont {
38
68
  let buffer = new Uint8Array(totalSize);
39
69
  for (let { entry, newOffset } of layout) {
40
70
  this.stream.pos = entry.offset;
71
+ /** @type {BinaryBuffer | Uint8Array} */
41
72
  let data;
42
- if (entry.compLength < entry.length) {
43
- data = unzlibSync(this.stream.readBuffer(entry.compLength), {
73
+ let compLength = entry.compLength != null ? entry.compLength : entry.length;
74
+ if (compLength < entry.length) {
75
+ let compressed = this.stream.readBuffer(compLength);
76
+ let compressedBytes
77
+ = compressed instanceof Uint8Array
78
+ ? compressed
79
+ : new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
80
+ data = unzlibSync(compressedBytes, {
44
81
  out: new Uint8Array(entry.length)
45
82
  });
46
83
  } else {
47
84
  data = this.stream.readBuffer(entry.length);
48
85
  }
49
- buffer.set(data, newOffset);
86
+ buffer.set(data instanceof Uint8Array ? data : new Uint8Array(data.buffer, data.byteOffset, data.byteLength), newOffset);
50
87
  entry.offset = newOffset;
51
88
  }
52
89
 
53
- this.stream = new r.DecodeStream(buffer);
90
+ this._setStream(new r.DecodeStream(buffer));
54
91
  this._decompressed = true;
55
92
  }
56
93
  }