@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
@@ -1,6 +1,32 @@
1
1
  import { inflateSync } from 'fflate';
2
2
  import { swap32LE } from './swap.js';
3
3
 
4
+ /**
5
+ * Pre-parsed trie payload (e.g. from UnicodeTrieBuilder.freeze).
6
+ * @typedef {object} UnicodeTrieInit
7
+ * @property {Int32Array | Uint32Array} data
8
+ * @property {number} highStart
9
+ * @property {number} errorValue
10
+ */
11
+
12
+ /**
13
+ * Duck-type Node Buffer (has LE/BE readers + slice), matching prior runtime checks.
14
+ * @param {unknown} value
15
+ * @returns {value is Buffer}
16
+ */
17
+ function isNodeBuffer(value) {
18
+ return (
19
+ typeof value === 'object'
20
+ && value !== null
21
+ && 'readUInt32BE' in value
22
+ && 'readUInt32LE' in value
23
+ && 'slice' in value
24
+ && typeof value.readUInt32BE === 'function'
25
+ && typeof value.readUInt32LE === 'function'
26
+ && typeof value.slice === 'function'
27
+ );
28
+ }
29
+
4
30
  // Shift size for getting the index-1 table offset.
5
31
  const SHIFT_1 = 6 + 5;
6
32
 
@@ -64,38 +90,64 @@ const INDEX_1_OFFSET = UTF8_2B_INDEX_2_OFFSET + UTF8_2B_INDEX_2_LENGTH;
64
90
  const DATA_GRANULARITY = 1 << INDEX_SHIFT;
65
91
 
