@cantoo/fontkit 2.0.4

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +274 -0
  3. package/dist/browser-module.mjs +13660 -0
  4. package/dist/browser-module.mjs.map +1 -0
  5. package/dist/browser.cjs +13689 -0
  6. package/dist/browser.cjs.map +1 -0
  7. package/dist/main.cjs +13712 -0
  8. package/dist/main.cjs.map +1 -0
  9. package/dist/module.mjs +13673 -0
  10. package/dist/module.mjs.map +1 -0
  11. package/package.json +99 -0
  12. package/src/CmapProcessor.js +285 -0
  13. package/src/DFont.js +113 -0
  14. package/src/TTFFont.js +621 -0
  15. package/src/TrueTypeCollection.js +65 -0
  16. package/src/WOFF2Font.js +222 -0
  17. package/src/WOFFFont.js +56 -0
  18. package/src/aat/AATFeatureMap.js +545 -0
  19. package/src/aat/AATLayoutEngine.js +48 -0
  20. package/src/aat/AATLookupTable.js +125 -0
  21. package/src/aat/AATMorxProcessor.js +429 -0
  22. package/src/aat/AATStateMachine.js +96 -0
  23. package/src/base.js +29 -0
  24. package/src/cff/CFFCharsets.js +99 -0
  25. package/src/cff/CFFDict.js +166 -0
  26. package/src/cff/CFFEncodings.js +48 -0
  27. package/src/cff/CFFFont.js +158 -0
  28. package/src/cff/CFFIndex.js +150 -0
  29. package/src/cff/CFFOperand.js +135 -0
  30. package/src/cff/CFFPointer.js +50 -0
  31. package/src/cff/CFFPrivateDict.js +39 -0
  32. package/src/cff/CFFStandardStrings.js +71 -0
  33. package/src/cff/CFFTop.js +236 -0
  34. package/src/decorators.js +36 -0
  35. package/src/encodings.js +219 -0
  36. package/src/fs.js +12 -0
  37. package/src/glyph/BBox.js +72 -0
  38. package/src/glyph/CBDTGlyph.js +9 -0
  39. package/src/glyph/CFFGlyph.js +635 -0
  40. package/src/glyph/COLRGlyph.js +108 -0
  41. package/src/glyph/Glyph.js +270 -0
  42. package/src/glyph/GlyphVariationProcessor.js +487 -0
  43. package/src/glyph/Path.js +257 -0
  44. package/src/glyph/SBIXGlyph.js +54 -0
  45. package/src/glyph/StandardNames.js +27 -0
  46. package/src/glyph/TTFGlyph.js +408 -0
  47. package/src/glyph/TTFGlyphEncoder.js +158 -0
  48. package/src/glyph/WOFF2Glyph.js +70 -0
  49. package/src/index.js +15 -0
  50. package/src/layout/GlyphPosition.js +30 -0
  51. package/src/layout/GlyphRun.js +108 -0
  52. package/src/layout/KernProcessor.js +97 -0
  53. package/src/layout/LayoutEngine.js +190 -0
  54. package/src/layout/Script.js +231 -0
  55. package/src/layout/UnicodeLayoutEngine.js +250 -0
  56. package/src/node.js +16 -0
  57. package/src/opentype/GPOSProcessor.js +311 -0
  58. package/src/opentype/GSUBProcessor.js +208 -0
  59. package/src/opentype/GlyphInfo.js +57 -0
  60. package/src/opentype/GlyphIterator.js +82 -0
  61. package/src/opentype/OTLayoutEngine.js +117 -0
  62. package/src/opentype/OTProcessor.js +472 -0
  63. package/src/opentype/ShapingPlan.js +121 -0
  64. package/src/opentype/shapers/ArabicShaper.js +124 -0
  65. package/src/opentype/shapers/DefaultShaper.js +116 -0
  66. package/src/opentype/shapers/HangulShaper.js +285 -0
  67. package/src/opentype/shapers/HebrewShaper.js +105 -0
  68. package/src/opentype/shapers/IndicShaper.js +914 -0
  69. package/src/opentype/shapers/ThaiShaper.js +238 -0
  70. package/src/opentype/shapers/UniversalShaper.js +186 -0
  71. package/src/opentype/shapers/data.trie +0 -0
  72. package/src/opentype/shapers/gen-indic.js +208 -0
  73. package/src/opentype/shapers/gen-use.js +287 -0
  74. package/src/opentype/shapers/generate-data.js +33 -0
  75. package/src/opentype/shapers/index.js +109 -0
  76. package/src/opentype/shapers/indic-data.js +175 -0
  77. package/src/opentype/shapers/indic.json +1 -0
  78. package/src/opentype/shapers/indic.machine +31 -0
  79. package/src/opentype/shapers/indic.trie +0 -0
  80. package/src/opentype/shapers/use.json +1 -0
  81. package/src/opentype/shapers/use.machine +44 -0
  82. package/src/opentype/shapers/use.trie +0 -0
  83. package/src/packages/unicode-properties/LICENSE +8 -0
  84. package/src/packages/unicode-properties/README.md +12 -0
  85. package/src/packages/unicode-properties/data-trie.js +1 -0
  86. package/src/packages/unicode-properties/data.js +1 -0
  87. package/src/packages/unicode-properties/data.json +1 -0
  88. package/src/packages/unicode-properties/data.trie +0 -0
  89. package/src/packages/unicode-properties/dist/index.cjs +257 -0
  90. package/src/packages/unicode-properties/dist/index.cjs.map +7 -0
  91. package/src/packages/unicode-properties/dist/index.mjs +234 -0
  92. package/src/packages/unicode-properties/dist/index.mjs.map +7 -0
  93. package/src/packages/unicode-properties/generate.js +107 -0
  94. package/src/packages/unicode-properties/index.js +184 -0
  95. package/src/packages/unicode-properties/package.json +50 -0
  96. package/src/packages/unicode-trie/LICENSE +8 -0
  97. package/src/packages/unicode-trie/README.md +13 -0
  98. package/src/packages/unicode-trie/builder.js +965 -0
  99. package/src/packages/unicode-trie/dist/builder.cjs +711 -0
  100. package/src/packages/unicode-trie/dist/builder.cjs.map +7 -0
  101. package/src/packages/unicode-trie/dist/builder.mjs +690 -0
  102. package/src/packages/unicode-trie/dist/builder.mjs.map +7 -0
  103. package/src/packages/unicode-trie/dist/index.cjs +112 -0
  104. package/src/packages/unicode-trie/dist/index.cjs.map +7 -0
  105. package/src/packages/unicode-trie/dist/index.mjs +91 -0
  106. package/src/packages/unicode-trie/dist/index.mjs.map +7 -0
  107. package/src/packages/unicode-trie/index.js +135 -0
  108. package/src/packages/unicode-trie/package.json +49 -0
  109. package/src/packages/unicode-trie/swap.js +21 -0
  110. package/src/subset/CFFSubset.js +176 -0
  111. package/src/subset/Subset.js +63 -0
  112. package/src/subset/TTFSubset.js +130 -0
  113. package/src/tables/BASE.js +78 -0
  114. package/src/tables/COLR.js +25 -0
  115. package/src/tables/CPAL.js +24 -0
  116. package/src/tables/DSIG.js +21 -0
  117. package/src/tables/EBDT.js +91 -0
  118. package/src/tables/EBLC.js +80 -0
  119. package/src/tables/GDEF.js +57 -0
  120. package/src/tables/GPOS.js +209 -0
  121. package/src/tables/GSUB.js +85 -0
  122. package/src/tables/HVAR.js +44 -0
  123. package/src/tables/JSTF.js +43 -0
  124. package/src/tables/LTSH.js +10 -0
  125. package/src/tables/OS2.js +84 -0
  126. package/src/tables/PCLT.js +21 -0
  127. package/src/tables/STAT.js +99 -0
  128. package/src/tables/VDMX.js +33 -0
  129. package/src/tables/VORG.js +14 -0
  130. package/src/tables/WOFF2Directory.js +73 -0
  131. package/src/tables/WOFFDirectory.js +37 -0
  132. package/src/tables/aat.js +157 -0
  133. package/src/tables/avar.js +19 -0
  134. package/src/tables/bsln.js +31 -0
  135. package/src/tables/cmap.js +127 -0
  136. package/src/tables/cvt.js +6 -0
  137. package/src/tables/directory.js +58 -0
  138. package/src/tables/feat.js +28 -0
  139. package/src/tables/fpgm.js +8 -0
  140. package/src/tables/fvar.js +31 -0
  141. package/src/tables/gasp.js +15 -0
  142. package/src/tables/glyf.js +4 -0
  143. package/src/tables/gvar.js +27 -0
  144. package/src/tables/hdmx.js +15 -0
  145. package/src/tables/head.js +25 -0
  146. package/src/tables/hhea.js +19 -0
  147. package/src/tables/hmtx.js +11 -0
  148. package/src/tables/index.js +119 -0
  149. package/src/tables/just.js +81 -0
  150. package/src/tables/kern.js +91 -0
  151. package/src/tables/loca.js +30 -0
  152. package/src/tables/maxp.js +20 -0
  153. package/src/tables/morx.js +79 -0
  154. package/src/tables/name.js +157 -0
  155. package/src/tables/opbd.js +15 -0
  156. package/src/tables/opentype.js +217 -0
  157. package/src/tables/post.js +34 -0
  158. package/src/tables/prep.js +6 -0
  159. package/src/tables/sbix.js +17 -0
  160. package/src/tables/variations.js +80 -0
  161. package/src/tables/vhea.js +19 -0
  162. package/src/tables/vmtx.js +12 -0
  163. package/src/utils.js +62 -0
