jspdf-customfonts
Version:
Add Custom fonts support to jsPDF
1,762 lines (1,418 loc) • 68.9 kB
JavaScript
/*!
* jsPDF customfonts plugin v0.0.4-rc.4
* Copyright (c) 2018 GH Lee, https://github.com/sphilee/jsPDF-CustomFonts-support
*
* Licensed under the MIT License.
* https://opensource.org/licenses/mit-license
*
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("jspdf"));
else if(typeof define === 'function' && define.amd)
define(["jspdf"], factory);
else {
var a = typeof exports === 'object' ? factory(require("jspdf")) : factory(root["jsPDF"]);
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(window, function(__WEBPACK_EXTERNAL_MODULE__2__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.postProcessText = exports.putFont = void 0;
var putFont = function putFont(_ref) {
var font = _ref.font,
newObject = _ref.newObject,
out = _ref.out;
if (font.id.slice(1) < 15) return;
var dictionary = font.metadata.embedTTF(font.encoding, newObject, out);
if (dictionary) {
font.objectNumber = dictionary;
font.isAlreadyPutted = true;
}
};
exports.putFont = putFont;
var postProcessText = function postProcessText(args) {
var text = args.text,
_args$mutex = args.mutex,
activeFontKey = _args$mutex.activeFontKey,
fonts = _args$mutex.fonts;
var isHex = activeFontKey.slice(1) >= 15;
var activeFont = fonts[activeFontKey];
var _activeFont$metadata = activeFont.metadata,
encode = _activeFont$metadata.encode,
subset = _activeFont$metadata.subset;
args.text = isHex ? text.map(function (str) {
return Array.isArray(str) ? [encode(subset, str[0]), str[1], str[2]] : encode(subset, str);
}) : text;
args.mutex.isHex = isHex;
};
exports.postProcessText = postProcessText;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = function () {
TTFFont.open = function (filename, name, vfs) {
var contents;
contents = b64ToByteArray(vfs);
return new TTFFont(contents, name);
};
function TTFFont(rawData, name) {
var data, i, numFonts, offset, offsets, version, _i, _j, _len;
this.rawData = rawData;
data = this.contents = new Data(rawData);
this.contents.pos = 4;
if (data.readString(4) === 'ttcf') {
if (!name) {
throw new Error("Must specify a font name for TTC files.");
}
offsets = [];
throw new Error("Font " + name + " not found in TTC file.");
} else {
data.pos = 0;
this.parse();
this.subset = new Subset(this);
this.registerTTF();
}
}
TTFFont.prototype.parse = function () {
this.directory = new Directory(this.contents);
this.head = new HeadTable(this);
this.name = new NameTable(this);
this.cmap = new CmapTable(this);
this.hhea = new HheaTable(this);
this.maxp = new MaxpTable(this);
this.hmtx = new HmtxTable(this);
this.post = new PostTable(this);
this.os2 = new OS2Table(this);
this.loca = new LocaTable(this);
this.glyf = new GlyfTable(this);
this.ascender = this.os2.exists && this.os2.ascender || this.hhea.ascender;
this.decender = this.os2.exists && this.os2.decender || this.hhea.decender;
this.lineGap = this.os2.exists && this.os2.lineGap || this.hhea.lineGap;
this.bbox = [this.head.xMin, this.head.yMin, this.head.xMax, this.head.yMax];
return this;
};
TTFFont.prototype.registerTTF = function () {
var e, hi, low, raw, _ref;
this.scaleFactor = 1000.0 / this.head.unitsPerEm;
this.bbox = function () {
var _i, _len, _ref, _results;
_ref = this.bbox;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
e = _ref[_i];
_results.push(Math.round(e * this.scaleFactor));
}
return _results;
}.call(this);
this.stemV = 0;
if (this.post.exists) {
raw = this.post.italic_angle;
hi = raw >> 16;
low = raw & 0xFF;
if (hi & 0x8000 !== 0) {
hi = -((hi ^ 0xFFFF) + 1);
}
this.italicAngle = +("" + hi + "." + low);
} else {
this.italicAngle = 0;
}
this.ascender = Math.round(this.ascender * this.scaleFactor);
this.decender = Math.round(this.decender * this.scaleFactor);
this.lineGap = Math.round(this.lineGap * this.scaleFactor);
this.capHeight = this.os2.exists && this.os2.capHeight || this.ascender;
this.xHeight = this.os2.exists && this.os2.xHeight || 0;
this.familyClass = (this.os2.exists && this.os2.familyClass || 0) >> 8;
this.isSerif = (_ref = this.familyClass) === 1 || _ref === 2 || _ref === 3 || _ref === 4 || _ref === 5 || _ref === 7;
this.isScript = this.familyClass === 10;
this.flags = 0;
if (this.post.isFixedPitch) {
this.flags |= 1 << 0;
}
if (this.isSerif) {
this.flags |= 1 << 1;
}
if (this.isScript) {
this.flags |= 1 << 3;
}
if (this.italicAngle !== 0) {
this.flags |= 1 << 6;
}
this.flags |= 1 << 5;
if (!this.cmap.unicode) {
throw new Error('No unicode cmap for font');
}
};
TTFFont.prototype.characterToGlyph = function (character) {
var _ref;
return ((_ref = this.cmap.unicode) !== undefined ? _ref.codeMap[character] : undefined) || 0;
};
TTFFont.prototype.widthOfGlyph = function (glyph) {
var scale;
scale = 1000.0 / this.head.unitsPerEm;
return this.hmtx.forGlyph(glyph).advance * scale;
};
TTFFont.prototype.widthOfString = function (string, size, charSpace) {
var charCode, i, scale, width, _i, _ref;
string = '' + string;
width = 0;
for (i = _i = 0, _ref = string.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
charCode = string.charCodeAt(i);
width += this.widthOfGlyph(this.characterToGlyph(charCode)) + charSpace * (1000 / size) || 0;
}
scale = size / 1000;
return width * scale;
};
TTFFont.prototype.lineHeight = function (size, includeGap) {
var gap;
if (includeGap === undefined) {
includeGap = false;
}
gap = includeGap ? this.lineGap : 0;
return (this.ascender + gap - this.decender) / 1000 * size;
};
TTFFont.prototype.encode = function (font, text, reverse) {
font.use(text);
text = reverse ? reverseString(font.encodeText(text)) : font.encodeText(text);
text = function () {
var _results = [];
for (var i = 0, _ref2 = text.length; 0 <= _ref2 ? i < _ref2 : i > _ref2; 0 <= _ref2 ? i++ : i--) {
_results.push(text.charCodeAt(i).toString(16));
}
return _results;
}().join('');
return text;
};
TTFFont.prototype.embedTTF = function (encoding, newObject, out) {
/**
* It is a function to extract a table to make a custom font.
* Returns the object number.
@function
@param {Object} dictionary
@returns {Number} objectNumber
*/
function makeFontTable(data) {
var tableNumber;
if (data.Type === "Font") {
if (isHex) data.ToUnicode = makeFontTable(data.ToUnicode) + ' 0 R';
data.FontDescriptor = makeFontTable(data.FontDescriptor) + ' 0 R';
tableNumber = newObject();
out(PDFObject.convert(data));
} else if (data.Type === "FontDescriptor") {
data.FontFile2 = makeFontTable(data.FontFile2) + ' 0 R';
tableNumber = newObject();
out(PDFObject.convert(data));
} else {
tableNumber = newObject();
out('<</Length1 ' + data.length + '>>');
out('stream');
Array.isArray(data) || data.constructor === Uint8Array ? out(toString(data)) : out(data);
out('endstream');
}
out('endobj');
return tableNumber;
}
var charWidths, cmap, code, data, descriptor, firstChar, fontfile, glyph;
var isHex = encoding === 'MacRomanEncoding' ? true : false;
data = this.subset.encode();
fontfile = {};
fontfile = isHex ? data : this.rawData;
descriptor = {
Type: 'FontDescriptor',
FontName: this.subset.postscriptName,
FontFile2: fontfile,
FontBBox: this.bbox,
Flags: this.flags,
StemV: this.stemV,
ItalicAngle: this.italicAngle,
Ascent: this.ascender,
Descent: this.decender,
CapHeight: this.capHeight,
XHeight: this.xHeight
};
firstChar = +Object.keys(this.subset.cmap)[0];
if (firstChar !== 33 && isHex) return false;
charWidths = function () {
var _ref, _results;
_ref = this.subset.cmap;
_results = [];
for (code in _ref) {
if (Object.prototype.hasOwnProperty.call(_ref, code)) {
glyph = _ref[code];
_results.push(Math.round(this.widthOfGlyph(glyph)));
}
}
return _results;
}.call(this);
cmap = toUnicodeCmap(this.subset.subset);
var dictionary = isHex ? {
Type: 'Font',
BaseFont: this.subset.postscriptName,
Subtype: 'TrueType',
FontDescriptor: descriptor,
FirstChar: firstChar,
LastChar: firstChar + charWidths.length - 1,
Widths: charWidths,
Encoding: encoding,
ToUnicode: cmap
} : {
Type: 'Font',
BaseFont: this.subset.postscriptName,
Subtype: 'TrueType',
FontDescriptor: descriptor,
FirstChar: 0,
LastChar: 255,
Widths: makeWidths(this),
Encoding: encoding
};
return makeFontTable(dictionary);
};
var b64ToByteArray = function b64ToByteArray(b64) {
var i, j, l, tmp, placeHolders, arr;
if (b64.length % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4');
} // the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
var len = b64.length;
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0; // base64 is 4/3 + up to two characters of the original data
arr = new Uint8Array(b64.length * 3 / 4 - placeHolders); // if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length;
var L = 0;
function push(v) {
arr[L++] = v;
}
for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = decode(b64.charAt(i)) << 18 | decode(b64.charAt(i + 1)) << 12 | decode(b64.charAt(i + 2)) << 6 | decode(b64.charAt(i + 3));
push((tmp & 0xFF0000) >> 16);
push((tmp & 0xFF00) >> 8);
push(tmp & 0xFF);
}
if (placeHolders === 2) {
tmp = decode(b64.charAt(i)) << 2 | decode(b64.charAt(i + 1)) >> 4;
push(tmp & 0xFF);
} else if (placeHolders === 1) {
tmp = decode(b64.charAt(i)) << 10 | decode(b64.charAt(i + 1)) << 4 | decode(b64.charAt(i + 2)) >> 2;
push(tmp >> 8 & 0xFF);
push(tmp & 0xFF);
}
return arr;
};
var decode = function decode(elt) {
var PLUS = '+'.charCodeAt(0);
var SLASH = '/'.charCodeAt(0);
var NUMBER = '0'.charCodeAt(0);
var LOWER = 'a'.charCodeAt(0);
var UPPER = 'A'.charCodeAt(0);
var PLUS_URL_SAFE = '-'.charCodeAt(0);
var SLASH_URL_SAFE = '_'.charCodeAt(0);
var code = elt.charCodeAt(0);
if (code === PLUS || code === PLUS_URL_SAFE) return 62; // '+'
if (code === SLASH || code === SLASH_URL_SAFE) return 63; // '/'
if (code < NUMBER) return -1; //no match
if (code < NUMBER + 10) return code - NUMBER + 26 + 26;
if (code < UPPER + 26) return code - UPPER;
if (code < LOWER + 26) return code - LOWER + 26;
};
var toString = function toString(fontfile) {
var strings = [];
for (var i = 0, length = fontfile.length; i < length; i++) {
strings.push(String.fromCharCode(fontfile[i]));
}
return strings.join('');
};
var makeWidths = function makeWidths(font) {
var widths = [];
for (var i = 0; i < 256; i++) {
widths[i] = 0;
}
var scale = 1000.0 / font.head.unitsPerEm;
var codeMap = font.cmap.unicode.codeMap;
var WinAnsiEncoding = {
402: 131,
8211: 150,
8212: 151,
8216: 145,
8217: 146,
8218: 130,
8220: 147,
8221: 148,
8222: 132,
8224: 134,
8225: 135,
8226: 149,
8230: 133,
8364: 128,
8240: 137,
8249: 139,
8250: 155,
710: 136,
8482: 153,
338: 140,
339: 156,
732: 152,
352: 138,
353: 154,
376: 159,
381: 142,
382: 158
};
Object.keys(codeMap).map(function (key) {
var WinAnsiEncodingValue = WinAnsiEncoding[key];
var AssignedValue = Math.round(font.hmtx.metrics[codeMap[key]].advance * scale);
if (WinAnsiEncodingValue) {
widths[WinAnsiEncodingValue] = AssignedValue;
} else if (key < 256) {
widths[key] = AssignedValue;
}
});
return widths;
};
var toUnicodeCmap = function toUnicodeCmap(map) {
var code, codes, range, unicode, unicodeMap, _i, _len;
unicodeMap = '/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo <<\n /Registry (Adobe)\n /Ordering (UCS)\n /Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<00><ff>\nendcodespacerange';
codes = Object.keys(map).sort(function (a, b) {
return a - b;
});
range = [];
for (_i = 0, _len = codes.length; _i < _len; _i++) {
code = codes[_i];
if (range.length >= 100) {
unicodeMap += "\n" + range.length + " beginbfchar\n" + range.join('\n') + "\nendbfchar";
range = [];
}
unicode = ('0000' + map[code].toString(16)).slice(-4);
code = (+code).toString(16);
range.push("<" + code + "><" + unicode + ">");
}
if (range.length) {
unicodeMap += "\n" + range.length + " beginbfchar\n" + range.join('\n') + "\nendbfchar\n";
}
unicodeMap += 'endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend';
return unicodeMap;
};
var reverseString = function reverseString(s) {
return s.split("").reverse().join("");
};
return TTFFont;
}();
exports.default = _default;
var Data = function () {
function Data(data) {
this.data = data !== undefined ? data : [];
this.pos = 0;
this.length = this.data.length;
}
Data.prototype.readByte = function () {
return this.data[this.pos++];
};
Data.prototype.writeByte = function (byte) {
this.data[this.pos++] = byte;
return this;
};
Data.prototype.readUInt32 = function () {
var b1, b2, b3, b4;
b1 = this.readByte() * 0x1000000;
b2 = this.readByte() << 16;
b3 = this.readByte() << 8;
b4 = this.readByte();
return b1 + b2 + b3 + b4;
};
Data.prototype.writeUInt32 = function (val) {
this.writeByte(val >>> 24 & 0xff);
this.writeByte(val >> 16 & 0xff);
this.writeByte(val >> 8 & 0xff);
return this.writeByte(val & 0xff);
};
Data.prototype.readInt32 = function () {
var int;
int = this.readUInt32();
if (int >= 0x80000000) {
return int - 0x100000000;
} else {
return int;
}
};
Data.prototype.writeInt32 = function (val) {
if (val < 0) {
val += 0x100000000;
}
return this.writeUInt32(val);
};
Data.prototype.readUInt16 = function () {
var b1, b2;
b1 = this.readByte() << 8;
b2 = this.readByte();
return b1 | b2;
};
Data.prototype.writeUInt16 = function (val) {
this.writeByte(val >> 8 & 0xff);
return this.writeByte(val & 0xff);
};
Data.prototype.readInt16 = function () {
var int;
int = this.readUInt16();
if (int >= 0x8000) {
return int - 0x10000;
} else {
return int;
}
};
Data.prototype.writeInt16 = function (val) {
if (val < 0) {
val += 0x10000;
}
return this.writeUInt16(val);
};
Data.prototype.readString = function (length) {
var i, ret, _i;
ret = [];
for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) {
ret[i] = String.fromCharCode(this.readByte());
}
return ret.join('');
};
Data.prototype.writeString = function (val) {
var i, _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = val.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
_results.push(this.writeByte(val.charCodeAt(i)));
}
return _results;
};
Data.prototype.stringAt = function (pos, length) {
this.pos = pos;
return this.readString(length);
};
Data.prototype.readShort = function () {
return this.readInt16();
};
Data.prototype.writeShort = function (val) {
return this.writeInt16(val);
};
Data.prototype.readLongLong = function () {
var b1, b2, b3, b4, b5, b6, b7, b8;
b1 = this.readByte();
b2 = this.readByte();
b3 = this.readByte();
b4 = this.readByte();
b5 = this.readByte();
b6 = this.readByte();
b7 = this.readByte();
b8 = this.readByte();
if (b1 & 0x80) {
return ((b1 ^ 0xff) * 0x100000000000000 + (b2 ^ 0xff) * 0x1000000000000 + (b3 ^ 0xff) * 0x10000000000 + (b4 ^ 0xff) * 0x100000000 + (b5 ^ 0xff) * 0x1000000 + (b6 ^ 0xff) * 0x10000 + (b7 ^ 0xff) * 0x100 + (b8 ^ 0xff) + 1) * -1;
}
return b1 * 0x100000000000000 + b2 * 0x1000000000000 + b3 * 0x10000000000 + b4 * 0x100000000 + b5 * 0x1000000 + b6 * 0x10000 + b7 * 0x100 + b8;
};
Data.prototype.writeLongLong = function (val) {
var high, low;
high = Math.floor(val / 0x100000000);
low = val & 0xffffffff;
this.writeByte(high >> 24 & 0xff);
this.writeByte(high >> 16 & 0xff);
this.writeByte(high >> 8 & 0xff);
this.writeByte(high & 0xff);
this.writeByte(low >> 24 & 0xff);
this.writeByte(low >> 16 & 0xff);
this.writeByte(low >> 8 & 0xff);
return this.writeByte(low & 0xff);
};
Data.prototype.readInt = function () {
return this.readInt32();
};
Data.prototype.writeInt = function (val) {
return this.writeInt32(val);
};
Data.prototype.slice = function (start, end) {
return this.data.slice(start, end);
};
Data.prototype.read = function (bytes) {
var buf, i, _i;
buf = [];
for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) {
buf.push(this.readByte());
}
return buf;
};
Data.prototype.write = function (bytes) {
var byte, _i, _len, _results;
_results = [];
for (_i = 0, _len = bytes.length; _i < _len; _i++) {
byte = bytes[_i];
_results.push(this.writeByte(byte));
}
return _results;
};
return Data;
}();
var Directory = function () {
var checksum;
function Directory(data) {
var entry, i, _i, _ref;
this.scalarType = data.readInt();
this.tableCount = data.readShort();
this.searchRange = data.readShort();
this.entrySelector = data.readShort();
this.rangeShift = data.readShort();
this.tables = {};
for (i = _i = 0, _ref = this.tableCount; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
entry = {
tag: data.readString(4),
checksum: data.readInt(),
offset: data.readInt(),
length: data.readInt()
};
this.tables[entry.tag] = entry;
}
}
Directory.prototype.encode = function (tables) {
var adjustment, directory, directoryLength, entrySelector, headOffset, log2, offset, rangeShift, searchRange, sum, table, tableCount, tableData, tag;
tableCount = Object.keys(tables).length;
log2 = Math.log(2);
searchRange = Math.floor(Math.log(tableCount) / log2) * 16;
entrySelector = Math.floor(searchRange / log2);
rangeShift = tableCount * 16 - searchRange;
directory = new Data();
directory.writeInt(this.scalarType);
directory.writeShort(tableCount);
directory.writeShort(searchRange);
directory.writeShort(entrySelector);
directory.writeShort(rangeShift);
directoryLength = tableCount * 16;
offset = directory.pos + directoryLength;
headOffset = null;
tableData = [];
for (tag in tables) {
if (Object.prototype.hasOwnProperty.call(tables, tag)) {
table = tables[tag];
directory.writeString(tag);
directory.writeInt(checksum(table));
directory.writeInt(offset);
directory.writeInt(table.length);
tableData = tableData.concat(table);
if (tag === 'head') {
headOffset = offset;
}
offset += table.length;
while (offset % 4) {
tableData.push(0);
offset++;
}
}
}
directory.write(tableData);
sum = checksum(directory.data);
adjustment = 0xB1B0AFBA - sum;
directory.pos = headOffset + 8;
directory.writeUInt32(adjustment);
return directory.data;
};
checksum = function checksum(data) {
var i, sum, tmp, _i, _ref;
data = __slice.call(data);
while (data.length % 4) {
data.push(0);
}
tmp = new Data(data);
sum = 0;
for (i = _i = 0, _ref = data.length; _i < _ref; i = _i += 4) {
sum += tmp.readUInt32();
}
return sum & 0xFFFFFFFF;
};
return Directory;
}();
var __slice = [].slice;
var __extends = function __extends(child, parent) {
for (var key in parent) {
if ({}.hasOwnProperty.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
var Table = function () {
function Table(file) {
var info;
this.file = file;
info = this.file.directory.tables[this.tag];
this.exists = !!info;
if (info) {
this.offset = info.offset;
this.length = info.length;
this.parse(this.file.contents);
}
}
Table.prototype.parse = function () {};
Table.prototype.encode = function () {};
Table.prototype.raw = function () {
if (!this.exists) {
return null;
}
this.file.contents.pos = this.offset;
return this.file.contents.read(this.length);
};
return Table;
}();
var HeadTable = function (_super) {
__extends(HeadTable, _super);
function HeadTable() {
return HeadTable.__super__.constructor.apply(this, arguments);
}
HeadTable.prototype.tag = 'head';
HeadTable.prototype.parse = function (data) {
data.pos = this.offset;
this.version = data.readInt();
this.revision = data.readInt();
this.checkSumAdjustment = data.readInt();
this.magicNumber = data.readInt();
this.flags = data.readShort();
this.unitsPerEm = data.readShort();
this.created = data.readLongLong();
this.modified = data.readLongLong();
this.xMin = data.readShort();
this.yMin = data.readShort();
this.xMax = data.readShort();
this.yMax = data.readShort();
this.macStyle = data.readShort();
this.lowestRecPPEM = data.readShort();
this.fontDirectionHint = data.readShort();
this.indexToLocFormat = data.readShort();
this.glyphDataFormat = data.readShort();
return this;
};
HeadTable.prototype.encode = function (loca) {
var table;
table = new Data();
table.writeInt(this.version);
table.writeInt(this.revision);
table.writeInt(this.checkSumAdjustment);
table.writeInt(this.magicNumber);
table.writeShort(this.flags);
table.writeShort(this.unitsPerEm);
table.writeLongLong(this.created);
table.writeLongLong(this.modified);
table.writeShort(this.xMin);
table.writeShort(this.yMin);
table.writeShort(this.xMax);
table.writeShort(this.yMax);
table.writeShort(this.macStyle);
table.writeShort(this.lowestRecPPEM);
table.writeShort(this.fontDirectionHint);
table.writeShort(loca.type);
table.writeShort(this.glyphDataFormat);
return table.data;
};
return HeadTable;
}(Table);
var CmapTable = function (_super) {
__extends(CmapTable, _super);
function CmapTable() {
return CmapTable.__super__.constructor.apply(this, arguments);
}
CmapTable.prototype.tag = 'cmap';
CmapTable.prototype.parse = function (data) {
var entry, i, tableCount, _i;
data.pos = this.offset;
this.version = data.readUInt16();
tableCount = data.readUInt16();
this.tables = [];
for (i = _i = 0; 0 <= tableCount ? _i < tableCount : _i > tableCount; i = 0 <= tableCount ? ++_i : --_i) {
entry = new CmapEntry(data, this.offset);
this.tables.push(entry);
if (entry.isUnicode) {
if (this.unicode === undefined) {
this.unicode = entry;
}
}
}
return true;
};
CmapTable.encode = function (charmap, encoding) {
var result, table;
if (encoding === undefined) {
encoding = 'macroman';
}
result = CmapEntry.encode(charmap, encoding);
table = new Data();
table.writeUInt16(0);
table.writeUInt16(1);
result.table = table.data.concat(result.subtable);
return result;
};
return CmapTable;
}(Table);
var CmapEntry = function () {
function CmapEntry(data, offset) {
var code, count, endCode, glyphId, glyphIds, i, idDelta, idRangeOffset, index, saveOffset, segCount, segCountX2, start, startCode, tail, _i, _j, _k, _len;
this.platformID = data.readUInt16();
this.encodingID = data.readShort();
this.offset = offset + data.readInt();
saveOffset = data.pos;
data.pos = this.offset;
this.format = data.readUInt16();
this.length = data.readUInt16();
this.language = data.readUInt16();
this.isUnicode = this.platformID === 3 && this.encodingID === 1 && this.format === 4 || this.platformID === 0 && this.format === 4;
this.codeMap = {};
switch (this.format) {
case 0:
for (i = _i = 0; _i < 256; i = ++_i) {
this.codeMap[i] = data.readByte();
}
break;
case 4:
segCountX2 = data.readUInt16();
segCount = segCountX2 / 2;
data.pos += 6;
endCode = function () {
var _j, _results;
_results = [];
for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) {
_results.push(data.readUInt16());
}
return _results;
}();
data.pos += 2;
startCode = function () {
var _j, _results;
_results = [];
for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) {
_results.push(data.readUInt16());
}
return _results;
}();
idDelta = function () {
var _j, _results;
_results = [];
for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) {
_results.push(data.readUInt16());
}
return _results;
}();
idRangeOffset = function () {
var _j, _results;
_results = [];
for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) {
_results.push(data.readUInt16());
}
return _results;
}();
count = (this.length - data.pos + this.offset) / 2;
glyphIds = function () {
var _j, _results;
_results = [];
for (i = _j = 0; 0 <= count ? _j < count : _j > count; i = 0 <= count ? ++_j : --_j) {
_results.push(data.readUInt16());
}
return _results;
}();
for (i = _j = 0, _len = endCode.length; _j < _len; i = ++_j) {
tail = endCode[i];
start = startCode[i];
for (code = _k = start; start <= tail ? _k <= tail : _k >= tail; code = start <= tail ? ++_k : --_k) {
if (idRangeOffset[i] === 0) {
glyphId = code + idDelta[i];
} else {
index = idRangeOffset[i] / 2 + (code - start) - (segCount - i);
glyphId = glyphIds[index] || 0;
if (glyphId !== 0) {
glyphId += idDelta[i];
}
}
this.codeMap[code] = glyphId & 0xFFFF;
}
}
}
data.pos = saveOffset;
}
CmapEntry.encode = function (charmap, encoding) {
var charMap, code, codeMap, codes, delta, deltas, diff, endCode, endCodes, entrySelector, glyphIDs, i, id, indexes, last, map, nextID, offset, old, rangeOffsets, rangeShift, result, searchRange, segCount, segCountX2, startCode, startCodes, startGlyph, subtable, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _len7, _m, _n, _name, _o, _p, _q;
subtable = new Data();
codes = Object.keys(charmap).sort(function (a, b) {
return a - b;
});
switch (encoding) {
case 'macroman':
id = 0;
indexes = function () {
var _i, _results;
_results = [];
for (i = _i = 0; _i < 256; i = ++_i) {
_results.push(0);
}
return _results;
}();
map = {
0: 0
};
codeMap = {};
for (_i = 0, _len = codes.length; _i < _len; _i++) {
code = codes[_i];
if (map[_name = charmap[code]] === undefined) {
map[_name] = ++id;
}
codeMap[code] = {
old: charmap[code],
"new": map[charmap[code]]
};
indexes[code] = map[charmap[code]];
}
subtable.writeUInt16(1);
subtable.writeUInt16(0);
subtable.writeUInt32(12);
subtable.writeUInt16(0);
subtable.writeUInt16(262);
subtable.writeUInt16(0);
subtable.write(indexes);
result = {
charMap: codeMap,
subtable: subtable.data,
maxGlyphID: id + 1
};
return result;
case 'unicode':
startCodes = [];
endCodes = [];
nextID = 0;
map = {};
charMap = {};
for (_j = 0, _len1 = codes.length; _j < _len1; _j++) {
code = codes[_j];
old = charmap[code];
if (map[old] === undefined) {
map[old] = ++nextID;
}
charMap[code] = {
old: old,
"new": map[old]
};
delta = map[old] - code;
if (last === undefined || delta !== diff) {
if (last) {
endCodes.push(last);
}
startCodes.push(code);
diff = delta;
}
last = code;
}
if (last) {
endCodes.push(last);
}
endCodes.push(0xFFFF);
startCodes.push(0xFFFF);
segCount = startCodes.length;
segCountX2 = segCount * 2;
searchRange = 2 * Math.pow(Math.log(segCount) / Math.LN2, 2);
entrySelector = Math.log(searchRange / 2) / Math.LN2;
rangeShift = 2 * segCount - searchRange;
deltas = [];
rangeOffsets = [];
glyphIDs = [];
for (i = _k = 0, _len2 = startCodes.length; _k < _len2; i = ++_k) {
startCode = startCodes[i];
endCode = endCodes[i];
if (startCode === 0xFFFF) {
deltas.push(0);
rangeOffsets.push(0);
break;
}
startGlyph = charMap[startCode]["new"];
if (startCode - startGlyph >= 0x8000) {
deltas.push(0);
rangeOffsets.push(2 * (glyphIDs.length + segCount - i));
for (code = _l = startCode; startCode <= endCode ? _l <= endCode : _l >= endCode; code = startCode <= endCode ? ++_l : --_l) {
glyphIDs.push(charMap[code]["new"]);
}
} else {
deltas.push(startGlyph - startCode);
rangeOffsets.push(0);
}
}
subtable.writeUInt16(3);
subtable.writeUInt16(1);
subtable.writeUInt32(12);
subtable.writeUInt16(4);
subtable.writeUInt16(16 + segCount * 8 + glyphIDs.length * 2);
subtable.writeUInt16(0);
subtable.writeUInt16(segCountX2);
subtable.writeUInt16(searchRange);
subtable.writeUInt16(entrySelector);
subtable.writeUInt16(rangeShift);
for (_m = 0, _len3 = endCodes.length; _m < _len3; _m++) {
code = endCodes[_m];
subtable.writeUInt16(code);
}
subtable.writeUInt16(0);
for (_n = 0, _len4 = startCodes.length; _n < _len4; _n++) {
code = startCodes[_n];
subtable.writeUInt16(code);
}
for (_o = 0, _len5 = deltas.length; _o < _len5; _o++) {
delta = deltas[_o];
subtable.writeUInt16(delta);
}
for (_p = 0, _len6 = rangeOffsets.length; _p < _len6; _p++) {
offset = rangeOffsets[_p];
subtable.writeUInt16(offset);
}
for (_q = 0, _len7 = glyphIDs.length; _q < _len7; _q++) {
id = glyphIDs[_q];
subtable.writeUInt16(id);
}
result = {
charMap: charMap,
subtable: subtable.data,
maxGlyphID: nextID + 1
};
return result;
}
};
return CmapEntry;
}();
var HheaTable = function (_super) {
__extends(HheaTable, _super);
function HheaTable() {
return HheaTable.__super__.constructor.apply(this, arguments);
}
HheaTable.prototype.tag = 'hhea';
HheaTable.prototype.parse = function (data) {
data.pos = this.offset;
this.version = data.readInt();
this.ascender = data.readShort();
this.decender = data.readShort();
this.lineGap = data.readShort();
this.advanceWidthMax = data.readShort();
this.minLeftSideBearing = data.readShort();
this.minRightSideBearing = data.readShort();
this.xMaxExtent = data.readShort();
this.caretSlopeRise = data.readShort();
this.caretSlopeRun = data.readShort();
this.caretOffset = data.readShort();
data.pos += 4 * 2;
this.metricDataFormat = data.readShort();
this.numberOfMetrics = data.readUInt16();
return this;
};
HheaTable.prototype.encode = function (ids) {
var i, table, _i, _ref;
table = new Data();
table.writeInt(this.version);
table.writeShort(this.ascender);
table.writeShort(this.decender);
table.writeShort(this.lineGap);
table.writeShort(this.advanceWidthMax);
table.writeShort(this.minLeftSideBearing);
table.writeShort(this.minRightSideBearing);
table.writeShort(this.xMaxExtent);
table.writeShort(this.caretSlopeRise);
table.writeShort(this.caretSlopeRun);
table.writeShort(this.caretOffset);
for (i = _i = 0, _ref = 4 * 2; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
table.writeByte(0);
}
table.writeShort(this.metricDataFormat);
table.writeUInt16(ids.length);
return table.data;
};
return HheaTable;
}(Table);
var OS2Table = function (_super) {
__extends(OS2Table, _super);
function OS2Table() {
return OS2Table.__super__.constructor.apply(this, arguments);
}
OS2Table.prototype.tag = 'OS/2';
OS2Table.prototype.parse = function (data) {
var i;
data.pos = this.offset;
this.version = data.readUInt16();
this.averageCharWidth = data.readShort();
this.weightClass = data.readUInt16();
this.widthClass = data.readUInt16();
this.type = data.readShort();
this.ySubscriptXSize = data.readShort();
this.ySubscriptYSize = data.readShort();
this.ySubscriptXOffset = data.readShort();
this.ySubscriptYOffset = data.readShort();
this.ySuperscriptXSize = data.readShort();
this.ySuperscriptYSize = data.readShort();
this.ySuperscriptXOffset = data.readShort();
this.ySuperscriptYOffset = data.readShort();
this.yStrikeoutSize = data.readShort();
this.yStrikeoutPosition = data.readShort();
this.familyClass = data.readShort();
this.panose = function () {
var _i, _results;
_results = [];
for (i = _i = 0; _i < 10; i = ++_i) {
_results.push(data.readByte());
}
return _results;
}();
this.charRange = function () {
var _i, _results;
_results = [];
for (i = _i = 0; _i < 4; i = ++_i) {
_results.push(data.readInt());
}
return _results;
}();
this.vendorID = data.readString(4);
this.selection = data.readShort();
this.firstCharIndex = data.readShort();
this.lastCharIndex = data.readShort();
if (this.version > 0) {
this.ascent = data.readShort();
this.descent = data.readShort();
this.lineGap = data.readShort();
this.winAscent = data.readShort();
this.winDescent = data.readShort();
this.codePageRange = function () {
var _i, _results;
_results = [];
for (i = _i = 0; _i < 2; i = ++_i) {
_results.push(data.readInt());
}
return _results;
}();
if (this.version > 1) {
this.xHeight = data.readShort();
this.capHeight = data.readShort();
this.defaultChar = data.readShort();
this.breakChar = data.readShort();
this.maxContext = data.readShort();
return this;
}
}
};
OS2Table.prototype.encode = function () {
return this.raw();
};
return OS2Table;
}(Table);
var PostTable = function (_super) {
var POSTSCRIPT_GLYPHS;
__extends(PostTable, _super);
function PostTable() {
return PostTable.__super__.constructor.apply(this, arguments);
}
PostTable.prototype.tag = 'post';
PostTable.prototype.parse = function (data) {
var i, length, numberOfGlyphs, _i, _results;
data.pos = this.offset;
this.format = data.readInt();
this.italicAngle = data.readInt();
this.underlinePosition = data.readShort();
this.underlineThickness = data.readShort();
this.isFixedPitch = data.readInt();
this.minMemType42 = data.readInt();
this.maxMemType42 = data.readInt();
this.minMemType1 = data.readInt();
this.maxMemType1 = data.readInt();
switch (this.format) {
case 0x00010000:
break;
case 0x00020000:
numberOfGlyphs = data.readUInt16();
this.glyphNameIndex = [];
for (i = _i = 0; 0 <= numberOfGlyphs ? _i < numberOfGlyphs : _i > numberOfGlyphs; i = 0 <= numberOfGlyphs ? ++_i : --_i) {
this.glyphNameIndex.push(data.readUInt16());
}
this.names = [];
_results = [];
while (data.pos < this.offset + this.length) {
length = data.readByte();
_results.push(this.names.push(data.readString(length)));
}
return _results;
case 0x00025000:
numberOfGlyphs = data.readUInt16();
this.offsets = data.read(numberOfGlyphs);
return this.offsets;
case 0x00030000:
break;
case 0x00040000:
this.map = function () {
var _j, _ref, _results1;
_results1 = [];
for (i = _j = 0, _ref = this.file.maxp.numGlyphs; 0 <= _ref ? _j < _ref : _j > _ref; i = 0 <= _ref ? ++_j : --_j) {
_results1.push(data.readUInt32());
}
return _results1;
}.call(this);
return this.map;
}
};
PostTable.prototype.glyphFor = function (code) {
var index;
switch (this.format) {
case 0x00010000:
return POSTSCRIPT_GLYPHS[code] || '.notdef';
case 0x00020000:
index = this.glyphNameIndex[code];
return index <= 257 ? POSTSCRIPT_GLYPHS[index] : this.names[index - 258] || '.notdef';
case 0x00025000:
return POSTSCRIPT_GLYPHS[code + this.offsets[code]] || '.notdef';
case 0x00030000:
return '.notdef';
case 0x00040000:
return this.map[code] || 0xFFFF;
}
};
PostTable.prototype.encode = function (mapping) {
var id, index, indexes, position, post, raw, string, strings, table, _i, _j, _k, _len, _len1, _len2;
if (!this.exists) {
return null;
}
raw = this.raw();
if (this.format === 0x00030000) {
return raw;
}
table = new Data(raw.slice(0, 32));
table.writeUInt32(0x00020000);
table.pos = 32;
indexes = [];
strings = [];
for (_i = 0, _len = mapping.length; _i < _len; _i++) {
id = mapping[_i];
post = this.glyphFor(id);
position = POSTSCRIPT_GLYPHS.indexOf(post);
if (position !== -1) {
indexes.push(position);
} else {
indexes.push(257 + strings.length);
strings.push(post);
}
}
table.writeUInt16(Object.keys(mapping).length);
for (_j = 0, _len1 = indexes.length; _j < _len1; _j++) {
index = indexes[_j];
table.writeUInt16(index);
}
for (_k = 0, _len2 = strings.length; _k < _len2; _k++) {
string = strings[_k];
table.writeByte(string.length);
table.writeString(string);
}
return table.data;
};
POSTSCRIPT_GLYPHS = '.notdef .null nonmarkingreturn space exclam quotedbl numbersign dollar percent\nampersand quotesingle parenleft parenright asterisk plus comma hyphen period slash\nzero one two three four five six seven eight nine colon semicolon less equal greater\nquestion at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z\nbracketleft backslash bracketright asciicircum underscore grave\na b c d e f g h i j k l m n o p q r s t u v w x y z\nbraceleft bar braceright asciitilde Adieresis Aring Ccedilla Eacute Ntilde Odieresis\nUdieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave\necircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve\nocircumflex odieresis otilde uacute ugrave ucircumflex udieresis dagger degree cent\nsterling section bullet paragraph germandbls registered copyright trademark acute\ndieresis notequal AE Oslash infinity plusminus lessequal greaterequal yen mu\npartialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash\nquestiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft\nguillemotright ellipsis nonbreakingspace Agrave Atilde Otilde OE oe endash emdash\nquotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis\nfraction currency guilsinglleft guilsinglright fi fl daggerdbl periodcentered\nquotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis\nEgrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex apple Ograve Uacute\nUcircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla\nhungarumlaut ogonek caron Lslash lslash Scaron scaron Zcaron zcaron brokenbar Eth\neth Yacute yacute Thorn thorn minus multiply onesuperior twosuperior threesuperior\nonehalf onequarter threequarters franc Gbreve gbreve Idotaccent Scedilla scedilla\nCacute cacute Ccaron ccaron dcroat'.split(/\s+/g);
return PostTable;
}(Table);
var NameEntry = function () {
function NameEntry(raw, entry) {
this.raw = raw;
this.length = raw.length;
this.platformID = entry.platformID;
this.encodingID = entry.encodingID;
this.languageID = entry.languageID;
}
return NameEntry;
}();
var NameTable = function (_super) {
var subsetTag;
__extends(NameTable, _super);
function NameTable() {
return NameTable.__super__.constructor.apply(this, arguments);
}
NameTable.prototype.tag = 'name';
NameTable.prototype.parse = function (data) {
var count, entries, entry, format, i, name, stringOffset, strings, text, _i, _j, _len, _name;
data.pos = this.offset;
format = data.readShort();
count = data.readShort();
stringOffset = data.readShort();
entries = [];
for (i = _i = 0; 0 <= count ? _i < count : _i > count; i = 0 <= count ? ++_i : --_i) {
entries.push({
platformID: data.readShort(),
encodingID: data.readShort(),
languageID: data.readShort(),
nameID: data.readShort(),
length: data.readShort(),
offset: this.offset + stringOffset + data.readShort()
});
}
strings = {};
for (i = _j = 0, _len = entries.length; _j < _len; i = ++_j) {
entry = entries[i];
data.pos = entry.offset;
text = data.readString(entry.length);
name = new NameEntry(text, entry);
if (strings[_name = entry.nameID] === undefined) {
strings[_name] = [];
}
strings[entry.nameID].push(name);
}
this.strings = strings;
this.copyright = strings[0];
this.fontFamily = strings[1];
this.fontSubfamily = strings[2];
this.uniqueSubfamily = strings[3];
this.fontName = strings[4];
this.version = strings[5];
this.postscriptName = strings[6][0].raw.replace(/[\x00-\x19\x80-\xff]/g, "");
this.trademark = strings[7];
this.manufacturer = strings[8];
this.designer = strings[9];
this.description = strings[10];
this.vendorUrl = strings[11];
this.designerUrl = strings[12];
this.license = strings[13];
this.licenseUrl = strings[14];
this.preferredFamily = strings[15];
this.preferredSubfamily = strings[17];
this.compatibleFull = strings[18];
this.sampleText = strings[19];
return this;
};
subsetTag = "AAAAAA";
NameTable.prototype.encode = function () {
var id, list, nameID, nameTable, postscriptName, strCount, strTable, string, strings, table, val, _i, _len, _ref;
strings = {};
_ref = this.strings;
for (id in _ref) {
if (Object.prototype.hasOwnProperty.call(_ref, id)) {
val = _ref[id];
strings[id] = val;
}
}
postscriptName = new NameEntry("" + subsetTag + "+" + this.postscriptName, {
platformID: 1,
encodingID: 0,
languageID: 0
});
strings[6] = [postscriptName];
subsetTag = successorOf(subsetTag);
strCount = 0;
for (id in strings) {
if (Object.prototype.hasOwnProperty.call(strings, id)) {
list = strings[id];
if (list !== null) {
strCount += list.length;
}
}
}
table = new Data();
strTable = new Data();
table.writeShort(0);
table.writeShort(strCount);
table.writeShort(6 + 12 * strCount);
for (nameID in strings) {
if (Object.prototype.hasOwnProperty.call(strings, nameID)) {
list = strings[nameID];
if (list !== null) {
for (_i = 0, _len = list.length; _i < _len; _i++) {
string = list[_i];
table.writeShort(string.platformID);
table.writeShort(string.encodingID);
table.writeShort(string.languageID);
table.writeShort(nameID);
table.writeShort(string.length);
table.writeShort(strTable.pos);
strTable.writeString(string.raw);
}
}
}
}
nameTable = {
postscriptName: postscriptName.raw,
table: table.data.concat(strTable.data)
};
return nameTable;
};
return NameTable;
}(Table);
var successorOf = function successorOf(input) {
var added, alphabet, carry, i, index, isUpperCase, last, length, next, result;
alphabet = 'abcdefg