@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.
- package/dist/browser-module.mjs +4423 -1117
- package/dist/browser-module.mjs.map +1 -1
- package/dist/browser.cjs +4424 -1117
- package/dist/browser.cjs.map +1 -1
- package/dist/main.cjs +4424 -1117
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +4423 -1117
- package/dist/module.mjs.map +1 -1
- package/package.json +3 -1
- package/src/CmapProcessor.js +59 -20
- package/src/DFont.js +114 -14
- package/src/TTFFont.js +259 -57
- package/src/TrueTypeCollection.js +54 -10
- package/src/WOFF2Font.js +128 -16
- package/src/WOFFFont.js +45 -8
- package/src/aat/AATFeatureMap.js +62 -18
- package/src/aat/AATLayoutEngine.js +33 -1
- package/src/aat/AATLookupTable.js +22 -4
- package/src/aat/AATMorxProcessor.js +189 -52
- package/src/aat/AATStateMachine.js +32 -0
- package/src/base.js +19 -0
- package/src/cff/CFFCharsets.js +3 -0
- package/src/cff/CFFDict.js +79 -9
- package/src/cff/CFFEncodings.js +2 -0
- package/src/cff/CFFFont.js +119 -23
- package/src/cff/CFFIndex.js +62 -5
- package/src/cff/CFFOperand.js +52 -17
- package/src/cff/CFFPointer.js +41 -2
- package/src/cff/CFFPrivateDict.js +14 -4
- package/src/cff/CFFStandardStrings.js +1 -0
- package/src/cff/CFFTop.js +103 -10
- package/src/decorators.js +32 -13
- package/src/encodings.js +22 -5
- package/src/fs.js +10 -0
- package/src/glyph/BBox.js +14 -0
- package/src/glyph/CFFGlyph.js +290 -148
- package/src/glyph/COLRGlyph.js +38 -3
- package/src/glyph/Glyph.js +68 -9
- package/src/glyph/GlyphVariationProcessor.js +135 -34
- package/src/glyph/Path.js +107 -20
- package/src/glyph/SBIXGlyph.js +57 -8
- package/src/glyph/StandardNames.js +1 -0
- package/src/glyph/TTFGlyph.js +176 -29
- package/src/glyph/TTFGlyphEncoder.js +32 -0
- package/src/glyph/WOFF2Glyph.js +23 -4
- package/src/layout/GlyphPosition.js +6 -0
- package/src/layout/GlyphRun.js +25 -7
- package/src/layout/KernProcessor.js +62 -3
- package/src/layout/LayoutEngine.js +86 -12
- package/src/layout/Script.js +27 -1
- package/src/layout/UnicodeLayoutEngine.js +29 -4
- package/src/opentype/GPOSProcessor.js +156 -45
- package/src/opentype/GSUBProcessor.js +92 -33
- package/src/opentype/GlyphInfo.js +52 -4
- package/src/opentype/GlyphIterator.js +51 -2
- package/src/opentype/OTLayoutEngine.js +66 -8
- package/src/opentype/OTProcessor.js +283 -70
- package/src/opentype/ShapingPlan.js +51 -3
- package/src/opentype/shapers/ArabicShaper.js +19 -0
- package/src/opentype/shapers/DefaultShaper.js +51 -7
- package/src/opentype/shapers/HangulShaper.js +85 -7
- package/src/opentype/shapers/HebrewShaper.js +45 -11
- package/src/opentype/shapers/IndicShaper.js +141 -26
- package/src/opentype/shapers/ThaiShaper.js +35 -4
- package/src/opentype/shapers/UniversalShaper.js +83 -17
- package/src/opentype/shapers/index.js +15 -2
- package/src/packages/unicode-properties/data-trie.js +1 -0
- package/src/packages/unicode-properties/data.js +12 -0
- package/src/packages/unicode-properties/dist/index.cjs +22 -9
- package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
- package/src/packages/unicode-properties/dist/index.mjs +21 -9
- package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
- package/src/packages/unicode-properties/index.js +65 -0
- package/src/packages/unicode-properties/package.json +1 -1
- package/src/packages/unicode-trie/builder.js +128 -7
- package/src/packages/unicode-trie/dist/builder.cjs +129 -16
- package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/builder.mjs +128 -16
- package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
- package/src/packages/unicode-trie/dist/index.cjs +22 -9
- package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/index.mjs +21 -9
- package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
- package/src/packages/unicode-trie/index.js +62 -10
- package/src/packages/unicode-trie/package.json +1 -1
- package/src/packages/unicode-trie/swap.js +13 -0
- package/src/subset/CFFSubset.js +112 -22
- package/src/subset/Subset.js +21 -1
- package/src/subset/TTFSubset.js +79 -36
- package/src/tables/BASE.js +1 -0
- package/src/tables/COLR.js +1 -0
- package/src/tables/CPAL.js +1 -0
- package/src/tables/DSIG.js +1 -0
- package/src/tables/EBDT.js +10 -3
- package/src/tables/EBLC.js +67 -7
- package/src/tables/GDEF.js +1 -0
- package/src/tables/GPOS.js +79 -16
- package/src/tables/GSUB.js +14 -3
- package/src/tables/HVAR.js +51 -5
- package/src/tables/JSTF.js +3 -2
- package/src/tables/LTSH.js +1 -0
- package/src/tables/OS2.js +1 -0
- package/src/tables/PCLT.js +1 -0
- package/src/tables/STAT.js +46 -6
- package/src/tables/VDMX.js +1 -0
- package/src/tables/VORG.js +1 -0
- package/src/tables/WOFF2Directory.js +56 -8
- package/src/tables/WOFFDirectory.js +14 -2
- package/src/tables/aat.js +160 -35
- package/src/tables/avar.js +1 -0
- package/src/tables/bsln.js +3 -2
- package/src/tables/cmap.js +26 -6
- package/src/tables/cvt.js +1 -0
- package/src/tables/directory.js +44 -16
- package/src/tables/feat.js +24 -2
- package/src/tables/fpgm.js +1 -0
- package/src/tables/fvar.js +32 -4
- package/src/tables/gasp.js +1 -0
- package/src/tables/glyf.js +1 -0
- package/src/tables/gvar.js +29 -2
- package/src/tables/hdmx.js +12 -1
- package/src/tables/head.js +1 -0
- package/src/tables/hhea.js +1 -0
- package/src/tables/hmtx.js +18 -2
- package/src/tables/index.js +1 -0
- package/src/tables/just.js +15 -4
- package/src/tables/kern.js +43 -6
- package/src/tables/loca.js +21 -8
- package/src/tables/maxp.js +1 -0
- package/src/tables/morx.js +14 -7
- package/src/tables/name.js +104 -23
- package/src/tables/opbd.js +2 -1
- package/src/tables/opentype.js +39 -4
- package/src/tables/post.js +8 -1
- package/src/tables/prep.js +1 -0
- package/src/tables/sbix.js +12 -1
- package/src/tables/variations.js +23 -3
- package/src/tables/vhea.js +1 -0
- package/src/tables/vmtx.js +18 -2
- package/src/utils.js +15 -0
package/src/glyph/COLRGlyph.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import Glyph from './Glyph';
|
|
2
2
|
import BBox from './BBox';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('../../types/fontkit').CPALColor} CPALColor */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').GlyphLike} GlyphLike */
|
|
6
|
+
/** @typedef {import('../../types/fontkit').PathRenderingContext} PathRenderingContext */
|
|
7
|
+
|
|
4
8
|
class COLRLayer {
|
|
9
|
+
/**
|
|
10
|
+
* @param {GlyphLike | null | undefined} glyph
|
|
11
|
+
* @param {CPALColor} color
|
|
12
|
+
*/
|
|
5
13
|
constructor(glyph, color) {
|
|
14
|
+
/** @type {GlyphLike | null | undefined} */
|
|
6
15
|
this.glyph = glyph;
|
|
16
|
+
/** @type {CPALColor} */
|
|
7
17
|
this.color = color;
|
|
8
18
|
}
|
|
9
19
|
}
|
|
@@ -16,16 +26,22 @@ class COLRLayer {
|
|
|
16
26
|
export default class COLRGlyph extends Glyph {
|
|
17
27
|
type = 'COLR';
|
|
18
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @returns {BBox}
|
|
31
|
+
*/
|
|
19
32
|
_getBBox() {
|
|
20
33
|
let layers = this.layers;
|
|
21
34
|
if (!layers) {
|
|
22
35
|
let g = this._font._getBaseGlyph(this.id);
|
|
23
|
-
return g ? g.bbox : new BBox(0, 0, 0, 0);
|
|
36
|
+
return g ? new BBox(g.bbox.minX, g.bbox.minY, g.bbox.maxX, g.bbox.maxY) : new BBox(0, 0, 0, 0);
|
|
24
37
|
}
|
|
25
38
|
|
|
26
39
|
let bbox = new BBox();
|
|
27
40
|
for (let i = 0; i < layers.length; i++) {
|
|
28
41
|
let layer = layers[i];
|
|
42
|
+
if (!layer.glyph) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
29
45
|
let b = layer.glyph.bbox;
|
|
30
46
|
bbox.addPoint(b.minX, b.minY);
|
|
31
47
|
bbox.addPoint(b.maxX, b.maxY);
|
|
@@ -37,7 +53,7 @@ export default class COLRGlyph extends Glyph {
|
|
|
37
53
|
/**
|
|
38
54
|
* Returns an array of objects containing the glyph and color for
|
|
39
55
|
* each layer in the composite color glyph.
|
|
40
|
-
* @type {
|
|
56
|
+
* @type {COLRLayer[] | null}
|
|
41
57
|
*/
|
|
42
58
|
get layers() {
|
|
43
59
|
let cpal = this._font.CPAL;
|
|
@@ -50,6 +66,7 @@ export default class COLRGlyph extends Glyph {
|
|
|
50
66
|
|
|
51
67
|
let low = 0;
|
|
52
68
|
let high = colr.baseGlyphRecord.length - 1;
|
|
69
|
+
/** @type {import('../../types/fontkit').COLRBaseGlyphRecord | undefined} */
|
|
53
70
|
let baseLayer;
|
|
54
71
|
|
|
55
72
|
while (low <= high) {
|
|
@@ -70,6 +87,7 @@ export default class COLRGlyph extends Glyph {
|
|
|
70
87
|
// default to normal glyph from glyf or CFF
|
|
71
88
|
if (baseLayer == null) {
|
|
72
89
|
let g = this._font._getBaseGlyph(this.id);
|
|
90
|
+
/** @type {CPALColor} */
|
|
73
91
|
let color = {
|
|
74
92
|
red: 0,
|
|
75
93
|
green: 0,
|
|
@@ -81,10 +99,17 @@ export default class COLRGlyph extends Glyph {
|
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
// otherwise, return an array of all the layers
|
|
102
|
+
/** @type {COLRLayer[]} */
|
|
84
103
|
let layers = [];
|
|
85
104
|
for (let i = baseLayer.firstLayerIndex; i < baseLayer.firstLayerIndex + baseLayer.numLayers; i++) {
|
|
86
105
|
let rec = colr.layerRecords[i];
|
|
106
|
+
if (!cpal || !rec) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
87
109
|
let color = cpal.colorRecords[rec.paletteIndex];
|
|
110
|
+
if (!color) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
88
113
|
let g = this._font._getBaseGlyph(rec.gid);
|
|
89
114
|
layers.push(new COLRLayer(g, color));
|
|
90
115
|
}
|
|
@@ -92,6 +117,11 @@ export default class COLRGlyph extends Glyph {
|
|
|
92
117
|
return layers;
|
|
93
118
|
}
|
|
94
119
|
|
|
120
|
+
/**
|
|
121
|
+
* @param {PathRenderingContext} ctx
|
|
122
|
+
* @param {number} size
|
|
123
|
+
* @returns {void}
|
|
124
|
+
*/
|
|
95
125
|
render(ctx, size) {
|
|
96
126
|
let layers = this.layers;
|
|
97
127
|
if (!layers) {
|
|
@@ -99,7 +129,12 @@ export default class COLRGlyph extends Glyph {
|
|
|
99
129
|
}
|
|
100
130
|
|
|
101
131
|
for (let { glyph, color } of layers) {
|
|
102
|
-
|
|
132
|
+
if (!glyph) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (typeof ctx.fillColor === 'function') {
|
|
136
|
+
ctx.fillColor([color.red, color.green, color.blue], color.alpha / 255 * 100);
|
|
137
|
+
}
|
|
103
138
|
glyph.render(ctx, size);
|
|
104
139
|
}
|
|
105
140
|
|
package/src/glyph/Glyph.js
CHANGED
|
@@ -3,6 +3,12 @@ import Path from './Path';
|
|
|
3
3
|
import { isMark } from '../packages/unicode-properties/index.js';
|
|
4
4
|
import StandardNames from './StandardNames';
|
|
5
5
|
|
|
6
|
+
/** @typedef {import('../../types/fontkit').FontLike} FontLike */
|
|
7
|
+
/** @typedef {import('../../types/fontkit').GlyphMetrics} GlyphMetrics */
|
|
8
|
+
/** @typedef {import('../../types/fontkit').MetricsTable} MetricsTable */
|
|
9
|
+
/** @typedef {import('../../types/fontkit').PathRenderingContext} PathRenderingContext */
|
|
10
|
+
/** @typedef {import('./BBox').default} BBox */
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* Glyph objects represent a glyph in the font. They have various properties for accessing metrics and
|
|
8
14
|
* the actual vector path the glyph represents, and methods for rendering the glyph to a graphics context.
|
|
@@ -12,6 +18,11 @@ import StandardNames from './StandardNames';
|
|
|
12
18
|
* on the font format, but they all inherit from this class.
|
|
13
19
|
*/
|
|
14
20
|
export default class Glyph {
|
|
21
|
+
/**
|
|
22
|
+
* @param {number} id
|
|
23
|
+
* @param {number[]} codePoints
|
|
24
|
+
* @param {FontLike} font
|
|
25
|
+
*/
|
|
15
26
|
constructor(id, codePoints, font) {
|
|
16
27
|
/**
|
|
17
28
|
* The glyph id in the font
|
|
@@ -26,28 +37,50 @@ export default class Glyph {
|
|
|
26
37
|
* @type {number[]}
|
|
27
38
|
*/
|
|
28
39
|
this.codePoints = codePoints;
|
|
40
|
+
|
|
41
|
+
/** @type {FontLike} */
|
|
29
42
|
this._font = font;
|
|
30
43
|
|
|
44
|
+
/** @type {GlyphMetrics | undefined} */
|
|
45
|
+
this._metrics = undefined;
|
|
46
|
+
|
|
31
47
|
// TODO: get this info from GDEF if available
|
|
32
48
|
this.isMark = this.codePoints.length > 0 && this.codePoints.every(isMark);
|
|
33
49
|
this.isLigature = this.codePoints.length > 1;
|
|
34
50
|
}
|
|
35
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @returns {Path}
|
|
54
|
+
*/
|
|
36
55
|
_getPath() {
|
|
37
56
|
return new Path();
|
|
38
57
|
}
|
|
39
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @returns {BBox}
|
|
61
|
+
*/
|
|
40
62
|
_getCBox() {
|
|
41
63
|
return this.path.cbox;
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @returns {BBox}
|
|
68
|
+
*/
|
|
44
69
|
_getBBox() {
|
|
45
70
|
return this.path.bbox;
|
|
46
71
|
}
|
|
47
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @param {MetricsTable} table
|
|
75
|
+
* @returns {{ advance: number, bearing: number }}
|
|
76
|
+
*/
|
|
48
77
|
_getTableMetrics(table) {
|
|
49
78
|
if (this.id < table.metrics.length) {
|
|
50
|
-
|
|
79
|
+
let metric = table.metrics.get(this.id);
|
|
80
|
+
return {
|
|
81
|
+
advance: metric ? metric.advance : 0,
|
|
82
|
+
bearing: metric ? metric.bearing : 0
|
|
83
|
+
};
|
|
51
84
|
}
|
|
52
85
|
|
|
53
86
|
let metric = table.metrics.get(table.metrics.length - 1);
|
|
@@ -59,6 +92,10 @@ export default class Glyph {
|
|
|
59
92
|
return res;
|
|
60
93
|
}
|
|
61
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {BBox} [cbox]
|
|
97
|
+
* @returns {GlyphMetrics}
|
|
98
|
+
*/
|
|
62
99
|
_getMetrics(cbox) {
|
|
63
100
|
if (this._metrics) { return this._metrics; }
|
|
64
101
|
if (cbox == null) { ({ cbox } = this); }
|
|
@@ -210,10 +247,16 @@ export default class Glyph {
|
|
|
210
247
|
return this._getMetrics().bottomBearing;
|
|
211
248
|
}
|
|
212
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @returns {null}
|
|
252
|
+
*/
|
|
213
253
|
get ligatureCaretPositions() {
|
|
214
254
|
return null;
|
|
215
255
|
}
|
|
216
256
|
|
|
257
|
+
/**
|
|
258
|
+
* @returns {string | null | undefined}
|
|
259
|
+
*/
|
|
217
260
|
_getName() {
|
|
218
261
|
let { post } = this._font;
|
|
219
262
|
if (!post) {
|
|
@@ -225,25 +268,40 @@ export default class Glyph {
|
|
|
225
268
|
return StandardNames[this.id];
|
|
226
269
|
|
|
227
270
|
case 2: {
|
|
228
|
-
let id = post.glyphNameIndex[this.id];
|
|
271
|
+
let id = post.glyphNameIndex?.[this.id];
|
|
272
|
+
if (id == null) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
229
275
|
if (id < StandardNames.length) {
|
|
230
276
|
return StandardNames[id];
|
|
231
277
|
}
|
|
232
278
|
|
|
233
|
-
return post.names[id - StandardNames.length];
|
|
279
|
+
return post.names?.[id - StandardNames.length];
|
|
234
280
|
}
|
|
235
281
|
|
|
236
|
-
case 2.5:
|
|
237
|
-
|
|
282
|
+
case 2.5: {
|
|
283
|
+
let offset = post.offsets?.[this.id];
|
|
284
|
+
if (offset == null) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
return StandardNames[this.id + offset];
|
|
288
|
+
}
|
|
238
289
|
|
|
239
|
-
case 4:
|
|
240
|
-
|
|
290
|
+
case 4: {
|
|
291
|
+
let mapped = post.map?.[this.id];
|
|
292
|
+
if (mapped == null) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
return String.fromCharCode(mapped);
|
|
296
|
+
}
|
|
241
297
|
}
|
|
298
|
+
|
|
299
|
+
return null;
|
|
242
300
|
}
|
|
243
301
|
|
|
244
302
|
/**
|
|
245
303
|
* The glyph's name
|
|
246
|
-
* @type {string}
|
|
304
|
+
* @type {string | null | undefined}
|
|
247
305
|
*/
|
|
248
306
|
@cache
|
|
249
307
|
get name() {
|
|
@@ -252,8 +310,9 @@ export default class Glyph {
|
|
|
252
310
|
|
|
253
311
|
/**
|
|
254
312
|
* Renders the glyph to the given graphics context, at the specified font size.
|
|
255
|
-
* @param {
|
|
313
|
+
* @param {PathRenderingContext} ctx
|
|
256
314
|
* @param {number} size
|
|
315
|
+
* @returns {void}
|
|
257
316
|
*/
|
|
258
317
|
render(ctx, size) {
|
|
259
318
|
ctx.save();
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** @typedef {import('../../types/fontkit').FontLike} FontLike */
|
|
2
|
+
/** @typedef {import('../../types/fontkit').GlyphPoint} GlyphPoint */
|
|
3
|
+
/** @typedef {import('../../types/fontkit').HVARTable} HVARTable */
|
|
4
|
+
/** @typedef {import('../../types/fontkit').ItemVariationStore} ItemVariationStore */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').ItemVariationData} ItemVariationData */
|
|
6
|
+
|
|
1
7
|
const TUPLES_SHARE_POINT_NUMBERS = 0x8000;
|
|
2
8
|
const TUPLE_COUNT_MASK = 0x0fff;
|
|
3
9
|
const EMBEDDED_TUPLE_COORD = 0x8000;
|
|
@@ -22,18 +28,35 @@ const DELTA_RUN_COUNT_MASK = 0x3f;
|
|
|
22
28
|
* @private
|
|
23
29
|
*/
|
|
24
30
|
export default class GlyphVariationProcessor {
|
|
31
|
+
/**
|
|
32
|
+
* @param {FontLike} font
|
|
33
|
+
* @param {number[]} coords
|
|
34
|
+
*/
|
|
25
35
|
constructor(font, coords) {
|
|
36
|
+
/** @type {FontLike} */
|
|
26
37
|
this.font = font;
|
|
38
|
+
/** @type {number[]} */
|
|
27
39
|
this.normalizedCoords = this.normalizeCoords(coords);
|
|
40
|
+
/** @type {Map<ItemVariationData, number[]>} */
|
|
28
41
|
this.blendVectors = new Map();
|
|
29
42
|
}
|
|
30
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @param {number[]} coords
|
|
46
|
+
* @returns {number[]}
|
|
47
|
+
*/
|
|
31
48
|
normalizeCoords(coords) {
|
|
32
49
|
// the default mapping is linear along each axis, in two segments:
|
|
33
50
|
// from the minValue to defaultValue, and from defaultValue to maxValue.
|
|
51
|
+
/** @type {number[]} */
|
|
34
52
|
let normalized = [];
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
let fvar = this.font.fvar;
|
|
54
|
+
if (!fvar) {
|
|
55
|
+
return normalized;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < fvar.axis.length; i++) {
|
|
59
|
+
let axis = fvar.axis[i];
|
|
37
60
|
if (coords[i] < axis.defaultValue) {
|
|
38
61
|
normalized.push((coords[i] - axis.defaultValue + Number.EPSILON) / (axis.defaultValue - axis.minValue + Number.EPSILON));
|
|
39
62
|
} else {
|
|
@@ -63,6 +86,11 @@ export default class GlyphVariationProcessor {
|
|
|
63
86
|
return normalized;
|
|
64
87
|
}
|
|
65
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @param {number} gid
|
|
91
|
+
* @param {GlyphPoint[]} glyphPoints
|
|
92
|
+
* @returns {void}
|
|
93
|
+
*/
|
|
66
94
|
transformPoints(gid, glyphPoints) {
|
|
67
95
|
if (!this.font.fvar || !this.font.gvar) { return; }
|
|
68
96
|
|
|
@@ -82,10 +110,12 @@ export default class GlyphVariationProcessor {
|
|
|
82
110
|
let tupleCount = stream.readUInt16BE();
|
|
83
111
|
let offsetToData = offset + stream.readUInt16BE();
|
|
84
112
|
|
|
113
|
+
/** @type {Uint16Array} */
|
|
114
|
+
let sharedPoints = new Uint16Array(0);
|
|
85
115
|
if (tupleCount & TUPLES_SHARE_POINT_NUMBERS) {
|
|
86
116
|
let here = stream.pos;
|
|
87
117
|
stream.pos = offsetToData;
|
|
88
|
-
|
|
118
|
+
sharedPoints = this.decodePoints();
|
|
89
119
|
offsetToData = stream.pos;
|
|
90
120
|
stream.pos = here;
|
|
91
121
|
}
|
|
@@ -97,6 +127,7 @@ export default class GlyphVariationProcessor {
|
|
|
97
127
|
let tupleDataSize = stream.readUInt16BE();
|
|
98
128
|
let tupleIndex = stream.readUInt16BE();
|
|
99
129
|
|
|
130
|
+
/** @type {number[]} */
|
|
100
131
|
let tupleCoords;
|
|
101
132
|
if (tupleIndex & EMBEDDED_TUPLE_COORD) {
|
|
102
133
|
tupleCoords = [];
|
|
@@ -111,13 +142,17 @@ export default class GlyphVariationProcessor {
|
|
|
111
142
|
tupleCoords = gvar.globalCoords[tupleIndex & TUPLE_INDEX_MASK];
|
|
112
143
|
}
|
|
113
144
|
|
|
145
|
+
/** @type {number[] | undefined} */
|
|
146
|
+
let startCoords;
|
|
147
|
+
/** @type {number[] | undefined} */
|
|
148
|
+
let endCoords;
|
|
114
149
|
if (tupleIndex & INTERMEDIATE_TUPLE) {
|
|
115
|
-
|
|
150
|
+
startCoords = [];
|
|
116
151
|
for (let a = 0; a < gvar.axisCount; a++) {
|
|
117
152
|
startCoords.push(stream.readInt16BE() / 16384);
|
|
118
153
|
}
|
|
119
154
|
|
|
120
|
-
|
|
155
|
+
endCoords = [];
|
|
121
156
|
for (let a = 0; a < gvar.axisCount; a++) {
|
|
122
157
|
endCoords.push(stream.readInt16BE() / 16384);
|
|
123
158
|
}
|
|
@@ -133,6 +168,7 @@ export default class GlyphVariationProcessor {
|
|
|
133
168
|
let here = stream.pos;
|
|
134
169
|
stream.pos = offsetToData;
|
|
135
170
|
|
|
171
|
+
/** @type {Uint16Array} */
|
|
136
172
|
let points;
|
|
137
173
|
if (tupleIndex & PRIVATE_POINT_NUMBERS) {
|
|
138
174
|
points = this.decodePoints();
|
|
@@ -146,34 +182,34 @@ export default class GlyphVariationProcessor {
|
|
|
146
182
|
let yDeltas = this.decodeDeltas(nPoints);
|
|
147
183
|
|
|
148
184
|
if (points.length === 0) { // all points
|
|
149
|
-
for (let
|
|
150
|
-
|
|
151
|
-
point.x += Math.round(xDeltas[
|
|
152
|
-
point.y += Math.round(yDeltas[
|
|
185
|
+
for (let pi = 0; pi < glyphPoints.length; pi++) {
|
|
186
|
+
let point = glyphPoints[pi];
|
|
187
|
+
point.x += Math.round(xDeltas[pi] * factor);
|
|
188
|
+
point.y += Math.round(yDeltas[pi] * factor);
|
|
153
189
|
}
|
|
154
190
|
} else {
|
|
155
191
|
let outPoints = origPoints.map(pt => pt.copy());
|
|
156
192
|
let hasDelta = glyphPoints.map(() => false);
|
|
157
193
|
|
|
158
|
-
for (let
|
|
159
|
-
let idx = points[
|
|
194
|
+
for (let pi = 0; pi < points.length; pi++) {
|
|
195
|
+
let idx = points[pi];
|
|
160
196
|
if (idx < glyphPoints.length) {
|
|
161
197
|
let point = outPoints[idx];
|
|
162
198
|
hasDelta[idx] = true;
|
|
163
199
|
|
|
164
|
-
point.x += xDeltas[
|
|
165
|
-
point.y += yDeltas[
|
|
200
|
+
point.x += xDeltas[pi] * factor;
|
|
201
|
+
point.y += yDeltas[pi] * factor;
|
|
166
202
|
}
|
|
167
203
|
}
|
|
168
204
|
|
|
169
205
|
this.interpolateMissingDeltas(outPoints, origPoints, hasDelta);
|
|
170
206
|
|
|
171
|
-
for (let
|
|
172
|
-
let deltaX = outPoints[
|
|
173
|
-
let deltaY = outPoints[
|
|
207
|
+
for (let pi = 0; pi < glyphPoints.length; pi++) {
|
|
208
|
+
let deltaX = outPoints[pi].x - origPoints[pi].x;
|
|
209
|
+
let deltaY = outPoints[pi].y - origPoints[pi].y;
|
|
174
210
|
|
|
175
|
-
glyphPoints[
|
|
176
|
-
glyphPoints[
|
|
211
|
+
glyphPoints[pi].x = Math.round(glyphPoints[pi].x + deltaX);
|
|
212
|
+
glyphPoints[pi].y = Math.round(glyphPoints[pi].y + deltaY);
|
|
177
213
|
}
|
|
178
214
|
}
|
|
179
215
|
|
|
@@ -182,6 +218,9 @@ export default class GlyphVariationProcessor {
|
|
|
182
218
|
}
|
|
183
219
|
}
|
|
184
220
|
|
|
221
|
+
/**
|
|
222
|
+
* @returns {Uint16Array}
|
|
223
|
+
*/
|
|
185
224
|
decodePoints() {
|
|
186
225
|
let stream = this.font.stream;
|
|
187
226
|
let count = stream.readUInt8();
|
|
@@ -196,7 +235,8 @@ export default class GlyphVariationProcessor {
|
|
|
196
235
|
while (i < count) {
|
|
197
236
|
let run = stream.readUInt8();
|
|
198
237
|
let runCount = (run & POINT_RUN_COUNT_MASK) + 1;
|
|
199
|
-
|
|
238
|
+
// gvar point numbers are big-endian (OpenType); was incorrectly readUInt16.
|
|
239
|
+
let fn = run & POINTS_ARE_WORDS ? stream.readUInt16BE : stream.readUInt8;
|
|
200
240
|
|
|
201
241
|
for (let j = 0; j < runCount && i < count; j++) {
|
|
202
242
|
point += fn.call(stream);
|
|
@@ -207,6 +247,10 @@ export default class GlyphVariationProcessor {
|
|
|
207
247
|
return points;
|
|
208
248
|
}
|
|
209
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @param {number} count
|
|
252
|
+
* @returns {Int16Array}
|
|
253
|
+
*/
|
|
210
254
|
decodeDeltas(count) {
|
|
211
255
|
let stream = this.font.stream;
|
|
212
256
|
let i = 0;
|
|
@@ -229,9 +273,19 @@ export default class GlyphVariationProcessor {
|
|
|
229
273
|
return deltas;
|
|
230
274
|
}
|
|
231
275
|
|
|
276
|
+
/**
|
|
277
|
+
* @param {number} tupleIndex
|
|
278
|
+
* @param {number[]} tupleCoords
|
|
279
|
+
* @param {number[] | undefined} startCoords
|
|
280
|
+
* @param {number[] | undefined} endCoords
|
|
281
|
+
* @returns {number}
|
|
282
|
+
*/
|
|
232
283
|
tupleFactor(tupleIndex, tupleCoords, startCoords, endCoords) {
|
|
233
284
|
let normalized = this.normalizedCoords;
|
|
234
285
|
let { gvar } = this.font;
|
|
286
|
+
if (!gvar) {
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
235
289
|
let factor = 1;
|
|
236
290
|
|
|
237
291
|
for (let i = 0; i < gvar.axisCount; i++) {
|
|
@@ -251,6 +305,9 @@ export default class GlyphVariationProcessor {
|
|
|
251
305
|
|
|
252
306
|
factor = (factor * normalized[i] + Number.EPSILON) / (tupleCoords[i] + Number.EPSILON);
|
|
253
307
|
} else {
|
|
308
|
+
if (!startCoords || !endCoords) {
|
|
309
|
+
return 0;
|
|
310
|
+
}
|
|
254
311
|
if ((normalized[i] < startCoords[i])
|
|
255
312
|
|| (normalized[i] > endCoords[i])) {
|
|
256
313
|
return 0;
|
|
@@ -265,9 +322,15 @@ export default class GlyphVariationProcessor {
|
|
|
265
322
|
return factor;
|
|
266
323
|
}
|
|
267
324
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
325
|
+
/**
|
|
326
|
+
* Interpolates points without delta values.
|
|
327
|
+
* Needed for the Ø and Q glyphs in Skia.
|
|
328
|
+
* Algorithm from Freetype.
|
|
329
|
+
* @param {GlyphPoint[]} points
|
|
330
|
+
* @param {GlyphPoint[]} inPoints
|
|
331
|
+
* @param {boolean[]} hasDelta
|
|
332
|
+
* @returns {void}
|
|
333
|
+
*/
|
|
271
334
|
interpolateMissingDeltas(points, inPoints, hasDelta) {
|
|
272
335
|
if (points.length === 0) {
|
|
273
336
|
return;
|
|
@@ -323,24 +386,36 @@ export default class GlyphVariationProcessor {
|
|
|
323
386
|
}
|
|
324
387
|
}
|
|
325
388
|
|
|
389
|
+
/**
|
|
390
|
+
* @param {number} p1
|
|
391
|
+
* @param {number} p2
|
|
392
|
+
* @param {number} ref1
|
|
393
|
+
* @param {number} ref2
|
|
394
|
+
* @param {GlyphPoint[]} inPoints
|
|
395
|
+
* @param {GlyphPoint[]} outPoints
|
|
396
|
+
* @returns {void}
|
|
397
|
+
*/
|
|
326
398
|
deltaInterpolate(p1, p2, ref1, ref2, inPoints, outPoints) {
|
|
327
399
|
if (p1 > p2) {
|
|
328
400
|
return;
|
|
329
401
|
}
|
|
330
402
|
|
|
403
|
+
/** @type {Array<'x' | 'y'>} */
|
|
331
404
|
let iterable = ['x', 'y'];
|
|
332
405
|
for (let i = 0; i < iterable.length; i++) {
|
|
333
406
|
let k = iterable[i];
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
407
|
+
let r1 = ref1;
|
|
408
|
+
let r2 = ref2;
|
|
409
|
+
if (inPoints[r1][k] > inPoints[r2][k]) {
|
|
410
|
+
let p = r1;
|
|
411
|
+
r1 = r2;
|
|
412
|
+
r2 = p;
|
|
338
413
|
}
|
|
339
414
|
|
|
340
|
-
let in1 = inPoints[
|
|
341
|
-
let in2 = inPoints[
|
|
342
|
-
let out1 = outPoints[
|
|
343
|
-
let out2 = outPoints[
|
|
415
|
+
let in1 = inPoints[r1][k];
|
|
416
|
+
let in2 = inPoints[r2][k];
|
|
417
|
+
let out1 = outPoints[r1][k];
|
|
418
|
+
let out2 = outPoints[r2][k];
|
|
344
419
|
|
|
345
420
|
// If the reference points have the same coordinate but different
|
|
346
421
|
// delta, inferred delta is zero. Otherwise interpolate.
|
|
@@ -364,6 +439,14 @@ export default class GlyphVariationProcessor {
|
|
|
364
439
|
}
|
|
365
440
|
}
|
|
366
441
|
|
|
442
|
+
/**
|
|
443
|
+
* @param {number} p1
|
|
444
|
+
* @param {number} p2
|
|
445
|
+
* @param {number} ref
|
|
446
|
+
* @param {GlyphPoint[]} inPoints
|
|
447
|
+
* @param {GlyphPoint[]} outPoints
|
|
448
|
+
* @returns {void}
|
|
449
|
+
*/
|
|
367
450
|
deltaShift(p1, p2, ref, inPoints, outPoints) {
|
|
368
451
|
let deltaX = outPoints[ref].x - inPoints[ref].x;
|
|
369
452
|
let deltaY = outPoints[ref].y - inPoints[ref].y;
|
|
@@ -380,6 +463,11 @@ export default class GlyphVariationProcessor {
|
|
|
380
463
|
}
|
|
381
464
|
}
|
|
382
465
|
|
|
466
|
+
/**
|
|
467
|
+
* @param {number} gid
|
|
468
|
+
* @param {HVARTable} table
|
|
469
|
+
* @returns {number}
|
|
470
|
+
*/
|
|
383
471
|
getAdvanceAdjustment(gid, table) {
|
|
384
472
|
let outerIndex, innerIndex;
|
|
385
473
|
|
|
@@ -398,8 +486,14 @@ export default class GlyphVariationProcessor {
|
|
|
398
486
|
return this.getDelta(table.itemVariationStore, outerIndex, innerIndex);
|
|
399
487
|
}
|
|
400
488
|
|
|
401
|
-
|
|
402
|
-
|
|
489
|
+
/**
|
|
490
|
+
* See pseudo code from `Font Variations Overview'
|
|
491
|
+
* in the OpenType specification.
|
|
492
|
+
* @param {ItemVariationStore} itemStore
|
|
493
|
+
* @param {number} outerIndex
|
|
494
|
+
* @param {number} innerIndex
|
|
495
|
+
* @returns {number}
|
|
496
|
+
*/
|
|
403
497
|
getDelta(itemStore, outerIndex, innerIndex) {
|
|
404
498
|
if (outerIndex >= itemStore.itemVariationData.length) {
|
|
405
499
|
return 0;
|
|
@@ -421,13 +515,20 @@ export default class GlyphVariationProcessor {
|
|
|
421
515
|
return netAdjustment;
|
|
422
516
|
}
|
|
423
517
|
|
|
518
|
+
/**
|
|
519
|
+
* @param {ItemVariationStore} itemStore
|
|
520
|
+
* @param {number} outerIndex
|
|
521
|
+
* @returns {number[]}
|
|
522
|
+
*/
|
|
424
523
|
getBlendVector(itemStore, outerIndex) {
|
|
425
524
|
let varData = itemStore.itemVariationData[outerIndex];
|
|
426
|
-
|
|
427
|
-
|
|
525
|
+
let cached = this.blendVectors.get(varData);
|
|
526
|
+
if (cached) {
|
|
527
|
+
return cached;
|
|
428
528
|
}
|
|
429
529
|
|
|
430
530
|
let normalizedCoords = this.normalizedCoords;
|
|
531
|
+
/** @type {number[]} */
|
|
431
532
|
let blendVector = [];
|
|
432
533
|
|
|
433
534
|
// outer loop steps through master designs to be blended
|