|
|
import cloneDeep from 'clone';
|
|
|
import Subset from './Subset';
|
|
|
import Directory from '../tables/directory';
|
|
|
import Tables from '../tables';
|
|
|
import TTFGlyphEncoder from '../glyph/TTFGlyphEncoder';
|
|
|
|
|
|
export default class TTFSubset extends Subset {
|
|
|
constructor(font) {
|
|
|
super(font);
|
|
|
this.glyphEncoder = new TTFGlyphEncoder;
|
|
|
}
|
|
|
|
|
|
_addGlyph(gid) {
|
|
|
let glyph = this.font.getGlyph(gid);
|
|
|
let glyf = glyph._decode();
|
|
|
|
|
|
// get the offset to the glyph from the loca table
|
|
|
let curOffset = this.font.loca.offsets[gid];
|
|
|
let nextOffset = this.font.loca.offsets[gid + 1];
|
|
|
|
|
|
let stream = this.font._getTableStream('glyf');
|
|
|
stream.pos += curOffset;
|
|
|
|
|
|
let buffer = stream.readBuffer(nextOffset - curOffset);
|
|
|
|
|
|
// if it is a compound glyph, include its components
|
|
|
if (glyf && glyf.numberOfContours < 0) {
|
|
|
buffer = new Buffer(buffer);
|
|
|
for (let component of glyf.components) {
|
|
|
gid = this.includeGlyph(component.glyphID);
|
|
|
buffer.writeUInt16BE(gid, component.pos);
|
|
|
}
|
|
|
} else if (glyf && this.font._variationProcessor) {
|
|
|
// If this is a TrueType variation glyph, re-encode the path
|
|
|
buffer = this.glyphEncoder.encodeSimple(glyph.path, glyf.instructions);
|
|
|
}
|
|
|
|
|
|
this.glyf.push(buffer);
|
|
|
this.loca.offsets.push(this.offset);
|
|
|
|
|
|
this.hmtx.metrics.push({
|
|
|
advance: glyph.advanceWidth,
|
|
|
bearing: glyph._getMetrics().leftBearing
|
|
|
});
|
|
|
|
|
|
this.offset += buffer.length;
|
|
|
return this.glyf.length - 1;
|
|
|
}
|
|
|
|
|
|
encode(stream) {
|
|
|
// tables required by PDF spec:
|
|
|
// head, hhea, loca, maxp, cvt , prep, glyf, hmtx, fpgm
|
|
|
//
|
|
|
// additional tables required for standalone fonts:
|
|
|
// name, cmap, OS/2, post
|
|
|
|
|
|
this.glyf = [];
|
|
|
this.offset = 0;
|
|
|
this.loca = {
|
|
|
offsets: [],
|
|
|
version: this.font.loca.version
|
|
|
};
|
|
|
|
|
|
this.hmtx = {
|
|
|
metrics: [],
|
|
|
bearings: []
|
|
|
};
|
|
|
|
|
|
// include all the glyphs
|
|
|
// not using a for loop because we need to support adding more
|
|
|
// glyphs to the array as we go, and CoffeeScript caches the length.
|
|
|
let i = 0;
|
|
|
while (i < this.glyphs.length) {
|
|
|
this._addGlyph(this.glyphs[i++]);
|
|
|
}
|
|
|
|
|
|
let maxp = cloneDeep(this.font.maxp);
|
|
|
maxp.numGlyphs = this.glyf.length;
|
|
|
|
|
|
this.loca.offsets.push(this.offset);
|
|
|
|
|
|
let head = cloneDeep(this.font.head);
|
|
|
head.indexToLocFormat = this.loca.version;
|
|
|
|
|
|
let hhea = cloneDeep(this.font.hhea);
|
|
|
hhea.numberOfMetrics = this.hmtx.metrics.length;
|
|
|
|
|
|
// map = []
|
|
|
// for index in [0...256]
|
|
|
// if index < @numGlyphs
|
|
|
// map[index] = index
|
|
|
// else
|
|
|
// map[index] = 0
|
|
|
//
|
|
|
// cmapTable =
|
|
|
// version: 0
|
|
|
// length: 262
|
|
|
// language: 0
|
|
|
// codeMap: map
|
|
|
//
|
|
|
// cmap =
|
|
|
// version: 0
|
|
|
// numSubtables: 1
|
|
|
// tables: [
|
|
|
// platformID: 1
|
|
|
// encodingID: 0
|
|
|
// table: cmapTable
|
|
|
// ]
|
|
|
|
|
|
// TODO: subset prep, cvt, fpgm?
|
|
|
Directory.encode(stream, {
|
|
|
tables: {
|
|
|
head,
|
|
|
hhea,
|
|
|
loca: this.loca,
|
|
|
maxp,
|
|
|
'cvt ': this.font['cvt '],
|
|
|
prep: this.font.prep,
|
|
|
glyf: this.glyf,
|
|
|
hmtx: this.hmtx,
|
|
|
fpgm: this.font.fpgm
|
|
|
|
|
|
// name: clone @font.name
|
|
|
// 'OS/2': clone @font['OS/2']
|
|
|
// post: clone @font.post
|
|
|
// cmap: cmap
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|