|
|
import r from 'restructure';
|
|
|
import { resolveLength } from 'restructure/src/utils';
|
|
|
import CFFDict from './CFFDict';
|
|
|
import CFFIndex from './CFFIndex';
|
|
|
import CFFPointer from './CFFPointer';
|
|
|
import CFFPrivateDict from './CFFPrivateDict';
|
|
|
import StandardStrings from './CFFStandardStrings';
|
|
|
import { StandardEncoding, ExpertEncoding } from './CFFEncodings';
|
|
|
import { ISOAdobeCharset, ExpertCharset, ExpertSubsetCharset } from './CFFCharsets';
|
|
|
import { ItemVariationStore } from '../tables/variations';
|
|
|
|
|
|
// Checks if an operand is an index of a predefined value,
|
|
|
// otherwise delegates to the provided type.
|
|
|
class PredefinedOp {
|
|
|
constructor(predefinedOps, type) {
|
|
|
this.predefinedOps = predefinedOps;
|
|
|
this.type = type;
|
|
|
}
|
|
|
|
|
|
decode(stream, parent, operands) {
|
|
|
if (this.predefinedOps[operands[0]]) {
|
|
|
return this.predefinedOps[operands[0]];
|
|
|
}
|
|
|
|
|
|
return this.type.decode(stream, parent, operands);
|
|
|
}
|
|
|
|
|
|
size(value, ctx) {
|
|
|
return this.type.size(value, ctx);
|
|
|
}
|
|
|
|
|
|
encode(stream, value, ctx) {
|
|
|
let index = this.predefinedOps.indexOf(value);
|
|
|
if (index !== -1) {
|
|
|
return index;
|
|
|
}
|
|
|
|
|
|
return this.type.encode(stream, value, ctx);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class CFFEncodingVersion extends r.Number {
|
|
|
constructor() {
|
|
|
super('UInt8');
|
|
|
}
|
|
|
|
|
|
decode(stream) {
|
|
|
return r.uint8.decode(stream) & 0x7f;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let Range1 = new r.Struct({
|
|
|
first: r.uint16,
|
|
|
nLeft: r.uint8
|
|
|
});
|
|
|
|
|
|
let Range2 = new r.Struct({
|
|
|
first: r.uint16,
|
|
|
nLeft: r.uint16
|
|
|
});
|
|
|
|
|
|
let CFFCustomEncoding = new r.VersionedStruct(new CFFEncodingVersion(), {
|
|
|
0: {
|
|
|
nCodes: r.uint8,
|
|
|
codes: new r.Array(r.uint8, 'nCodes')
|
|
|
},
|
|
|
|
|
|
1: {
|
|
|
nRanges: r.uint8,
|
|
|
ranges: new r.Array(Range1, 'nRanges')
|
|
|
}
|
|
|
|
|
|
// TODO: supplement?
|
|
|
});
|
|
|
|
|
|
let CFFEncoding = new PredefinedOp([ StandardEncoding, ExpertEncoding ], new CFFPointer(CFFCustomEncoding, { lazy: true }));
|
|
|
|
|
|
// Decodes an array of ranges until the total
|
|
|
// length is equal to the provided length.
|
|
|
class RangeArray extends r.Array {
|
|
|
decode(stream, parent) {
|
|
|
let length = resolveLength(this.length, stream, parent);
|
|
|
let count = 0;
|
|
|
let res = [];
|
|
|
while (count < length) {
|
|
|
let range = this.type.decode(stream, parent);
|
|
|
range.offset = count;
|
|
|
count += range.nLeft + 1;
|
|
|
res.push(range);
|
|
|
}
|
|
|
|
|
|
return res;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let CFFCustomCharset = new r.VersionedStruct(r.uint8, {
|
|
|
0: {
|
|
|
glyphs: new r.Array(r.uint16, t => t.parent.CharStrings.length - 1)
|
|
|
},
|
|
|
|
|
|
1: {
|
|
|
ranges: new RangeArray(Range1, t => t.parent.CharStrings.length - 1)
|
|
|
},
|
|
|
|
|
|
2: {
|
|
|
ranges: new RangeArray(Range2, t => t.parent.CharStrings.length - 1)
|
|
|
}
|
|
|
});
|
|
|
|
|
|
let CFFCharset = new PredefinedOp([ ISOAdobeCharset, ExpertCharset, ExpertSubsetCharset ], new CFFPointer(CFFCustomCharset, {lazy: true}));
|
|
|
|
|
|
let FDRange3 = new r.Struct({
|
|
|
first: r.uint16,
|
|
|
fd: r.uint8
|
|
|
});
|
|
|
|
|
|
let FDRange4 = new r.Struct({
|
|
|
first: r.uint32,
|
|
|
fd: r.uint16
|
|
|
});
|
|
|
|
|
|
let FDSelect = new r.VersionedStruct(r.uint8, {
|
|
|
0: {
|
|
|
fds: new r.Array(r.uint8, t => t.parent.CharStrings.length)
|
|
|
},
|
|
|
|
|
|
3: {
|
|
|
nRanges: r.uint16,
|
|
|
ranges: new r.Array(FDRange3, 'nRanges'),
|
|
|
sentinel: r.uint16
|
|
|
},
|
|
|
|
|
|
4: {
|
|
|
nRanges: r.uint32,
|
|
|
ranges: new r.Array(FDRange4, 'nRanges'),
|
|
|
sentinel: r.uint32
|
|
|
}
|
|
|
});
|
|
|
|
|
|
let ptr = new CFFPointer(CFFPrivateDict);
|
|
|
class CFFPrivateOp {
|
|
|
decode(stream, parent, operands) {
|
|
|
parent.length = operands[0];
|
|
|
return ptr.decode(stream, parent, [operands[1]]);
|
|
|
}
|
|
|
|
|
|
size(dict, ctx) {
|
|
|
return [CFFPrivateDict.size(dict, ctx, false), ptr.size(dict, ctx)[0]];
|
|
|
}
|
|
|
|
|
|
encode(stream, dict, ctx) {
|
|
|
return [CFFPrivateDict.size(dict, ctx, false), ptr.encode(stream, dict, ctx)[0]];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let FontDict = new CFFDict([
|
|
|
// key name type(s) default
|
|
|
[18, 'Private', new CFFPrivateOp, null],
|
|
|
[[12, 38], 'FontName', 'sid', null]
|
|
|
]);
|
|
|
|
|
|
let CFFTopDict = new CFFDict([
|
|
|
// key name type(s) default
|
|
|
[[12, 30], 'ROS', ['sid', 'sid', 'number'], null],
|
|
|
|
|
|
[0, 'version', 'sid', null],
|
|
|
[1, 'Notice', 'sid', null],
|
|
|
[[12, 0], 'Copyright', 'sid', null],
|
|
|
[2, 'FullName', 'sid', null],
|
|
|
[3, 'FamilyName', 'sid', null],
|
|
|
[4, 'Weight', 'sid', null],
|
|
|
[[12, 1], 'isFixedPitch', 'boolean', false],
|
|
|
[[12, 2], 'ItalicAngle', 'number', 0],
|
|
|
[[12, 3], 'UnderlinePosition', 'number', -100],
|
|
|
[[12, 4], 'UnderlineThickness', 'number', 50],
|
|
|
[[12, 5], 'PaintType', 'number', 0],
|
|
|
[[12, 6], 'CharstringType', 'number', 2],
|
|
|
[[12, 7], 'FontMatrix', 'array', [0.001, 0, 0, 0.001, 0, 0]],
|
|
|
[13, 'UniqueID', 'number', null],
|
|
|
[5, 'FontBBox', 'array', [0, 0, 0, 0]],
|
|
|
[[12, 8], 'StrokeWidth', 'number', 0],
|
|
|
[14, 'XUID', 'array', null],
|
|
|
[15, 'charset', CFFCharset, ISOAdobeCharset],
|
|
|
[16, 'Encoding', CFFEncoding, StandardEncoding],
|
|
|
[17, 'CharStrings', new CFFPointer(new CFFIndex), null],
|
|
|
[18, 'Private', new CFFPrivateOp, null],
|
|
|
[[12, 20], 'SyntheticBase', 'number', null],
|
|
|
[[12, 21], 'PostScript', 'sid', null],
|
|
|
[[12, 22], 'BaseFontName', 'sid', null],
|
|
|
[[12, 23], 'BaseFontBlend', 'delta', null],
|
|
|
|
|
|
// CID font specific
|
|
|
[[12, 31], 'CIDFontVersion', 'number', 0],
|
|
|
[[12, 32], 'CIDFontRevision', 'number', 0],
|
|
|
[[12, 33], 'CIDFontType', 'number', 0],
|
|
|
[[12, 34], 'CIDCount', 'number', 8720],
|
|
|
[[12, 35], 'UIDBase', 'number', null],
|
|
|
[[12, 37], 'FDSelect', new CFFPointer(FDSelect), null],
|
|
|
[[12, 36], 'FDArray', new CFFPointer(new CFFIndex(FontDict)), null],
|
|
|
[[12, 38], 'FontName', 'sid', null]
|
|
|
]);
|
|
|
|
|
|
let VariationStore = new r.Struct({
|
|
|
length: r.uint16,
|
|
|
itemVariationStore: ItemVariationStore
|
|
|
})
|
|
|
|
|
|
let CFF2TopDict = new CFFDict([
|
|
|
[[12, 7], 'FontMatrix', 'array', [0.001, 0, 0, 0.001, 0, 0]],
|
|
|
[17, 'CharStrings', new CFFPointer(new CFFIndex), null],
|
|
|
[[12, 37], 'FDSelect', new CFFPointer(FDSelect), null],
|
|
|
[[12, 36], 'FDArray', new CFFPointer(new CFFIndex(FontDict)), null],
|
|
|
[24, 'vstore', new CFFPointer(VariationStore), null],
|
|
|
[25, 'maxstack', 'number', 193]
|
|
|
]);
|
|
|
|
|
|
let CFFTop = new r.VersionedStruct(r.fixed16, {
|
|
|
1: {
|
|
|
hdrSize: r.uint8,
|
|
|
offSize: r.uint8,
|
|
|
nameIndex: new CFFIndex(new r.String('length')),
|
|
|
topDictIndex: new CFFIndex(CFFTopDict),
|
|
|
stringIndex: new CFFIndex(new r.String('length')),
|
|
|
globalSubrIndex: new CFFIndex
|
|
|
},
|
|
|
|
|
|
2: {
|
|
|
hdrSize: r.uint8,
|
|
|
length: r.uint16,
|
|
|
topDict: CFF2TopDict,
|
|
|
globalSubrIndex: new CFFIndex
|
|
|
}
|
|
|
});
|
|
|
|
|
|
export default CFFTop;
|
|
|
|