66
92
  class UnicodeTrie {
93
+ /**
94
+ * @param {Uint8Array | Buffer | UnicodeTrieInit} data
95
+ */
67
96
  constructor(data) {
68
- const isBuffer = (typeof data.readUInt32BE === 'function') && (typeof data.slice === 'function');
69
-
70
- if (isBuffer || data instanceof Uint8Array) {
97
+ if (isNodeBuffer(data) || data instanceof Uint8Array) {
71
98
  // read binary format
99
+ /** @type {Uint8Array} */
100
+ let bytes;
72
101
  let uncompressedLength;
73
- if (isBuffer) {
102
+ if (isNodeBuffer(data)) {
74
103
  this.highStart = data.readUInt32LE(0);
75
104
  this.errorValue = data.readUInt32LE(4);
76
105
  uncompressedLength = data.readUInt32LE(8);
77
- data = data.slice(12);
106
+ // View past the 12-byte header (avoids Buffer.slice → Uint8Array assignability issues).
107
+ bytes = new Uint8Array(data.buffer, data.byteOffset + 12, data.byteLength - 12);
78
108
  } else {
79
109
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
80
110
  this.highStart = view.getUint32(0, true);
81
111
  this.errorValue = view.getUint32(4, true);
82
112
  uncompressedLength = view.getUint32(8, true);
83
- data = data.subarray(12);
113
+ bytes = data.subarray(12);
84
114
  }
85
115
 
86
116
  // inflate the actual trie data (raw deflate, matching builder)
87
- data = inflateSync(data, { out: new Uint8Array(uncompressedLength) });
117
+ bytes = inflateSync(bytes, { out: new Uint8Array(uncompressedLength) });
88
118
 
89
119
  // swap bytes from little-endian
90
- swap32LE(data);
120
+ swap32LE(bytes);
91
121
 
92
- this.data = new Uint32Array(data.buffer, data.byteOffset, data.byteLength >> 2);
122
+ /**
123
+ * Compacted index + data table.
124
+ * @type {Uint32Array | Int32Array}
125
+ */
126
+ this.data = new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength >> 2);
93
127
  } else {
94
128
  // pre-parsed data
95
- ({ data: this.data, highStart: this.highStart, errorValue: this.errorValue } = data);
129
+ /**
130
+ * Compacted index + data table.
131
+ * @type {Uint32Array | Int32Array}
132
+ */
133
+ this.data = data.data;
134
+ /**
135
+ * First code point of the final single-value range.
136
+ * @type {number}
137
+ */
138
+ this.highStart = data.highStart;
139
+ /**
140
+ * Value returned for out-of-range code points.
141
+ * @type {number}
142
+ */
143
+ this.errorValue = data.errorValue;
96
144
  }
97
145
  }
98
146
 
147
+ /**
148
+ * @param {number} codePoint
149
+ * @returns {number}
150
+ */
99
151
  get(codePoint) {
100
152
  let index;
101
153
  if ((codePoint < 0) || (codePoint > 0x10ffff)) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cantoo/unicode-trie",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Unicode Trie data structure for fast character metadata lookup, ported from ICU (fflate, no tiny-inflate/pako)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1,11 +1,19 @@
1
1
  const isBigEndian = (new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12);
2
2
 
3
+ /**
4
+ * @param {Uint8Array} b
5
+ * @param {number} n
6
+ * @param {number} m
7
+ */
3
8
  const swap = (b, n, m) => {
4
9
  let i = b[n];
5
10
  b[n] = b[m];
6
11
  b[m] = i;
7
12
  };
8
13
 
14
+ /**
15
+ * @param {Uint8Array} array
16
+ */
9
17
  const swap32 = (array) => {
10
18
  const len = array.length;
11
19
  for (let i = 0; i < len; i += 4) {
@@ -14,6 +22,11 @@ const swap32 = (array) => {
14
22
  }
15
23
  };
16
24
 
25
+ /**
26
+ * Swap byte order of each 32-bit word in-place when running on a big-endian host,
27
+ * so the trie data is little-endian as expected by the runtime.
28
+ * @param {Uint8Array} array
29
+ */
17
30
  export const swap32LE = (array) => {
18
31
  if (isBigEndian) {
19
32
  swap32(array);
@@ -2,39 +2,84 @@ import Subset from './Subset';
2
2
  import CFFTop from '../cff/CFFTop';
3
3
  import standardStrings from '../cff/CFFStandardStrings';
4
4
 
5
+ /** @typedef {import('../../types/fontkit').SubsetFont} SubsetFont */
6
+ /** @typedef {import('../../types/fontkit').CFFFontLike} CFFFontLike */
7
+ /** @typedef {import('../../types/fontkit').CFFTopDict} CFFTopDict */
8
+ /** @typedef {import('../../types/fontkit').CFFCharString} CFFCharString */
9
+ /** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
10
+ /** @typedef {import('restructure').StructValue} StructValue */
11
+
5
12
  export default class CFFSubset extends Subset {
13
+ /** @type {CFFFontLike} */
14
+ cff;
15
+ /** @type {BinaryBuffer[] | null} */
16
+ charstrings = null;
17
+ /** @type {BinaryBuffer[] | null} */
18
+ gsubrs = null;
19
+ /** @type {string[] | null} */
20
+ strings = null;
21
+
22
+ /**
23
+ * @param {SubsetFont} font
24
+ */
6
25
  constructor(font) {
7
26
  super(font);
8
27
 
9
- this.cff = this.font['CFF '];
10
- if (!this.cff) {
28
+ let cff = this.font['CFF '];
29
+ if (!cff) {
11
30
  throw new Error('Not a CFF Font');
12
31
  }
32
+ this.cff = cff;
13
33
  }
14
34
 
15
- includeTable() {
35
+ /**
36
+ * @param {string} [_tag]
37
+ * @returns {never}
38
+ */
39
+ includeTable(_tag) {
16
40
  throw new Error('includeTable is only supported for TrueType (glyf) subsets, not CFF.');
17
41
  }
18
42
 
43
+ /**
44
+ * @returns {void}
45
+ */
19
46
  subsetCharstrings() {
20
47
  this.charstrings = [];
48
+ /** @type {Record<string, boolean>} */
21
49
  let gsubrs = {};
22
50
 
23
51
  for (let gid of this.glyphs) {
52
+ if (!this.cff.getCharString) {
53
+ throw new Error('CFF font missing getCharString()');
54
+ }
24
55
  this.charstrings.push(this.cff.getCharString(gid));
25
56
 
26
57
  let glyph = this.font.getGlyph(gid);
58
+ if (!glyph) {
59
+ continue;
60
+ }
27
61
  void glyph.path; // this causes the glyph to be parsed
28
62
 
29
- for (let subr in glyph._usedGsubrs) {
30
- gsubrs[subr] = true;
63
+ if (glyph._usedGsubrs) {
64
+ for (let subr in glyph._usedGsubrs) {
65
+ gsubrs[subr] = true;
66
+ }
31
67
  }
32
68
  }
33
69
 
34
- this.gsubrs = this.subsetSubrs(this.cff.globalSubrIndex, gsubrs);
70
+ this.gsubrs = this.subsetSubrs(
71
+ /** @type {Array<{ offset: number, length: number }>} */ (this.cff.globalSubrIndex || []),
72
+ gsubrs
73
+ );
35
74
  }
36
75
 
76
+ /**
77
+ * @param {Array<{ offset: number, length: number }>} subrs
78
+ * @param {Record<string | number, boolean>} used
79
+ * @returns {BinaryBuffer[]}
80
+ */
37
81
  subsetSubrs(subrs, used) {
82
+ /** @type {BinaryBuffer[]} */
38
83
  let res = [];
39
84
  for (let i = 0; i < subrs.length; i++) {
40
85
  let subr = subrs[i];
@@ -49,6 +94,10 @@ export default class CFFSubset extends Subset {
49
94
  return res;
50
95
  }
51
96
 
97
+ /**
98
+ * @param {CFFTopDict} topDict
99
+ * @returns {void}
100
+ */
52
101
  subsetFontdict(topDict) {
53
102
  topDict.FDArray = [];
54
103
  topDict.FDSelect = {
@@ -56,28 +105,42 @@ export default class CFFSubset extends Subset {
56
105
  fds: []
57
106
  };
58
107
 
108
+ /** @type {Record<number, boolean>} */
59
109
  let used_fds = {};
110
+ /** @type {Array<Record<string, boolean>>} */
60
111
  let used_subrs = [];
112
+ /** @type {Record<number, number>} */
61
113
  let fd_select = {};
62
114
  for (let gid of this.glyphs) {
115
+ if (!this.cff.fdForGlyph) {
116
+ continue;
117
+ }
63
118
  let fd = this.cff.fdForGlyph(gid);
64
119
  if (fd == null) {
65
120
  continue;
66
121
  }
67
122
 
68
123
  if (!used_fds[fd]) {
69
- topDict.FDArray.push(Object.assign({}, this.cff.topDict.FDArray[fd]));
124
+ let src = this.cff.topDict.FDArray && this.cff.topDict.FDArray[fd];
125
+ topDict.FDArray.push(Object.assign({}, src));
70
126
  used_subrs.push({});
71
127
  fd_select[fd] = topDict.FDArray.length - 1;
72
128
  }
73
129
 
74
130
  used_fds[fd] = true;
75
- topDict.FDSelect.fds.push(fd_select[fd]);
131
+ if (topDict.FDSelect.fds) {
132
+ topDict.FDSelect.fds.push(fd_select[fd]);
133
+ }
76
134
 
77
135
  let glyph = this.font.getGlyph(gid);
136
+ if (!glyph) {
137
+ continue;
138
+ }
78
139
  void glyph.path; // this causes the glyph to be parsed
79
- for (let subr in glyph._usedSubrs) {
80
- used_subrs[fd_select[fd]][subr] = true;
140
+ if (glyph._usedSubrs) {
141
+ for (let subr in glyph._usedSubrs) {
142
+ used_subrs[fd_select[fd]][subr] = true;
143
+ }
81
144
  }
82
145
  }
83
146
 
@@ -86,38 +149,59 @@ export default class CFFSubset extends Subset {
86
149
  delete dict.FontName;
87
150
  if (dict.Private && dict.Private.Subrs) {
88
151
  dict.Private = Object.assign({}, dict.Private);
89
- dict.Private.Subrs = this.subsetSubrs(dict.Private.Subrs, used_subrs[i]);
152
+ dict.Private.Subrs = this.subsetSubrs(
153
+ /** @type {Array<{ offset: number, length: number }>} */ (dict.Private.Subrs),
154
+ used_subrs[i]
155
+ );
90
156
  }
91
157
  }
92
158
 
93
159
  return;
94
160
  }
95
161
 
162
+ /**
163
+ * @param {CFFTopDict} topDict
164
+ * @returns {StructValue}
165
+ */
96
166
  createCIDFontdict(topDict) {
167
+ /** @type {Record<string, boolean>} */
97
168
  let used_subrs = {};
98
169
  for (let gid of this.glyphs) {
99
170
  let glyph = this.font.getGlyph(gid);
171
+ if (!glyph) {
172
+ continue;
173
+ }
100
174
  void glyph.path; // this causes the glyph to be parsed
101
175
 
102
- for (let subr in glyph._usedSubrs) {
103
- used_subrs[subr] = true;
176
+ if (glyph._usedSubrs) {
177
+ for (let subr in glyph._usedSubrs) {
178
+ used_subrs[subr] = true;
179
+ }
104
180
  }
105
181
  }
106
182
 
107
183
  let privateDict = Object.assign({}, this.cff.topDict.Private);
108
184
  if (this.cff.topDict.Private && this.cff.topDict.Private.Subrs) {
109
- privateDict.Subrs = this.subsetSubrs(this.cff.topDict.Private.Subrs, used_subrs);
185
+ privateDict.Subrs = this.subsetSubrs(
186
+ /** @type {Array<{ offset: number, length: number }>} */ (this.cff.topDict.Private.Subrs),
187
+ used_subrs
188
+ );
110
189
  }
111
190
 
112
191
  topDict.FDArray = [{ Private: privateDict }];
192
+ let charstrings = this.charstrings || [];
113
193
  return topDict.FDSelect = {
114
194
  version: 3,
115
195
  nRanges: 1,
116
196
  ranges: [{ first: 0, fd: 0 }],
117
- sentinel: this.charstrings.length
197
+ sentinel: charstrings.length
118
198
  };
119
199
  }
120
200
 
201
+ /**
202
+ * @param {string | null | undefined} string
203
+ * @returns {number | null}
204
+ */
121
205
  addString(string) {
122
206
  if (!string) {
123
207
  return null;
@@ -131,28 +215,34 @@ export default class CFFSubset extends Subset {
131
215
  return standardStrings.length + this.strings.length - 1;
132
216
  }
133
217
 
218
+ /**
219
+ * @returns {import('restructure').BinaryBuffer}
220
+ */
134
221
  encode() {
135
222
  this.subsetCharstrings();
136
223
 
224
+ let charstrings = this.charstrings || [];
225
+
137
226
  let charset = {
138
- version: this.charstrings.length > 255 ? 2 : 1,
139
- ranges: this.charstrings.length > 1
140
- ? [{ first: 1, nLeft: this.charstrings.length - 2 }]
227
+ version: charstrings.length > 255 ? 2 : 1,
228
+ ranges: charstrings.length > 1
229
+ ? [{ first: 1, nLeft: charstrings.length - 2 }]
141
230
  : []
142
231
  };
143
232
 
144
- let topDict = Object.assign({}, this.cff.topDict);
233
+ let topDict = /** @type {CFFTopDict} */ (Object.assign({}, this.cff.topDict));
145
234
  topDict.Private = null;
146
235
  topDict.charset = charset;
147
236
  topDict.Encoding = null;
148
- topDict.CharStrings = this.charstrings;
237
+ topDict.CharStrings = charstrings;
149
238
 
150
239
  for (let key of ['version', 'Notice', 'Copyright', 'FullName', 'FamilyName', 'Weight', 'PostScript', 'BaseFontName', 'FontName']) {
151
- topDict[key] = this.addString(this.cff.string(topDict[key]));
240
+ let sid = /** @type {number | null | undefined} */ (topDict[key]);
241
+ topDict[key] = this.cff.string ? this.addString(this.cff.string(sid)) : null;
152
242
  }
153
243
 
154
244
  topDict.ROS = [this.addString('Adobe'), this.addString('Identity'), 0];
155
- topDict.CIDCount = this.charstrings.length;
245
+ topDict.CIDCount = charstrings.length;
156
246
 
157
247
  if (this.cff.isCIDFont) {
158
248
  this.subsetFontdict(topDict);
@@ -1,22 +1,37 @@
1
1
  import Tables from '../tables';
2
2
 
3
+ /** @typedef {import('../../types/fontkit').SubsetFont} SubsetFont */
4
+ /** @typedef {import('../../types/fontkit').SubsetGlyph} SubsetGlyph */
5
+ /** @typedef {import('../../types/fontkit').TableCodec} TableCodec */
6
+
3
7
  // Tables written by TTFSubset.encode; must not be overwritten via includeTable.
4
8
  const MANAGED_TTF_TABLES = [
5
9
  'head', 'hhea', 'loca', 'maxp', 'cvt ', 'prep', 'glyf', 'hmtx', 'fpgm'
6
10
  ];
7
11
 
8
12
  export default class Subset {
13
+ /**
14
+ * @param {SubsetFont} font
15
+ */
9
16
  constructor(font) {
17
+ /** @type {SubsetFont} */
10
18
  this.font = font;
19
+ /** @type {number[]} */
11
20
  this.glyphs = [];
21
+ /** @type {Record<number, number>} */
12
22
  this.mapping = {};
13
23
  // Extra OT table tags to copy into a TTF subset (e.g. 'OS/2', 'name', 'post').
24
+ /** @type {string[]} */
14
25
  this.includedTables = [];
15
26
 
16
27
  // always include the missing glyph
17
28
  this.includeGlyph(0);
18
29
  }
19
30
 
31
+ /**
32
+ * @param {number | SubsetGlyph} glyph
33
+ * @returns {number}
34
+ */
20
35
  includeGlyph(glyph) {
21
36
  if (typeof glyph === 'object') {
22
37
  glyph = glyph.id;
@@ -32,6 +47,10 @@ export default class Subset {
32
47
 
33
48
  // Copy an OT table into the TTF subset (tag is a 4-byte string, e.g. 'OS/2').
34
49
  // Only honored by TTFSubset.encode; CFFSubset overrides this to throw.
50
+ /**
51
+ * @param {string} [tag]
52
+ * @returns {this}
53
+ */
35
54
  includeTable(tag) {
36
55
  if (typeof tag !== 'string' || tag.length !== 4) {
37
56
  throw new Error(
@@ -50,7 +69,8 @@ export default class Subset {
50
69
  throw new Error(`Table "${tag}" is not present in this font.`);
51
70
  }
52
71
 
53
- if (!Tables[tag]) {
72
+ let encoders = /** @type {Record<string, TableCodec | undefined>} */ (/** @type {unknown} */ (Tables));
73
+ if (!encoders[tag]) {
54
74
  throw new Error(`Table "${tag}" cannot be encoded (no encoder registered in fontkit).`);
55
75
  }
56
76
 
@@ -3,36 +3,88 @@ import Subset from './Subset';
3
3
  import Directory from '../tables/directory';
4
4
  import TTFGlyphEncoder from '../glyph/TTFGlyphEncoder';
5
5
 
6
+ /** @typedef {import('../../types/fontkit').SubsetFont} SubsetFont */
7
+ /** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
8
+ /** @typedef {import('restructure').BinaryBuffer} BinaryBuffer */
9
+ /** @typedef {import('restructure').StructValue} StructValue */
10
+
11
+ /**
12
+ * @typedef {{ advance: number, bearing: number }} HmtxMetric
13
+ */
14
+
6
15
  export default class TTFSubset extends Subset {
16
+ /** @type {TTFGlyphEncoder} */
17
+ glyphEncoder;
18
+ /** @type {Uint8Array[] | null} */
19
+ glyf = null;
20
+ /** @type {number} */
21
+ offset = 0;
22
+ /** @type {{ offsets: number[], version: number } | null} */
23
+ loca = null;
24
+ /** @type {{ metrics: HmtxMetric[], bearings: number[] } | null} */
25
+ hmtx = null;
26
+
27
+ /**
28
+ * @param {SubsetFont} font
29
+ */
7
30
  constructor(font) {
8
31
  super(font);
9
32
  this.glyphEncoder = new TTFGlyphEncoder();
10
33
  }
11
34
 
35
+ /**
36
+ * @param {number} gid
37
+ * @returns {number}
38
+ */
12
39
  _addGlyph(gid) {
13
40
  let glyph = this.font.getGlyph(gid);
41
+ if (!glyph || !glyph._decode) {
42
+ throw new Error(`Cannot decode glyph ${gid}`);
43
+ }
14
44
  let glyf = glyph._decode();
15
45
 
46
+ if (!this.font.loca) {
47
+ throw new Error('Font missing loca table');
48
+ }
49
+
16
50
  // get the offset to the glyph from the loca table
17
51
  let curOffset = this.font.loca.offsets[gid];
18
52
  let nextOffset = this.font.loca.offsets[gid + 1];
19
53
 
20
54
  let stream = this.font._getTableStream('glyf');
55
+ if (!stream) {
56
+ throw new Error('Font missing glyf table');
57
+ }
21
58
  stream.pos += curOffset;
22
59
 
23
- let buffer = stream.readBuffer(nextOffset - curOffset);
60
+ let raw = stream.readBuffer(nextOffset - curOffset);
61
+ /** @type {Uint8Array} */
62
+ let buffer = raw instanceof Uint8Array
63
+ ? raw
64
+ : Uint8Array.from(/** @type {Iterable<number>} */ (/** @type {unknown} */ (raw)));
24
65
 
25
66
  // if it is a compound glyph, include its components
26
67
  if (glyf && glyf.numberOfContours < 0) {
27
68
  buffer = new Uint8Array(buffer);
28
- let view = new DataView(buffer.buffer);
29
- for (let component of glyf.components) {
69
+ let ab = buffer.buffer instanceof ArrayBuffer ? buffer.buffer : new Uint8Array(buffer).buffer;
70
+ let view = new DataView(ab, buffer.byteOffset, buffer.byteLength);
71
+ for (let component of glyf.components || []) {
30
72
  gid = this.includeGlyph(component.glyphID);
31
73
  view.setUint16(component.pos, gid);
32
74
  }
33
75
  } else if (glyf && this.font._variationProcessor) {
34
76
  // If this is a TrueType variation glyph, re-encode the path
35
- buffer = this.glyphEncoder.encodeSimple(glyph.path, glyf.instructions);
77
+ let encoded = this.glyphEncoder.encodeSimple(
78
+ /** @type {import('../glyph/Path').default} */ (/** @type {unknown} */ (glyph.path)),
79
+ glyf.instructions
80
+ );
81
+ buffer = encoded instanceof Uint8Array
82
+ ? encoded
83
+ : Uint8Array.from(/** @type {Iterable<number>} */ (/** @type {unknown} */ (encoded)));
84
+ }
85
+
86
+ if (!this.glyf || !this.loca || !this.hmtx) {
87
+ throw new Error('TTFSubset.encode must initialize glyf/loca/hmtx before _addGlyph');
36
88
  }
37
89
 
38
90
  this.glyf.push(buffer);
@@ -43,10 +95,13 @@ export default class TTFSubset extends Subset {
43
95
  bearing: glyph._getMetrics().leftBearing
44
96
  });
45
97
 
46
- this.offset += buffer.length;
98
+ this.offset += buffer.byteLength;
47
99
  return this.glyf.length - 1;
48
100
  }
49
101
 
102
+ /**
103
+ * @returns {BinaryBuffer}
104
+ */
50
105
  encode() {
51
106
  // tables required by PDF spec:
52
107
  // head, hhea, loca, maxp, cvt , prep, glyf, hmtx, fpgm
@@ -54,16 +109,17 @@ export default class TTFSubset extends Subset {
54
109
  // additional tables required for standalone fonts:
55
110
  // name, cmap, OS/2, post
56
111
 
112
+ /** @type {Uint8Array[]} */
57
113
  this.glyf = [];
58
114
  this.offset = 0;
59
115
  this.loca = {
60
- offsets: [],
61
- version: this.font.loca.version
116
+ offsets: /** @type {number[]} */ ([]),
117
+ version: this.font.loca ? (this.font.loca.version ?? 0) : 0
62
118
  };
63
119
 
64
120
  this.hmtx = {
65
- metrics: [],
66
- bearings: []
121
+ metrics: /** @type {HmtxMetric[]} */ ([]),
122
+ bearings: /** @type {number[]} */ ([])
67
123
  };
68
124
 
69
125
  // include all the glyphs
@@ -75,40 +131,26 @@ export default class TTFSubset extends Subset {
75
131
  }
76
132
 
77
133
  let maxp = cloneDeep(this.font.maxp);
78
- maxp.numGlyphs = this.glyf.length;
134
+ if (maxp && typeof maxp === 'object') {
135
+ /** @type {StructValue} */ (maxp).numGlyphs = this.glyf.length;
136
+ }
79
137
 
80
- this.loca.offsets.push(this.offset);
138
+ let loca = this.loca;
139
+ loca.offsets.push(this.offset);
81
140
 
82
141
  let head = cloneDeep(this.font.head);
83
- head.indexToLocFormat = this.loca.version;
142
+ if (head && typeof head === 'object') {
143
+ /** @type {StructValue} */ (head).indexToLocFormat = loca.version;
144
+ }
84
145
 
85
146
  let hhea = cloneDeep(this.font.hhea);
86
- hhea.numberOfMetrics = this.hmtx.metrics.length;
87
-
88
- // map = []
89
- // for index in [0...256]
90
- // if index < @numGlyphs
91
- // map[index] = index
92
- // else
93
- // map[index] = 0
94
- //
95
- // cmapTable =
96
- // version: 0
97
- // length: 262
98
- // language: 0
99
- // codeMap: map
100
- //
101
- // cmap =
102
- // version: 0
103
- // numSubtables: 1
104
- // tables: [
105
- // platformID: 1
106
- // encodingID: 0
107
- // table: cmapTable
108
- // ]
147
+ if (hhea && typeof hhea === 'object') {
148
+ /** @type {StructValue} */ (hhea).numberOfMetrics = this.hmtx.metrics.length;
149
+ }
109
150
 
110
151
  // TODO: subset prep, cvt, fpgm?
111
152
  // The following is the minimum set of tables.
153
+ /** @type {Record<string, unknown>} */
112
154
  let t = {
113
155
  head,
114
156
  hhea,
@@ -121,8 +163,9 @@ export default class TTFSubset extends Subset {
121
163
  fpgm: this.font.fpgm
122
164
  };
123
165
 
166
+ let fontTables = /** @type {Record<string, unknown>} */ (/** @type {unknown} */ (this.font));
124
167
  for (const tag of this.includedTables) {
125
- t[tag] = cloneDeep(this.font[tag]);
168
+ t[tag] = cloneDeep(fontTables[tag]);
126
169
  }
127
170
 
128
171
  return Directory.toBuffer({ tables: t });
@@ -65,6 +65,7 @@ let Axis = new r.Struct({
65
65
  baseScriptList: new r.Pointer(r.uint16, BaseScriptList)
66
66
  });
67
67
 
68
+ /** @type {import('restructure').VersionedStruct} */
68
69
  export default new r.VersionedStruct(r.uint32, {
69
70
  header: {
70
71
  horizAxis: new r.Pointer(r.uint16, Axis), // May be NULL
@@ -16,6 +16,7 @@ let BaseGlyphRecord = new r.Struct({
16
16
  numLayers: r.uint16
17
17
  });
18
18
 
19
+ /** @type {import('restructure').Struct} */
19
20
  export default new r.Struct({
20
21
  version: r.uint16,
21
22
  numBaseGlyphRecords: r.uint16,
@@ -7,6 +7,7 @@ let ColorRecord = new r.Struct({
7
7
  alpha: r.uint8
8
8
  });
9
9
 
10
+ /** @type {import('restructure').VersionedStruct} */
10
11
  export default new r.VersionedStruct(r.uint16, {
11
12
  header: {
12
13
  numPaletteEntries: r.uint16,
@@ -12,6 +12,7 @@ let SignatureBlock = new r.Struct({
12
12
  signature: new r.Buffer('cbSignature')
13
13
  });
14
14
 
15
+ /** @type {import('restructure').Struct} */
15
16
  export default new r.Struct({
16
17
  ulVersion: r.uint32, // Version number of the DSIG table (0x00000001)
17
18
  usNumSigs: r.uint16, // Number of signatures in the table