import r from 'restructure'; import CFFIndex from './CFFIndex'; import CFFTop from './CFFTop'; import CFFPrivateDict from './CFFPrivateDict'; import standardStrings from './CFFStandardStrings'; class CFFFont { constructor(stream) { this.stream = stream; this.decode(); } static decode(stream) { return new CFFFont(stream); } decode() { let start = this.stream.pos; let top = CFFTop.decode(this.stream); for (let key in top) { let val = top[key]; this[key] = val; } if (this.version < 2) { if (this.topDictIndex.length !== 1) { throw new Error("Only a single font is allowed in CFF"); } this.topDict = this.topDictIndex[0]; } this.isCIDFont = this.topDict.ROS != null; return this; } string(sid) { if (this.version >= 2) { return null; } if (sid < standardStrings.length) { return standardStrings[sid]; } return this.stringIndex[sid - standardStrings.length]; } get postscriptName() { if (this.version < 2) { return this.nameIndex[0]; } return null; } get fullName() { return this.string(this.topDict.FullName); } get familyName() { return this.string(this.topDict.FamilyName); } getCharString(glyph) { this.stream.pos = this.topDict.CharStrings[glyph].offset; return this.stream.readBuffer(this.topDict.CharStrings[glyph].length); } getGlyphName(gid) { // CFF2 glyph names are in the post table. if (this.version >= 2) { return null; } // CID-keyed fonts don't have glyph names if (this.isCIDFont) { return null; } let { charset } = this.topDict; if (Array.isArray(charset)) { return charset[gid]; } if (gid === 0) { return '.notdef'; } gid -= 1; switch (charset.version) { case 0: return this.string(charset.glyphs[gid]); case 1: case 2: for (let i = 0; i < charset.ranges.length; i++) { let range = charset.ranges[i]; if (range.offset <= gid && gid <= range.offset + range.nLeft) { return this.string(range.first + (gid - range.offset)); } } break; } return null; } fdForGlyph(gid) { if (!this.topDict.FDSelect) { return null; } switch (this.topDict.FDSelect.version) { case 0: return this.topDict.FDSelect.fds[gid]; case 3: case 4: let { ranges } = this.topDict.FDSelect; let low = 0; let high = ranges.length - 1; while (low <= high) { let mid = (low + high) >> 1; if (gid < ranges[mid].first) { high = mid - 1; } else if (mid < high && gid > ranges[mid + 1].first) { low = mid + 1; } else { return ranges[mid].fd; } } default: throw new Error(`Unknown FDSelect version: ${this.topDict.FDSelect.version}`); } } privateDictForGlyph(gid) { if (this.topDict.FDSelect) { let fd = this.fdForGlyph(gid); if (this.topDict.FDArray[fd]) { return this.topDict.FDArray[fd].Private; } return null; } if (this.version < 2) { return this.topDict.Private; } return this.topDict.FDArray[0].Private; } } export default CFFFont;