|
|
import Subset from './Subset';
|
|
|
import CFFTop from '../cff/CFFTop';
|
|
|
import CFFPrivateDict from '../cff/CFFPrivateDict';
|
|
|
import standardStrings from '../cff/CFFStandardStrings';
|
|
|
|
|
|
export default class CFFSubset extends Subset {
|
|
|
constructor(font) {
|
|
|
super(font);
|
|
|
|
|
|
this.cff = this.font['CFF '];
|
|
|
if (!this.cff) {
|
|
|
throw new Error('Not a CFF Font');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
subsetCharstrings() {
|
|
|
this.charstrings = [];
|
|
|
let gsubrs = {};
|
|
|
|
|
|
for (let gid of this.glyphs) {
|
|
|
this.charstrings.push(this.cff.getCharString(gid));
|
|
|
|
|
|
let glyph = this.font.getGlyph(gid);
|
|
|
let path = glyph.path; // this causes the glyph to be parsed
|
|
|
|
|
|
for (let subr in glyph._usedGsubrs) {
|
|
|
gsubrs[subr] = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this.gsubrs = this.subsetSubrs(this.cff.globalSubrIndex, gsubrs);
|
|
|
}
|
|
|
|
|
|
subsetSubrs(subrs, used) {
|
|
|
let res = [];
|
|
|
for (let i = 0; i < subrs.length; i++) {
|
|
|
let subr = subrs[i];
|
|
|
if (used[i]) {
|
|
|
this.cff.stream.pos = subr.offset;
|
|
|
res.push(this.cff.stream.readBuffer(subr.length));
|
|
|
} else {
|
|
|
res.push(new Buffer([11])); // return
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
subsetFontdict(topDict) {
|
|
|
topDict.FDArray = [];
|
|
|
topDict.FDSelect = {
|
|
|
version: 0,
|
|
|
fds: []
|
|
|
};
|
|
|
|
|
|
let used_fds = {};
|
|
|
let used_subrs = [];
|
|
|
for (let gid of this.glyphs) {
|
|
|
let fd = this.cff.fdForGlyph(gid);
|
|
|
if (fd == null) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (!used_fds[fd]) {
|
|
|
topDict.FDArray.push(Object.assign({}, this.cff.topDict.FDArray[fd]));
|
|
|
used_subrs.push({});
|
|
|
}
|
|
|
|
|
|
used_fds[fd] = true;
|
|
|
topDict.FDSelect.fds.push(topDict.FDArray.length - 1);
|
|
|
|
|
|
let glyph = this.font.getGlyph(gid);
|
|
|
let path = glyph.path; // this causes the glyph to be parsed
|
|
|
for (let subr in glyph._usedSubrs) {
|
|
|
used_subrs[used_subrs.length - 1][subr] = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for (let i = 0; i < topDict.FDArray.length; i++) {
|
|
|
let dict = topDict.FDArray[i];
|
|
|
delete dict.FontName;
|
|
|
if (dict.Private && dict.Private.Subrs) {
|
|
|
dict.Private = Object.assign({}, dict.Private);
|
|
|
dict.Private.Subrs = this.subsetSubrs(dict.Private.Subrs, used_subrs[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
createCIDFontdict(topDict) {
|
|
|
let used_subrs = {};
|
|
|
for (let gid of this.glyphs) {
|
|
|
let glyph = this.font.getGlyph(gid);
|
|
|
let path = glyph.path; // this causes the glyph to be parsed
|
|
|
|
|
|
for (let subr in glyph._usedSubrs) {
|
|
|
used_subrs[subr] = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let privateDict = Object.assign({}, this.cff.topDict.Private);
|
|
|
if (this.cff.topDict.Private && this.cff.topDict.Private.Subrs) {
|
|
|
privateDict.Subrs = this.subsetSubrs(this.cff.topDict.Private.Subrs, used_subrs);
|
|
|
}
|
|
|
|
|
|
topDict.FDArray = [{ Private: privateDict }];
|
|
|
return topDict.FDSelect = {
|
|
|
version: 3,
|
|
|
nRanges: 1,
|
|
|
ranges: [{ first: 0, fd: 0 }],
|
|
|
sentinel: this.charstrings.length
|
|
|
};
|
|
|
}
|
|
|
|
|
|
addString(string) {
|
|
|
if (!string) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
if (!this.strings) {
|
|
|
this.strings = [];
|
|
|
}
|
|
|
|
|
|
this.strings.push(string);
|
|
|
return standardStrings.length + this.strings.length - 1;
|
|
|
}
|
|
|
|
|
|
encode(stream) {
|
|
|
this.subsetCharstrings();
|
|
|
|
|
|
let charset = {
|
|
|
version: this.charstrings.length > 255 ? 2 : 1,
|
|
|
ranges: [{ first: 1, nLeft: this.charstrings.length - 2 }]
|
|
|
};
|
|
|
|
|
|
let topDict = Object.assign({}, this.cff.topDict);
|
|
|
topDict.Private = null;
|
|
|
topDict.charset = charset;
|
|
|
topDict.Encoding = null;
|
|
|
topDict.CharStrings = this.charstrings;
|
|
|
|
|
|
for (let key of ['version', 'Notice', 'Copyright', 'FullName', 'FamilyName', 'Weight', 'PostScript', 'BaseFontName', 'FontName']) {
|
|
|
topDict[key] = this.addString(this.cff.string(topDict[key]));
|
|
|
}
|
|
|
|
|
|
topDict.ROS = [this.addString('Adobe'), this.addString('Identity'), 0];
|
|
|
topDict.CIDCount = this.charstrings.length;
|
|
|
|
|
|
if (this.cff.isCIDFont) {
|
|
|
this.subsetFontdict(topDict);
|
|
|
} else {
|
|
|
this.createCIDFontdict(topDict);
|
|
|
}
|
|
|
|
|
|
let top = {
|
|
|
version: 1,
|
|
|
hdrSize: this.cff.hdrSize,
|
|
|
offSize: 4,
|
|
|
header: this.cff.header,
|
|
|
nameIndex: [this.cff.postscriptName],
|
|
|
topDictIndex: [topDict],
|
|
|
stringIndex: this.strings,
|
|
|
globalSubrIndex: this.gsubrs
|
|
|
};
|
|
|
|
|
|
CFFTop.encode(stream, top);
|
|
|
}
|
|
|
}
|
|
|
|