@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,250 @@
|
|
|
1
|
+
import {getCombiningClass} from '../packages/unicode-properties/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This class is used when GPOS does not define 'mark' or 'mkmk' features
|
|
5
|
+
* for positioning marks relative to base glyphs. It uses the unicode
|
|
6
|
+
* combining class property to position marks.
|
|
7
|
+
*
|
|
8
|
+
* Based on code from Harfbuzz, thanks!
|
|
9
|
+
* https://github.com/behdad/harfbuzz/blob/master/src/hb-ot-shape-fallback.cc
|
|
10
|
+
*/
|
|
11
|
+
export default class UnicodeLayoutEngine {
|
|
12
|
+
constructor(font) {
|
|
13
|
+
this.font = font;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
positionGlyphs(glyphs, positions) {
|
|
17
|
+
// find each base + mark cluster, and position the marks relative to the base
|
|
18
|
+
let clusterStart = 0;
|
|
19
|
+
let clusterEnd = 0;
|
|
20
|
+
for (let index = 0; index < glyphs.length; index++) {
|
|
21
|
+
let glyph = glyphs[index];
|
|
22
|
+
if (glyph.isMark) { // TODO: handle ligatures
|
|
23
|
+
clusterEnd = index;
|
|
24
|
+
} else {
|
|
25
|
+
if (clusterStart !== clusterEnd) {
|
|
26
|
+
this.positionCluster(glyphs, positions, clusterStart, clusterEnd);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clusterStart = clusterEnd = index;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (clusterStart !== clusterEnd) {
|
|
34
|
+
this.positionCluster(glyphs, positions, clusterStart, clusterEnd);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return positions;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
positionCluster(glyphs, positions, clusterStart, clusterEnd) {
|
|
41
|
+
let base = glyphs[clusterStart];
|
|
42
|
+
let baseBox = base.cbox.copy();
|
|
43
|
+
|
|
44
|
+
// adjust bounding box for ligature glyphs
|
|
45
|
+
if (base.codePoints.length > 1) {
|
|
46
|
+
// LTR. TODO: RTL support.
|
|
47
|
+
baseBox.minX += ((base.codePoints.length - 1) * baseBox.width) / base.codePoints.length;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let xOffset = -positions[clusterStart].xAdvance;
|
|
51
|
+
let yOffset = 0;
|
|
52
|
+
let yGap = this.font.unitsPerEm / 16;
|
|
53
|
+
|
|
54
|
+
// position each of the mark glyphs relative to the base glyph
|
|
55
|
+
for (let index = clusterStart + 1; index <= clusterEnd; index++) {
|
|
56
|
+
let mark = glyphs[index];
|
|
57
|
+
let markBox = mark.cbox;
|
|
58
|
+
let position = positions[index];
|
|
59
|
+
|
|
60
|
+
let combiningClass = this.getCombiningClass(mark.codePoints[0]);
|
|
61
|
+
|
|
62
|
+
if (combiningClass !== 'Not_Reordered') {
|
|
63
|
+
position.xOffset = position.yOffset = 0;
|
|
64
|
+
|
|
65
|
+
// x positioning
|
|
66
|
+
switch (combiningClass) {
|
|
67
|
+
case 'Double_Above':
|
|
68
|
+
case 'Double_Below':
|
|
69
|
+
// LTR. TODO: RTL support.
|
|
70
|
+
position.xOffset += baseBox.minX - markBox.width / 2 - markBox.minX;
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case 'Attached_Below_Left':
|
|
74
|
+
case 'Below_Left':
|
|
75
|
+
case 'Above_Left':
|
|
76
|
+
// left align
|
|
77
|
+
position.xOffset += baseBox.minX - markBox.minX;
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case 'Attached_Above_Right':
|
|
81
|
+
case 'Below_Right':
|
|
82
|
+
case 'Above_Right':
|
|
83
|
+
// right align
|
|
84
|
+
position.xOffset += baseBox.maxX - markBox.width - markBox.minX;
|
|
85
|
+
break;
|
|
86
|
+
|
|
87
|
+
default: // Attached_Below, Attached_Above, Below, Above, other
|
|
88
|
+
// center align
|
|
89
|
+
position.xOffset += baseBox.minX + (baseBox.width - markBox.width) / 2 - markBox.minX;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// y positioning
|
|
93
|
+
switch (combiningClass) {
|
|
94
|
+
case 'Double_Below':
|
|
95
|
+
case 'Below_Left':
|
|
96
|
+
case 'Below':
|
|
97
|
+
case 'Below_Right':
|
|
98
|
+
case 'Attached_Below_Left':
|
|
99
|
+
case 'Attached_Below':
|
|
100
|
+
// add a small gap between the glyphs if they are not attached
|
|
101
|
+
if (combiningClass === 'Attached_Below_Left' || combiningClass === 'Attached_Below') {
|
|
102
|
+
baseBox.minY += yGap;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
position.yOffset = -baseBox.minY - markBox.maxY;
|
|
106
|
+
baseBox.minY += markBox.height;
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'Double_Above':
|
|
110
|
+
case 'Above_Left':
|
|
111
|
+
case 'Above':
|
|
112
|
+
case 'Above_Right':
|
|
113
|
+
case 'Attached_Above':
|
|
114
|
+
case 'Attached_Above_Right':
|
|
115
|
+
// add a small gap between the glyphs if they are not attached
|
|
116
|
+
if (combiningClass === 'Attached_Above' || combiningClass === 'Attached_Above_Right') {
|
|
117
|
+
baseBox.maxY += yGap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
position.yOffset = baseBox.maxY - markBox.minY;
|
|
121
|
+
baseBox.maxY += markBox.height;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
position.xAdvance = position.yAdvance = 0;
|
|
126
|
+
position.xOffset += xOffset;
|
|
127
|
+
position.yOffset += yOffset;
|
|
128
|
+
|
|
129
|
+
} else {
|
|
130
|
+
xOffset -= position.xAdvance;
|
|
131
|
+
yOffset -= position.yAdvance;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getCombiningClass(codePoint) {
|
|
139
|
+
let combiningClass = getCombiningClass(codePoint);
|
|
140
|
+
|
|
141
|
+
// Thai / Lao need some per-character work
|
|
142
|
+
if ((codePoint & ~0xff) === 0x0e00) {
|
|
143
|
+
if (combiningClass === 'Not_Reordered') {
|
|
144
|
+
switch (codePoint) {
|
|
145
|
+
case 0x0e31:
|
|
146
|
+
case 0x0e34:
|
|
147
|
+
case 0x0e35:
|
|
148
|
+
case 0x0e36:
|
|
149
|
+
case 0x0e37:
|
|
150
|
+
case 0x0e47:
|
|
151
|
+
case 0x0e4c:
|
|
152
|
+
case 0x0e4d:
|
|
153
|
+
case 0x0e4e:
|
|
154
|
+
return 'Above_Right';
|
|
155
|
+
|
|
156
|
+
case 0x0eb1:
|
|
157
|
+
case 0x0eb4:
|
|
158
|
+
case 0x0eb5:
|
|
159
|
+
case 0x0eb6:
|
|
160
|
+
case 0x0eb7:
|
|
161
|
+
case 0x0ebb:
|
|
162
|
+
case 0x0ecc:
|
|
163
|
+
case 0x0ecd:
|
|
164
|
+
return 'Above';
|
|
165
|
+
|
|
166
|
+
case 0x0ebc:
|
|
167
|
+
return 'Below';
|
|
168
|
+
}
|
|
169
|
+
} else if (codePoint === 0x0e3a) { // virama
|
|
170
|
+
return 'Below_Right';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
switch (combiningClass) {
|
|
175
|
+
// Hebrew
|
|
176
|
+
|
|
177
|
+
case 'CCC10': // sheva
|
|
178
|
+
case 'CCC11': // hataf segol
|
|
179
|
+
case 'CCC12': // hataf patah
|
|
180
|
+
case 'CCC13': // hataf qamats
|
|
181
|
+
case 'CCC14': // hiriq
|
|
182
|
+
case 'CCC15': // tsere
|
|
183
|
+
case 'CCC16': // segol
|
|
184
|
+
case 'CCC17': // patah
|
|
185
|
+
case 'CCC18': // qamats
|
|
186
|
+
case 'CCC20': // qubuts
|
|
187
|
+
case 'CCC22': // meteg
|
|
188
|
+
return 'Below';
|
|
189
|
+
|
|
190
|
+
case 'CCC23': // rafe
|
|
191
|
+
return 'Attached_Above';
|
|
192
|
+
|
|
193
|
+
case 'CCC24': // shin dot
|
|
194
|
+
return 'Above_Right';
|
|
195
|
+
|
|
196
|
+
case 'CCC25': // sin dot
|
|
197
|
+
case 'CCC19': // holam
|
|
198
|
+
return 'Above_Left';
|
|
199
|
+
|
|
200
|
+
case 'CCC26': // point varika
|
|
201
|
+
return 'Above';
|
|
202
|
+
|
|
203
|
+
case 'CCC21': // dagesh
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
// Arabic and Syriac
|
|
207
|
+
|
|
208
|
+
case 'CCC27': // fathatan
|
|
209
|
+
case 'CCC28': // dammatan
|
|
210
|
+
case 'CCC30': // fatha
|
|
211
|
+
case 'CCC31': // damma
|
|
212
|
+
case 'CCC33': // shadda
|
|
213
|
+
case 'CCC34': // sukun
|
|
214
|
+
case 'CCC35': // superscript alef
|
|
215
|
+
case 'CCC36': // superscript alaph
|
|
216
|
+
return 'Above';
|
|
217
|
+
|
|
218
|
+
case 'CCC29': // kasratan
|
|
219
|
+
case 'CCC32': // kasra
|
|
220
|
+
return 'Below';
|
|
221
|
+
|
|
222
|
+
// Thai
|
|
223
|
+
|
|
224
|
+
case 'CCC103': // sara u / sara uu
|
|
225
|
+
return 'Below_Right';
|
|
226
|
+
|
|
227
|
+
case 'CCC107': // mai
|
|
228
|
+
return 'Above_Right';
|
|
229
|
+
|
|
230
|
+
// Lao
|
|
231
|
+
|
|
232
|
+
case 'CCC118': // sign u / sign uu
|
|
233
|
+
return 'Below';
|
|
234
|
+
|
|
235
|
+
case 'CCC122': // mai
|
|
236
|
+
return 'Above';
|
|
237
|
+
|
|
238
|
+
// Tibetan
|
|
239
|
+
|
|
240
|
+
case 'CCC129': // sign aa
|
|
241
|
+
case 'CCC132': // sign u
|
|
242
|
+
return 'Below';
|
|
243
|
+
|
|
244
|
+
case 'CCC130': // sign i
|
|
245
|
+
return 'Above';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return combiningClass;
|
|
249
|
+
}
|
|
250
|
+
}
|
package/src/node.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { registerFormat } from './base';
|
|
2
|
+
import TTFFont from './TTFFont';
|
|
3
|
+
import WOFFFont from './WOFFFont';
|
|
4
|
+
import WOFF2Font from './WOFF2Font';
|
|
5
|
+
import TrueTypeCollection from './TrueTypeCollection';
|
|
6
|
+
import DFont from './DFont';
|
|
7
|
+
|
|
8
|
+
// Register font formats
|
|
9
|
+
registerFormat(TTFFont);
|
|
10
|
+
registerFormat(WOFFFont);
|
|
11
|
+
registerFormat(WOFF2Font);
|
|
12
|
+
registerFormat(TrueTypeCollection);
|
|
13
|
+
registerFormat(DFont);
|
|
14
|
+
|
|
15
|
+
export * from './base';
|
|
16
|
+
export * from './fs';
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import OTProcessor from './OTProcessor';
|
|
2
|
+
|
|
3
|
+
export default class GPOSProcessor extends OTProcessor {
|
|
4
|
+
applyPositionValue(sequenceIndex, value) {
|
|
5
|
+
let position = this.positions[this.glyphIterator.peekIndex(sequenceIndex)];
|
|
6
|
+
if (value.xAdvance != null) position.xAdvance += value.xAdvance;
|
|
7
|
+
if (value.yAdvance != null) position.yAdvance += value.yAdvance;
|
|
8
|
+
if (value.xPlacement != null) position.xOffset += value.xPlacement;
|
|
9
|
+
if (value.yPlacement != null) position.yOffset += value.yPlacement;
|
|
10
|
+
|
|
11
|
+
// Adjustments for font variations
|
|
12
|
+
let variationProcessor = this.font._variationProcessor;
|
|
13
|
+
let variationStore = this.font.GDEF && this.font.GDEF.itemVariationStore;
|
|
14
|
+
if (variationProcessor && variationStore) {
|
|
15
|
+
if (value.xPlaDevice) {
|
|
16
|
+
position.xOffset += variationProcessor.getDelta(variationStore, value.xPlaDevice.a, value.xPlaDevice.b);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (value.yPlaDevice) {
|
|
20
|
+
position.yOffset += variationProcessor.getDelta(variationStore, value.yPlaDevice.a, value.yPlaDevice.b);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (value.xAdvDevice) {
|
|
24
|
+
position.xAdvance += variationProcessor.getDelta(variationStore, value.xAdvDevice.a, value.xAdvDevice.b);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (value.yAdvDevice) {
|
|
28
|
+
position.yAdvance += variationProcessor.getDelta(variationStore, value.yAdvDevice.a, value.yAdvDevice.b);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// TODO: device tables
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
applyLookup(lookupType, table) {
|
|
36
|
+
switch (lookupType) {
|
|
37
|
+
case 1: { // Single positioning value
|
|
38
|
+
let index = this.coverageIndex(table.coverage);
|
|
39
|
+
if (index === -1) return false;
|
|
40
|
+
|
|
41
|
+
switch (table.version) {
|
|
42
|
+
case 1:
|
|
43
|
+
this.applyPositionValue(0, table.value);
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case 2:
|
|
47
|
+
this.applyPositionValue(0, table.values.get(index));
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 2: { // Pair Adjustment Positioning
|
|
55
|
+
let nextGlyph = this.glyphIterator.peek();
|
|
56
|
+
if (!nextGlyph) return false;
|
|
57
|
+
|
|
58
|
+
let index = this.coverageIndex(table.coverage);
|
|
59
|
+
if (index === -1) return false;
|
|
60
|
+
|
|
61
|
+
switch (table.version) {
|
|
62
|
+
case 1: { // Adjustments for glyph pairs
|
|
63
|
+
let set = table.pairSets.get(index);
|
|
64
|
+
|
|
65
|
+
for (let pair of set) {
|
|
66
|
+
if (pair.secondGlyph === nextGlyph.id) {
|
|
67
|
+
this.applyPositionValue(0, pair.value1);
|
|
68
|
+
this.applyPositionValue(1, pair.value2);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case 2: { // Class pair adjustment
|
|
77
|
+
let class1 = this.getClassID(this.glyphIterator.cur.id, table.classDef1);
|
|
78
|
+
let class2 = this.getClassID(nextGlyph.id, table.classDef2);
|
|
79
|
+
if (class1 === -1 || class2 === -1) return false;
|
|
80
|
+
|
|
81
|
+
let pair = table.classRecords.get(class1).get(class2);
|
|
82
|
+
this.applyPositionValue(0, pair.value1);
|
|
83
|
+
this.applyPositionValue(1, pair.value2);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case 3: { // Cursive Attachment Positioning
|
|
92
|
+
let nextIndex = this.glyphIterator.peekIndex();
|
|
93
|
+
let nextGlyph = this.glyphs[nextIndex];
|
|
94
|
+
if (!nextGlyph) return false;
|
|
95
|
+
|
|
96
|
+
let curRecord = table.entryExitRecords[this.coverageIndex(table.coverage)];
|
|
97
|
+
if (!curRecord || !curRecord.exitAnchor) return false;
|
|
98
|
+
|
|
99
|
+
let nextRecord = table.entryExitRecords[this.coverageIndex(table.coverage, nextGlyph.id)];
|
|
100
|
+
if (!nextRecord || !nextRecord.entryAnchor) return false;
|
|
101
|
+
|
|
102
|
+
let entry = this.getAnchor(nextRecord.entryAnchor);
|
|
103
|
+
let exit = this.getAnchor(curRecord.exitAnchor);
|
|
104
|
+
|
|
105
|
+
let cur = this.positions[this.glyphIterator.index];
|
|
106
|
+
let next = this.positions[nextIndex];
|
|
107
|
+
let d;
|
|
108
|
+
|
|
109
|
+
switch (this.direction) {
|
|
110
|
+
case 'ltr':
|
|
111
|
+
cur.xAdvance = exit.x + cur.xOffset;
|
|
112
|
+
|
|
113
|
+
d = entry.x + next.xOffset;
|
|
114
|
+
next.xAdvance -= d;
|
|
115
|
+
next.xOffset -= d;
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
case 'rtl':
|
|
119
|
+
d = exit.x + cur.xOffset;
|
|
120
|
+
cur.xAdvance -= d;
|
|
121
|
+
cur.xOffset -= d;
|
|
122
|
+
next.xAdvance = entry.x + next.xOffset;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.glyphIterator.flags.rightToLeft) {
|
|
127
|
+
this.glyphIterator.cur.cursiveAttachment = nextIndex;
|
|
128
|
+
cur.yOffset = entry.y - exit.y;
|
|
129
|
+
} else {
|
|
130
|
+
nextGlyph.cursiveAttachment = this.glyphIterator.index;
|
|
131
|
+
cur.yOffset = exit.y - entry.y;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
case 4: { // Mark to base positioning
|
|
138
|
+
let markIndex = this.coverageIndex(table.markCoverage);
|
|
139
|
+
if (markIndex === -1) return false;
|
|
140
|
+
|
|
141
|
+
// search backward for a base glyph
|
|
142
|
+
let baseGlyphIndex = this.glyphIterator.index;
|
|
143
|
+
while (--baseGlyphIndex >= 0 && (this.glyphs[baseGlyphIndex].isMark || this.glyphs[baseGlyphIndex].ligatureComponent > 0));
|
|
144
|
+
|
|
145
|
+
if (baseGlyphIndex < 0) return false;
|
|
146
|
+
|
|
147
|
+
let baseIndex = this.coverageIndex(table.baseCoverage, this.glyphs[baseGlyphIndex].id);
|
|
148
|
+
if (baseIndex === -1) return false;
|
|
149
|
+
|
|
150
|
+
let markRecord = table.markArray[markIndex];
|
|
151
|
+
let baseAnchor = table.baseArray[baseIndex][markRecord.class];
|
|
152
|
+
return this.applyAnchor(markRecord, baseAnchor, baseGlyphIndex);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
case 5: { // Mark to ligature positioning
|
|
156
|
+
let markIndex = this.coverageIndex(table.markCoverage);
|
|
157
|
+
if (markIndex === -1) return false;
|
|
158
|
+
|
|
159
|
+
// search backward for a base glyph
|
|
160
|
+
let baseGlyphIndex = this.glyphIterator.index;
|
|
161
|
+
while (--baseGlyphIndex >= 0 && this.glyphs[baseGlyphIndex].isMark);
|
|
162
|
+
|
|
163
|
+
if (baseGlyphIndex < 0) return false;
|
|
164
|
+
|
|
165
|
+
let ligIndex = this.coverageIndex(table.ligatureCoverage, this.glyphs[baseGlyphIndex].id);
|
|
166
|
+
if (ligIndex === -1) return false;
|
|
167
|
+
|
|
168
|
+
let ligAttach = table.ligatureArray[ligIndex];
|
|
169
|
+
let markGlyph = this.glyphIterator.cur;
|
|
170
|
+
let ligGlyph = this.glyphs[baseGlyphIndex];
|
|
171
|
+
let compIndex = ligGlyph.ligatureID && ligGlyph.ligatureID === markGlyph.ligatureID && (markGlyph.ligatureComponent > 0)
|
|
172
|
+
? Math.min(markGlyph.ligatureComponent, ligGlyph.codePoints.length) - 1
|
|
173
|
+
: ligGlyph.codePoints.length - 1;
|
|
174
|
+
|
|
175
|
+
let markRecord = table.markArray[markIndex];
|
|
176
|
+
let baseAnchor = ligAttach[compIndex][markRecord.class];
|
|
177
|
+
return this.applyAnchor(markRecord, baseAnchor, baseGlyphIndex);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
case 6: { // Mark to mark positioning
|
|
181
|
+
let mark1Index = this.coverageIndex(table.mark1Coverage);
|
|
182
|
+
if (mark1Index === -1) return false;
|
|
183
|
+
|
|
184
|
+
// get the previous mark to attach to
|
|
185
|
+
let prevIndex = this.glyphIterator.peekIndex(-1);
|
|
186
|
+
let prev = this.glyphs[prevIndex];
|
|
187
|
+
if (!prev || !prev.isMark) return false;
|
|
188
|
+
|
|
189
|
+
let cur = this.glyphIterator.cur;
|
|
190
|
+
|
|
191
|
+
// The following logic was borrowed from Harfbuzz
|
|
192
|
+
let good = false;
|
|
193
|
+
if (cur.ligatureID === prev.ligatureID) {
|
|
194
|
+
if (!cur.ligatureID) { // Marks belonging to the same base
|
|
195
|
+
good = true;
|
|
196
|
+
} else if (cur.ligatureComponent === prev.ligatureComponent) { // Marks belonging to the same ligature component
|
|
197
|
+
good = true;
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
// If ligature ids don't match, it may be the case that one of the marks
|
|
201
|
+
// itself is a ligature, in which case match.
|
|
202
|
+
if ((cur.ligatureID && !cur.ligatureComponent) || (prev.ligatureID && !prev.ligatureComponent)) {
|
|
203
|
+
good = true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!good) return false;
|
|
208
|
+
|
|
209
|
+
let mark2Index = this.coverageIndex(table.mark2Coverage, prev.id);
|
|
210
|
+
if (mark2Index === -1) return false;
|
|
211
|
+
|
|
212
|
+
let markRecord = table.mark1Array[mark1Index];
|
|
213
|
+
let baseAnchor = table.mark2Array[mark2Index][markRecord.class];
|
|
214
|
+
return this.applyAnchor(markRecord, baseAnchor, prevIndex);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
case 7: // Contextual positioning
|
|
218
|
+
return this.applyContext(table);
|
|
219
|
+
|
|
220
|
+
case 8: // Chaining contextual positioning
|
|
221
|
+
return this.applyChainingContext(table);
|
|
222
|
+
|
|
223
|
+
case 9: // Extension positioning
|
|
224
|
+
return this.applyLookup(table.lookupType, table.extension);
|
|
225
|
+
|
|
226
|
+
default:
|
|
227
|
+
throw new Error(`Unsupported GPOS table: ${lookupType}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
applyAnchor(markRecord, baseAnchor, baseGlyphIndex) {
|
|
232
|
+
// NULL offset = no anchor for this class; false so later subtables can try (HarfBuzz).
|
|
233
|
+
if (!baseAnchor || !markRecord.markAnchor) return false;
|
|
234
|
+
|
|
235
|
+
let baseCoords = this.getAnchor(baseAnchor);
|
|
236
|
+
let markCoords = this.getAnchor(markRecord.markAnchor);
|
|
237
|
+
let markPos = this.positions[this.glyphIterator.index];
|
|
238
|
+
|
|
239
|
+
markPos.xOffset = baseCoords.x - markCoords.x;
|
|
240
|
+
markPos.yOffset = baseCoords.y - markCoords.y;
|
|
241
|
+
this.glyphIterator.cur.markAttachment = baseGlyphIndex;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getAnchor(anchor) {
|
|
246
|
+
// TODO: contour point, device tables
|
|
247
|
+
let x = anchor.xCoordinate;
|
|
248
|
+
let y = anchor.yCoordinate;
|
|
249
|
+
|
|
250
|
+
// Adjustments for font variations
|
|
251
|
+
let variationProcessor = this.font._variationProcessor;
|
|
252
|
+
let variationStore = this.font.GDEF && this.font.GDEF.itemVariationStore;
|
|
253
|
+
if (variationProcessor && variationStore) {
|
|
254
|
+
if (anchor.xDeviceTable) {
|
|
255
|
+
x += variationProcessor.getDelta(variationStore, anchor.xDeviceTable.a, anchor.xDeviceTable.b);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (anchor.yDeviceTable) {
|
|
259
|
+
y += variationProcessor.getDelta(variationStore, anchor.yDeviceTable.a, anchor.yDeviceTable.b);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return { x, y };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
applyFeatures(userFeatures, glyphs, advances) {
|
|
267
|
+
super.applyFeatures(userFeatures, glyphs, advances);
|
|
268
|
+
|
|
269
|
+
for (var i = 0; i < this.glyphs.length; i++) {
|
|
270
|
+
this.fixCursiveAttachment(i);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this.fixMarkAttachment();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fixCursiveAttachment(i) {
|
|
277
|
+
let glyph = this.glyphs[i];
|
|
278
|
+
if (glyph.cursiveAttachment != null) {
|
|
279
|
+
let j = glyph.cursiveAttachment;
|
|
280
|
+
|
|
281
|
+
glyph.cursiveAttachment = null;
|
|
282
|
+
this.fixCursiveAttachment(j);
|
|
283
|
+
|
|
284
|
+
this.positions[i].yOffset += this.positions[j].yOffset;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
fixMarkAttachment() {
|
|
289
|
+
for (let i = 0; i < this.glyphs.length; i++) {
|
|
290
|
+
let glyph = this.glyphs[i];
|
|
291
|
+
if (glyph.markAttachment != null) {
|
|
292
|
+
let j = glyph.markAttachment;
|
|
293
|
+
|
|
294
|
+
this.positions[i].xOffset += this.positions[j].xOffset;
|
|
295
|
+
this.positions[i].yOffset += this.positions[j].yOffset;
|
|
296
|
+
|
|
297
|
+
if (this.direction === 'ltr') {
|
|
298
|
+
for (let k = j; k < i; k++) {
|
|
299
|
+
this.positions[i].xOffset -= this.positions[k].xAdvance;
|
|
300
|
+
this.positions[i].yOffset -= this.positions[k].yAdvance;
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
for (let k = j + 1; k < i + 1; k++) {
|
|
304
|
+
this.positions[i].xOffset += this.positions[k].xAdvance;
|
|
305
|
+
this.positions[i].yOffset += this.positions[k].yAdvance;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|