@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,57 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
import {Coverage, ClassDef, Device} from './opentype';
|
|
3
|
+
import {ItemVariationStore} from './variations';
|
|
4
|
+
|
|
5
|
+
let AttachPoint = new r.Array(r.uint16, r.uint16);
|
|
6
|
+
let AttachList = new r.Struct({
|
|
7
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
8
|
+
glyphCount: r.uint16,
|
|
9
|
+
attachPoints: new r.Array(new r.Pointer(r.uint16, AttachPoint), 'glyphCount')
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
let CaretValue = new r.VersionedStruct(r.uint16, {
|
|
13
|
+
1: { // Design units only
|
|
14
|
+
coordinate: r.int16
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
2: { // Contour point
|
|
18
|
+
caretValuePoint: r.uint16
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
3: { // Design units plus Device table
|
|
22
|
+
coordinate: r.int16,
|
|
23
|
+
deviceTable: new r.Pointer(r.uint16, Device)
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let LigGlyph = new r.Array(new r.Pointer(r.uint16, CaretValue), r.uint16);
|
|
28
|
+
|
|
29
|
+
let LigCaretList = new r.Struct({
|
|
30
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
31
|
+
ligGlyphCount: r.uint16,
|
|
32
|
+
ligGlyphs: new r.Array(new r.Pointer(r.uint16, LigGlyph), 'ligGlyphCount')
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
let MarkGlyphSetsDef = new r.Struct({
|
|
36
|
+
markSetTableFormat: r.uint16,
|
|
37
|
+
markSetCount: r.uint16,
|
|
38
|
+
coverage: new r.Array(new r.Pointer(r.uint32, Coverage), 'markSetCount')
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default new r.VersionedStruct(r.uint32, {
|
|
42
|
+
header: {
|
|
43
|
+
glyphClassDef: new r.Pointer(r.uint16, ClassDef),
|
|
44
|
+
attachList: new r.Pointer(r.uint16, AttachList),
|
|
45
|
+
ligCaretList: new r.Pointer(r.uint16, LigCaretList),
|
|
46
|
+
markAttachClassDef: new r.Pointer(r.uint16, ClassDef)
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
0x00010000: {},
|
|
50
|
+
0x00010002: {
|
|
51
|
+
markGlyphSetsDef: new r.Pointer(r.uint16, MarkGlyphSetsDef)
|
|
52
|
+
},
|
|
53
|
+
0x00010003: {
|
|
54
|
+
markGlyphSetsDef: new r.Pointer(r.uint16, MarkGlyphSetsDef),
|
|
55
|
+
itemVariationStore: new r.Pointer(r.uint32, ItemVariationStore)
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
import {ScriptList, FeatureList, LookupList, Coverage, ClassDef, Device, Context, ChainingContext} from './opentype';
|
|
3
|
+
import {FeatureVariations} from './variations';
|
|
4
|
+
|
|
5
|
+
let ValueFormat = new r.Bitfield(r.uint16, [
|
|
6
|
+
'xPlacement', 'yPlacement',
|
|
7
|
+
'xAdvance', 'yAdvance',
|
|
8
|
+
'xPlaDevice', 'yPlaDevice',
|
|
9
|
+
'xAdvDevice', 'yAdvDevice'
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
let types = {
|
|
13
|
+
xPlacement: r.int16,
|
|
14
|
+
yPlacement: r.int16,
|
|
15
|
+
xAdvance: r.int16,
|
|
16
|
+
yAdvance: r.int16,
|
|
17
|
+
xPlaDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: ctx => ctx.rel }),
|
|
18
|
+
yPlaDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: ctx => ctx.rel }),
|
|
19
|
+
xAdvDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: ctx => ctx.rel }),
|
|
20
|
+
yAdvDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: ctx => ctx.rel })
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
class ValueRecord {
|
|
24
|
+
constructor(key = 'valueFormat') {
|
|
25
|
+
this.key = key;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
buildStruct(parent) {
|
|
29
|
+
let struct = parent;
|
|
30
|
+
while (!struct[this.key] && struct.parent) {
|
|
31
|
+
struct = struct.parent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!struct[this.key]) return;
|
|
35
|
+
|
|
36
|
+
let fields = {};
|
|
37
|
+
fields.rel = () => struct._startOffset;
|
|
38
|
+
|
|
39
|
+
let format = struct[this.key];
|
|
40
|
+
for (let key in format) {
|
|
41
|
+
if (format[key]) {
|
|
42
|
+
fields[key] = types[key];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return new r.Struct(fields);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
size(val, ctx) {
|
|
50
|
+
return this.buildStruct(ctx).size(val, ctx);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
decode(stream, parent) {
|
|
54
|
+
let res = this.buildStruct(parent).decode(stream, parent);
|
|
55
|
+
delete res.rel;
|
|
56
|
+
return res;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let PairValueRecord = new r.Struct({
|
|
61
|
+
secondGlyph: r.uint16,
|
|
62
|
+
value1: new ValueRecord('valueFormat1'),
|
|
63
|
+
value2: new ValueRecord('valueFormat2')
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
let PairSet = new r.Array(PairValueRecord, r.uint16);
|
|
67
|
+
|
|
68
|
+
let Class2Record = new r.Struct({
|
|
69
|
+
value1: new ValueRecord('valueFormat1'),
|
|
70
|
+
value2: new ValueRecord('valueFormat2')
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let Anchor = new r.VersionedStruct(r.uint16, {
|
|
74
|
+
1: { // Design units only
|
|
75
|
+
xCoordinate: r.int16,
|
|
76
|
+
yCoordinate: r.int16
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
2: { // Design units plus contour point
|
|
80
|
+
xCoordinate: r.int16,
|
|
81
|
+
yCoordinate: r.int16,
|
|
82
|
+
anchorPoint: r.uint16
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
3: { // Design units plus Device tables
|
|
86
|
+
xCoordinate: r.int16,
|
|
87
|
+
yCoordinate: r.int16,
|
|
88
|
+
xDeviceTable: new r.Pointer(r.uint16, Device),
|
|
89
|
+
yDeviceTable: new r.Pointer(r.uint16, Device)
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
let EntryExitRecord = new r.Struct({
|
|
94
|
+
entryAnchor: new r.Pointer(r.uint16, Anchor, {type: 'parent'}),
|
|
95
|
+
exitAnchor: new r.Pointer(r.uint16, Anchor, {type: 'parent'})
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
let MarkRecord = new r.Struct({
|
|
99
|
+
class: r.uint16,
|
|
100
|
+
markAnchor: new r.Pointer(r.uint16, Anchor, {type: 'parent'})
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
let MarkArray = new r.Array(MarkRecord, r.uint16);
|
|
104
|
+
|
|
105
|
+
let BaseRecord = new r.Array(new r.Pointer(r.uint16, Anchor), t => t.parent.classCount);
|
|
106
|
+
let BaseArray = new r.Array(BaseRecord, r.uint16);
|
|
107
|
+
|
|
108
|
+
let ComponentRecord = new r.Array(new r.Pointer(r.uint16, Anchor), t => t.parent.parent.classCount);
|
|
109
|
+
let LigatureAttach = new r.Array(ComponentRecord, r.uint16);
|
|
110
|
+
let LigatureArray = new r.Array(new r.Pointer(r.uint16, LigatureAttach), r.uint16);
|
|
111
|
+
|
|
112
|
+
let GPOSLookup = new r.VersionedStruct('lookupType', {
|
|
113
|
+
1: new r.VersionedStruct(r.uint16, { // Single Adjustment
|
|
114
|
+
1: { // Single positioning value
|
|
115
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
116
|
+
valueFormat: ValueFormat,
|
|
117
|
+
value: new ValueRecord()
|
|
118
|
+
},
|
|
119
|
+
2: {
|
|
120
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
121
|
+
valueFormat: ValueFormat,
|
|
122
|
+
valueCount: r.uint16,
|
|
123
|
+
values: new r.LazyArray(new ValueRecord(), 'valueCount')
|
|
124
|
+
}
|
|
125
|
+
}),
|
|
126
|
+
|
|
127
|
+
2: new r.VersionedStruct(r.uint16, { // Pair Adjustment Positioning
|
|
128
|
+
1: { // Adjustments for glyph pairs
|
|
129
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
130
|
+
valueFormat1: ValueFormat,
|
|
131
|
+
valueFormat2: ValueFormat,
|
|
132
|
+
pairSetCount: r.uint16,
|
|
133
|
+
pairSets: new r.LazyArray(new r.Pointer(r.uint16, PairSet), 'pairSetCount')
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
2: { // Class pair adjustment
|
|
137
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
138
|
+
valueFormat1: ValueFormat,
|
|
139
|
+
valueFormat2: ValueFormat,
|
|
140
|
+
classDef1: new r.Pointer(r.uint16, ClassDef),
|
|
141
|
+
classDef2: new r.Pointer(r.uint16, ClassDef),
|
|
142
|
+
class1Count: r.uint16,
|
|
143
|
+
class2Count: r.uint16,
|
|
144
|
+
classRecords: new r.LazyArray(new r.LazyArray(Class2Record, 'class2Count'), 'class1Count')
|
|
145
|
+
}
|
|
146
|
+
}),
|
|
147
|
+
|
|
148
|
+
3: { // Cursive Attachment Positioning
|
|
149
|
+
format: r.uint16,
|
|
150
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
151
|
+
entryExitCount: r.uint16,
|
|
152
|
+
entryExitRecords: new r.Array(EntryExitRecord, 'entryExitCount')
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
4: { // MarkToBase Attachment Positioning
|
|
156
|
+
format: r.uint16,
|
|
157
|
+
markCoverage: new r.Pointer(r.uint16, Coverage),
|
|
158
|
+
baseCoverage: new r.Pointer(r.uint16, Coverage),
|
|
159
|
+
classCount: r.uint16,
|
|
160
|
+
markArray: new r.Pointer(r.uint16, MarkArray),
|
|
161
|
+
baseArray: new r.Pointer(r.uint16, BaseArray)
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
5: { // MarkToLigature Attachment Positioning
|
|
165
|
+
format: r.uint16,
|
|
166
|
+
markCoverage: new r.Pointer(r.uint16, Coverage),
|
|
167
|
+
ligatureCoverage: new r.Pointer(r.uint16, Coverage),
|
|
168
|
+
classCount: r.uint16,
|
|
169
|
+
markArray: new r.Pointer(r.uint16, MarkArray),
|
|
170
|
+
ligatureArray: new r.Pointer(r.uint16, LigatureArray)
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
6: { // MarkToMark Attachment Positioning
|
|
174
|
+
format: r.uint16,
|
|
175
|
+
mark1Coverage: new r.Pointer(r.uint16, Coverage),
|
|
176
|
+
mark2Coverage: new r.Pointer(r.uint16, Coverage),
|
|
177
|
+
classCount: r.uint16,
|
|
178
|
+
mark1Array: new r.Pointer(r.uint16, MarkArray),
|
|
179
|
+
mark2Array: new r.Pointer(r.uint16, BaseArray)
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
7: Context, // Contextual positioning
|
|
183
|
+
8: ChainingContext, // Chaining contextual positioning
|
|
184
|
+
|
|
185
|
+
9: { // Extension Positioning
|
|
186
|
+
posFormat: r.uint16,
|
|
187
|
+
lookupType: r.uint16, // cannot also be 9
|
|
188
|
+
extension: new r.Pointer(r.uint32, null)
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Fix circular reference
|
|
193
|
+
GPOSLookup.versions[9].extension.type = GPOSLookup;
|
|
194
|
+
|
|
195
|
+
export default new r.VersionedStruct(r.uint32, {
|
|
196
|
+
header: {
|
|
197
|
+
scriptList: new r.Pointer(r.uint16, ScriptList),
|
|
198
|
+
featureList: new r.Pointer(r.uint16, FeatureList),
|
|
199
|
+
lookupList: new r.Pointer(r.uint16, new LookupList(GPOSLookup))
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
0x00010000: {},
|
|
203
|
+
0x00010001: {
|
|
204
|
+
featureVariations: new r.Pointer(r.uint32, FeatureVariations)
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// export GPOSLookup for JSTF table
|
|
209
|
+
export { GPOSLookup };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
import {ScriptList, FeatureList, LookupList, Coverage, Context, ChainingContext} from './opentype';
|
|
3
|
+
import {FeatureVariations} from './variations';
|
|
4
|
+
|
|
5
|
+
let Sequence = new r.Array(r.uint16, r.uint16);
|
|
6
|
+
let AlternateSet = Sequence;
|
|
7
|
+
|
|
8
|
+
let Ligature = new r.Struct({
|
|
9
|
+
glyph: r.uint16,
|
|
10
|
+
compCount: r.uint16,
|
|
11
|
+
components: new r.Array(r.uint16, t => t.compCount - 1)
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let LigatureSet = new r.Array(new r.Pointer(r.uint16, Ligature), r.uint16);
|
|
15
|
+
|
|
16
|
+
let GSUBLookup = new r.VersionedStruct('lookupType', {
|
|
17
|
+
1: new r.VersionedStruct(r.uint16, {// Single Substitution
|
|
18
|
+
1: {
|
|
19
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
20
|
+
deltaGlyphID: r.int16
|
|
21
|
+
},
|
|
22
|
+
2: {
|
|
23
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
24
|
+
glyphCount: r.uint16,
|
|
25
|
+
substitute: new r.LazyArray(r.uint16, 'glyphCount')
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
28
|
+
|
|
29
|
+
2: { // Multiple Substitution
|
|
30
|
+
substFormat: r.uint16,
|
|
31
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
32
|
+
count: r.uint16,
|
|
33
|
+
sequences: new r.LazyArray(new r.Pointer(r.uint16, Sequence), 'count')
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
3: { // Alternate Substitution
|
|
37
|
+
substFormat: r.uint16,
|
|
38
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
39
|
+
count: r.uint16,
|
|
40
|
+
alternateSet: new r.LazyArray(new r.Pointer(r.uint16, AlternateSet), 'count')
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
4: { // Ligature Substitution
|
|
44
|
+
substFormat: r.uint16,
|
|
45
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
46
|
+
count: r.uint16,
|
|
47
|
+
ligatureSets: new r.LazyArray(new r.Pointer(r.uint16, LigatureSet), 'count')
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
5: Context, // Contextual Substitution
|
|
51
|
+
6: ChainingContext, // Chaining Contextual Substitution
|
|
52
|
+
|
|
53
|
+
7: { // Extension Substitution
|
|
54
|
+
substFormat: r.uint16,
|
|
55
|
+
lookupType: r.uint16, // cannot also be 7
|
|
56
|
+
extension: new r.Pointer(r.uint32, null)
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
8: { // Reverse Chaining Contextual Single Substitution
|
|
60
|
+
substFormat: r.uint16,
|
|
61
|
+
coverage: new r.Pointer(r.uint16, Coverage),
|
|
62
|
+
backtrackGlyphCount: r.uint16,
|
|
63
|
+
backtrackCoverage: new r.Array(new r.Pointer(r.uint16, Coverage), 'backtrackGlyphCount'),
|
|
64
|
+
lookaheadGlyphCount: r.uint16,
|
|
65
|
+
lookaheadCoverage: new r.Array(new r.Pointer(r.uint16, Coverage), 'lookaheadGlyphCount'),
|
|
66
|
+
glyphCount: r.uint16,
|
|
67
|
+
substitute: new r.LazyArray(r.uint16, 'glyphCount')
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Fix circular reference
|
|
72
|
+
GSUBLookup.versions[7].extension.type = GSUBLookup;
|
|
73
|
+
|
|
74
|
+
export default new r.VersionedStruct(r.uint32, {
|
|
75
|
+
header: {
|
|
76
|
+
scriptList: new r.Pointer(r.uint16, ScriptList),
|
|
77
|
+
featureList: new r.Pointer(r.uint16, FeatureList),
|
|
78
|
+
lookupList: new r.Pointer(r.uint16, new LookupList(GSUBLookup))
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
0x00010000: {},
|
|
82
|
+
0x00010001: {
|
|
83
|
+
featureVariations: new r.Pointer(r.uint32, FeatureVariations)
|
|
84
|
+
}
|
|
85
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
import { resolveLength } from 'restructure';
|
|
3
|
+
import { ItemVariationStore } from './variations';
|
|
4
|
+
|
|
5
|
+
// TODO: add this to restructure
|
|
6
|
+
class VariableSizeNumber {
|
|
7
|
+
constructor(size) {
|
|
8
|
+
this._size = size;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
decode(stream, parent) {
|
|
12
|
+
switch (this.size(0, parent)) {
|
|
13
|
+
case 1: return stream.readUInt8();
|
|
14
|
+
case 2: return stream.readUInt16BE();
|
|
15
|
+
case 3: return stream.readUInt24BE();
|
|
16
|
+
case 4: return stream.readUInt32BE();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
size(val, parent) {
|
|
21
|
+
return resolveLength(this._size, null, parent);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let MapDataEntry = new r.Struct({
|
|
26
|
+
entry: new VariableSizeNumber(t => ((t.parent.entryFormat & 0x0030) >> 4) + 1),
|
|
27
|
+
outerIndex: t => t.entry >> ((t.parent.entryFormat & 0x000F) + 1),
|
|
28
|
+
innerIndex: t => t.entry & ((1 << ((t.parent.entryFormat & 0x000F) + 1)) - 1)
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let DeltaSetIndexMap = new r.Struct({
|
|
32
|
+
entryFormat: r.uint16,
|
|
33
|
+
mapCount: r.uint16,
|
|
34
|
+
mapData: new r.Array(MapDataEntry, 'mapCount')
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export default new r.Struct({
|
|
38
|
+
majorVersion: r.uint16,
|
|
39
|
+
minorVersion: r.uint16,
|
|
40
|
+
itemVariationStore: new r.Pointer(r.uint32, ItemVariationStore),
|
|
41
|
+
advanceWidthMapping: new r.Pointer(r.uint32, DeltaSetIndexMap),
|
|
42
|
+
LSBMapping: new r.Pointer(r.uint32, DeltaSetIndexMap),
|
|
43
|
+
RSBMapping: new r.Pointer(r.uint32, DeltaSetIndexMap)
|
|
44
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
import { LookupList } from './opentype';
|
|
3
|
+
import { GPOSLookup } from './GPOS';
|
|
4
|
+
|
|
5
|
+
let JstfGSUBModList = new r.Array(r.uint16, r.uint16);
|
|
6
|
+
|
|
7
|
+
let JstfPriority = new r.Struct({
|
|
8
|
+
shrinkageEnableGSUB: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
9
|
+
shrinkageDisableGSUB: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
10
|
+
shrinkageEnableGPOS: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
11
|
+
shrinkageDisableGPOS: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
12
|
+
shrinkageJstfMax: new r.Pointer(r.uint16, new LookupList(GPOSLookup)),
|
|
13
|
+
extensionEnableGSUB: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
14
|
+
extensionDisableGSUB: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
15
|
+
extensionEnableGPOS: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
16
|
+
extensionDisableGPOS: new r.Pointer(r.uint16, JstfGSUBModList),
|
|
17
|
+
extensionJstfMax: new r.Pointer(r.uint16, new LookupList(GPOSLookup))
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
let JstfLangSys = new r.Array(new r.Pointer(r.uint16, JstfPriority), r.uint16);
|
|
21
|
+
|
|
22
|
+
let JstfLangSysRecord = new r.Struct({
|
|
23
|
+
tag: new r.String(4),
|
|
24
|
+
jstfLangSys: new r.Pointer(r.uint16, JstfLangSys)
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let JstfScript = new r.Struct({
|
|
28
|
+
extenderGlyphs: new r.Pointer(r.uint16, new r.Array(r.uint16, r.uint16)), // array of glyphs to extend line length
|
|
29
|
+
defaultLangSys: new r.Pointer(r.uint16, JstfLangSys),
|
|
30
|
+
langSysCount: r.uint16,
|
|
31
|
+
langSysRecords: new r.Array(JstfLangSysRecord, 'langSysCount')
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
let JstfScriptRecord = new r.Struct({
|
|
35
|
+
tag: new r.String(4),
|
|
36
|
+
script: new r.Pointer(r.uint16, JstfScript, {type: 'parent'})
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export default new r.Struct({
|
|
40
|
+
version: r.uint32, // should be 0x00010000
|
|
41
|
+
scriptCount: r.uint16,
|
|
42
|
+
scriptList: new r.Array(JstfScriptRecord, 'scriptCount')
|
|
43
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
|
|
3
|
+
// Linear Threshold table
|
|
4
|
+
// Records the ppem for each glyph at which the scaling becomes linear again,
|
|
5
|
+
// despite instructions effecting the advance width
|
|
6
|
+
export default new r.Struct({
|
|
7
|
+
version: r.uint16,
|
|
8
|
+
numGlyphs: r.uint16,
|
|
9
|
+
yPels: new r.Array(r.uint8, 'numGlyphs')
|
|
10
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
|
|
3
|
+
var OS2 = new r.VersionedStruct(r.uint16, {
|
|
4
|
+
header: {
|
|
5
|
+
xAvgCharWidth: r.int16, // average weighted advance width of lower case letters and space
|
|
6
|
+
usWeightClass: r.uint16, // visual weight of stroke in glyphs
|
|
7
|
+
usWidthClass: r.uint16, // relative change from the normal aspect ratio (width to height ratio)
|
|
8
|
+
fsType: new r.Bitfield(r.uint16, [ // Indicates font embedding licensing rights
|
|
9
|
+
null, 'noEmbedding', 'viewOnly', 'editable', null,
|
|
10
|
+
null, null, null, 'noSubsetting', 'bitmapOnly'
|
|
11
|
+
]),
|
|
12
|
+
ySubscriptXSize: r.int16, // recommended horizontal size in pixels for subscripts
|
|
13
|
+
ySubscriptYSize: r.int16, // recommended vertical size in pixels for subscripts
|
|
14
|
+
ySubscriptXOffset: r.int16, // recommended horizontal offset for subscripts
|
|
15
|
+
ySubscriptYOffset: r.int16, // recommended vertical offset form the baseline for subscripts
|
|
16
|
+
ySuperscriptXSize: r.int16, // recommended horizontal size in pixels for superscripts
|
|
17
|
+
ySuperscriptYSize: r.int16, // recommended vertical size in pixels for superscripts
|
|
18
|
+
ySuperscriptXOffset: r.int16, // recommended horizontal offset for superscripts
|
|
19
|
+
ySuperscriptYOffset: r.int16, // recommended vertical offset from the baseline for superscripts
|
|
20
|
+
yStrikeoutSize: r.int16, // width of the strikeout stroke
|
|
21
|
+
yStrikeoutPosition: r.int16, // position of the strikeout stroke relative to the baseline
|
|
22
|
+
sFamilyClass: r.int16, // classification of font-family design
|
|
23
|
+
panose: new r.Array(r.uint8, 10), // describe the visual characteristics of a given typeface
|
|
24
|
+
ulCharRange: new r.Array(r.uint32, 4),
|
|
25
|
+
vendorID: new r.String(4), // four character identifier for the font vendor
|
|
26
|
+
fsSelection: new r.Bitfield(r.uint16, [ // bit field containing information about the font
|
|
27
|
+
'italic', 'underscore', 'negative', 'outlined', 'strikeout',
|
|
28
|
+
'bold', 'regular', 'useTypoMetrics', 'wws', 'oblique'
|
|
29
|
+
]),
|
|
30
|
+
usFirstCharIndex: r.uint16, // The minimum Unicode index in this font
|
|
31
|
+
usLastCharIndex: r.uint16 // The maximum Unicode index in this font
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// The Apple version of this table ends here, but the Microsoft one continues on...
|
|
35
|
+
0: {},
|
|
36
|
+
|
|
37
|
+
1: {
|
|
38
|
+
typoAscender: r.int16,
|
|
39
|
+
typoDescender: r.int16,
|
|
40
|
+
typoLineGap: r.int16,
|
|
41
|
+
winAscent: r.uint16,
|
|
42
|
+
winDescent: r.uint16,
|
|
43
|
+
codePageRange: new r.Array(r.uint32, 2)
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
2: {
|
|
47
|
+
// these should be common with version 1 somehow
|
|
48
|
+
typoAscender: r.int16,
|
|
49
|
+
typoDescender: r.int16,
|
|
50
|
+
typoLineGap: r.int16,
|
|
51
|
+
winAscent: r.uint16,
|
|
52
|
+
winDescent: r.uint16,
|
|
53
|
+
codePageRange: new r.Array(r.uint32, 2),
|
|
54
|
+
|
|
55
|
+
xHeight: r.int16,
|
|
56
|
+
capHeight: r.int16,
|
|
57
|
+
defaultChar: r.uint16,
|
|
58
|
+
breakChar: r.uint16,
|
|
59
|
+
maxContent: r.uint16
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
5: {
|
|
63
|
+
typoAscender: r.int16,
|
|
64
|
+
typoDescender: r.int16,
|
|
65
|
+
typoLineGap: r.int16,
|
|
66
|
+
winAscent: r.uint16,
|
|
67
|
+
winDescent: r.uint16,
|
|
68
|
+
codePageRange: new r.Array(r.uint32, 2),
|
|
69
|
+
|
|
70
|
+
xHeight: r.int16,
|
|
71
|
+
capHeight: r.int16,
|
|
72
|
+
defaultChar: r.uint16,
|
|
73
|
+
breakChar: r.uint16,
|
|
74
|
+
maxContent: r.uint16,
|
|
75
|
+
|
|
76
|
+
usLowerOpticalPointSize: r.uint16,
|
|
77
|
+
usUpperOpticalPointSize: r.uint16
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
let versions = OS2.versions;
|
|
82
|
+
versions[3] = versions[4] = versions[2];
|
|
83
|
+
|
|
84
|
+
export default OS2;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
|
|
3
|
+
// PCL 5 Table
|
|
4
|
+
// NOTE: The PCLT table is strongly discouraged for OpenType fonts with TrueType outlines
|
|
5
|
+
export default new r.Struct({
|
|
6
|
+
version: r.uint16,
|
|
7
|
+
fontNumber: r.uint32,
|
|
8
|
+
pitch: r.uint16,
|
|
9
|
+
xHeight: r.uint16,
|
|
10
|
+
style: r.uint16,
|
|
11
|
+
typeFamily: r.uint16,
|
|
12
|
+
capHeight: r.uint16,
|
|
13
|
+
symbolSet: r.uint16,
|
|
14
|
+
typeface: new r.String(16),
|
|
15
|
+
characterComplement: new r.String(8),
|
|
16
|
+
fileName: new r.String(6),
|
|
17
|
+
strokeWeight: new r.String(1),
|
|
18
|
+
widthType: new r.String(1),
|
|
19
|
+
serifStyle: r.uint8,
|
|
20
|
+
reserved: new r.Reserved(r.uint8)
|
|
21
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
|
|
3
|
+
let AxisRecord = new r.Struct({
|
|
4
|
+
axisTag: new r.String(4),
|
|
5
|
+
axisNameID: r.uint16,
|
|
6
|
+
axisOrdering: r.uint16
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// Formats 1–3 share axisIndex after format; format 4 uses axisCount instead.
|
|
10
|
+
let AxisValue = new r.VersionedStruct(r.uint16, {
|
|
11
|
+
1: {
|
|
12
|
+
axisIndex: r.uint16,
|
|
13
|
+
flags: r.uint16,
|
|
14
|
+
valueNameID: r.uint16,
|
|
15
|
+
value: r.fixed32
|
|
16
|
+
},
|
|
17
|
+
2: {
|
|
18
|
+
axisIndex: r.uint16,
|
|
19
|
+
flags: r.uint16,
|
|
20
|
+
valueNameID: r.uint16,
|
|
21
|
+
nominalValue: r.fixed32,
|
|
22
|
+
rangeMinValue: r.fixed32,
|
|
23
|
+
rangeMaxValue: r.fixed32
|
|
24
|
+
},
|
|
25
|
+
3: {
|
|
26
|
+
axisIndex: r.uint16,
|
|
27
|
+
flags: r.uint16,
|
|
28
|
+
valueNameID: r.uint16,
|
|
29
|
+
value: r.fixed32,
|
|
30
|
+
linkedValue: r.fixed32
|
|
31
|
+
},
|
|
32
|
+
4: {
|
|
33
|
+
axisCount: r.uint16,
|
|
34
|
+
flags: r.uint16,
|
|
35
|
+
valueNameID: r.uint16,
|
|
36
|
+
axisValues: new r.Array(new r.Struct({
|
|
37
|
+
axisIndex: r.uint16,
|
|
38
|
+
value: r.fixed32
|
|
39
|
+
}), 'axisCount')
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
let AxisValueArray = new r.Struct({
|
|
44
|
+
axisValues: new r.Array(new r.Pointer(r.uint16, AxisValue), t => t.parent.axisValueCount)
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Use designAxisSize as record stride (OpenType forward-compat).
|
|
48
|
+
class DesignAxisArray {
|
|
49
|
+
_stride(parent) {
|
|
50
|
+
// Spec requires room for Tag + 2×uint16 (8 bytes); never shrink below that.
|
|
51
|
+
return Math.max(8, parent.designAxisSize ?? parent.val?.designAxisSize ?? 8);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
decode(stream, parent) {
|
|
55
|
+
let count = parent.designAxisCount;
|
|
56
|
+
let size = this._stride(parent);
|
|
57
|
+
let res = [];
|
|
58
|
+
for (let i = 0; i < count; i++) {
|
|
59
|
+
let start = stream.pos;
|
|
60
|
+
res.push(AxisRecord.decode(stream, parent));
|
|
61
|
+
stream.pos = start + size;
|
|
62
|
+
}
|
|
63
|
+
return res;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
size(array, parent) {
|
|
67
|
+
return (array?.length ?? 0) * this._stride(parent);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
encode(stream, array, parent) {
|
|
71
|
+
let size = this._stride(parent);
|
|
72
|
+
for (let item of array) {
|
|
73
|
+
let start = stream.pos;
|
|
74
|
+
AxisRecord.encode(stream, item, parent);
|
|
75
|
+
let pad = size - (stream.pos - start);
|
|
76
|
+
if (pad > 0) {
|
|
77
|
+
stream.fill(0, pad);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default new r.VersionedStruct(r.uint32, {
|
|
84
|
+
header: {
|
|
85
|
+
designAxisSize: r.uint16,
|
|
86
|
+
designAxisCount: r.uint16,
|
|
87
|
+
offsetToDesignAxes: new r.Pointer(r.uint32, new DesignAxisArray()),
|
|
88
|
+
axisValueCount: r.uint16,
|
|
89
|
+
offsetToAxisValueOffsets: new r.Pointer(r.uint32, AxisValueArray)
|
|
90
|
+
},
|
|
91
|
+
0x00010000: {},
|
|
92
|
+
0x00010001: {
|
|
93
|
+
elidedFallbackNameID: r.uint16
|
|
94
|
+
},
|
|
95
|
+
// v1.2: same header as v1.1; adds AxisValue format 4.
|
|
96
|
+
0x00010002: {
|
|
97
|
+
elidedFallbackNameID: r.uint16
|
|
98
|
+
}
|
|
99
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as r from 'restructure';
|
|
2
|
+
|
|
3
|
+
// VDMX tables contain ascender/descender overrides for certain (usually small)
|
|
4
|
+
// sizes. This is needed in order to match font metrics on Windows.
|
|
5
|
+
|
|
6
|
+
let Ratio = new r.Struct({
|
|
7
|
+
bCharSet: r.uint8, // Character set
|
|
8
|
+
xRatio: r.uint8, // Value to use for x-Ratio
|
|
9
|
+
yStartRatio: r.uint8, // Starting y-Ratio value
|
|
10
|
+
yEndRatio: r.uint8 // Ending y-Ratio value
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
let vTable = new r.Struct({
|
|
14
|
+
yPelHeight: r.uint16, // yPelHeight to which values apply
|
|
15
|
+
yMax: r.int16, // Maximum value (in pels) for this yPelHeight
|
|
16
|
+
yMin: r.int16 // Minimum value (in pels) for this yPelHeight
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
let VdmxGroup = new r.Struct({
|
|
20
|
+
recs: r.uint16, // Number of height records in this group
|
|
21
|
+
startsz: r.uint8, // Starting yPelHeight
|
|
22
|
+
endsz: r.uint8, // Ending yPelHeight
|
|
23
|
+
entries: new r.Array(vTable, 'recs') // The VDMX records
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export default new r.Struct({
|
|
27
|
+
version: r.uint16, // Version number (0 or 1)
|
|
28
|
+
numRecs: r.uint16, // Number of VDMX groups present
|
|
29
|
+
numRatios: r.uint16, // Number of aspect ratio groupings
|
|
30
|
+
ratioRanges: new r.Array(Ratio, 'numRatios'), // Ratio ranges
|
|
31
|
+
offsets: new r.Array(r.uint16, 'numRatios'), // Offset to the VDMX group for this ratio range
|
|
32
|
+
groups: new r.Array(VdmxGroup, 'numRecs') // The actual VDMX groupings
|
|
33
|
+
});
|