@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.
- package/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/browser-module.mjs +13660 -0
- package/dist/browser-module.mjs.map +1 -0
- package/dist/browser.cjs +13689 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/main.cjs +13712 -0
- package/dist/main.cjs.map +1 -0
- package/dist/module.mjs +13673 -0
- package/dist/module.mjs.map +1 -0
- package/package.json +99 -0
- package/src/CmapProcessor.js +285 -0
- package/src/DFont.js +113 -0
- package/src/TTFFont.js +621 -0
- package/src/TrueTypeCollection.js +65 -0
- package/src/WOFF2Font.js +222 -0
- package/src/WOFFFont.js +56 -0
- package/src/aat/AATFeatureMap.js +545 -0
- package/src/aat/AATLayoutEngine.js +48 -0
- package/src/aat/AATLookupTable.js +125 -0
- package/src/aat/AATMorxProcessor.js +429 -0
- package/src/aat/AATStateMachine.js +96 -0
- package/src/base.js +29 -0
- package/src/cff/CFFCharsets.js +99 -0
- package/src/cff/CFFDict.js +166 -0
- package/src/cff/CFFEncodings.js +48 -0
- package/src/cff/CFFFont.js +158 -0
- package/src/cff/CFFIndex.js +150 -0
- package/src/cff/CFFOperand.js +135 -0
- package/src/cff/CFFPointer.js +50 -0
- package/src/cff/CFFPrivateDict.js +39 -0
- package/src/cff/CFFStandardStrings.js +71 -0
- package/src/cff/CFFTop.js +236 -0
- package/src/decorators.js +36 -0
- package/src/encodings.js +219 -0
- package/src/fs.js +12 -0
- package/src/glyph/BBox.js +72 -0
- package/src/glyph/CBDTGlyph.js +9 -0
- package/src/glyph/CFFGlyph.js +635 -0
- package/src/glyph/COLRGlyph.js +108 -0
- package/src/glyph/Glyph.js +270 -0
- package/src/glyph/GlyphVariationProcessor.js +487 -0
- package/src/glyph/Path.js +257 -0
- package/src/glyph/SBIXGlyph.js +54 -0
- package/src/glyph/StandardNames.js +27 -0
- package/src/glyph/TTFGlyph.js +408 -0
- package/src/glyph/TTFGlyphEncoder.js +158 -0
- package/src/glyph/WOFF2Glyph.js +70 -0
- package/src/index.js +15 -0
- package/src/layout/GlyphPosition.js +30 -0
- package/src/layout/GlyphRun.js +108 -0
- package/src/layout/KernProcessor.js +97 -0
- package/src/layout/LayoutEngine.js +190 -0
- package/src/layout/Script.js +231 -0
- package/src/layout/UnicodeLayoutEngine.js +250 -0
- package/src/node.js +16 -0
- package/src/opentype/GPOSProcessor.js +311 -0
- package/src/opentype/GSUBProcessor.js +208 -0
- package/src/opentype/GlyphInfo.js +57 -0
- package/src/opentype/GlyphIterator.js +82 -0
- package/src/opentype/OTLayoutEngine.js +117 -0
- package/src/opentype/OTProcessor.js +472 -0
- package/src/opentype/ShapingPlan.js +121 -0
- package/src/opentype/shapers/ArabicShaper.js +124 -0
- package/src/opentype/shapers/DefaultShaper.js +116 -0
- package/src/opentype/shapers/HangulShaper.js +285 -0
- package/src/opentype/shapers/HebrewShaper.js +105 -0
- package/src/opentype/shapers/IndicShaper.js +914 -0
- package/src/opentype/shapers/ThaiShaper.js +238 -0
- package/src/opentype/shapers/UniversalShaper.js +186 -0
- package/src/opentype/shapers/data.trie +0 -0
- package/src/opentype/shapers/gen-indic.js +208 -0
- package/src/opentype/shapers/gen-use.js +287 -0
- package/src/opentype/shapers/generate-data.js +33 -0
- package/src/opentype/shapers/index.js +109 -0
- package/src/opentype/shapers/indic-data.js +175 -0
- package/src/opentype/shapers/indic.json +1 -0
- package/src/opentype/shapers/indic.machine +31 -0
- package/src/opentype/shapers/indic.trie +0 -0
- package/src/opentype/shapers/use.json +1 -0
- package/src/opentype/shapers/use.machine +44 -0
- package/src/opentype/shapers/use.trie +0 -0
- package/src/packages/unicode-properties/LICENSE +8 -0
- package/src/packages/unicode-properties/README.md +12 -0
- package/src/packages/unicode-properties/data-trie.js +1 -0
- package/src/packages/unicode-properties/data.js +1 -0
- package/src/packages/unicode-properties/data.json +1 -0
- package/src/packages/unicode-properties/data.trie +0 -0
- package/src/packages/unicode-properties/dist/index.cjs +257 -0
- package/src/packages/unicode-properties/dist/index.cjs.map +7 -0
- package/src/packages/unicode-properties/dist/index.mjs +234 -0
- package/src/packages/unicode-properties/dist/index.mjs.map +7 -0
- package/src/packages/unicode-properties/generate.js +107 -0
- package/src/packages/unicode-properties/index.js +184 -0
- package/src/packages/unicode-properties/package.json +50 -0
- package/src/packages/unicode-trie/LICENSE +8 -0
- package/src/packages/unicode-trie/README.md +13 -0
- package/src/packages/unicode-trie/builder.js +965 -0
- package/src/packages/unicode-trie/dist/builder.cjs +711 -0
- package/src/packages/unicode-trie/dist/builder.cjs.map +7 -0
- package/src/packages/unicode-trie/dist/builder.mjs +690 -0
- package/src/packages/unicode-trie/dist/builder.mjs.map +7 -0
- package/src/packages/unicode-trie/dist/index.cjs +112 -0
- package/src/packages/unicode-trie/dist/index.cjs.map +7 -0
- package/src/packages/unicode-trie/dist/index.mjs +91 -0
- package/src/packages/unicode-trie/dist/index.mjs.map +7 -0
- package/src/packages/unicode-trie/index.js +135 -0
- package/src/packages/unicode-trie/package.json +49 -0
- package/src/packages/unicode-trie/swap.js +21 -0
- package/src/subset/CFFSubset.js +176 -0
- package/src/subset/Subset.js +63 -0
- package/src/subset/TTFSubset.js +130 -0
- package/src/tables/BASE.js +78 -0
- package/src/tables/COLR.js +25 -0
- package/src/tables/CPAL.js +24 -0
- package/src/tables/DSIG.js +21 -0
- package/src/tables/EBDT.js +91 -0
- package/src/tables/EBLC.js +80 -0
- package/src/tables/GDEF.js +57 -0
- package/src/tables/GPOS.js +209 -0
- package/src/tables/GSUB.js +85 -0
- package/src/tables/HVAR.js +44 -0
- package/src/tables/JSTF.js +43 -0
- package/src/tables/LTSH.js +10 -0
- package/src/tables/OS2.js +84 -0
- package/src/tables/PCLT.js +21 -0
- package/src/tables/STAT.js +99 -0
- package/src/tables/VDMX.js +33 -0
- package/src/tables/VORG.js +14 -0
- package/src/tables/WOFF2Directory.js +73 -0
- package/src/tables/WOFFDirectory.js +37 -0
- package/src/tables/aat.js +157 -0
- package/src/tables/avar.js +19 -0
- package/src/tables/bsln.js +31 -0
- package/src/tables/cmap.js +127 -0
- package/src/tables/cvt.js +6 -0
- package/src/tables/directory.js +58 -0
- package/src/tables/feat.js +28 -0
- package/src/tables/fpgm.js +8 -0
- package/src/tables/fvar.js +31 -0
- package/src/tables/gasp.js +15 -0
- package/src/tables/glyf.js +4 -0
- package/src/tables/gvar.js +27 -0
- package/src/tables/hdmx.js +15 -0
- package/src/tables/head.js +25 -0
- package/src/tables/hhea.js +19 -0
- package/src/tables/hmtx.js +11 -0
- package/src/tables/index.js +119 -0
- package/src/tables/just.js +81 -0
- package/src/tables/kern.js +91 -0
- package/src/tables/loca.js +30 -0
- package/src/tables/maxp.js +20 -0
- package/src/tables/morx.js +79 -0
- package/src/tables/name.js +157 -0
- package/src/tables/opbd.js +15 -0
- package/src/tables/opentype.js +217 -0
- package/src/tables/post.js +34 -0
- package/src/tables/prep.js +6 -0
- package/src/tables/sbix.js +17 -0
- package/src/tables/variations.js +80 -0
- package/src/tables/vhea.js +19 -0
- package/src/tables/vmtx.js +12 -0
- 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
|
+
}
|