@@ -0,0 +1,487 @@
1
+ const TUPLES_SHARE_POINT_NUMBERS = 0x8000;
2
+ const TUPLE_COUNT_MASK = 0x0fff;
3
+ const EMBEDDED_TUPLE_COORD = 0x8000;
4
+ const INTERMEDIATE_TUPLE = 0x4000;
5
+ const PRIVATE_POINT_NUMBERS = 0x2000;
6
+ const TUPLE_INDEX_MASK = 0x0fff;
7
+ const POINTS_ARE_WORDS = 0x80;
8
+ const POINT_RUN_COUNT_MASK = 0x7f;
9
+ const DELTAS_ARE_ZERO = 0x80;
10
+ const DELTAS_ARE_WORDS = 0x40;
11
+ const DELTA_RUN_COUNT_MASK = 0x3f;
12
+
13
+ /**
14
+ * This class is transforms TrueType glyphs according to the data from
15
+ * the Apple Advanced Typography variation tables (fvar, gvar, and avar).
16
+ * These tables allow infinite adjustments to glyph weight, width, slant,
17
+ * and optical size without the designer needing to specify every exact style.
18
+ *
19
+ * Apple's documentation for these tables is not great, so thanks to the
20
+ * Freetype project for figuring much of this out.
21
+ *
22
+ * @private
23
+ */
24
+ export default class GlyphVariationProcessor {
25
+ constructor(font, coords) {
26
+ this.font = font;
27
+ this.normalizedCoords = this.normalizeCoords(coords);
28
+ this.blendVectors = new Map;
29
+ }
30
+
31
+ normalizeCoords(coords) {
32
+ // the default mapping is linear along each axis, in two segments:
33
+ // from the minValue to defaultValue, and from defaultValue to maxValue.
34
+ let normalized = [];
35
+ for (let i = 0; i < this.font.fvar.axis.length; i++) {
36
+ let axis = this.font.fvar.axis[i];
37
+ if (coords[i] < axis.defaultValue) {
38
+ normalized.push((coords[i] - axis.defaultValue + Number.EPSILON) / (axis.defaultValue - axis.minValue + Number.EPSILON));
39
+ } else {
40
+ normalized.push((coords[i] - axis.defaultValue + Number.EPSILON) / (axis.maxValue - axis.defaultValue + Number.EPSILON));
41
+ }
42
+ }
43
+
44
+ // if there is an avar table, the normalized value is calculated
45
+ // by interpolating between the two nearest mapped values.
46
+ if (this.font.avar) {
47
+ for (let i = 0; i < this.font.avar.segment.length; i++) {
48
+ let segment = this.font.avar.segment[i];
49
+ for (let j = 0; j < segment.correspondence.length; j++) {
50
+ let pair = segment.correspondence[j];
51
+ if (j >= 1 && normalized[i] < pair.fromCoord) {
52
+ let prev = segment.correspondence[j - 1];
53
+ normalized[i] = ((normalized[i] - prev.fromCoord) * (pair.toCoord - prev.toCoord) + Number.EPSILON) /
54
+ (pair.fromCoord - prev.fromCoord + Number.EPSILON) +
55
+ prev.toCoord;
56
+
57
+ break;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ return normalized;
64
+ }
65
+
66
+ transformPoints(gid, glyphPoints) {
67
+ if (!this.font.fvar || !this.font.gvar) { return; }
68
+
69
+ let { gvar } = this.font;
70
+ if (gid >= gvar.glyphCount) { return; }
71
+
72
+ let offset = gvar.offsets[gid];
73
+ if (offset === gvar.offsets[gid + 1]) { return; }
74
+
75
+ // Read the gvar data for this glyph
76
+ let { stream } = this.font;
77
+ stream.pos = offset;
78
+ if (stream.pos >= stream.length) {
79
+ return;
80
+ }
81
+
82
+ let tupleCount = stream.readUInt16BE();
83
+ let offsetToData = offset + stream.readUInt16BE();
84
+
85
+ if (tupleCount & TUPLES_SHARE_POINT_NUMBERS) {
86
+ let here = stream.pos;
87
+ stream.pos = offsetToData;
88
+ var sharedPoints = this.decodePoints();
89
+ offsetToData = stream.pos;
90
+ stream.pos = here;
91
+ }
92
+
93
+ let origPoints = glyphPoints.map(pt => pt.copy());
94
+
95
+ tupleCount &= TUPLE_COUNT_MASK;
96
+ for (let i = 0; i < tupleCount; i++) {
97
+ let tupleDataSize = stream.readUInt16BE();
98
+ let tupleIndex = stream.readUInt16BE();
99
+
100
+ let tupleCoords;
101
+ if (tupleIndex & EMBEDDED_TUPLE_COORD) {
102
+ tupleCoords = [];
103
+ for (let a = 0; a < gvar.axisCount; a++) {
104
+ tupleCoords.push(stream.readInt16BE() / 16384);
105
+ }
106
+
107
+ } else {
108
+ if ((tupleIndex & TUPLE_INDEX_MASK) >= gvar.globalCoordCount) {
109
+ throw new Error('Invalid gvar table');
110
+ }
111
+
112
+ tupleCoords = gvar.globalCoords[tupleIndex & TUPLE_INDEX_MASK];
113
+ }
114
+
115
+ if (tupleIndex & INTERMEDIATE_TUPLE) {
116
+ var startCoords = [];
117
+ for (let a = 0; a < gvar.axisCount; a++) {
118
+ startCoords.push(stream.readInt16BE() / 16384);
119
+ }
120
+
121
+ var endCoords = [];
122
+ for (let a = 0; a < gvar.axisCount; a++) {
123
+ endCoords.push(stream.readInt16BE() / 16384);
124
+ }
125
+ }
126
+
127
+ // Get the factor at which to apply this tuple
128
+ let factor = this.tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords);
129
+ if (factor === 0) {
130
+ offsetToData += tupleDataSize;
131
+ continue;
132
+ }
133
+
134
+ let here = stream.pos;
135
+ stream.pos = offsetToData;
136
+
137
+ let points;
138
+ if (tupleIndex & PRIVATE_POINT_NUMBERS) {
139
+ points = this.decodePoints();
140
+ } else {
141
+ points = sharedPoints;
142
+ }
143
+
144
+ // points.length = 0 means there are deltas for all points
145
+ let nPoints = points.length === 0 ? glyphPoints.length : points.length;
146
+ let xDeltas = this.decodeDeltas(nPoints);
147
+ let yDeltas = this.decodeDeltas(nPoints);
148
+
149
+ if (points.length === 0) { // all points
150
+ for (let i = 0; i < glyphPoints.length; i++) {
151
+ var point = glyphPoints[i];
152
+ point.x += Math.round(xDeltas[i] * factor);
153
+ point.y += Math.round(yDeltas[i] * factor);
154
+ }
155
+ } else {
156
+ let outPoints = origPoints.map(pt => pt.copy());
157
+ let hasDelta = glyphPoints.map(() => false);
158
+
159
+ for (let i = 0; i < points.length; i++) {
160
+ let idx = points[i];
161
+ if (idx < glyphPoints.length) {
162
+ let point = outPoints[idx];
163
+ hasDelta[idx] = true;
164
+
165
+ point.x += xDeltas[i] * factor;
166
+ point.y += yDeltas[i] * factor;
167
+ }
168
+ }
169
+
170
+ this.interpolateMissingDeltas(outPoints, origPoints, hasDelta);
171
+
172
+ for (let i = 0; i < glyphPoints.length; i++) {
173
+ let deltaX = outPoints[i].x - origPoints[i].x;
174
+ let deltaY = outPoints[i].y - origPoints[i].y;
175
+
176
+ glyphPoints[i].x = Math.round(glyphPoints[i].x + deltaX);
177
+ glyphPoints[i].y = Math.round(glyphPoints[i].y + deltaY);
178
+ }
179
+ }
180
+
181
+ offsetToData += tupleDataSize;
182
+ stream.pos = here;
183
+ }
184
+ }
185
+
186
+ decodePoints() {
187
+ let stream = this.font.stream;
188
+ let count = stream.readUInt8();
189
+
190
+ if (count & POINTS_ARE_WORDS) {
191
+ count = (count & POINT_RUN_COUNT_MASK) << 8 | stream.readUInt8();
192
+ }
193
+
194
+ let points = new Uint16Array(count);
195
+ let i = 0;
196
+ let point = 0;
197
+ while (i < count) {
198
+ let run = stream.readUInt8();
199
+ let runCount = (run & POINT_RUN_COUNT_MASK) + 1;
200
+ let fn = run & POINTS_ARE_WORDS ? stream.readUInt16 : stream.readUInt8;
201
+
202
+ for (let j = 0; j < runCount && i < count; j++) {
203
+ point += fn.call(stream);
204
+ points[i++] = point;
205
+ }
206
+ }
207
+
208
+ return points;
209
+ }
210
+
211
+ decodeDeltas(count) {
212
+ let stream = this.font.stream;
213
+ let i = 0;
214
+ let deltas = new Int16Array(count);
215
+
216
+ while (i < count) {
217
+ let run = stream.readUInt8();
218
+ let runCount = (run & DELTA_RUN_COUNT_MASK) + 1;
219
+
220
+ if (run & DELTAS_ARE_ZERO) {
221
+ i += runCount;
222
+
223
+ } else {
224
+ let fn = run & DELTAS_ARE_WORDS ? stream.readInt16BE : stream.readInt8;
225
+ for (let j = 0; j < runCount && i < count; j++) {
226
+ deltas[i++] = fn.call(stream);
227
+ }
228
+ }
229
+ }
230
+
231
+ return deltas;
232
+ }
233
+
234
+ tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords) {
235
+ let normalized = this.normalizedCoords;
236
+ let { gvar } = this.font;
237
+ let factor = 1;
238
+
239
+ for (let i = 0; i < gvar.axisCount; i++) {
240
+ if (tupleCoords[i] === 0) {
241
+ continue;
242
+ }
243
+
244
+ if (normalized[i] === 0) {
245
+ return 0;
246
+ }
247
+
248
+ if ((tupleIndex & INTERMEDIATE_TUPLE) === 0) {
249
+ if ((normalized[i] < Math.min(0, tupleCoords[i])) ||
250
+ (normalized[i] > Math.max(0, tupleCoords[i]))) {
251
+ return 0;
252
+ }
253
+
254
+ factor = (factor * normalized[i] + Number.EPSILON) / (tupleCoords[i] + Number.EPSILON);
255
+ } else {
256
+ if ((normalized[i] < startCoords[i]) ||
257
+ (normalized[i] > endCoords[i])) {
258
+ return 0;
259
+
260
+ } else if (normalized[i] < tupleCoords[i]) {
261
+ factor = factor * (normalized[i] - startCoords[i] + Number.EPSILON) / (tupleCoords[i] - startCoords[i] + Number.EPSILON);
262
+
263
+ } else {
264
+ factor = factor * (endCoords[i] - normalized[i] + Number.EPSILON) / (endCoords[i] - tupleCoords[i] + Number.EPSILON);
265
+ }
266
+ }
267
+ }
268
+
269
+ return factor;
270
+ }
271
+
272
+ // Interpolates points without delta values.
273
+ // Needed for the Ø and Q glyphs in Skia.
274
+ // Algorithm from Freetype.
275
+ interpolateMissingDeltas(points, inPoints, hasDelta) {
276
+ if (points.length === 0) {
277
+ return;
278
+ }
279
+
280
+ let point = 0;
281
+ while (point < points.length) {
282
+ let firstPoint = point;
283
+
284
+ // find the end point of the contour
285
+ let endPoint = point;
286
+ let pt = points[endPoint];
287
+ while (!pt.endContour) {
288
+ pt = points[++endPoint];
289
+ }
290
+
291
+ // find the first point that has a delta
292
+ while (point <= endPoint && !hasDelta[point]) {
293
+ point++;
294
+ }
295
+
296
+ if (point > endPoint) {
297
+ continue;
298
+ }
299
+
300
+ let firstDelta = point;
301
+ let curDelta = point;
302
+ point++;
303
+
304
+ while (point <= endPoint) {
305
+ // find the next point with a delta, and interpolate intermediate points
306
+ if (hasDelta[point]) {
307
+ this.deltaInterpolate(curDelta + 1, point - 1, curDelta, point, inPoints, points);
308
+ curDelta = point;
309
+ }
310
+
311
+ point++;
312
+ }
313
+
314
+ // shift contour if we only have a single delta
315
+ if (curDelta === firstDelta) {
316
+ this.deltaShift(firstPoint, endPoint, curDelta, inPoints, points);
317
+ } else {
318
+ // otherwise, handle the remaining points at the end and beginning of the contour
319
+ this.deltaInterpolate(curDelta + 1, endPoint, curDelta, firstDelta, inPoints, points);
320
+
321
+ if (firstDelta > 0) {
322
+ this.deltaInterpolate(firstPoint, firstDelta - 1, curDelta, firstDelta, inPoints, points);
323
+ }
324
+ }
325
+
326
+ point = endPoint + 1;
327
+ }
328
+ }
329
+
330
+ deltaInterpolate(p1, p2, ref1, ref2, inPoints, outPoints) {
331
+ if (p1 > p2) {
332
+ return;
333
+ }
334
+
335
+ let iterable = ['x', 'y'];
336
+ for (let i = 0; i < iterable.length; i++) {
337
+ let k = iterable[i];
338
+ if (inPoints[ref1][k] > inPoints[ref2][k]) {
339
+ var p = ref1;
340
+ ref1 = ref2;
341
+ ref2 = p;
342
+ }
343
+
344
+ let in1 = inPoints[ref1][k];
345
+ let in2 = inPoints[ref2][k];
346
+ let out1 = outPoints[ref1][k];
347
+ let out2 = outPoints[ref2][k];
348
+
349
+ // If the reference points have the same coordinate but different
350
+ // delta, inferred delta is zero. Otherwise interpolate.
351
+ if (in1 !== in2 || out1 === out2) {
352
+ let scale = in1 === in2 ? 0 : (out2 - out1) / (in2 - in1);
353
+
354
+ for (let p = p1; p <= p2; p++) {
355
+ let out = inPoints[p][k];
356
+
357
+ if (out <= in1) {
358
+ out += out1 - in1;
359
+ } else if (out >= in2) {
360
+ out += out2 - in2;
361
+ } else {
362
+ out = out1 + (out - in1) * scale;
363
+ }
364
+
365
+ outPoints[p][k] = out;
366
+ }
367
+ }
368
+ }
369
+ }
370
+
371
+ deltaShift(p1, p2, ref, inPoints, outPoints) {
372
+ let deltaX = outPoints[ref].x - inPoints[ref].x;
373
+ let deltaY = outPoints[ref].y - inPoints[ref].y;
374
+
375
+ if (deltaX === 0 && deltaY === 0) {
376
+ return;
377
+ }
378
+
379
+ for (let p = p1; p <= p2; p++) {
380
+ if (p !== ref) {
381
+ outPoints[p].x += deltaX;
382
+ outPoints[p].y += deltaY;
383
+ }
384
+ }
385
+ }
386
+
387
+ getAdvanceAdjustment(gid, table) {
388
+ let outerIndex, innerIndex;
389
+
390
+ if (table.advanceWidthMapping) {
391
+ let idx = gid;
392
+ if (idx >= table.advanceWidthMapping.mapCount) {
393
+ idx = table.advanceWidthMapping.mapCount - 1;
394
+ }
395
+
396
+ ({outerIndex, innerIndex} = table.advanceWidthMapping.mapData[idx]);
397
+ } else {
398
+ outerIndex = 0;
399
+ innerIndex = gid;
400
+ }
401
+
402
+ return this.getDelta(table.itemVariationStore, outerIndex, innerIndex);
403
+ }
404
+
405
+ // See pseudo code from `Font Variations Overview'
406
+ // in the OpenType specification.
407
+ getDelta(itemStore, outerIndex, innerIndex) {
408
+ if (outerIndex >= itemStore.itemVariationData.length) {
409
+ return 0;
410
+ }
411
+
412
+ let varData = itemStore.itemVariationData[outerIndex];
413
+ if (innerIndex >= varData.deltaSets.length) {
414
+ return 0;
415
+ }
416
+
417
+ let deltaSet = varData.deltaSets[innerIndex];
418
+ let blendVector = this.getBlendVector(itemStore, outerIndex);
419
+ let netAdjustment = 0;
420
+
421
+ for (let master = 0; master < varData.regionIndexCount; master++) {
422
+ netAdjustment += deltaSet.deltas[master] * blendVector[master];
423
+ }
424
+
425
+ return netAdjustment;
426
+ }
427
+
428
+ getBlendVector(itemStore, outerIndex) {
429
+ let varData = itemStore.itemVariationData[outerIndex];
430
+ if (this.blendVectors.has(varData)) {
431
+ return this.blendVectors.get(varData);
432
+ }
433
+
434
+ let normalizedCoords = this.normalizedCoords;
435
+ let blendVector = [];
436
+
437
+ // outer loop steps through master designs to be blended
438
+ for (let master = 0; master < varData.regionIndexCount; master++) {
439
+ let scalar = 1;
440
+ let regionIndex = varData.regionIndexes[master];
441
+ let axes = itemStore.variationRegionList.variationRegions[regionIndex];
442
+
443
+ // inner loop steps through axes in this region
444
+ for (let j = 0; j < axes.length; j++) {
445
+ let axis = axes[j];
446
+ let axisScalar;
447
+
448
+ // compute the scalar contribution of this axis
449
+ // ignore invalid ranges
450
+ if (axis.startCoord > axis.peakCoord || axis.peakCoord > axis.endCoord) {
451
+ axisScalar = 1;
452
+
453
+ } else if (axis.startCoord < 0 && axis.endCoord > 0 && axis.peakCoord !== 0) {
454
+ axisScalar = 1;
455
+
456
+ // peak of 0 means ignore this axis
457
+ } else if (axis.peakCoord === 0) {
458
+ axisScalar = 1;
459
+
460
+ // ignore this region if coords are out of range
461
+ } else if (normalizedCoords[j] < axis.startCoord || normalizedCoords[j] > axis.endCoord) {
462
+ axisScalar = 0;
463
+
464
+ // calculate a proportional factor
465
+ } else {
466
+ if (normalizedCoords[j] === axis.peakCoord) {
467
+ axisScalar = 1;
468
+ } else if (normalizedCoords[j] < axis.peakCoord) {
469
+ axisScalar = (normalizedCoords[j] - axis.startCoord + Number.EPSILON) /
470
+ (axis.peakCoord - axis.startCoord + Number.EPSILON);
471
+ } else {
472
+ axisScalar = (axis.endCoord - normalizedCoords[j] + Number.EPSILON) /
473
+ (axis.endCoord - axis.peakCoord + Number.EPSILON);
474
+ }
475
+ }
476
+
477
+ // take product of all the axis scalars
478
+ scalar *= axisScalar;
479
+ }
480
+
481
+ blendVector[master] = scalar;
482
+ }
483
+
484
+ this.blendVectors.set(varData, blendVector);
485
+ return blendVector;
486
+ }
487
+ }