import stream from 'stream'; import fs from 'fs'; import zlib from 'zlib'; import CryptoJS from 'crypto-js'; import fontkit from '@foliojs-fork/fontkit'; import { EventEmitter } from 'events'; import LineBreaker from '@foliojs-fork/linebreak'; import PNG from 'png-js'; /* PDFAbstractReference - abstract class for PDF reference */ class PDFAbstractReference { toString() { throw new Error('Must be implemented by subclasses'); } } /* PDFTree - abstract base class for name and number tree objects */ class PDFTree { constructor() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this._items = {}; // disable /Limits output for this tree this.limits = typeof options.limits === 'boolean' ? options.limits : true; } add(key, val) { return this._items[key] = val; } get(key) { return this._items[key]; } toString() { // Needs to be sorted by key var sortedKeys = Object.keys(this._items).sort((a, b) => this._compareKeys(a, b)); var out = ['<<']; if (this.limits && sortedKeys.length > 1) { var first = sortedKeys[0], last = sortedKeys[sortedKeys.length - 1]; out.push(" /Limits ".concat(PDFObject.convert([this._dataForKey(first), this._dataForKey(last)]))); } out.push(" /".concat(this._keysName(), " [")); for (var key of sortedKeys) { out.push(" ".concat(PDFObject.convert(this._dataForKey(key)), " ").concat(PDFObject.convert(this._items[key]))); } out.push(']'); out.push('>>'); return out.join('\n'); } _compareKeys() /*a, b*/ { throw new Error('Must be implemented by subclasses'); } _keysName() { throw new Error('Must be implemented by subclasses'); } _dataForKey() /*k*/ { throw new Error('Must be implemented by subclasses'); } } /* PDFObject - converts JavaScript types into their corresponding PDF types. By Devon Govett */ var pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length); var escapableRe = /[\n\r\t\b\f()\\]/g; var escapable = { '\n': '\\n', '\r': '\\r', '\t': '\\t', '\b': '\\b', '\f': '\\f', '\\': '\\\\', '(': '\\(', ')': '\\)' }; // Convert little endian UTF-16 to big endian var swapBytes = function swapBytes(buff) { var l = buff.length; if (l & 0x01) { throw new Error('Buffer length must be even'); } else { for (var i = 0, end = l - 1; i < end; i += 2) { var a = buff[i]; buff[i] = buff[i + 1]; buff[i + 1] = a; } } return buff; }; class PDFObject { static convert(object) { var encryptFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; // String literals are converted to the PDF name type if (typeof object === 'string') { return "/".concat(object); // String objects are converted to PDF strings (UTF-16) } else if (object instanceof String) { var string = object; // Detect if this is a unicode string var isUnicode = false; for (var i = 0, end = string.length; i < end; i++) { if (string.charCodeAt(i) > 0x7f) { isUnicode = true; break; } } // If so, encode it as big endian UTF-16 var stringBuffer; if (isUnicode) { stringBuffer = swapBytes(Buffer.from("\uFEFF".concat(string), 'utf16le')); } else { stringBuffer = Buffer.from(string.valueOf(), 'ascii'); } // Encrypt the string when necessary if (encryptFn) { string = encryptFn(stringBuffer).toString('binary'); } else { string = stringBuffer.toString('binary'); } // Escape characters as required by the spec string = string.replace(escapableRe, c => escapable[c]); return "(".concat(string, ")"); // Buffers are converted to PDF hex strings } else if (Buffer.isBuffer(object)) { return "<".concat(object.toString('hex'), ">"); } else if (object instanceof PDFAbstractReference || object instanceof PDFTree) { return object.toString(); } else if (object instanceof Date) { var _string = "D:".concat(pad(object.getUTCFullYear(), 4)) + pad(object.getUTCMonth() + 1, 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z'; // Encrypt the string when necessary if (encryptFn) { _string = encryptFn(Buffer.from(_string, 'ascii')).toString('binary'); // Escape characters as required by the spec _string = _string.replace(escapableRe, c => escapable[c]); } return "(".concat(_string, ")"); } else if (Array.isArray(object)) { var items = object.map(e => PDFObject.convert(e, encryptFn)).join(' '); return "[".concat(items, "]"); } else if ({}.toString.call(object) === '[object Object]') { var out = ['<<']; for (var key in object) { var val = object[key]; out.push("/".concat(key, " ").concat(PDFObject.convert(val, encryptFn))); } out.push('>>'); return out.join('\n'); } else if (typeof object === 'number') { return PDFObject.number(object); } else { return "".concat(object); } } static number(n) { if (n > -1e21 && n < 1e21) { return Math.round(n * 1e6) / 1e6; } throw new Error("unsupported number: ".concat(n)); } } /* PDFReference - represents a reference to another object in the PDF object heirarchy By Devon Govett */ class PDFReference extends PDFAbstractReference { constructor(document, id) { var data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; super(); this.document = document; this.id = id; this.data = data; this.gen = 0; this.compress = this.document.compress && !this.data.Filter; this.uncompressedLength = 0; this.buffer = []; } write(chunk) { if (!Buffer.isBuffer(chunk)) { chunk = Buffer.from(chunk + '\n', 'binary'); } this.uncompressedLength += chunk.length; if (this.data.Length == null) { this.data.Length = 0; } this.buffer.push(chunk); this.data.Length += chunk.length; if (this.compress) { return this.data.Filter = 'FlateDecode'; } } end(chunk) { if (chunk) { this.write(chunk); } return this.finalize(); } finalize() { this.offset = this.document._offset; var encryptFn = this.document._security ? this.document._security.getEncryptFn(this.id, this.gen) : null; if (this.buffer.length) { this.buffer = Buffer.concat(this.buffer); if (this.compress) { this.buffer = zlib.deflateSync(this.buffer); } if (encryptFn) { this.buffer = encryptFn(this.buffer); } this.data.Length = this.buffer.length; } this.document._write("".concat(this.id, " ").concat(this.gen, " obj")); this.document._write(PDFObject.convert(this.data, encryptFn)); if (this.buffer.length) { this.document._write('stream'); this.document._write(this.buffer); this.buffer = []; // free up memory this.document._write('\nendstream'); } this.document._write('endobj'); this.document._refEnd(this); } toString() { return "".concat(this.id, " ").concat(this.gen, " R"); } } /* PDFPage - represents a single page in the PDF document By Devon Govett */ var DEFAULT_MARGINS = { top: 72, left: 72, bottom: 72, right: 72 }; var SIZES = { '4A0': [4767.87, 6740.79], '2A0': [3370.39, 4767.87], A0: [2383.94, 3370.39], A1: [1683.78, 2383.94], A2: [1190.55, 1683.78], A3: [841.89, 1190.55], A4: [595.28, 841.89], A5: [419.53, 595.28], A6: [297.64, 419.53], A7: [209.76, 297.64], A8: [147.4, 209.76], A9: [104.88, 147.4], A10: [73.7, 104.88], B0: [2834.65, 4008.19], B1: [2004.09, 2834.65], B2: [1417.32, 2004.09], B3: [1000.63, 1417.32], B4: [708.66, 1000.63], B5: [498.9, 708.66], B6: [354.33, 498.9], B7: [249.45, 354.33], B8: [175.75, 249.45], B9: [124.72, 175.75], B10: [87.87, 124.72], C0: [2599.37, 3676.54], C1: [1836.85, 2599.37], C2: [1298.27, 1836.85], C3: [918.43, 1298.27], C4: [649.13, 918.43], C5: [459.21, 649.13], C6: [323.15, 459.21], C7: [229.61, 323.15], C8: [161.57, 229.61], C9: [113.39, 161.57], C10: [79.37, 113.39], RA0: [2437.8, 3458.27], RA1: [1729.13, 2437.8], RA2: [1218.9, 1729.13], RA3: [864.57, 1218.9], RA4: [609.45, 864.57], SRA0: [2551.18, 3628.35], SRA1: [1814.17, 2551.18], SRA2: [1275.59, 1814.17], SRA3: [907.09, 1275.59], SRA4: [637.8, 907.09], EXECUTIVE: [521.86, 756.0], FOLIO: [612.0, 936.0], LEGAL: [612.0, 1008.0], LETTER: [612.0, 792.0], TABLOID: [792.0, 1224.0] }; class PDFPage { constructor(document) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; this.document = document; this.size = options.size || 'letter'; this.layout = options.layout || 'portrait'; // process margins if (typeof options.margin === 'number') { this.margins = { top: options.margin, left: options.margin, bottom: options.margin, right: options.margin }; // default to 1 inch margins } else { this.margins = options.margins || DEFAULT_MARGINS; } // calculate page dimensions var dimensions = Array.isArray(this.size) ? this.size : SIZES[this.size.toUpperCase()]; this.width = dimensions[this.layout === 'portrait' ? 0 : 1]; this.height = dimensions[this.layout === 'portrait' ? 1 : 0]; this.content = this.document.ref(); // Initialize the Font, XObject, and ExtGState dictionaries this.resources = this.document.ref({ ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'] }); // The page dictionary this.dictionary = this.document.ref({ Type: 'Page', Parent: this.document._root.data.Pages, MediaBox: [0, 0, this.width, this.height], Contents: this.content, Resources: this.resources }); this.markings = []; } // Lazily create these objects get fonts() { var data = this.resources.data; return data.Font != null ? data.Font : data.Font = {}; } get xobjects() { var data = this.resources.data; return data.XObject != null ? data.XObject : data.XObject = {}; } get ext_gstates() { var data = this.resources.data; return data.ExtGState != null ? data.ExtGState : data.ExtGState = {}; } get patterns() { var data = this.resources.data; return data.Pattern != null ? data.Pattern : data.Pattern = {}; } get colorSpaces() { var data = this.resources.data; return data.ColorSpace || (data.ColorSpace = {}); } get annotations() { var data = this.dictionary.data; return data.Annots != null ? data.Annots : data.Annots = []; } get structParentTreeKey() { var data = this.dictionary.data; return data.StructParents != null ? data.StructParents : data.StructParents = this.document.createStructParentTreeNextKey(); } maxY() { return this.height - this.margins.bottom; } write(chunk) { return this.content.write(chunk); } end() { this.dictionary.end(); this.resources.end(); return this.content.end(); } } /* PDFNameTree - represents a name tree object */ class PDFNameTree extends PDFTree { _compareKeys(a, b) { return a.localeCompare(b); } _keysName() { return "Names"; } _dataForKey(k) { return new String(k); } } /** * Check if value is in a range group. * @param {number} value * @param {number[]} rangeGroup * @returns {boolean} */ function inRange(value, rangeGroup) { if (value < rangeGroup[0]) return false; var startRange = 0; var endRange = rangeGroup.length / 2; while (startRange <= endRange) { var middleRange = Math.floor((startRange + endRange) / 2); // actual array index var arrayIndex = middleRange * 2; // Check if value is in range pointed by actual index if (value >= rangeGroup[arrayIndex] && value <= rangeGroup[arrayIndex + 1]) { return true; } if (value > rangeGroup[arrayIndex + 1]) { // Search Right Side Of Array startRange = middleRange + 1; } else { // Search Left Side Of Array endRange = middleRange - 1; } } return false; } /** * A.1 Unassigned code points in Unicode 3.2 * @link https://tools.ietf.org/html/rfc3454#appendix-A.1 */ var unassigned_code_points = [0x0221, 0x0221, 0x0234, 0x024f, 0x02ae, 0x02af, 0x02ef, 0x02ff, 0x0350, 0x035f, 0x0370, 0x0373, 0x0376, 0x0379, 0x037b, 0x037d, 0x037f, 0x0383, 0x038b, 0x038b, 0x038d, 0x038d, 0x03a2, 0x03a2, 0x03cf, 0x03cf, 0x03f7, 0x03ff, 0x0487, 0x0487, 0x04cf, 0x04cf, 0x04f6, 0x04f7, 0x04fa, 0x04ff, 0x0510, 0x0530, 0x0557, 0x0558, 0x0560, 0x0560, 0x0588, 0x0588, 0x058b, 0x0590, 0x05a2, 0x05a2, 0x05ba, 0x05ba, 0x05c5, 0x05cf, 0x05eb, 0x05ef, 0x05f5, 0x060b, 0x060d, 0x061a, 0x061c, 0x061e, 0x0620, 0x0620, 0x063b, 0x063f, 0x0656, 0x065f, 0x06ee, 0x06ef, 0x06ff, 0x06ff, 0x070e, 0x070e, 0x072d, 0x072f, 0x074b, 0x077f, 0x07b2, 0x0900, 0x0904, 0x0904, 0x093a, 0x093b, 0x094e, 0x094f, 0x0955, 0x0957, 0x0971, 0x0980, 0x0984, 0x0984, 0x098d, 0x098e, 0x0991, 0x0992, 0x09a9, 0x09a9, 0x09b1, 0x09b1, 0x09b3, 0x09b5, 0x09ba, 0x09bb, 0x09bd, 0x09bd, 0x09c5, 0x09c6, 0x09c9, 0x09ca, 0x09ce, 0x09d6, 0x09d8, 0x09db, 0x09de, 0x09de, 0x09e4, 0x09e5, 0x09fb, 0x0a01, 0x0a03, 0x0a04, 0x0a0b, 0x0a0e, 0x0a11, 0x0a12, 0x0a29, 0x0a29, 0x0a31, 0x0a31, 0x0a34, 0x0a34, 0x0a37, 0x0a37, 0x0a3a, 0x0a3b, 0x0a3d, 0x0a3d, 0x0a43, 0x0a46, 0x0a49, 0x0a4a, 0x0a4e, 0x0a58, 0x0a5d, 0x0a5d, 0x0a5f, 0x0a65, 0x0a75, 0x0a80, 0x0a84, 0x0a84, 0x0a8c, 0x0a8c, 0x0a8e, 0x0a8e, 0x0a92, 0x0a92, 0x0aa9, 0x0aa9, 0x0ab1, 0x0ab1, 0x0ab4, 0x0ab4, 0x0aba, 0x0abb, 0x0ac6, 0x0ac6, 0x0aca, 0x0aca, 0x0ace, 0x0acf, 0x0ad1, 0x0adf, 0x0ae1, 0x0ae5, 0x0af0, 0x0b00, 0x0b04, 0x0b04, 0x0b0d, 0x0b0e, 0x0b11, 0x0b12, 0x0b29, 0x0b29, 0x0b31, 0x0b31, 0x0b34, 0x0b35, 0x0b3a, 0x0b3b, 0x0b44, 0x0b46, 0x0b49, 0x0b4a, 0x0b4e, 0x0b55, 0x0b58, 0x0b5b, 0x0b5e, 0x0b5e, 0x0b62, 0x0b65, 0x0b71, 0x0b81, 0x0b84, 0x0b84, 0x0b8b, 0x0b8d, 0x0b91, 0x0b91, 0x0b96, 0x0b98, 0x0b9b, 0x0b9b, 0x0b9d, 0x0b9d, 0x0ba0, 0x0ba2, 0x0ba5, 0x0ba7, 0x0bab, 0x0bad, 0x0bb6, 0x0bb6, 0x0bba, 0x0bbd, 0x0bc3, 0x0bc5, 0x0bc9, 0x0bc9, 0x0bce, 0x0bd6, 0x0bd8, 0x0be6, 0x0bf3, 0x0c00, 0x0c04, 0x0c04, 0x0c0d, 0x0c0d, 0x0c11, 0x0c11, 0x0c29, 0x0c29, 0x0c34, 0x0c34, 0x0c3a, 0x0c3d, 0x0c45, 0x0c45, 0x0c49, 0x0c49, 0x0c4e, 0x0c54, 0x0c57, 0x0c5f, 0x0c62, 0x0c65, 0x0c70, 0x0c81, 0x0c84, 0x0c84, 0x0c8d, 0x0c8d, 0x0c91, 0x0c91, 0x0ca9, 0x0ca9, 0x0cb4, 0x0cb4, 0x0cba, 0x0cbd, 0x0cc5, 0x0cc5, 0x0cc9, 0x0cc9, 0x0cce, 0x0cd4, 0x0cd7, 0x0cdd, 0x0cdf, 0x0cdf, 0x0ce2, 0x0ce5, 0x0cf0, 0x0d01, 0x0d04, 0x0d04, 0x0d0d, 0x0d0d, 0x0d11, 0x0d11, 0x0d29, 0x0d29, 0x0d3a, 0x0d3d, 0x0d44, 0x0d45, 0x0d49, 0x0d49, 0x0d4e, 0x0d56, 0x0d58, 0x0d5f, 0x0d62, 0x0d65, 0x0d70, 0x0d81, 0x0d84, 0x0d84, 0x0d97, 0x0d99, 0x0db2, 0x0db2, 0x0dbc, 0x0dbc, 0x0dbe, 0x0dbf, 0x0dc7, 0x0dc9, 0x0dcb, 0x0dce, 0x0dd5, 0x0dd5, 0x0dd7, 0x0dd7, 0x0de0, 0x0df1, 0x0df5, 0x0e00, 0x0e3b, 0x0e3e, 0x0e5c, 0x0e80, 0x0e83, 0x0e83, 0x0e85, 0x0e86, 0x0e89, 0x0e89, 0x0e8b, 0x0e8c, 0x0e8e, 0x0e93, 0x0e98, 0x0e98, 0x0ea0, 0x0ea0, 0x0ea4, 0x0ea4, 0x0ea6, 0x0ea6, 0x0ea8, 0x0ea9, 0x0eac, 0x0eac, 0x0eba, 0x0eba, 0x0ebe, 0x0ebf, 0x0ec5, 0x0ec5, 0x0ec7, 0x0ec7, 0x0ece, 0x0ecf, 0x0eda, 0x0edb, 0x0ede, 0x0eff, 0x0f48, 0x0f48, 0x0f6b, 0x0f70, 0x0f8c, 0x0f8f, 0x0f98, 0x0f98, 0x0fbd, 0x0fbd, 0x0fcd, 0x0fce, 0x0fd0, 0x0fff, 0x1022, 0x1022, 0x1028, 0x1028, 0x102b, 0x102b, 0x1033, 0x1035, 0x103a, 0x103f, 0x105a, 0x109f, 0x10c6, 0x10cf, 0x10f9, 0x10fa, 0x10fc, 0x10ff, 0x115a, 0x115e, 0x11a3, 0x11a7, 0x11fa, 0x11ff, 0x1207, 0x1207, 0x1247, 0x1247, 0x1249, 0x1249, 0x124e, 0x124f, 0x1257, 0x1257, 0x1259, 0x1259, 0x125e, 0x125f, 0x1287, 0x1287, 0x1289, 0x1289, 0x128e, 0x128f, 0x12af, 0x12af, 0x12b1, 0x12b1, 0x12b6, 0x12b7, 0x12bf, 0x12bf, 0x12c1, 0x12c1, 0x12c6, 0x12c7, 0x12cf, 0x12cf, 0x12d7, 0x12d7, 0x12ef, 0x12ef, 0x130f, 0x130f, 0x1311, 0x1311, 0x1316, 0x1317, 0x131f, 0x131f, 0x1347, 0x1347, 0x135b, 0x1360, 0x137d, 0x139f, 0x13f5, 0x1400, 0x1677, 0x167f, 0x169d, 0x169f, 0x16f1, 0x16ff, 0x170d, 0x170d, 0x1715, 0x171f, 0x1737, 0x173f, 0x1754, 0x175f, 0x176d, 0x176d, 0x1771, 0x1771, 0x1774, 0x177f, 0x17dd, 0x17df, 0x17ea, 0x17ff, 0x180f, 0x180f, 0x181a, 0x181f, 0x1878, 0x187f, 0x18aa, 0x1dff, 0x1e9c, 0x1e9f, 0x1efa, 0x1eff, 0x1f16, 0x1f17, 0x1f1e, 0x1f1f, 0x1f46, 0x1f47, 0x1f4e, 0x1f4f, 0x1f58, 0x1f58, 0x1f5a, 0x1f5a, 0x1f5c, 0x1f5c, 0x1f5e, 0x1f5e, 0x1f7e, 0x1f7f, 0x1fb5, 0x1fb5, 0x1fc5, 0x1fc5, 0x1fd4, 0x1fd5, 0x1fdc, 0x1fdc, 0x1ff0, 0x1ff1, 0x1ff5, 0x1ff5, 0x1fff, 0x1fff, 0x2053, 0x2056, 0x2058, 0x205e, 0x2064, 0x2069, 0x2072, 0x2073, 0x208f, 0x209f, 0x20b2, 0x20cf, 0x20eb, 0x20ff, 0x213b, 0x213c, 0x214c, 0x2152, 0x2184, 0x218f, 0x23cf, 0x23ff, 0x2427, 0x243f, 0x244b, 0x245f, 0x24ff, 0x24ff, 0x2614, 0x2615, 0x2618, 0x2618, 0x267e, 0x267f, 0x268a, 0x2700, 0x2705, 0x2705, 0x270a, 0x270b, 0x2728, 0x2728, 0x274c, 0x274c, 0x274e, 0x274e, 0x2753, 0x2755, 0x2757, 0x2757, 0x275f, 0x2760, 0x2795, 0x2797, 0x27b0, 0x27b0, 0x27bf, 0x27cf, 0x27ec, 0x27ef, 0x2b00, 0x2e7f, 0x2e9a, 0x2e9a, 0x2ef4, 0x2eff, 0x2fd6, 0x2fef, 0x2ffc, 0x2fff, 0x3040, 0x3040, 0x3097, 0x3098, 0x3100, 0x3104, 0x312d, 0x3130, 0x318f, 0x318f, 0x31b8, 0x31ef, 0x321d, 0x321f, 0x3244, 0x3250, 0x327c, 0x327e, 0x32cc, 0x32cf, 0x32ff, 0x32ff, 0x3377, 0x337a, 0x33de, 0x33df, 0x33ff, 0x33ff, 0x4db6, 0x4dff, 0x9fa6, 0x9fff, 0xa48d, 0xa48f, 0xa4c7, 0xabff, 0xd7a4, 0xd7ff, 0xfa2e, 0xfa2f, 0xfa6b, 0xfaff, 0xfb07, 0xfb12, 0xfb18, 0xfb1c, 0xfb37, 0xfb37, 0xfb3d, 0xfb3d, 0xfb3f, 0xfb3f, 0xfb42, 0xfb42, 0xfb45, 0xfb45, 0xfbb2, 0xfbd2, 0xfd40, 0xfd4f, 0xfd90, 0xfd91, 0xfdc8, 0xfdcf, 0xfdfd, 0xfdff, 0xfe10, 0xfe1f, 0xfe24, 0xfe2f, 0xfe47, 0xfe48, 0xfe53, 0xfe53, 0xfe67, 0xfe67, 0xfe6c, 0xfe6f, 0xfe75, 0xfe75, 0xfefd, 0xfefe, 0xff00, 0xff00, 0xffbf, 0xffc1, 0xffc8, 0xffc9, 0xffd0, 0xffd1, 0xffd8, 0xffd9, 0xffdd, 0xffdf, 0xffe7, 0xffe7, 0xffef, 0xfff8, 0x10000, 0x102ff, 0x1031f, 0x1031f, 0x10324, 0x1032f, 0x1034b, 0x103ff, 0x10426, 0x10427, 0x1044e, 0x1cfff, 0x1d0f6, 0x1d0ff, 0x1d127, 0x1d129, 0x1d1de, 0x1d3ff, 0x1d455, 0x1d455, 0x1d49d, 0x1d49d, 0x1d4a0, 0x1d4a1, 0x1d4a3, 0x1d4a4, 0x1d4a7, 0x1d4a8, 0x1d4ad, 0x1d4ad, 0x1d4ba, 0x1d4ba, 0x1d4bc, 0x1d4bc, 0x1d4c1, 0x1d4c1, 0x1d4c4, 0x1d4c4, 0x1d506, 0x1d506, 0x1d50b, 0x1d50c, 0x1d515, 0x1d515, 0x1d51d, 0x1d51d, 0x1d53a, 0x1d53a, 0x1d53f, 0x1d53f, 0x1d545, 0x1d545, 0x1d547, 0x1d549, 0x1d551, 0x1d551, 0x1d6a4, 0x1d6a7, 0x1d7ca, 0x1d7cd, 0x1d800, 0x1fffd, 0x2a6d7, 0x2f7ff, 0x2fa1e, 0x2fffd, 0x30000, 0x3fffd, 0x40000, 0x4fffd, 0x50000, 0x5fffd, 0x60000, 0x6fffd, 0x70000, 0x7fffd, 0x80000, 0x8fffd, 0x90000, 0x9fffd, 0xa0000, 0xafffd, 0xb0000, 0xbfffd, 0xc0000, 0xcfffd, 0xd0000, 0xdfffd, 0xe0000, 0xe0000, 0xe0002, 0xe001f, 0xe0080, 0xefffd]; // prettier-ignore-end var isUnassignedCodePoint = character => inRange(character, unassigned_code_points); // prettier-ignore-start /** * B.1 Commonly mapped to nothing * @link https://tools.ietf.org/html/rfc3454#appendix-B.1 */ var commonly_mapped_to_nothing = [0x00ad, 0x00ad, 0x034f, 0x034f, 0x1806, 0x1806, 0x180b, 0x180b, 0x180c, 0x180c, 0x180d, 0x180d, 0x200b, 0x200b, 0x200c, 0x200c, 0x200d, 0x200d, 0x2060, 0x2060, 0xfe00, 0xfe00, 0xfe01, 0xfe01, 0xfe02, 0xfe02, 0xfe03, 0xfe03, 0xfe04, 0xfe04, 0xfe05, 0xfe05, 0xfe06, 0xfe06, 0xfe07, 0xfe07, 0xfe08, 0xfe08, 0xfe09, 0xfe09, 0xfe0a, 0xfe0a, 0xfe0b, 0xfe0b, 0xfe0c, 0xfe0c, 0xfe0d, 0xfe0d, 0xfe0e, 0xfe0e, 0xfe0f, 0xfe0f, 0xfeff, 0xfeff]; // prettier-ignore-end var isCommonlyMappedToNothing = character => inRange(character, commonly_mapped_to_nothing); // prettier-ignore-start /** * C.1.2 Non-ASCII space characters * @link https://tools.ietf.org/html/rfc3454#appendix-C.1.2 */ var non_ASCII_space_characters = [0x00a0, 0x00a0 /* NO-BREAK SPACE */ , 0x1680, 0x1680 /* OGHAM SPACE MARK */ , 0x2000, 0x2000 /* EN QUAD */ , 0x2001, 0x2001 /* EM QUAD */ , 0x2002, 0x2002 /* EN SPACE */ , 0x2003, 0x2003 /* EM SPACE */ , 0x2004, 0x2004 /* THREE-PER-EM SPACE */ , 0x2005, 0x2005 /* FOUR-PER-EM SPACE */ , 0x2006, 0x2006 /* SIX-PER-EM SPACE */ , 0x2007, 0x2007 /* FIGURE SPACE */ , 0x2008, 0x2008 /* PUNCTUATION SPACE */ , 0x2009, 0x2009 /* THIN SPACE */ , 0x200a, 0x200a /* HAIR SPACE */ , 0x200b, 0x200b /* ZERO WIDTH SPACE */ , 0x202f, 0x202f /* NARROW NO-BREAK SPACE */ , 0x205f, 0x205f /* MEDIUM MATHEMATICAL SPACE */ , 0x3000, 0x3000 /* IDEOGRAPHIC SPACE */ ]; // prettier-ignore-end var isNonASCIISpaceCharacter = character => inRange(character, non_ASCII_space_characters); // prettier-ignore-start var non_ASCII_controls_characters = [ /** * C.2.2 Non-ASCII control characters * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.2 */ 0x0080, 0x009f /* [CONTROL CHARACTERS] */ , 0x06dd, 0x06dd /* ARABIC END OF AYAH */ , 0x070f, 0x070f /* SYRIAC ABBREVIATION MARK */ , 0x180e, 0x180e /* MONGOLIAN VOWEL SEPARATOR */ , 0x200c, 0x200c /* ZERO WIDTH NON-JOINER */ , 0x200d, 0x200d /* ZERO WIDTH JOINER */ , 0x2028, 0x2028 /* LINE SEPARATOR */ , 0x2029, 0x2029 /* PARAGRAPH SEPARATOR */ , 0x2060, 0x2060 /* WORD JOINER */ , 0x2061, 0x2061 /* FUNCTION APPLICATION */ , 0x2062, 0x2062 /* INVISIBLE TIMES */ , 0x2063, 0x2063 /* INVISIBLE SEPARATOR */ , 0x206a, 0x206f /* [CONTROL CHARACTERS] */ , 0xfeff, 0xfeff /* ZERO WIDTH NO-BREAK SPACE */ , 0xfff9, 0xfffc /* [CONTROL CHARACTERS] */ , 0x1d173, 0x1d17a /* [MUSICAL CONTROL CHARACTERS] */ ]; var non_character_codepoints = [ /** * C.4 Non-character code points * @link https://tools.ietf.org/html/rfc3454#appendix-C.4 */ 0xfdd0, 0xfdef /* [NONCHARACTER CODE POINTS] */ , 0xfffe, 0xffff /* [NONCHARACTER CODE POINTS] */ , 0x1fffe, 0x1ffff /* [NONCHARACTER CODE POINTS] */ , 0x2fffe, 0x2ffff /* [NONCHARACTER CODE POINTS] */ , 0x3fffe, 0x3ffff /* [NONCHARACTER CODE POINTS] */ , 0x4fffe, 0x4ffff /* [NONCHARACTER CODE POINTS] */ , 0x5fffe, 0x5ffff /* [NONCHARACTER CODE POINTS] */ , 0x6fffe, 0x6ffff /* [NONCHARACTER CODE POINTS] */ , 0x7fffe, 0x7ffff /* [NONCHARACTER CODE POINTS] */ , 0x8fffe, 0x8ffff /* [NONCHARACTER CODE POINTS] */ , 0x9fffe, 0x9ffff /* [NONCHARACTER CODE POINTS] */ , 0xafffe, 0xaffff /* [NONCHARACTER CODE POINTS] */ , 0xbfffe, 0xbffff /* [NONCHARACTER CODE POINTS] */ , 0xcfffe, 0xcffff /* [NONCHARACTER CODE POINTS] */ , 0xdfffe, 0xdffff /* [NONCHARACTER CODE POINTS] */ , 0xefffe, 0xeffff /* [NONCHARACTER CODE POINTS] */ , 0x10fffe, 0x10ffff /* [NONCHARACTER CODE POINTS] */ ]; /** * 2.3. Prohibited Output */ var prohibited_characters = [ /** * C.2.1 ASCII control characters * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.1 */ 0, 0x001f /* [CONTROL CHARACTERS] */ , 0x007f, 0x007f /* DELETE */ , /** * C.8 Change display properties or are deprecated * @link https://tools.ietf.org/html/rfc3454#appendix-C.8 */ 0x0340, 0x0340 /* COMBINING GRAVE TONE MARK */ , 0x0341, 0x0341 /* COMBINING ACUTE TONE MARK */ , 0x200e, 0x200e /* LEFT-TO-RIGHT MARK */ , 0x200f, 0x200f /* RIGHT-TO-LEFT MARK */ , 0x202a, 0x202a /* LEFT-TO-RIGHT EMBEDDING */ , 0x202b, 0x202b /* RIGHT-TO-LEFT EMBEDDING */ , 0x202c, 0x202c /* POP DIRECTIONAL FORMATTING */ , 0x202d, 0x202d /* LEFT-TO-RIGHT OVERRIDE */ , 0x202e, 0x202e /* RIGHT-TO-LEFT OVERRIDE */ , 0x206a, 0x206a /* INHIBIT SYMMETRIC SWAPPING */ , 0x206b, 0x206b /* ACTIVATE SYMMETRIC SWAPPING */ , 0x206c, 0x206c /* INHIBIT ARABIC FORM SHAPING */ , 0x206d, 0x206d /* ACTIVATE ARABIC FORM SHAPING */ , 0x206e, 0x206e /* NATIONAL DIGIT SHAPES */ , 0x206f, 0x206f /* NOMINAL DIGIT SHAPES */ , /** * C.7 Inappropriate for canonical representation * @link https://tools.ietf.org/html/rfc3454#appendix-C.7 */ 0x2ff0, 0x2ffb /* [IDEOGRAPHIC DESCRIPTION CHARACTERS] */ , /** * C.5 Surrogate codes * @link https://tools.ietf.org/html/rfc3454#appendix-C.5 */ 0xd800, 0xdfff, /** * C.3 Private use * @link https://tools.ietf.org/html/rfc3454#appendix-C.3 */ 0xe000, 0xf8ff /* [PRIVATE USE, PLANE 0] */ , /** * C.6 Inappropriate for plain text * @link https://tools.ietf.org/html/rfc3454#appendix-C.6 */ 0xfff9, 0xfff9 /* INTERLINEAR ANNOTATION ANCHOR */ , 0xfffa, 0xfffa /* INTERLINEAR ANNOTATION SEPARATOR */ , 0xfffb, 0xfffb /* INTERLINEAR ANNOTATION TERMINATOR */ , 0xfffc, 0xfffc /* OBJECT REPLACEMENT CHARACTER */ , 0xfffd, 0xfffd /* REPLACEMENT CHARACTER */ , /** * C.9 Tagging characters * @link https://tools.ietf.org/html/rfc3454#appendix-C.9 */ 0xe0001, 0xe0001 /* LANGUAGE TAG */ , 0xe0020, 0xe007f /* [TAGGING CHARACTERS] */ , /** * C.3 Private use * @link https://tools.ietf.org/html/rfc3454#appendix-C.3 */ 0xf0000, 0xffffd /* [PRIVATE USE, PLANE 15] */ , 0x100000, 0x10fffd /* [PRIVATE USE, PLANE 16] */ ]; // prettier-ignore-end var isProhibitedCharacter = character => inRange(character, non_ASCII_space_characters) || inRange(character, prohibited_characters) || inRange(character, non_ASCII_controls_characters) || inRange(character, non_character_codepoints); // prettier-ignore-start /** * D.1 Characters with bidirectional property "R" or "AL" * @link https://tools.ietf.org/html/rfc3454#appendix-D.1 */ var bidirectional_r_al = [0x05be, 0x05be, 0x05c0, 0x05c0, 0x05c3, 0x05c3, 0x05d0, 0x05ea, 0x05f0, 0x05f4, 0x061b, 0x061b, 0x061f, 0x061f, 0x0621, 0x063a, 0x0640, 0x064a, 0x066d, 0x066f, 0x0671, 0x06d5, 0x06dd, 0x06dd, 0x06e5, 0x06e6, 0x06fa, 0x06fe, 0x0700, 0x070d, 0x0710, 0x0710, 0x0712, 0x072c, 0x0780, 0x07a5, 0x07b1, 0x07b1, 0x200f, 0x200f, 0xfb1d, 0xfb1d, 0xfb1f, 0xfb28, 0xfb2a, 0xfb36, 0xfb38, 0xfb3c, 0xfb3e, 0xfb3e, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfb46, 0xfbb1, 0xfbd3, 0xfd3d, 0xfd50, 0xfd8f, 0xfd92, 0xfdc7, 0xfdf0, 0xfdfc, 0xfe70, 0xfe74, 0xfe76, 0xfefc]; // prettier-ignore-end var isBidirectionalRAL = character => inRange(character, bidirectional_r_al); // prettier-ignore-start /** * D.2 Characters with bidirectional property "L" * @link https://tools.ietf.org/html/rfc3454#appendix-D.2 */ var bidirectional_l = [0x0041, 0x005a, 0x0061, 0x007a, 0x00aa, 0x00aa, 0x00b5, 0x00b5, 0x00ba, 0x00ba, 0x00c0, 0x00d6, 0x00d8, 0x00f6, 0x00f8, 0x0220, 0x0222, 0x0233, 0x0250, 0x02ad, 0x02b0, 0x02b8, 0x02bb, 0x02c1, 0x02d0, 0x02d1, 0x02e0, 0x02e4, 0x02ee, 0x02ee, 0x037a, 0x037a, 0x0386, 0x0386, 0x0388, 0x038a, 0x038c, 0x038c, 0x038e, 0x03a1, 0x03a3, 0x03ce, 0x03d0, 0x03f5, 0x0400, 0x0482, 0x048a, 0x04ce, 0x04d0, 0x04f5, 0x04f8, 0x04f9, 0x0500, 0x050f, 0x0531, 0x0556, 0x0559, 0x055f, 0x0561, 0x0587, 0x0589, 0x0589, 0x0903, 0x0903, 0x0905, 0x0939, 0x093d, 0x0940, 0x0949, 0x094c, 0x0950, 0x0950, 0x0958, 0x0961, 0x0964, 0x0970, 0x0982, 0x0983, 0x0985, 0x098c, 0x098f, 0x0990, 0x0993, 0x09a8, 0x09aa, 0x09b0, 0x09b2, 0x09b2, 0x09b6, 0x09b9, 0x09be, 0x09c0, 0x09c7, 0x09c8, 0x09cb, 0x09cc, 0x09d7, 0x09d7, 0x09dc, 0x09dd, 0x09df, 0x09e1, 0x09e6, 0x09f1, 0x09f4, 0x09fa, 0x0a05, 0x0a0a, 0x0a0f, 0x0a10, 0x0a13, 0x0a28, 0x0a2a, 0x0a30, 0x0a32, 0x0a33, 0x0a35, 0x0a36, 0x0a38, 0x0a39, 0x0a3e, 0x0a40, 0x0a59, 0x0a5c, 0x0a5e, 0x0a5e, 0x0a66, 0x0a6f, 0x0a72, 0x0a74, 0x0a83, 0x0a83, 0x0a85, 0x0a8b, 0x0a8d, 0x0a8d, 0x0a8f, 0x0a91, 0x0a93, 0x0aa8, 0x0aaa, 0x0ab0, 0x0ab2, 0x0ab3, 0x0ab5, 0x0ab9, 0x0abd, 0x0ac0, 0x0ac9, 0x0ac9, 0x0acb, 0x0acc, 0x0ad0, 0x0ad0, 0x0ae0, 0x0ae0, 0x0ae6, 0x0aef, 0x0b02, 0x0b03, 0x0b05, 0x0b0c, 0x0b0f, 0x0b10, 0x0b13, 0x0b28, 0x0b2a, 0x0b30, 0x0b32, 0x0b33, 0x0b36, 0x0b39, 0x0b3d, 0x0b3e, 0x0b40, 0x0b40, 0x0b47, 0x0b48, 0x0b4b, 0x0b4c, 0x0b57, 0x0b57, 0x0b5c, 0x0b5d, 0x0b5f, 0x0b61, 0x0b66, 0x0b70, 0x0b83, 0x0b83, 0x0b85, 0x0b8a, 0x0b8e, 0x0b90, 0x0b92, 0x0b95, 0x0b99, 0x0b9a, 0x0b9c, 0x0b9c, 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, 0x0ba8, 0x0baa, 0x0bae, 0x0bb5, 0x0bb7, 0x0bb9, 0x0bbe, 0x0bbf, 0x0bc1, 0x0bc2, 0x0bc6, 0x0bc8, 0x0bca, 0x0bcc, 0x0bd7, 0x0bd7, 0x0be7, 0x0bf2, 0x0c01, 0x0c03, 0x0c05, 0x0c0c, 0x0c0e, 0x0c10, 0x0c12, 0x0c28, 0x0c2a, 0x0c33, 0x0c35, 0x0c39, 0x0c41, 0x0c44, 0x0c60, 0x0c61, 0x0c66, 0x0c6f, 0x0c82, 0x0c83, 0x0c85, 0x0c8c, 0x0c8e, 0x0c90, 0x0c92, 0x0ca8, 0x0caa, 0x0cb3, 0x0cb5, 0x0cb9, 0x0cbe, 0x0cbe, 0x0cc0, 0x0cc4, 0x0cc7, 0x0cc8, 0x0cca, 0x0ccb, 0x0cd5, 0x0cd6, 0x0cde, 0x0cde, 0x0ce0, 0x0ce1, 0x0ce6, 0x0cef, 0x0d02, 0x0d03, 0x0d05, 0x0d0c, 0x0d0e, 0x0d10, 0x0d12, 0x0d28, 0x0d2a, 0x0d39, 0x0d3e, 0x0d40, 0x0d46, 0x0d48, 0x0d4a, 0x0d4c, 0x0d57, 0x0d57, 0x0d60, 0x0d61, 0x0d66, 0x0d6f, 0x0d82, 0x0d83, 0x0d85, 0x0d96, 0x0d9a, 0x0db1, 0x0db3, 0x0dbb, 0x0dbd, 0x0dbd, 0x0dc0, 0x0dc6, 0x0dcf, 0x0dd1, 0x0dd8, 0x0ddf, 0x0df2, 0x0df4, 0x0e01, 0x0e30, 0x0e32, 0x0e33, 0x0e40, 0x0e46, 0x0e4f, 0x0e5b, 0x0e81, 0x0e82, 0x0e84, 0x0e84, 0x0e87, 0x0e88, 0x0e8a, 0x0e8a, 0x0e8d, 0x0e8d, 0x0e94, 0x0e97, 0x0e99, 0x0e9f, 0x0ea1, 0x0ea3, 0x0ea5, 0x0ea5, 0x0ea7, 0x0ea7, 0x0eaa, 0x0eab, 0x0ead, 0x0eb0, 0x0eb2, 0x0eb3, 0x0ebd, 0x0ebd, 0x0ec0, 0x0ec4, 0x0ec6, 0x0ec6, 0x0ed0, 0x0ed9, 0x0edc, 0x0edd, 0x0f00, 0x0f17, 0x0f1a, 0x0f34, 0x0f36, 0x0f36, 0x0f38, 0x0f38, 0x0f3e, 0x0f47, 0x0f49, 0x0f6a, 0x0f7f, 0x0f7f, 0x0f85, 0x0f85, 0x0f88, 0x0f8b, 0x0fbe, 0x0fc5, 0x0fc7, 0x0fcc, 0x0fcf, 0x0fcf, 0x1000, 0x1021, 0x1023, 0x1027, 0x1029, 0x102a, 0x102c, 0x102c, 0x1031, 0x1031, 0x1038, 0x1038, 0x1040, 0x1057, 0x10a0, 0x10c5, 0x10d0, 0x10f8, 0x10fb, 0x10fb, 0x1100, 0x1159, 0x115f, 0x11a2, 0x11a8, 0x11f9, 0x1200, 0x1206, 0x1208, 0x1246, 0x1248, 0x1248, 0x124a, 0x124d, 0x1250, 0x1256, 0x1258, 0x1258, 0x125a, 0x125d, 0x1260, 0x1286, 0x1288, 0x1288, 0x128a, 0x128d, 0x1290, 0x12ae, 0x12b0, 0x12b0, 0x12b2, 0x12b5, 0x12b8, 0x12be, 0x12c0, 0x12c0, 0x12c2, 0x12c5, 0x12c8, 0x12ce, 0x12d0, 0x12d6, 0x12d8, 0x12ee, 0x12f0, 0x130e, 0x1310, 0x1310, 0x1312, 0x1315, 0x1318, 0x131e, 0x1320, 0x1346, 0x1348, 0x135a, 0x1361, 0x137c, 0x13a0, 0x13f4, 0x1401, 0x1676, 0x1681, 0x169a, 0x16a0, 0x16f0, 0x1700, 0x170c, 0x170e, 0x1711, 0x1720, 0x1731, 0x1735, 0x1736, 0x1740, 0x1751, 0x1760, 0x176c, 0x176e, 0x1770, 0x1780, 0x17b6, 0x17be, 0x17c5, 0x17c7, 0x17c8, 0x17d4, 0x17da, 0x17dc, 0x17dc, 0x17e0, 0x17e9, 0x1810, 0x1819, 0x1820, 0x1877, 0x1880, 0x18a8, 0x1e00, 0x1e9b, 0x1ea0, 0x1ef9, 0x1f00, 0x1f15, 0x1f18, 0x1f1d, 0x1f20, 0x1f45, 0x1f48, 0x1f4d, 0x1f50, 0x1f57, 0x1f59, 0x1f59, 0x1f5b, 0x1f5b, 0x1f5d, 0x1f5d, 0x1f5f, 0x1f7d, 0x1f80, 0x1fb4, 0x1fb6, 0x1fbc, 0x1fbe, 0x1fbe, 0x1fc2, 0x1fc4, 0x1fc6, 0x1fcc, 0x1fd0, 0x1fd3, 0x1fd6, 0x1fdb, 0x1fe0, 0x1fec, 0x1ff2, 0x1ff4, 0x1ff6, 0x1ffc, 0x200e, 0x200e, 0x2071, 0x2071, 0x207f, 0x207f, 0x2102, 0x2102, 0x2107, 0x2107, 0x210a, 0x2113, 0x2115, 0x2115, 0x2119, 0x211d, 0x2124, 0x2124, 0x2126, 0x2126, 0x2128, 0x2128, 0x212a, 0x212d, 0x212f, 0x2131, 0x2133, 0x2139, 0x213d, 0x213f, 0x2145, 0x2149, 0x2160, 0x2183, 0x2336, 0x237a, 0x2395, 0x2395, 0x249c, 0x24e9, 0x3005, 0x3007, 0x3021, 0x3029, 0x3031, 0x3035, 0x3038, 0x303c, 0x3041, 0x3096, 0x309d, 0x309f, 0x30a1, 0x30fa, 0x30fc, 0x30ff, 0x3105, 0x312c, 0x3131, 0x318e, 0x3190, 0x31b7, 0x31f0, 0x321c, 0x3220, 0x3243, 0x3260, 0x327b, 0x327f, 0x32b0, 0x32c0, 0x32cb, 0x32d0, 0x32fe, 0x3300, 0x3376, 0x337b, 0x33dd, 0x33e0, 0x33fe, 0x3400, 0x4db5, 0x4e00, 0x9fa5, 0xa000, 0xa48c, 0xac00, 0xd7a3, 0xd800, 0xfa2d, 0xfa30, 0xfa6a, 0xfb00, 0xfb06, 0xfb13, 0xfb17, 0xff21, 0xff3a, 0xff41, 0xff5a, 0xff66, 0xffbe, 0xffc2, 0xffc7, 0xffca, 0xffcf, 0xffd2, 0xffd7, 0xffda, 0xffdc, 0x10300, 0x1031e, 0x10320, 0x10323, 0x10330, 0x1034a, 0x10400, 0x10425, 0x10428, 0x1044d, 0x1d000, 0x1d0f5, 0x1d100, 0x1d126, 0x1d12a, 0x1d166, 0x1d16a, 0x1d172, 0x1d183, 0x1d184, 0x1d18c, 0x1d1a9, 0x1d1ae, 0x1d1dd, 0x1d400, 0x1d454, 0x1d456, 0x1d49c, 0x1d49e, 0x1d49f, 0x1d4a2, 0x1d4a2, 0x1d4a5, 0x1d4a6, 0x1d4a9, 0x1d4ac, 0x1d4ae, 0x1d4b9, 0x1d4bb, 0x1d4bb, 0x1d4bd, 0x1d4c0, 0x1d4c2, 0x1d4c3, 0x1d4c5, 0x1d505, 0x1d507, 0x1d50a, 0x1d50d, 0x1d514, 0x1d516, 0x1d51c, 0x1d51e, 0x1d539, 0x1d53b, 0x1d53e, 0x1d540, 0x1d544, 0x1d546, 0x1d546, 0x1d54a, 0x1d550, 0x1d552, 0x1d6a3, 0x1d6a8, 0x1d7c9, 0x20000, 0x2a6d6, 0x2f800, 0x2fa1d, 0xf0000, 0xffffd, 0x100000, 0x10fffd]; // prettier-ignore-end var isBidirectionalL = character => inRange(character, bidirectional_l); /** * non-ASCII space characters [StringPrep, C.1.2] that can be * mapped to SPACE (U+0020) */ var mapping2space = isNonASCIISpaceCharacter; /** * the "commonly mapped to nothing" characters [StringPrep, B.1] * that can be mapped to nothing. */ var mapping2nothing = isCommonlyMappedToNothing; // utils var getCodePoint = character => character.codePointAt(0); var first = x => x[0]; var last = x => x[x.length - 1]; /** * Convert provided string into an array of Unicode Code Points. * Based on https://stackoverflow.com/a/21409165/1556249 * and https://www.npmjs.com/package/code-point-at. * @param {string} input * @returns {number[]} */ function toCodePoints(input) { var codepoints = []; var size = input.length; for (var i = 0; i < size; i += 1) { var before = input.charCodeAt(i); if (before >= 0xd800 && before <= 0xdbff && size > i + 1) { var next = input.charCodeAt(i + 1); if (next >= 0xdc00 && next <= 0xdfff) { codepoints.push((before - 0xd800) * 0x400 + next - 0xdc00 + 0x10000); i += 1; continue; } } codepoints.push(before); } return codepoints; } /** * SASLprep. * @param {string} input * @param {Object} opts * @param {boolean} opts.allowUnassigned * @returns {string} */ function saslprep(input) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (typeof input !== 'string') { throw new TypeError('Expected string.'); } if (input.length === 0) { return ''; } // 1. Map var mapped_input = toCodePoints(input) // 1.1 mapping to space .map(character => mapping2space(character) ? 0x20 : character) // 1.2 mapping to nothing .filter(character => !mapping2nothing(character)); // 2. Normalize var normalized_input = String.fromCodePoint.apply(null, mapped_input).normalize('NFKC'); var normalized_map = toCodePoints(normalized_input); // 3. Prohibit var hasProhibited = normalized_map.some(isProhibitedCharacter); if (hasProhibited) { throw new Error('Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3'); } // Unassigned Code Points if (opts.allowUnassigned !== true) { var hasUnassigned = normalized_map.some(isUnassignedCodePoint); if (hasUnassigned) { throw new Error('Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5'); } } // 4. check bidi var hasBidiRAL = normalized_map.some(isBidirectionalRAL); var hasBidiL = normalized_map.some(isBidirectionalL); // 4.1 If a string contains any RandALCat character, the string MUST NOT // contain any LCat character. if (hasBidiRAL && hasBidiL) { throw new Error('String must not contain RandALCat and LCat at the same time,' + ' see https://tools.ietf.org/html/rfc3454#section-6'); } /** * 4.2 If a string contains any RandALCat character, a RandALCat * character MUST be the first character of the string, and a * RandALCat character MUST be the last character of the string. */ var isFirstBidiRAL = isBidirectionalRAL(getCodePoint(first(normalized_input))); var isLastBidiRAL = isBidirectionalRAL(getCodePoint(last(normalized_input))); if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) { throw new Error('Bidirectional RandALCat character must be the first and the last' + ' character of the string, see https://tools.ietf.org/html/rfc3454#section-6'); } return normalized_input; } /* PDFSecurity - represents PDF security settings By Yang Liu */ class PDFSecurity { static generateFileID() { var info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var infoStr = "".concat(info.CreationDate.getTime(), "\n"); for (var key in info) { // eslint-disable-next-line no-prototype-builtins if (!info.hasOwnProperty(key)) { continue; } infoStr += "".concat(key, ": ").concat(info[key].valueOf(), "\n"); } return wordArrayToBuffer(CryptoJS.MD5(infoStr)); } static generateRandomWordArray(bytes) { return CryptoJS.lib.WordArray.random(bytes); } static create(document) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!options.ownerPassword && !options.userPassword) { return null; } return new PDFSecurity(document, options); } constructor(document) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!options.ownerPassword && !options.userPassword) { throw new Error('None of owner password and user password is defined.'); } this.document = document; this._setupEncryption(options); } _setupEncryption(options) { switch (options.pdfVersion) { case '1.4': case '1.5': this.version = 2; break; case '1.6': case '1.7': this.version = 4; break; case '1.7ext3': this.version = 5; break; default: this.version = 1; break; } var encDict = { Filter: 'Standard' }; switch (this.version) { case 1: case 2: case 4: this._setupEncryptionV1V2V4(this.version, encDict, options); break; case 5: this._setupEncryptionV5(encDict, options); break; } this.dictionary = this.document.ref(encDict); } _setupEncryptionV1V2V4(v, encDict, options) { var r, permissions; switch (v) { case 1: r = 2; this.keyBits = 40; permissions = getPermissionsR2(options.permissions); break; case 2: r = 3; this.keyBits = 128; permissions = getPermissionsR3(options.permissions); break; case 4: r = 4; this.keyBits = 128; permissions = getPermissionsR3(options.permissions); break; } var paddedUserPassword = processPasswordR2R3R4(options.userPassword); var paddedOwnerPassword = options.ownerPassword ? processPasswordR2R3R4(options.ownerPassword) : paddedUserPassword; var ownerPasswordEntry = getOwnerPasswordR2R3R4(r, this.keyBits, paddedUserPassword, paddedOwnerPassword); this.encryptionKey = getEncryptionKeyR2R3R4(r, this.keyBits, this.document._id, paddedUserPassword, ownerPasswordEntry, permissions); var userPasswordEntry; if (r === 2) { userPasswordEntry = getUserPasswordR2(this.encryptionKey); } else { userPasswordEntry = getUserPasswordR3R4(this.document._id, this.encryptionKey); } encDict.V = v; if (v >= 2) { encDict.Length = this.keyBits; } if (v === 4) { encDict.CF = { StdCF: { AuthEvent: 'DocOpen', CFM: 'AESV2', Length: this.keyBits / 8 } }; encDict.StmF = 'StdCF'; encDict.StrF = 'StdCF'; } encDict.R = r; encDict.O = wordArrayToBuffer(ownerPasswordEntry); encDict.U = wordArrayToBuffer(userPasswordEntry); encDict.P = permissions; } _setupEncryptionV5(encDict, options) { this.keyBits = 256; var permissions = getPermissionsR3(options.permissions); var processedUserPassword = processPasswordR5(options.userPassword); var processedOwnerPassword = options.ownerPassword ? processPasswordR5(options.ownerPassword) : processedUserPassword; this.encryptionKey = getEncryptionKeyR5(PDFSecurity.generateRandomWordArray); var userPasswordEntry = getUserPasswordR5(processedUserPassword, PDFSecurity.generateRandomWordArray); var userKeySalt = CryptoJS.lib.WordArray.create(userPasswordEntry.words.slice(10, 12), 8); var userEncryptionKeyEntry = getUserEncryptionKeyR5(processedUserPassword, userKeySalt, this.encryptionKey); var ownerPasswordEntry = getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, PDFSecurity.generateRandomWordArray); var ownerKeySalt = CryptoJS.lib.WordArray.create(ownerPasswordEntry.words.slice(10, 12), 8); var ownerEncryptionKeyEntry = getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, this.encryptionKey); var permsEntry = getEncryptedPermissionsR5(permissions, this.encryptionKey, PDFSecurity.generateRandomWordArray); encDict.V = 5; encDict.Length = this.keyBits; encDict.CF = { StdCF: { AuthEvent: 'DocOpen', CFM: 'AESV3', Length: this.keyBits / 8 } }; encDict.StmF = 'StdCF'; encDict.StrF = 'StdCF'; encDict.R = 5; encDict.O = wordArrayToBuffer(ownerPasswordEntry); encDict.OE = wordArrayToBuffer(ownerEncryptionKeyEntry); encDict.U = wordArrayToBuffer(userPasswordEntry); encDict.UE = wordArrayToBuffer(userEncryptionKeyEntry); encDict.P = permissions; encDict.Perms = wordArrayToBuffer(permsEntry); } getEncryptFn(obj, gen) { var digest; if (this.version < 5) { digest = this.encryptionKey.clone().concat(CryptoJS.lib.WordArray.create([(obj & 0xff) << 24 | (obj & 0xff00) << 8 | obj >> 8 & 0xff00 | gen & 0xff, (gen & 0xff00) << 16], 5)); } if (this.version === 1 || this.version === 2) { var _key = CryptoJS.MD5(digest); _key.sigBytes = Math.min(16, this.keyBits / 8 + 5); return buffer => wordArrayToBuffer(CryptoJS.RC4.encrypt(CryptoJS.lib.WordArray.create(buffer), _key).ciphertext); } var key; if (this.version === 4) { key = CryptoJS.MD5(digest.concat(CryptoJS.lib.WordArray.create([0x73416c54], 4))); } else { key = this.encryptionKey; } var iv = PDFSecurity.generateRandomWordArray(16); var options = { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv }; return buffer => wordArrayToBuffer(iv.clone().concat(CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(buffer), key, options).ciphertext)); } end() { this.dictionary.end(); } } function getPermissionsR2() { var permissionObject = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var permissions = 0xffffffc0 >> 0; if (permissionObject.printing) { permissions |= 0b000000000100; } if (permissionObject.modifying) { permissions |= 0b000000001000; } if (permissionObject.copying) { permissions |= 0b000000010000; } if (permissionObject.annotating) { permissions |= 0b000000100000; } return permissions; } function getPermissionsR3() { var permissionObject = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var permissions = 0xfffff0c0 >> 0; if (permissionObject.printing === 'lowResolution') { permissions |= 0b000000000100; } if (permissionObject.printing === 'highResolution') { permissions |= 0b100000000100; } if (permissionObject.modifying) { permissions |= 0b000000001000; } if (permissionObject.copying) { permissions |= 0b000000010000; } if (permissionObject.annotating) { permissions |= 0b000000100000; } if (permissionObject.fillingForms) { permissions |= 0b000100000000; } if (permissionObject.contentAccessibility) { permissions |= 0b001000000000; } if (permissionObject.documentAssembly) { permissions |= 0b010000000000; } return permissions; } function getUserPasswordR2(encryptionKey) { return CryptoJS.RC4.encrypt(processPasswordR2R3R4(), encryptionKey).ciphertext; } function getUserPasswordR3R4(documentId, encryptionKey) { var key = encryptionKey.clone(); var cipher = CryptoJS.MD5(processPasswordR2R3R4().concat(CryptoJS.lib.WordArray.create(documentId))); for (var i = 0; i < 20; i++) { var xorRound = Math.ceil(key.sigBytes / 4); for (var j = 0; j < xorRound; j++) { key.words[j] = encryptionKey.words[j] ^ (i | i << 8 | i << 16 | i << 24); } cipher = CryptoJS.RC4.encrypt(cipher, key).ciphertext; } return cipher.concat(CryptoJS.lib.WordArray.create(null, 16)); } function getOwnerPasswordR2R3R4(r, keyBits, paddedUserPassword, paddedOwnerPassword) { var digest = paddedOwnerPassword; var round = r >= 3 ? 51 : 1; for (var i = 0; i < round; i++) { digest = CryptoJS.MD5(digest); } var key = digest.clone(); key.sigBytes = keyBits / 8; var cipher = paddedUserPassword; round = r >= 3 ? 20 : 1; for (var _i = 0; _i < round; _i++) { var xorRound = Math.ceil(key.sigBytes / 4); for (var j = 0; j < xorRound; j++) { key.words[j] = digest.words[j] ^ (_i | _i << 8 | _i << 16 | _i << 24); } cipher = CryptoJS.RC4.encrypt(cipher, key).ciphertext; } return cipher; } function getEncryptionKeyR2R3R4(r, keyBits, documentId, paddedUserPassword, ownerPasswordEntry, permissions) { var key = paddedUserPassword.clone().concat(ownerPasswordEntry).concat(CryptoJS.lib.WordArray.create([lsbFirstWord(permissions)], 4)).concat(CryptoJS.lib.WordArray.create(documentId)); var round = r >= 3 ? 51 : 1; for (var i = 0; i < round; i++) { key = CryptoJS.MD5(key); key.sigBytes = keyBits / 8; } return key; } function getUserPasswordR5(processedUserPassword, generateRandomWordArray) { var validationSalt = generateRandomWordArray(8); var keySalt = generateRandomWordArray(8); return CryptoJS.SHA256(processedUserPassword.clone().concat(validationSalt)).concat(validationSalt).concat(keySalt); } function getUserEncryptionKeyR5(processedUserPassword, userKeySalt, encryptionKey) { var key = CryptoJS.SHA256(processedUserPassword.clone().concat(userKeySalt)); var options = { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding, iv: CryptoJS.lib.WordArray.create(null, 16) }; return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext; } function getOwnerPasswordR5(processedOwnerPassword, userPasswordEntry, generateRandomWordArray) { var validationSalt = generateRandomWordArray(8); var keySalt = generateRandomWordArray(8); return CryptoJS.SHA256(processedOwnerPassword.clone().concat(validationSalt).concat(userPasswordEntry)).concat(validationSalt).concat(keySalt); } function getOwnerEncryptionKeyR5(processedOwnerPassword, ownerKeySalt, userPasswordEntry, encryptionKey) { var key = CryptoJS.SHA256(processedOwnerPassword.clone().concat(ownerKeySalt).concat(userPasswordEntry)); var options = { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding, iv: CryptoJS.lib.WordArray.create(null, 16) }; return CryptoJS.AES.encrypt(encryptionKey, key, options).ciphertext; } function getEncryptionKeyR5(generateRandomWordArray) { return generateRandomWordArray(32); } function getEncryptedPermissionsR5(permissions, encryptionKey, generateRandomWordArray) { var cipher = CryptoJS.lib.WordArray.create([lsbFirstWord(permissions), 0xffffffff, 0x54616462], 12).concat(generateRandomWordArray(4)); var options = { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding }; return CryptoJS.AES.encrypt(cipher, encryptionKey, options).ciphertext; } function processPasswordR2R3R4() { var password = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var out = Buffer.alloc(32); var length = password.length; var index = 0; while (index < length && index < 32) { var code = password.charCodeAt(index); if (code > 0xff) { throw new Error('Password contains one or more invalid characters.'); } out[index] = code; index++; } while (index < 32) { out[index] = PASSWORD_PADDING[index - length]; index++; } return CryptoJS.lib.WordArray.create(out); } function processPasswordR5() { var password = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; password = unescape(encodeURIComponent(saslprep(password))); var length = Math.min(127, password.length); var out = Buffer.alloc(length); for (var i = 0; i < length; i++) { out[i] = password.charCodeAt(i); } return CryptoJS.lib.WordArray.create(out); } function lsbFirstWord(data) { return (data & 0xff) << 24 | (data & 0xff00) << 8 | data >> 8 & 0xff00 | data >> 24 & 0xff; } function wordArrayToBuffer(wordArray) { var byteArray = []; for (var i = 0; i < wordArray.sigBytes; i++) { byteArray.push(wordArray.words[Math.floor(i / 4)] >> 8 * (3 - i % 4) & 0xff); } return Buffer.from(byteArray); } var PASSWORD_PADDING = [0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a]; var { number } = PDFObject; class PDFGradient { constructor(doc) { this.doc = doc; this.stops = []; this.embedded = false; this.transform = [1, 0, 0, 1, 0, 0]; } stop(pos, color, opacity) { if (opacity == null) { opacity = 1; } color = this.doc._normalizeColor(color); if (this.stops.length === 0) { if (color.length === 3) { this._colorSpace = 'DeviceRGB'; } else if (color.length === 4) { this._colorSpace = 'DeviceCMYK'; } else if (color.length === 1) { this._colorSpace = 'DeviceGray'; } else { throw new Error('Unknown color space'); } } else if (this._colorSpace === 'DeviceRGB' && color.length !== 3 || this._colorSpace === 'DeviceCMYK' && color.length !== 4 || this._colorSpace === 'DeviceGray' && color.length !== 1) { throw new Error('All gradient stops must use the same color space'); } opacity = Math.max(0, Math.min(1, opacity)); this.stops.push([pos, color, opacity]); return this; } setTransform(m11, m12, m21, m22, dx, dy) { this.transform = [m11, m12, m21, m22, dx, dy]; return this; } embed(m) { var fn; var stopsLength = this.stops.length; if (stopsLength === 0) { return; } this.embedded = true; this.matrix = m; // if the last stop comes before 100%, add a copy at 100% var last = this.stops[stopsLength - 1]; if (last[0] < 1) { this.stops.push([1, last[1], last[2]]); } var bounds = []; var encode = []; var stops = []; for (var i = 0; i < stopsLength - 1; i++) { encode.push(0, 1); if (i + 2 !== stopsLength) { bounds.push(this.stops[i + 1][0]); } fn = this.doc.ref({ FunctionType: 2, Domain: [0, 1], C0: this.stops[i + 0][1], C1: this.stops[i + 1][1], N: 1 }); stops.push(fn); fn.end(); } // if there are only two stops, we don't need a stitching function if (stopsLength === 1) { fn = stops[0]; } else { fn = this.doc.ref({ FunctionType: 3, // stitching function Domain: [0, 1], Functions: stops, Bounds: bounds, Encode: encode }); fn.end(); } this.id = "Sh".concat(++this.doc._gradCount); var shader = this.shader(fn); shader.end(); var pattern = this.doc.ref({ Type: 'Pattern', PatternType: 2, Shading: shader, Matrix: this.matrix.map(number) }); pattern.end(); if (this.stops.some(stop => stop[2] < 1)) { var grad = this.opacityGradient(); grad._colorSpace = 'DeviceGray'; for (var stop of this.stops) { grad.stop(stop[0], [stop[2]]); } grad = grad.embed(this.matrix); var pageBBox = [0, 0, this.doc.page.width, this.doc.page.height]; var form = this.doc.ref({ Type: 'XObject', Subtype: 'Form', FormType: 1, BBox: pageBBox, Group: { Type: 'Group', S: 'Transparency', CS: 'DeviceGray' }, Resources: { ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'], Pattern: { Sh1: grad } } }); form.write('/Pattern cs /Sh1 scn'); form.end("".concat(pageBBox.join(' '), " re f")); var gstate = this.doc.ref({ Type: 'ExtGState', SMask: { Type: 'Mask', S: 'Luminosity', G: form } }); gstate.end(); var opacityPattern = this.doc.ref({ Type: 'Pattern', PatternType: 1, PaintType: 1, TilingType: 2, BBox: pageBBox, XStep: pageBBox[2], YStep: pageBBox[3], Resources: { ProcSet: ['PDF', 'Text', 'ImageB', 'ImageC', 'ImageI'], Pattern: { Sh1: pattern }, ExtGState: { Gs1: gstate } } }); opacityPattern.write('/Gs1 gs /Pattern cs /Sh1 scn'); opacityPattern.end("".concat(pageBBox.join(' '), " re f")); this.doc.page.patterns[this.id] = opacityPattern; } else { this.doc.page.patterns[this.id] = pattern; } return pattern; } apply(stroke) { // apply gradient transform to existing document ctm var [m0, m1, m2, m3, m4, m5] = this.doc._ctm; var [m11, m12, m21, m22, dx, dy] = this.transform; var m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5]; if (!this.embedded || m.join(' ') !== this.matrix.join(' ')) { this.embed(m); } this.doc._setColorSpace('Pattern', stroke); var op = stroke ? 'SCN' : 'scn'; return this.doc.addContent("/".concat(this.id, " ").concat(op)); } } class PDFLinearGradient extends PDFGradient { constructor(doc, x1, y1, x2, y2) { super(doc); this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } shader(fn) { return this.doc.ref({ ShadingType: 2, ColorSpace: this._colorSpace, Coords: [this.x1, this.y1, this.x2, this.y2], Function: fn, Extend: [true, true] }); } opacityGradient() { return new PDFLinearGradient(this.doc, this.x1, this.y1, this.x2, this.y2); } } class PDFRadialGradient extends PDFGradient { constructor(doc, x1, y1, r1, x2, y2, r2) { super(doc); this.doc = doc; this.x1 = x1; this.y1 = y1; this.r1 = r1; this.x2 = x2; this.y2 = y2; this.r2 = r2; } shader(fn) { return this.doc.ref({ ShadingType: 3, ColorSpace: this._colorSpace, Coords: [this.x1, this.y1, this.r1, this.x2, this.y2, this.r2], Function: fn, Extend: [true, true] }); } opacityGradient() { return new PDFRadialGradient(this.doc, this.x1, this.y1, this.r1, this.x2, this.y2, this.r2); } } var Gradient = { PDFGradient, PDFLinearGradient, PDFRadialGradient }; /* PDF tiling pattern support. Uncolored only. */ var underlyingColorSpaces = ['DeviceCMYK', 'DeviceRGB']; class PDFTilingPattern { constructor(doc, bBox, xStep, yStep, stream) { this.doc = doc; this.bBox = bBox; this.xStep = xStep; this.yStep = yStep; this.stream = stream; } createPattern() { // no resources needed for our current usage // required entry var resources = this.doc.ref(); resources.end(); // apply default transform matrix (flipped in the default doc._ctm) // see document.js & gradient.js var [m0, m1, m2, m3, m4, m5] = this.doc._ctm; var [m11, m12, m21, m22, dx, dy] = [1, 0, 0, 1, 0, 0]; var m = [m0 * m11 + m2 * m12, m1 * m11 + m3 * m12, m0 * m21 + m2 * m22, m1 * m21 + m3 * m22, m0 * dx + m2 * dy + m4, m1 * dx + m3 * dy + m5]; var pattern = this.doc.ref({ Type: 'Pattern', PatternType: 1, // tiling PaintType: 2, // 1-colored, 2-uncolored TilingType: 2, // 2-no distortion BBox: this.bBox, XStep: this.xStep, YStep: this.yStep, Matrix: m.map(v => +v.toFixed(5)), Resources: resources }); pattern.end(this.stream); return pattern; } embedPatternColorSpaces() { // map each pattern to an underlying color space // and embed on each page underlyingColorSpaces.forEach(csName => { var csId = this.getPatternColorSpaceId(csName); if (this.doc.page.colorSpaces[csId]) return; var cs = this.doc.ref(['Pattern', csName]); cs.end(); this.doc.page.colorSpaces[csId] = cs; }); } getPatternColorSpaceId(underlyingColorspace) { return "CsP".concat(underlyingColorspace); } embed() { if (!this.id) { this.doc._patternCount = this.doc._patternCount + 1; this.id = 'P' + this.doc._patternCount; this.pattern = this.createPattern(); } // patterns are embedded in each page if (!this.doc.page.patterns[this.id]) { this.doc.page.patterns[this.id] = this.pattern; } } apply(stroke, patternColor) { // do any embedding/creating that might be needed this.embedPatternColorSpaces(); this.embed(); var normalizedColor = this.doc._normalizeColor(patternColor); if (!normalizedColor) throw Error("invalid pattern color. (value: ".concat(patternColor, ")")); // select one of the pattern color spaces var csId = this.getPatternColorSpaceId(this.doc._getColorSpace(normalizedColor)); this.doc._setColorSpace(csId, stroke); // stroke/fill using the pattern and color (in the above underlying color space) var op = stroke ? 'SCN' : 'scn'; return this.doc.addContent("".concat(normalizedColor.join(' '), " /").concat(this.id, " ").concat(op)); } } var pattern = { PDFTilingPattern }; var { PDFGradient: PDFGradient$1, PDFLinearGradient: PDFLinearGradient$1, PDFRadialGradient: PDFRadialGradient$1 } = Gradient; var { PDFTilingPattern: PDFTilingPattern$1 } = pattern; var ColorMixin = { initColor() { // The opacity dictionaries this._opacityRegistry = {}; this._opacityCount = 0; this._patternCount = 0; return this._gradCount = 0; }, _normalizeColor(color) { if (typeof color === 'string') { if (color.charAt(0) === '#') { if (color.length === 4) { color = color.replace(/#([0-9A-F])([0-9A-F])([0-9A-F])/i, '#$1$1$2$2$3$3'); } var hex = parseInt(color.slice(1), 16); color = [hex >> 16, hex >> 8 & 0xff, hex & 0xff]; } else if (namedColors[color]) { color = namedColors[color]; } } if (Array.isArray(color)) { // RGB if (color.length === 3) { color = color.map(part => part / 255); // CMYK } else if (color.length === 4) { color = color.map(part => part / 100); } return color; } return null; }, _setColor(color, stroke) { if (color instanceof PDFGradient$1) { color.apply(stroke); return true; // see if tiling pattern, decode & apply it it } else if (Array.isArray(color) && color[0] instanceof PDFTilingPattern$1) { color[0].apply(stroke, color[1]); return true; } // any other case should be a normal color and not a pattern return this._setColorCore(color, stroke); }, _setColorCore(color, stroke) { color = this._normalizeColor(color); if (!color) { return false; } var op = stroke ? 'SCN' : 'scn'; var space = this._getColorSpace(color); this._setColorSpace(space, stroke); color = color.join(' '); this.addContent("".concat(color, " ").concat(op)); return true; }, _setColorSpace(space, stroke) { var op = stroke ? 'CS' : 'cs'; return this.addContent("/".concat(space, " ").concat(op)); }, _getColorSpace(color) { return color.length === 4 ? 'DeviceCMYK' : 'DeviceRGB'; }, fillColor(color, opacity) { var set = this._setColor(color, false); if (set) { this.fillOpacity(opacity); } // save this for text wrapper, which needs to reset // the fill color on new pages this._fillColor = [color, opacity]; return this; }, strokeColor(color, opacity) { var set = this._setColor(color, true); if (set) { this.strokeOpacity(opacity); } return this; }, opacity(opacity) { this._doOpacity(opacity, opacity); return this; }, fillOpacity(opacity) { this._doOpacity(opacity, null); return this; }, strokeOpacity(opacity) { this._doOpacity(null, opacity); return this; }, _doOpacity(fillOpacity, strokeOpacity) { var dictionary, name; if (fillOpacity == null && strokeOpacity == null) { return; } if (fillOpacity != null) { fillOpacity = Math.max(0, Math.min(1, fillOpacity)); } if (strokeOpacity != null) { strokeOpacity = Math.max(0, Math.min(1, strokeOpacity)); } var key = "".concat(fillOpacity, "_").concat(strokeOpacity); if (this._opacityRegistry[key]) { [dictionary, name] = this._opacityRegistry[key]; } else { dictionary = { Type: 'ExtGState' }; if (fillOpacity != null) { dictionary.ca = fillOpacity; } if (strokeOpacity != null) { dictionary.CA = strokeOpacity; } dictionary = this.ref(dictionary); dictionary.end(); var id = ++this._opacityCount; name = "Gs".concat(id); this._opacityRegistry[key] = [dictionary, name]; } this.page.ext_gstates[name] = dictionary; return this.addContent("/".concat(name, " gs")); }, linearGradient(x1, y1, x2, y2) { return new PDFLinearGradient$1(this, x1, y1, x2, y2); }, radialGradient(x1, y1, r1, x2, y2, r2) { return new PDFRadialGradient$1(this, x1, y1, r1, x2, y2, r2); }, pattern(bbox, xStep, yStep, stream) { return new PDFTilingPattern$1(this, bbox, xStep, yStep, stream); } }; var namedColors = { aliceblue: [240, 248, 255], antiquewhite: [250, 235, 215], aqua: [0, 255, 255], aquamarine: [127, 255, 212], azure: [240, 255, 255], beige: [245, 245, 220], bisque: [255, 228, 196], black: [0, 0, 0], blanchedalmond: [255, 235, 205], blue: [0, 0, 255], blueviolet: [138, 43, 226], brown: [165, 42, 42], burlywood: [222, 184, 135], cadetblue: [95, 158, 160], chartreuse: [127, 255, 0], chocolate: [210, 105, 30], coral: [255, 127, 80], cornflowerblue: [100, 149, 237], cornsilk: [255, 248, 220], crimson: [220, 20, 60], cyan: [0, 255, 255], darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgoldenrod: [184, 134, 11], darkgray: [169, 169, 169], darkgreen: [0, 100, 0], darkgrey: [169, 169, 169], darkkhaki: [189, 183, 107], darkmagenta: [139, 0, 139], darkolivegreen: [85, 107, 47], darkorange: [255, 140, 0], darkorchid: [153, 50, 204], darkred: [139, 0, 0], darksalmon: [233, 150, 122], darkseagreen: [143, 188, 143], darkslateblue: [72, 61, 139], darkslategray: [47, 79, 79], darkslategrey: [47, 79, 79], darkturquoise: [0, 206, 209], darkviolet: [148, 0, 211], deeppink: [255, 20, 147], deepskyblue: [0, 191, 255], dimgray: [105, 105, 105], dimgrey: [105, 105, 105], dodgerblue: [30, 144, 255], firebrick: [178, 34, 34], floralwhite: [255, 250, 240], forestgreen: [34, 139, 34], fuchsia: [255, 0, 255], gainsboro: [220, 220, 220], ghostwhite: [248, 248, 255], gold: [255, 215, 0], goldenrod: [218, 165, 32], gray: [128, 128, 128], grey: [128, 128, 128], green: [0, 128, 0], greenyellow: [173, 255, 47], honeydew: [240, 255, 240], hotpink: [255, 105, 180], indianred: [205, 92, 92], indigo: [75, 0, 130], ivory: [255, 255, 240], khaki: [240, 230, 140], lavender: [230, 230, 250], lavenderblush: [255, 240, 245], lawngreen: [124, 252, 0], lemonchiffon: [255, 250, 205], lightblue: [173, 216, 230], lightcoral: [240, 128, 128], lightcyan: [224, 255, 255], lightgoldenrodyellow: [250, 250, 210], lightgray: [211, 211, 211], lightgreen: [144, 238, 144], lightgrey: [211, 211, 211], lightpink: [255, 182, 193], lightsalmon: [255, 160, 122], lightseagreen: [32, 178, 170], lightskyblue: [135, 206, 250], lightslategray: [119, 136, 153], lightslategrey: [119, 136, 153], lightsteelblue: [176, 196, 222], lightyellow: [255, 255, 224], lime: [0, 255, 0], limegreen: [50, 205, 50], linen: [250, 240, 230], magenta: [255, 0, 255], maroon: [128, 0, 0], mediumaquamarine: [102, 205, 170], mediumblue: [0, 0, 205], mediumorchid: [186, 85, 211], mediumpurple: [147, 112, 219], mediumseagreen: [60, 179, 113], mediumslateblue: [123, 104, 238], mediumspringgreen: [0, 250, 154], mediumturquoise: [72, 209, 204], mediumvioletred: [199, 21, 133], midnightblue: [25, 25, 112], mintcream: [245, 255, 250], mistyrose: [255, 228, 225], moccasin: [255, 228, 181], navajowhite: [255, 222, 173], navy: [0, 0, 128], oldlace: [253, 245, 230], olive: [128, 128, 0], olivedrab: [107, 142, 35], orange: [255, 165, 0], orangered: [255, 69, 0], orchid: [218, 112, 214], palegoldenrod: [238, 232, 170], palegreen: [152, 251, 152], paleturquoise: [175, 238, 238], palevioletred: [219, 112, 147], papayawhip: [255, 239, 213], peachpuff: [255, 218, 185], peru: [205, 133, 63], pink: [255, 192, 203], plum: [221, 160, 221], powderblue: [176, 224, 230], purple: [128, 0, 128], red: [255, 0, 0], rosybrown: [188, 143, 143], royalblue: [65, 105, 225], saddlebrown: [139, 69, 19], salmon: [250, 128, 114], sandybrown: [244, 164, 96], seagreen: [46, 139, 87], seashell: [255, 245, 238], sienna: [160, 82, 45], silver: [192, 192, 192], skyblue: [135, 206, 235], slateblue: [106, 90, 205], slategray: [112, 128, 144], slategrey: [112, 128, 144], snow: [255, 250, 250], springgreen: [0, 255, 127], steelblue: [70, 130, 180], tan: [210, 180, 140], teal: [0, 128, 128], thistle: [216, 191, 216], tomato: [255, 99, 71], turquoise: [64, 224, 208], violet: [238, 130, 238], wheat: [245, 222, 179], white: [255, 255, 255], whitesmoke: [245, 245, 245], yellow: [255, 255, 0], yellowgreen: [154, 205, 50] }; var cx, cy, px, py, sx, sy; cx = cy = px = py = sx = sy = 0; var parameters = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 }; var parse = function parse(path) { var cmd; var ret = []; var args = []; var curArg = ''; var foundDecimal = false; var params = 0; for (var c of path) { if (parameters[c] != null) { params = parameters[c]; if (cmd) { // save existing command if (curArg.length > 0) { args[args.length] = +curArg; } ret[ret.length] = { cmd, args }; args = []; curArg = ''; foundDecimal = false; } cmd = c; } else if ([' ', ','].includes(c) || c === '-' && curArg.length > 0 && curArg[curArg.length - 1] !== 'e' || c === '.' && foundDecimal) { if (curArg.length === 0) { continue; } if (args.length === params) { // handle reused commands ret[ret.length] = { cmd, args }; args = [+curArg]; // handle assumed commands if (cmd === 'M') { cmd = 'L'; } if (cmd === 'm') { cmd = 'l'; } } else { args[args.length] = +curArg; } foundDecimal = c === '.'; // fix for negative numbers or repeated decimals with no delimeter between commands curArg = ['-', '.'].includes(c) ? c : ''; } else { curArg += c; if (c === '.') { foundDecimal = true; } } } // add the last command if (curArg.length > 0) { if (args.length === params) { // handle reused commands ret[ret.length] = { cmd, args }; args = [+curArg]; // handle assumed commands if (cmd === 'M') { cmd = 'L'; } if (cmd === 'm') { cmd = 'l'; } } else { args[args.length] = +curArg; } } ret[ret.length] = { cmd, args }; return ret; }; var apply = function apply(commands, doc) { // current point, control point, and subpath starting point cx = cy = px = py = sx = sy = 0; // run the commands for (var i = 0; i < commands.length; i++) { var c = commands[i]; if (typeof runners[c.cmd] === 'function') { runners[c.cmd](doc, c.args); } } }; var runners = { M(doc, a) { cx = a[0]; cy = a[1]; px = py = null; sx = cx; sy = cy; return doc.moveTo(cx, cy); }, m(doc, a) { cx += a[0]; cy += a[1]; px = py = null; sx = cx; sy = cy; return doc.moveTo(cx, cy); }, C(doc, a) { cx = a[4]; cy = a[5]; px = a[2]; py = a[3]; return doc.bezierCurveTo(...a); }, c(doc, a) { doc.bezierCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy, a[4] + cx, a[5] + cy); px = cx + a[2]; py = cy + a[3]; cx += a[4]; return cy += a[5]; }, S(doc, a) { if (px === null) { px = cx; py = cy; } doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), a[0], a[1], a[2], a[3]); px = a[0]; py = a[1]; cx = a[2]; return cy = a[3]; }, s(doc, a) { if (px === null) { px = cx; py = cy; } doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), cx + a[0], cy + a[1], cx + a[2], cy + a[3]); px = cx + a[0]; py = cy + a[1]; cx += a[2]; return cy += a[3]; }, Q(doc, a) { px = a[0]; py = a[1]; cx = a[2]; cy = a[3]; return doc.quadraticCurveTo(a[0], a[1], cx, cy); }, q(doc, a) { doc.quadraticCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy); px = cx + a[0]; py = cy + a[1]; cx += a[2]; return cy += a[3]; }, T(doc, a) { if (px === null) { px = cx; py = cy; } else { px = cx - (px - cx); py = cy - (py - cy); } doc.quadraticCurveTo(px, py, a[0], a[1]); px = cx - (px - cx); py = cy - (py - cy); cx = a[0]; return cy = a[1]; }, t(doc, a) { if (px === null) { px = cx; py = cy; } else { px = cx - (px - cx); py = cy - (py - cy); } doc.quadraticCurveTo(px, py, cx + a[0], cy + a[1]); cx += a[0]; return cy += a[1]; }, A(doc, a) { solveArc(doc, cx, cy, a); cx = a[5]; return cy = a[6]; }, a(doc, a) { a[5] += cx; a[6] += cy; solveArc(doc, cx, cy, a); cx = a[5]; return cy = a[6]; }, L(doc, a) { cx = a[0]; cy = a[1]; px = py = null; return doc.lineTo(cx, cy); }, l(doc, a) { cx += a[0]; cy += a[1]; px = py = null; return doc.lineTo(cx, cy); }, H(doc, a) { cx = a[0]; px = py = null; return doc.lineTo(cx, cy); }, h(doc, a) { cx += a[0]; px = py = null; return doc.lineTo(cx, cy); }, V(doc, a) { cy = a[0]; px = py = null; return doc.lineTo(cx, cy); }, v(doc, a) { cy += a[0]; px = py = null; return doc.lineTo(cx, cy); }, Z(doc) { doc.closePath(); cx = sx; return cy = sy; }, z(doc) { doc.closePath(); cx = sx; return cy = sy; } }; var solveArc = function solveArc(doc, x, y, coords) { var [rx, ry, rot, large, sweep, ex, ey] = coords; var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y); for (var seg of segs) { var bez = segmentToBezier(...seg); doc.bezierCurveTo(...bez); } }; // from Inkscape svgtopdf, thanks! var arcToSegments = function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) { var th = rotateX * (Math.PI / 180); var sin_th = Math.sin(th); var cos_th = Math.cos(th); rx = Math.abs(rx); ry = Math.abs(ry); px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5; py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5; var pl = px * px / (rx * rx) + py * py / (ry * ry); if (pl > 1) { pl = Math.sqrt(pl); rx *= pl; ry *= pl; } var a00 = cos_th / rx; var a01 = sin_th / rx; var a10 = -sin_th / ry; var a11 = cos_th / ry; var x0 = a00 * ox + a01 * oy; var y0 = a10 * ox + a11 * oy; var x1 = a00 * x + a01 * y; var y1 = a10 * x + a11 * y; var d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); var sfactor_sq = 1 / d - 0.25; if (sfactor_sq < 0) { sfactor_sq = 0; } var sfactor = Math.sqrt(sfactor_sq); if (sweep === large) { sfactor = -sfactor; } var xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0); var yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0); var th0 = Math.atan2(y0 - yc, x0 - xc); var th1 = Math.atan2(y1 - yc, x1 - xc); var th_arc = th1 - th0; if (th_arc < 0 && sweep === 1) { th_arc += 2 * Math.PI; } else if (th_arc > 0 && sweep === 0) { th_arc -= 2 * Math.PI; } var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001))); var result = []; for (var i = 0; i < segments; i++) { var th2 = th0 + i * th_arc / segments; var th3 = th0 + (i + 1) * th_arc / segments; result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th]; } return result; }; var segmentToBezier = function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) { var a00 = cos_th * rx; var a01 = -sin_th * ry; var a10 = sin_th * rx; var a11 = cos_th * ry; var th_half = 0.5 * (th1 - th0); var t = 8 / 3 * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half); var x1 = cx + Math.cos(th0) - t * Math.sin(th0); var y1 = cy + Math.sin(th0) + t * Math.cos(th0); var x3 = cx + Math.cos(th1); var y3 = cy + Math.sin(th1); var x2 = x3 + t * Math.sin(th1); var y2 = y3 - t * Math.cos(th1); return [a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3]; }; class SVGPath { static apply(doc, path) { var commands = parse(path); apply(commands, doc); } } var { number: number$1 } = PDFObject; // This constant is used to approximate a symmetrical arc using a cubic // Bezier curve. var KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0); var VectorMixin = { initVector() { this._ctm = [1, 0, 0, 1, 0, 0]; // current transformation matrix return this._ctmStack = []; }, save() { this._ctmStack.push(this._ctm.slice()); // TODO: save/restore colorspace and styles so not setting it unnessesarily all the time? return this.addContent('q'); }, restore() { this._ctm = this._ctmStack.pop() || [1, 0, 0, 1, 0, 0]; return this.addContent('Q'); }, closePath() { return this.addContent('h'); }, lineWidth(w) { return this.addContent("".concat(number$1(w), " w")); }, _CAP_STYLES: { BUTT: 0, ROUND: 1, SQUARE: 2 }, lineCap(c) { if (typeof c === 'string') { c = this._CAP_STYLES[c.toUpperCase()]; } return this.addContent("".concat(c, " J")); }, _JOIN_STYLES: { MITER: 0, ROUND: 1, BEVEL: 2 }, lineJoin(j) { if (typeof j === 'string') { j = this._JOIN_STYLES[j.toUpperCase()]; } return this.addContent("".concat(j, " j")); }, miterLimit(m) { return this.addContent("".concat(number$1(m), " M")); }, dash(length) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var originalLength = length; if (!Array.isArray(length)) { length = [length, options.space || length]; } var valid = length.every(x => Number.isFinite(x) && x > 0); if (!valid) { throw new Error("dash(".concat(JSON.stringify(originalLength), ", ").concat(JSON.stringify(options), ") invalid, lengths must be numeric and greater than zero")); } length = length.map(number$1).join(' '); return this.addContent("[".concat(length, "] ").concat(number$1(options.phase || 0), " d")); }, undash() { return this.addContent('[] 0 d'); }, moveTo(x, y) { return this.addContent("".concat(number$1(x), " ").concat(number$1(y), " m")); }, lineTo(x, y) { return this.addContent("".concat(number$1(x), " ").concat(number$1(y), " l")); }, bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) { return this.addContent("".concat(number$1(cp1x), " ").concat(number$1(cp1y), " ").concat(number$1(cp2x), " ").concat(number$1(cp2y), " ").concat(number$1(x), " ").concat(number$1(y), " c")); }, quadraticCurveTo(cpx, cpy, x, y) { return this.addContent("".concat(number$1(cpx), " ").concat(number$1(cpy), " ").concat(number$1(x), " ").concat(number$1(y), " v")); }, rect(x, y, w, h) { return this.addContent("".concat(number$1(x), " ").concat(number$1(y), " ").concat(number$1(w), " ").concat(number$1(h), " re")); }, roundedRect(x, y, w, h, r) { if (r == null) { r = 0; } r = Math.min(r, 0.5 * w, 0.5 * h); // amount to inset control points from corners (see `ellipse`) var c = r * (1.0 - KAPPA); this.moveTo(x + r, y); this.lineTo(x + w - r, y); this.bezierCurveTo(x + w - c, y, x + w, y + c, x + w, y + r); this.lineTo(x + w, y + h - r); this.bezierCurveTo(x + w, y + h - c, x + w - c, y + h, x + w - r, y + h); this.lineTo(x + r, y + h); this.bezierCurveTo(x + c, y + h, x, y + h - c, x, y + h - r); this.lineTo(x, y + r); this.bezierCurveTo(x, y + c, x + c, y, x + r, y); return this.closePath(); }, ellipse(x, y, r1, r2) { // based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084 if (r2 == null) { r2 = r1; } x -= r1; y -= r2; var ox = r1 * KAPPA; var oy = r2 * KAPPA; var xe = x + r1 * 2; var ye = y + r2 * 2; var xm = x + r1; var ym = y + r2; this.moveTo(x, ym); this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); return this.closePath(); }, circle(x, y, radius) { return this.ellipse(x, y, radius); }, arc(x, y, radius, startAngle, endAngle, anticlockwise) { if (anticlockwise == null) { anticlockwise = false; } var TWO_PI = 2.0 * Math.PI; var HALF_PI = 0.5 * Math.PI; var deltaAng = endAngle - startAngle; if (Math.abs(deltaAng) > TWO_PI) { // draw only full circle if more than that is specified deltaAng = TWO_PI; } else if (deltaAng !== 0 && anticlockwise !== deltaAng < 0) { // necessary to flip direction of rendering var dir = anticlockwise ? -1 : 1; deltaAng = dir * TWO_PI + deltaAng; } var numSegs = Math.ceil(Math.abs(deltaAng) / HALF_PI); var segAng = deltaAng / numSegs; var handleLen = segAng / HALF_PI * KAPPA * radius; var curAng = startAngle; // component distances between anchor point and control point var deltaCx = -Math.sin(curAng) * handleLen; var deltaCy = Math.cos(curAng) * handleLen; // anchor point var ax = x + Math.cos(curAng) * radius; var ay = y + Math.sin(curAng) * radius; // calculate and render segments this.moveTo(ax, ay); for (var segIdx = 0; segIdx < numSegs; segIdx++) { // starting control point var cp1x = ax + deltaCx; var cp1y = ay + deltaCy; // step angle curAng += segAng; // next anchor point ax = x + Math.cos(curAng) * radius; ay = y + Math.sin(curAng) * radius; // next control point delta deltaCx = -Math.sin(curAng) * handleLen; deltaCy = Math.cos(curAng) * handleLen; // ending control point var cp2x = ax - deltaCx; var cp2y = ay - deltaCy; // render segment this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, ax, ay); } return this; }, polygon() { for (var _len = arguments.length, points = new Array(_len), _key = 0; _key < _len; _key++) { points[_key] = arguments[_key]; } this.moveTo(...(points.shift() || [])); for (var point of points) { this.lineTo(...(point || [])); } return this.closePath(); }, path(path) { SVGPath.apply(this, path); return this; }, _windingRule(rule) { if (/even-?odd/.test(rule)) { return '*'; } return ''; }, fill(color, rule) { if (/(even-?odd)|(non-?zero)/.test(color)) { rule = color; color = null; } if (color) { this.fillColor(color); } return this.addContent("f".concat(this._windingRule(rule))); }, stroke(color) { if (color) { this.strokeColor(color); } return this.addContent('S'); }, fillAndStroke(fillColor, strokeColor, rule) { if (strokeColor == null) { strokeColor = fillColor; } var isFillRule = /(even-?odd)|(non-?zero)/; if (isFillRule.test(fillColor)) { rule = fillColor; fillColor = null; } if (isFillRule.test(strokeColor)) { rule = strokeColor; strokeColor = fillColor; } if (fillColor) { this.fillColor(fillColor); this.strokeColor(strokeColor); } return this.addContent("B".concat(this._windingRule(rule))); }, clip(rule) { return this.addContent("W".concat(this._windingRule(rule), " n")); }, transform(m11, m12, m21, m22, dx, dy) { // keep track of the current transformation matrix var m = this._ctm; var [m0, m1, m2, m3, m4, m5] = m; m[0] = m0 * m11 + m2 * m12; m[1] = m1 * m11 + m3 * m12; m[2] = m0 * m21 + m2 * m22; m[3] = m1 * m21 + m3 * m22; m[4] = m0 * dx + m2 * dy + m4; m[5] = m1 * dx + m3 * dy + m5; var values = [m11, m12, m21, m22, dx, dy].map(v => number$1(v)).join(' '); return this.addContent("".concat(values, " cm")); }, translate(x, y) { return this.transform(1, 0, 0, 1, x, y); }, rotate(angle) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var y; var rad = angle * Math.PI / 180; var cos = Math.cos(rad); var sin = Math.sin(rad); var x = y = 0; if (options.origin != null) { [x, y] = options.origin; var x1 = x * cos - y * sin; var y1 = x * sin + y * cos; x -= x1; y -= y1; } return this.transform(cos, sin, -sin, cos, x, y); }, scale(xFactor, yFactor) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var y; if (yFactor == null) { yFactor = xFactor; } if (typeof yFactor === 'object') { options = yFactor; yFactor = xFactor; } var x = y = 0; if (options.origin != null) { [x, y] = options.origin; x -= xFactor * x; y -= yFactor * y; } return this.transform(xFactor, 0, 0, yFactor, x, y); } }; var WIN_ANSI_MAP = { 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 }; var characters = ".notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n.notdef .notdef .notdef .notdef\n \nspace exclam quotedbl numbersign\ndollar percent ampersand quotesingle\nparenleft parenright asterisk plus\ncomma hyphen period slash\nzero one two three\nfour five six seven\neight nine colon semicolon\nless equal greater question\n \nat A B C\nD E F G\nH I J K\nL M N O\nP Q R S\nT U V W\nX Y Z bracketleft\nbackslash bracketright asciicircum underscore\n \ngrave a b c\nd e f g\nh i j k\nl m n o\np q r s\nt u v w\nx y z braceleft\nbar braceright asciitilde .notdef\n \nEuro .notdef quotesinglbase florin\nquotedblbase ellipsis dagger daggerdbl\ncircumflex perthousand Scaron guilsinglleft\nOE .notdef Zcaron .notdef\n.notdef quoteleft quoteright quotedblleft\nquotedblright bullet endash emdash\ntilde trademark scaron guilsinglright\noe .notdef zcaron ydieresis\n \nspace exclamdown cent sterling\ncurrency yen brokenbar section\ndieresis copyright ordfeminine guillemotleft\nlogicalnot hyphen registered macron\ndegree plusminus twosuperior threesuperior\nacute mu paragraph periodcentered\ncedilla onesuperior ordmasculine guillemotright\nonequarter onehalf threequarters questiondown\n \nAgrave Aacute Acircumflex Atilde\nAdieresis Aring AE Ccedilla\nEgrave Eacute Ecircumflex Edieresis\nIgrave Iacute Icircumflex Idieresis\nEth Ntilde Ograve Oacute\nOcircumflex Otilde Odieresis multiply\nOslash Ugrave Uacute Ucircumflex\nUdieresis Yacute Thorn germandbls\n \nagrave aacute acircumflex atilde\nadieresis aring ae ccedilla\negrave eacute ecircumflex edieresis\nigrave iacute icircumflex idieresis\neth ntilde ograve oacute\nocircumflex otilde odieresis divide\noslash ugrave uacute ucircumflex\nudieresis yacute thorn ydieresis".split(/\s+/); class AFMFont { static open(filename) { return new AFMFont(fs.readFileSync(filename, 'utf8')); } constructor(contents) { this.contents = contents; this.attributes = {}; this.glyphWidths = {}; this.boundingBoxes = {}; this.kernPairs = {}; this.parse(); // todo: remove charWidths since appears to not be used this.charWidths = new Array(256); for (var char = 0; char <= 255; char++) { this.charWidths[char] = this.glyphWidths[characters[char]]; } this.bbox = this.attributes['FontBBox'].split(/\s+/).map(e => +e); this.ascender = +(this.attributes['Ascender'] || 0); this.descender = +(this.attributes['Descender'] || 0); this.xHeight = +(this.attributes['XHeight'] || 0); this.capHeight = +(this.attributes['CapHeight'] || 0); this.lineGap = this.bbox[3] - this.bbox[1] - (this.ascender - this.descender); } parse() { var section = ''; for (var line of this.contents.split('\n')) { var match; var a; if (match = line.match(/^Start(\w+)/)) { section = match[1]; continue; } else if (match = line.match(/^End(\w+)/)) { section = ''; continue; } switch (section) { case 'FontMetrics': match = line.match(/(^\w+)\s+(.*)/); var key = match[1]; var value = match[2]; if (a = this.attributes[key]) { if (!Array.isArray(a)) { a = this.attributes[key] = [a]; } a.push(value); } else { this.attributes[key] = value; } break; case 'CharMetrics': if (!/^CH?\s/.test(line)) { continue; } var name = line.match(/\bN\s+(\.?\w+)\s*;/)[1]; this.glyphWidths[name] = +line.match(/\bWX\s+(\d+)\s*;/)[1]; break; case 'KernPairs': match = line.match(/^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/); if (match) { this.kernPairs[match[1] + '\0' + match[2]] = parseInt(match[3]); } break; } } } encodeText(text) { var res = []; for (var i = 0, len = text.length; i < len; i++) { var char = text.charCodeAt(i); char = WIN_ANSI_MAP[char] || char; res.push(char.toString(16)); } return res; } glyphsForString(string) { var glyphs = []; for (var i = 0, len = string.length; i < len; i++) { var charCode = string.charCodeAt(i); glyphs.push(this.characterToGlyph(charCode)); } return glyphs; } characterToGlyph(character) { return characters[WIN_ANSI_MAP[character] || character] || '.notdef'; } widthOfGlyph(glyph) { return this.glyphWidths[glyph] || 0; } getKernPair(left, right) { return this.kernPairs[left + '\0' + right] || 0; } advancesForGlyphs(glyphs) { var advances = []; for (var index = 0; index < glyphs.length; index++) { var left = glyphs[index]; var right = glyphs[index + 1]; advances.push(this.widthOfGlyph(left) + this.getKernPair(left, right)); } return advances; } } class PDFFont { constructor() {} encode() { throw new Error('Must be implemented by subclasses'); } widthOfString() { throw new Error('Must be implemented by subclasses'); } ref() { return this.dictionary != null ? this.dictionary : this.dictionary = this.document.ref(); } finalize() { if (this.embedded || this.dictionary == null) { return; } this.embed(); return this.embedded = true; } embed() { throw new Error('Must be implemented by subclasses'); } lineHeight(size, includeGap) { if (includeGap == null) { includeGap = false; } var gap = includeGap ? this.lineGap : 0; return (this.ascender + gap - this.descender) / 1000 * size; } } var STANDARD_FONTS = { Courier() { return fs.readFileSync(__dirname + '/data/Courier.afm', 'utf8'); }, 'Courier-Bold'() { return fs.readFileSync(__dirname + '/data/Courier-Bold.afm', 'utf8'); }, 'Courier-Oblique'() { return fs.readFileSync(__dirname + '/data/Courier-Oblique.afm', 'utf8'); }, 'Courier-BoldOblique'() { return fs.readFileSync(__dirname + '/data/Courier-BoldOblique.afm', 'utf8'); }, Helvetica() { return fs.readFileSync(__dirname + '/data/Helvetica.afm', 'utf8'); }, 'Helvetica-Bold'() { return fs.readFileSync(__dirname + '/data/Helvetica-Bold.afm', 'utf8'); }, 'Helvetica-Oblique'() { return fs.readFileSync(__dirname + '/data/Helvetica-Oblique.afm', 'utf8'); }, 'Helvetica-BoldOblique'() { return fs.readFileSync(__dirname + '/data/Helvetica-BoldOblique.afm', 'utf8'); }, 'Times-Roman'() { return fs.readFileSync(__dirname + '/data/Times-Roman.afm', 'utf8'); }, 'Times-Bold'() { return fs.readFileSync(__dirname + '/data/Times-Bold.afm', 'utf8'); }, 'Times-Italic'() { return fs.readFileSync(__dirname + '/data/Times-Italic.afm', 'utf8'); }, 'Times-BoldItalic'() { return fs.readFileSync(__dirname + '/data/Times-BoldItalic.afm', 'utf8'); }, Symbol() { return fs.readFileSync(__dirname + '/data/Symbol.afm', 'utf8'); }, ZapfDingbats() { return fs.readFileSync(__dirname + '/data/ZapfDingbats.afm', 'utf8'); } }; class StandardFont extends PDFFont { constructor(document, name, id) { super(); this.document = document; this.name = name; this.id = id; this.font = new AFMFont(STANDARD_FONTS[this.name]()); ({ ascender: this.ascender, descender: this.descender, bbox: this.bbox, lineGap: this.lineGap, xHeight: this.xHeight, capHeight: this.capHeight } = this.font); } embed() { this.dictionary.data = { Type: 'Font', BaseFont: this.name, Subtype: 'Type1', Encoding: 'WinAnsiEncoding' }; return this.dictionary.end(); } encode(text) { var encoded = this.font.encodeText(text); var glyphs = this.font.glyphsForString("".concat(text)); var advances = this.font.advancesForGlyphs(glyphs); var positions = []; for (var i = 0; i < glyphs.length; i++) { var glyph = glyphs[i]; positions.push({ xAdvance: advances[i], yAdvance: 0, xOffset: 0, yOffset: 0, advanceWidth: this.font.widthOfGlyph(glyph) }); } return [encoded, positions]; } widthOfString(string, size) { var glyphs = this.font.glyphsForString("".concat(string)); var advances = this.font.advancesForGlyphs(glyphs); var width = 0; for (var advance of advances) { width += advance; } var scale = size / 1000; return width * scale; } static isStandardFont(name) { return name in STANDARD_FONTS; } } var toHex = function toHex(num) { return "0000".concat(num.toString(16)).slice(-4); }; class EmbeddedFont extends PDFFont { constructor(document, font, id) { super(); this.document = document; this.font = font; this.id = id; this.subset = this.font.createSubset(); this.unicode = [[0]]; this.widths = [this.font.getGlyph(0).advanceWidth]; this.name = this.font.postscriptName; this.scale = 1000 / this.font.unitsPerEm; this.ascender = this.font.ascent * this.scale; this.descender = this.font.descent * this.scale; this.xHeight = this.font.xHeight * this.scale; this.capHeight = this.font.capHeight * this.scale; this.lineGap = this.font.lineGap * this.scale; this.bbox = this.font.bbox; if (document.options.fontLayoutCache !== false) { this.layoutCache = Object.create(null); } } layoutRun(text, features) { var run = this.font.layout(text, features); // Normalize position values for (var i = 0; i < run.positions.length; i++) { var position = run.positions[i]; for (var key in position) { position[key] *= this.scale; } position.advanceWidth = run.glyphs[i].advanceWidth * this.scale; } return run; } layoutCached(text) { if (!this.layoutCache) { return this.layoutRun(text); } var cached; if (cached = this.layoutCache[text]) { return cached; } var run = this.layoutRun(text); this.layoutCache[text] = run; return run; } layout(text, features, onlyWidth) { // Skip the cache if any user defined features are applied if (features) { return this.layoutRun(text, features); } var glyphs = onlyWidth ? null : []; var positions = onlyWidth ? null : []; var advanceWidth = 0; // Split the string by words to increase cache efficiency. // For this purpose, spaces and tabs are a good enough delimeter. var last = 0; var index = 0; while (index <= text.length) { var needle; if (index === text.length && last < index || (needle = text.charAt(index), [' ', '\t'].includes(needle))) { var run = this.layoutCached(text.slice(last, ++index)); if (!onlyWidth) { glyphs = glyphs.concat(run.glyphs); positions = positions.concat(run.positions); } advanceWidth += run.advanceWidth; last = index; } else { index++; } } return { glyphs, positions, advanceWidth }; } encode(text, features) { var { glyphs, positions } = this.layout(text, features); var res = []; for (var i = 0; i < glyphs.length; i++) { var glyph = glyphs[i]; var gid = this.subset.includeGlyph(glyph.id); res.push("0000".concat(gid.toString(16)).slice(-4)); if (this.widths[gid] == null) { this.widths[gid] = glyph.advanceWidth * this.scale; } if (this.unicode[gid] == null) { this.unicode[gid] = glyph.codePoints; } } return [res, positions]; } widthOfString(string, size, features) { var width = this.layout(string, features, true).advanceWidth; var scale = size / 1000; return width * scale; } embed() { var isCFF = this.subset.cff != null; var fontFile = this.document.ref(); if (isCFF) { fontFile.data.Subtype = 'CIDFontType0C'; } this.subset.encodeStream().on('data', data => fontFile.write(data)).on('end', () => fontFile.end()); var familyClass = ((this.font['OS/2'] != null ? this.font['OS/2'].sFamilyClass : undefined) || 0) >> 8; var flags = 0; if (this.font.post.isFixedPitch) { flags |= 1 << 0; } if (1 <= familyClass && familyClass <= 7) { flags |= 1 << 1; } flags |= 1 << 2; // assume the font uses non-latin characters if (familyClass === 10) { flags |= 1 << 3; } if (this.font.head.macStyle.italic) { flags |= 1 << 6; } // generate a tag (6 uppercase letters. 17 is the char code offset from '0' to 'A'. 73 will map to 'Z') var tag = [1, 2, 3, 4, 5, 6].map(i => String.fromCharCode((this.id.charCodeAt(i) || 73) + 17)).join(''); var name = tag + '+' + this.font.postscriptName; var { bbox } = this.font; var descriptor = this.document.ref({ Type: 'FontDescriptor', FontName: name, Flags: flags, FontBBox: [bbox.minX * this.scale, bbox.minY * this.scale, bbox.maxX * this.scale, bbox.maxY * this.scale], ItalicAngle: this.font.italicAngle, Ascent: this.ascender, Descent: this.descender, CapHeight: (this.font.capHeight || this.font.ascent) * this.scale, XHeight: (this.font.xHeight || 0) * this.scale, StemV: 0 }); // not sure how to calculate this if (isCFF) { descriptor.data.FontFile3 = fontFile; } else { descriptor.data.FontFile2 = fontFile; } descriptor.end(); var descendantFontData = { Type: 'Font', Subtype: 'CIDFontType0', BaseFont: name, CIDSystemInfo: { Registry: new String('Adobe'), Ordering: new String('Identity'), Supplement: 0 }, FontDescriptor: descriptor, W: [0, this.widths] }; if (!isCFF) { descendantFontData.Subtype = 'CIDFontType2'; descendantFontData.CIDToGIDMap = 'Identity'; } var descendantFont = this.document.ref(descendantFontData); descendantFont.end(); this.dictionary.data = { Type: 'Font', Subtype: 'Type0', BaseFont: name, Encoding: 'Identity-H', DescendantFonts: [descendantFont], ToUnicode: this.toUnicodeCmap() }; return this.dictionary.end(); } // Maps the glyph ids encoded in the PDF back to unicode strings // Because of ligature substitutions and the like, there may be one or more // unicode characters represented by each glyph. toUnicodeCmap() { var cmap = this.document.ref(); var entries = []; for (var codePoints of this.unicode) { var encoded = []; // encode codePoints to utf16 for (var value of codePoints) { if (value > 0xffff) { value -= 0x10000; encoded.push(toHex(value >>> 10 & 0x3ff | 0xd800)); value = 0xdc00 | value & 0x3ff; } encoded.push(toHex(value)); } entries.push("<".concat(encoded.join(' '), ">")); } cmap.end("/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<0000>\nendcodespacerange\n1 beginbfrange\n<0000> <".concat(toHex(entries.length - 1), "> [").concat(entries.join(' '), "]\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend")); return cmap; } } class PDFFontFactory { static open(document, src, family, id) { var font; if (typeof src === 'string') { if (StandardFont.isStandardFont(src)) { return new StandardFont(document, src, id); } src = fs.readFileSync(src); } if (Buffer.isBuffer(src)) { font = fontkit.create(src, family); } else if (src instanceof Uint8Array) { font = fontkit.create(Buffer.from(src), family); } else if (src instanceof ArrayBuffer) { font = fontkit.create(Buffer.from(new Uint8Array(src)), family); } if (font == null) { throw new Error('Not a supported font format or standard PDF font.'); } return new EmbeddedFont(document, font, id); } } var FontsMixin = { initFonts() { var defaultFont = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Helvetica'; // Lookup table for embedded fonts this._fontFamilies = {}; this._fontCount = 0; // Font state this._fontSize = 12; this._font = null; this._registeredFonts = {}; // Set the default font if (defaultFont) { this.font(defaultFont); } }, font(src, family, size) { var cacheKey, font; if (typeof family === 'number') { size = family; family = null; } // check registered fonts if src is a string if (typeof src === 'string' && this._registeredFonts[src]) { cacheKey = src; ({ src, family } = this._registeredFonts[src]); } else { cacheKey = family || src; if (typeof cacheKey !== 'string') { cacheKey = null; } } if (size != null) { this.fontSize(size); } // fast path: check if the font is already in the PDF if (font = this._fontFamilies[cacheKey]) { this._font = font; return this; } // load the font var id = "F".concat(++this._fontCount); this._font = PDFFontFactory.open(this, src, family, id); // check for existing font familes with the same name already in the PDF // useful if the font was passed as a buffer if (font = this._fontFamilies[this._font.name]) { this._font = font; return this; } // save the font for reuse later if (cacheKey) { this._fontFamilies[cacheKey] = this._font; } if (this._font.name) { this._fontFamilies[this._font.name] = this._font; } return this; }, fontSize(_fontSize) { this._fontSize = _fontSize; return this; }, currentLineHeight(includeGap) { if (includeGap == null) { includeGap = false; } return this._font.lineHeight(this._fontSize, includeGap); }, registerFont(name, src, family) { this._registeredFonts[name] = { src, family }; return this; } }; class LineWrapper extends EventEmitter { constructor(document, options) { super(); this.document = document; this.indent = options.indent || 0; this.characterSpacing = options.characterSpacing || 0; this.wordSpacing = options.wordSpacing === 0; this.columns = options.columns || 1; this.columnGap = options.columnGap != null ? options.columnGap : 18; // 1/4 inch this.lineWidth = (options.width - this.columnGap * (this.columns - 1)) / this.columns; this.spaceLeft = this.lineWidth; this.startX = this.document.x; this.startY = this.document.y; this.column = 1; this.ellipsis = options.ellipsis; this.continuedX = 0; this.features = options.features; // calculate the maximum Y position the text can appear at if (options.height != null) { this.height = options.height; this.maxY = this.startY + options.height; } else { this.maxY = this.document.page.maxY(); } // handle paragraph indents this.on('firstLine', options => { // if this is the first line of the text segment, and // we're continuing where we left off, indent that much // otherwise use the user specified indent option var indent = this.continuedX || this.indent; this.document.x += indent; this.lineWidth -= indent; return this.once('line', () => { this.document.x -= indent; this.lineWidth += indent; if (options.continued && !this.continuedX) { this.continuedX = this.indent; } if (!options.continued) { return this.continuedX = 0; } }); }); // handle left aligning last lines of paragraphs this.on('lastLine', options => { var { align } = options; if (align === 'justify') { options.align = 'left'; } this.lastLine = true; return this.once('line', () => { this.document.y += options.paragraphGap || 0; options.align = align; return this.lastLine = false; }); }); } wordWidth(word) { return this.document.widthOfString(word, this) + this.characterSpacing + this.wordSpacing; } eachWord(text, fn) { // setup a unicode line breaker var bk; var breaker = new LineBreaker(text); var last = null; var wordWidths = Object.create(null); while (bk = breaker.nextBreak()) { var shouldContinue; var word = text.slice((last != null ? last.position : undefined) || 0, bk.position); var w = wordWidths[word] != null ? wordWidths[word] : wordWidths[word] = this.wordWidth(word); // if the word is longer than the whole line, chop it up // TODO: break by grapheme clusters, not JS string characters if (w > this.lineWidth + this.continuedX) { // make some fake break objects var lbk = last; var fbk = {}; while (word.length) { // fit as much of the word as possible into the space we have var l, mightGrow; if (w > this.spaceLeft) { // start our check at the end of our available space - this method is faster than a loop of each character and it resolves // an issue with long loops when processing massive words, such as a huge number of spaces l = Math.ceil(this.spaceLeft / (w / word.length)); w = this.wordWidth(word.slice(0, l)); mightGrow = w <= this.spaceLeft && l < word.length; } else { l = word.length; } var mustShrink = w > this.spaceLeft && l > 0; // shrink or grow word as necessary after our near-guess above while (mustShrink || mightGrow) { if (mustShrink) { w = this.wordWidth(word.slice(0, --l)); mustShrink = w > this.spaceLeft && l > 0; } else { w = this.wordWidth(word.slice(0, ++l)); mustShrink = w > this.spaceLeft && l > 0; mightGrow = w <= this.spaceLeft && l < word.length; } } // check for the edge case where a single character cannot fit into a line. if (l === 0 && this.spaceLeft === this.lineWidth) { l = 1; } // send a required break unless this is the last piece and a linebreak is not specified fbk.required = bk.required || l < word.length; shouldContinue = fn(word.slice(0, l), w, fbk, lbk); lbk = { required: false }; // get the remaining piece of the word word = word.slice(l); w = this.wordWidth(word); if (shouldContinue === false) { break; } } } else { // otherwise just emit the break as it was given to us shouldContinue = fn(word, w, bk, last); } if (shouldContinue === false) { break; } last = bk; } } wrap(text, options) { // override options from previous continued fragments if (options.indent != null) { this.indent = options.indent; } if (options.characterSpacing != null) { this.characterSpacing = options.characterSpacing; } if (options.wordSpacing != null) { this.wordSpacing = options.wordSpacing; } if (options.ellipsis != null) { this.ellipsis = options.ellipsis; } // make sure we're actually on the page // and that the first line of is never by // itself at the bottom of a page (orphans) var nextY = this.document.y + this.document.currentLineHeight(true); if (this.document.y > this.maxY || nextY > this.maxY) { this.nextSection(); } var buffer = ''; var textWidth = 0; var wc = 0; var lc = 0; var { y } = this.document; // used to reset Y pos if options.continued (below) var emitLine = () => { options.textWidth = textWidth + this.wordSpacing * (wc - 1); options.wordCount = wc; options.lineWidth = this.lineWidth; ({ y } = this.document); this.emit('line', buffer, options, this); return lc++; }; this.emit('sectionStart', options, this); this.eachWord(text, (word, w, bk, last) => { if (last == null || last.required) { this.emit('firstLine', options, this); this.spaceLeft = this.lineWidth; } if (w <= this.spaceLeft) { buffer += word; textWidth += w; wc++; } if (bk.required || w > this.spaceLeft) { // if the user specified a max height and an ellipsis, and is about to pass the // max height and max columns after the next line, append the ellipsis var lh = this.document.currentLineHeight(true); if (this.height != null && this.ellipsis && this.document.y + lh * 2 > this.maxY && this.column >= this.columns) { if (this.ellipsis === true) { this.ellipsis = '…'; } // map default ellipsis character buffer = buffer.replace(/\s+$/, ''); textWidth = this.wordWidth(buffer + this.ellipsis); // remove characters from the buffer until the ellipsis fits // to avoid infinite loop need to stop while-loop if buffer is empty string while (buffer && textWidth > this.lineWidth) { buffer = buffer.slice(0, -1).replace(/\s+$/, ''); textWidth = this.wordWidth(buffer + this.ellipsis); } // need to add ellipsis only if there is enough space for it if (textWidth <= this.lineWidth) { buffer = buffer + this.ellipsis; } textWidth = this.wordWidth(buffer); } if (bk.required) { if (w > this.spaceLeft) { emitLine(); buffer = word; textWidth = w; wc = 1; } this.emit('lastLine', options, this); } emitLine(); // if we've reached the edge of the page, // continue on a new page or column if (this.document.y + lh > this.maxY) { var shouldContinue = this.nextSection(); // stop if we reached the maximum height if (!shouldContinue) { wc = 0; buffer = ''; return false; } } // reset the space left and buffer if (bk.required) { this.spaceLeft = this.lineWidth; buffer = ''; textWidth = 0; return wc = 0; } else { // reset the space left and buffer this.spaceLeft = this.lineWidth - w; buffer = word; textWidth = w; return wc = 1; } } else { return this.spaceLeft -= w; } }); if (wc > 0) { this.emit('lastLine', options, this); emitLine(); } this.emit('sectionEnd', options, this); // if the wrap is set to be continued, save the X position // to start the first line of the next segment at, and reset // the y position if (options.continued === true) { if (lc > 1) { this.continuedX = 0; } this.continuedX += options.textWidth || 0; return this.document.y = y; } else { return this.document.x = this.startX; } } nextSection(options) { this.emit('sectionEnd', options, this); if (++this.column > this.columns) { // if a max height was specified by the user, we're done. // otherwise, the default is to make a new page at the bottom. if (this.height != null) { return false; } this.document.continueOnNewPage(); this.column = 1; this.startY = this.document.page.margins.top; this.maxY = this.document.page.maxY(); this.document.x = this.startX; if (this.document._fillColor) { this.document.fillColor(...this.document._fillColor); } this.emit('pageBreak', options, this); } else { this.document.x += this.lineWidth + this.columnGap; this.document.y = this.startY; this.emit('columnBreak', options, this); } this.emit('sectionStart', options, this); return true; } } var { number: number$2 } = PDFObject; var TextMixin = { initText() { this._line = this._line.bind(this); // Current coordinates this.x = 0; this.y = 0; return this._lineGap = 0; }, lineGap(_lineGap) { this._lineGap = _lineGap; return this; }, moveDown(lines) { if (lines == null) { lines = 1; } this.y += this.currentLineHeight(true) * lines + this._lineGap; return this; }, moveUp(lines) { if (lines == null) { lines = 1; } this.y -= this.currentLineHeight(true) * lines + this._lineGap; return this; }, _text(text, x, y, options, lineCallback) { options = this._initOptions(x, y, options); // Convert text to a string text = text == null ? '' : "".concat(text); // if the wordSpacing option is specified, remove multiple consecutive spaces if (options.wordSpacing) { text = text.replace(/\s{2,}/g, ' '); } var addStructure = () => { if (options.structParent) { options.structParent.add(this.struct(options.structType || 'P', [this.markStructureContent(options.structType || 'P')])); } }; // word wrapping if (options.width) { var wrapper = this._wrapper; if (!wrapper) { wrapper = new LineWrapper(this, options); wrapper.on('line', lineCallback); wrapper.on('firstLine', addStructure); } this._wrapper = options.continued ? wrapper : null; this._textOptions = options.continued ? options : null; wrapper.wrap(text, options); // render paragraphs as single lines } else { for (var line of text.split('\n')) { addStructure(); lineCallback(line, options); } } return this; }, text(text, x, y, options) { return this._text(text, x, y, options, this._line); }, widthOfString(string) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return this._font.widthOfString(string, this._fontSize, options.features) + (options.characterSpacing || 0) * (string.length - 1); }, heightOfString(text, options) { var { x, y } = this; options = this._initOptions(options); options.height = Infinity; // don't break pages var lineGap = options.lineGap || this._lineGap || 0; this._text(text, this.x, this.y, options, () => { return this.y += this.currentLineHeight(true) + lineGap; }); var height = this.y - y; this.x = x; this.y = y; return height; }, list(list, x, y, options, wrapper) { options = this._initOptions(x, y, options); var listType = options.listType || 'bullet'; var unit = Math.round(this._font.ascender / 1000 * this._fontSize); var midLine = unit / 2; var r = options.bulletRadius || unit / 3; var indent = options.textIndent || (listType === 'bullet' ? r * 5 : unit * 2); var itemIndent = options.bulletIndent || (listType === 'bullet' ? r * 8 : unit * 2); var level = 1; var items = []; var levels = []; var numbers = []; var flatten = function flatten(list) { var n = 1; for (var _i = 0; _i < list.length; _i++) { var item = list[_i]; if (Array.isArray(item)) { level++; flatten(item); level--; } else { items.push(item); levels.push(level); if (listType !== 'bullet') { numbers.push(n++); } } } }; flatten(list); var label = function label(n) { switch (listType) { case 'numbered': return "".concat(n, "."); case 'lettered': var letter = String.fromCharCode((n - 1) % 26 + 65); var times = Math.floor((n - 1) / 26 + 1); var text = Array(times + 1).join(letter); return "".concat(text, "."); } }; wrapper = new LineWrapper(this, options); wrapper.on('line', this._line); level = 1; var i = 0; wrapper.on('firstLine', () => { var item, itemType, labelType, bodyType; if (options.structParent) { if (options.structTypes) { [itemType, labelType, bodyType] = options.structTypes; } else { [itemType, labelType, bodyType] = ['LI', 'Lbl', 'LBody']; } } if (itemType) { item = this.struct(itemType); options.structParent.add(item); } else if (options.structParent) { item = options.structParent; } var l; if ((l = levels[i++]) !== level) { var diff = itemIndent * (l - level); this.x += diff; wrapper.lineWidth -= diff; level = l; } if (item && (labelType || bodyType)) { item.add(this.struct(labelType || bodyType, [this.markStructureContent(labelType || bodyType)])); } switch (listType) { case 'bullet': this.circle(this.x - indent + r, this.y + midLine, r); this.fill(); break; case 'numbered': case 'lettered': var text = label(numbers[i - 1]); this._fragment(text, this.x - indent, this.y, options); break; } if (item && labelType && bodyType) { item.add(this.struct(bodyType, [this.markStructureContent(bodyType)])); } if (item && item !== options.structParent) { item.end(); } }); wrapper.on('sectionStart', () => { var pos = indent + itemIndent * (level - 1); this.x += pos; return wrapper.lineWidth -= pos; }); wrapper.on('sectionEnd', () => { var pos = indent + itemIndent * (level - 1); this.x -= pos; return wrapper.lineWidth += pos; }); wrapper.wrap(items.join('\n'), options); return this; }, _initOptions() { var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var y = arguments.length > 1 ? arguments[1] : undefined; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (typeof x === 'object') { options = x; x = null; } // clone options object var result = Object.assign({}, options); // extend options with previous values for continued text if (this._textOptions) { for (var key in this._textOptions) { var val = this._textOptions[key]; if (key !== 'continued') { if (result[key] === undefined) { result[key] = val; } } } } // Update the current position if (x != null) { this.x = x; } if (y != null) { this.y = y; } // wrap to margins if no x or y position passed if (result.lineBreak !== false) { if (result.width == null) { result.width = this.page.width - this.x - this.page.margins.right; } result.width = Math.max(result.width, 0); } if (!result.columns) { result.columns = 0; } if (result.columnGap == null) { result.columnGap = 18; } // 1/4 inch return result; }, _line(text) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var wrapper = arguments.length > 2 ? arguments[2] : undefined; this._fragment(text, this.x, this.y, options); var lineGap = options.lineGap || this._lineGap || 0; if (!wrapper) { return this.x += this.widthOfString(text); } else { return this.y += this.currentLineHeight(true) + lineGap; } }, _fragment(text, x, y, options) { var dy, encoded, i, positions, textWidth, words; text = "".concat(text).replace(/\n/g, ''); if (text.length === 0) { return; } // handle options var align = options.align || 'left'; var wordSpacing = options.wordSpacing || 0; var characterSpacing = options.characterSpacing || 0; // text alignments if (options.width) { switch (align) { case 'right': textWidth = this.widthOfString(text.replace(/\s+$/, ''), options); x += options.lineWidth - textWidth; break; case 'center': x += options.lineWidth / 2 - options.textWidth / 2; break; case 'justify': // calculate the word spacing value words = text.trim().split(/\s+/); textWidth = this.widthOfString(text.replace(/\s+/g, ''), options); var spaceWidth = this.widthOfString(' ') + characterSpacing; wordSpacing = Math.max(0, (options.lineWidth - textWidth) / Math.max(1, words.length - 1) - spaceWidth); break; } } // text baseline alignments based on http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling if (typeof options.baseline === 'number') { dy = -options.baseline; } else { switch (options.baseline) { case 'svg-middle': dy = 0.5 * this._font.xHeight; break; case 'middle': case 'svg-central': dy = 0.5 * (this._font.descender + this._font.ascender); break; case 'bottom': case 'ideographic': dy = this._font.descender; break; case 'alphabetic': dy = 0; break; case 'mathematical': dy = 0.5 * this._font.ascender; break; case 'hanging': dy = 0.8 * this._font.ascender; break; case 'top': dy = this._font.ascender; break; default: dy = this._font.ascender; } dy = dy / 1000 * this._fontSize; } // calculate the actual rendered width of the string after word and character spacing var renderedWidth = options.textWidth + wordSpacing * (options.wordCount - 1) + characterSpacing * (text.length - 1); // create link annotations if the link option is given if (options.link != null) { this.link(x, y, renderedWidth, this.currentLineHeight(), options.link); } if (options.goTo != null) { this.goTo(x, y, renderedWidth, this.currentLineHeight(), options.goTo); } if (options.destination != null) { this.addNamedDestination(options.destination, 'XYZ', x, y, null); } // create underline if (options.underline) { this.save(); if (!options.stroke) { this.strokeColor(...(this._fillColor || [])); } var lineWidth = this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10); this.lineWidth(lineWidth); var lineY = y + this.currentLineHeight() - lineWidth; this.moveTo(x, lineY); this.lineTo(x + renderedWidth, lineY); this.stroke(); this.restore(); } // create strikethrough line if (options.strike) { this.save(); if (!options.stroke) { this.strokeColor(...(this._fillColor || [])); } var _lineWidth = this._fontSize < 10 ? 0.5 : Math.floor(this._fontSize / 10); this.lineWidth(_lineWidth); var _lineY = y + this.currentLineHeight() / 2; this.moveTo(x, _lineY); this.lineTo(x + renderedWidth, _lineY); this.stroke(); this.restore(); } this.save(); // oblique (angle in degrees or boolean) if (options.oblique) { var skew; if (typeof options.oblique === 'number') { skew = -Math.tan(options.oblique * Math.PI / 180); } else { skew = -0.25; } this.transform(1, 0, 0, 1, x, y); this.transform(1, 0, skew, 1, -skew * dy, 0); this.transform(1, 0, 0, 1, -x, -y); } // flip coordinate system this.transform(1, 0, 0, -1, 0, this.page.height); y = this.page.height - y - dy; // add current font to page if necessary if (this.page.fonts[this._font.id] == null) { this.page.fonts[this._font.id] = this._font.ref(); } // begin the text object this.addContent('BT'); // text position this.addContent("1 0 0 1 ".concat(number$2(x), " ").concat(number$2(y), " Tm")); // font and font size this.addContent("/".concat(this._font.id, " ").concat(number$2(this._fontSize), " Tf")); // rendering mode var mode = options.fill && options.stroke ? 2 : options.stroke ? 1 : 0; if (mode) { this.addContent("".concat(mode, " Tr")); } // Character spacing if (characterSpacing) { this.addContent("".concat(number$2(characterSpacing), " Tc")); } // Add the actual text // If we have a word spacing value, we need to encode each word separately // since the normal Tw operator only works on character code 32, which isn't // used for embedded fonts. if (wordSpacing) { words = text.trim().split(/\s+/); wordSpacing += this.widthOfString(' ') + characterSpacing; wordSpacing *= 1000 / this._fontSize; encoded = []; positions = []; for (var word of words) { var [encodedWord, positionsWord] = this._font.encode(word, options.features); encoded = encoded.concat(encodedWord); positions = positions.concat(positionsWord); // add the word spacing to the end of the word // clone object because of cache var space = {}; var object = positions[positions.length - 1]; for (var key in object) { var val = object[key]; space[key] = val; } space.xAdvance += wordSpacing; positions[positions.length - 1] = space; } } else { [encoded, positions] = this._font.encode(text, options.features); } var scale = this._fontSize / 1000; var commands = []; var last = 0; var hadOffset = false; // Adds a segment of text to the TJ command buffer var addSegment = cur => { if (last < cur) { var hex = encoded.slice(last, cur).join(''); var advance = positions[cur - 1].xAdvance - positions[cur - 1].advanceWidth; commands.push("<".concat(hex, "> ").concat(number$2(-advance))); } return last = cur; }; // Flushes the current TJ commands to the output stream var flush = i => { addSegment(i); if (commands.length > 0) { this.addContent("[".concat(commands.join(' '), "] TJ")); return commands.length = 0; } }; for (i = 0; i < positions.length; i++) { // If we have an x or y offset, we have to break out of the current TJ command // so we can move the text position. var pos = positions[i]; if (pos.xOffset || pos.yOffset) { // Flush the current buffer flush(i); // Move the text position and flush just the current character this.addContent("1 0 0 1 ".concat(number$2(x + pos.xOffset * scale), " ").concat(number$2(y + pos.yOffset * scale), " Tm")); flush(i + 1); hadOffset = true; } else { // If the last character had an offset, reset the text position if (hadOffset) { this.addContent("1 0 0 1 ".concat(number$2(x), " ").concat(number$2(y), " Tm")); hadOffset = false; } // Group segments that don't have any advance adjustments if (pos.xAdvance - pos.advanceWidth !== 0) { addSegment(i + 1); } } x += pos.xAdvance * scale; } // Flush any remaining commands flush(i); // end the text object this.addContent('ET'); // restore flipped coordinate system return this.restore(); } }; var MARKERS = [0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf]; var COLOR_SPACE_MAP = { 1: 'DeviceGray', 3: 'DeviceRGB', 4: 'DeviceCMYK' }; class JPEG { constructor(data, label) { var marker; this.data = data; this.label = label; if (this.data.readUInt16BE(0) !== 0xffd8) { throw 'SOI not found in JPEG'; } var pos = 2; while (pos < this.data.length) { marker = this.data.readUInt16BE(pos); pos += 2; if (MARKERS.includes(marker)) { break; } pos += this.data.readUInt16BE(pos); } if (!MARKERS.includes(marker)) { throw 'Invalid JPEG.'; } pos += 2; this.bits = this.data[pos++]; this.height = this.data.readUInt16BE(pos); pos += 2; this.width = this.data.readUInt16BE(pos); pos += 2; var channels = this.data[pos++]; this.colorSpace = COLOR_SPACE_MAP[channels]; this.obj = null; } embed(document) { if (this.obj) { return; } this.obj = document.ref({ Type: 'XObject', Subtype: 'Image', BitsPerComponent: this.bits, Width: this.width, Height: this.height, ColorSpace: this.colorSpace, Filter: 'DCTDecode' }); // add extra decode params for CMYK images. By swapping the // min and max values from the default, we invert the colors. See // section 4.8.4 of the spec. if (this.colorSpace === 'DeviceCMYK') { this.obj.data['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]; } this.obj.end(this.data); // free memory return this.data = null; } } class PNGImage { constructor(data, label) { this.label = label; this.image = new PNG(data); this.width = this.image.width; this.height = this.image.height; this.imgData = this.image.imgData; this.obj = null; } embed(document) { var dataDecoded = false; this.document = document; if (this.obj) { return; } var hasAlphaChannel = this.image.hasAlphaChannel; var isInterlaced = this.image.interlaceMethod === 1; this.obj = this.document.ref({ Type: 'XObject', Subtype: 'Image', BitsPerComponent: hasAlphaChannel ? 8 : this.image.bits, Width: this.width, Height: this.height, Filter: 'FlateDecode' }); if (!hasAlphaChannel) { var params = this.document.ref({ Predictor: isInterlaced ? 1 : 15, Colors: this.image.colors, BitsPerComponent: this.image.bits, Columns: this.width }); this.obj.data['DecodeParms'] = params; params.end(); } if (this.image.palette.length === 0) { this.obj.data['ColorSpace'] = this.image.colorSpace; } else { // embed the color palette in the PDF as an object stream var palette = this.document.ref(); palette.end(Buffer.from(this.image.palette)); // build the color space array for the image this.obj.data['ColorSpace'] = ['Indexed', 'DeviceRGB', this.image.palette.length / 3 - 1, palette]; } // For PNG color types 0, 2 and 3, the transparency data is stored in // a dedicated PNG chunk. if (this.image.transparency.grayscale != null) { // Use Color Key Masking (spec section 4.8.5) // An array with N elements, where N is two times the number of color components. var val = this.image.transparency.grayscale; this.obj.data['Mask'] = [val, val]; } else if (this.image.transparency.rgb) { // Use Color Key Masking (spec section 4.8.5) // An array with N elements, where N is two times the number of color components. var { rgb } = this.image.transparency; var mask = []; for (var x of rgb) { mask.push(x, x); } this.obj.data['Mask'] = mask; } else if (this.image.transparency.indexed) { // Create a transparency SMask for the image based on the data // in the PLTE and tRNS sections. See below for details on SMasks. dataDecoded = true; return this.loadIndexedAlphaChannel(); } else if (hasAlphaChannel) { // For PNG color types 4 and 6, the transparency data is stored as a alpha // channel mixed in with the main image data. Separate this data out into an // SMask object and store it separately in the PDF. dataDecoded = true; return this.splitAlphaChannel(); } if (isInterlaced && !dataDecoded) { return this.decodeData(); } this.finalize(); } finalize() { if (this.alphaChannel) { var sMask = this.document.ref({ Type: 'XObject', Subtype: 'Image', Height: this.height, Width: this.width, BitsPerComponent: 8, Filter: 'FlateDecode', ColorSpace: 'DeviceGray', Decode: [0, 1] }); sMask.end(this.alphaChannel); this.obj.data['SMask'] = sMask; } // add the actual image data this.obj.end(this.imgData); // free memory this.image = null; return this.imgData = null; } splitAlphaChannel() { return this.image.decodePixels(pixels => { var a, p; var colorCount = this.image.colors; var pixelCount = this.width * this.height; var imgData = Buffer.alloc(pixelCount * colorCount); var alphaChannel = Buffer.alloc(pixelCount); var i = p = a = 0; var len = pixels.length; // For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first) var skipByteCount = this.image.bits === 16 ? 1 : 0; while (i < len) { for (var colorIndex = 0; colorIndex < colorCount; colorIndex++) { imgData[p++] = pixels[i++]; i += skipByteCount; } alphaChannel[a++] = pixels[i++]; i += skipByteCount; } this.imgData = zlib.deflateSync(imgData); this.alphaChannel = zlib.deflateSync(alphaChannel); return this.finalize(); }); } loadIndexedAlphaChannel() { var transparency = this.image.transparency.indexed; return this.image.decodePixels(pixels => { var alphaChannel = Buffer.alloc(this.width * this.height); var i = 0; for (var j = 0, end = pixels.length; j < end; j++) { alphaChannel[i++] = transparency[pixels[j]]; } this.alphaChannel = zlib.deflateSync(alphaChannel); return this.finalize(); }); } decodeData() { this.image.decodePixels(pixels => { this.imgData = zlib.deflateSync(pixels); this.finalize(); }); } } /* PDFImage - embeds images in PDF documents By Devon Govett */ class PDFImage { static open(src, label) { var data; if (Buffer.isBuffer(src)) { data = src; } else if (src instanceof ArrayBuffer) { data = Buffer.from(new Uint8Array(src)); } else { var match; if (match = /^data:.+;base64,(.*)$/.exec(src)) { data = Buffer.from(match[1], 'base64'); } else { data = fs.readFileSync(src); if (!data) { return; } } } if (data[0] === 0xff && data[1] === 0xd8) { return new JPEG(data, label); } else if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') { return new PNGImage(data, label); } else { throw new Error('Unknown image format.'); } } } var ImagesMixin = { initImages() { this._imageRegistry = {}; return this._imageCount = 0; }, image(src, x, y) { var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var bh, bp, bw, image, ip, left, left1; if (typeof x === 'object') { options = x; x = null; } x = (left = x != null ? x : options.x) != null ? left : this.x; y = (left1 = y != null ? y : options.y) != null ? left1 : this.y; if (typeof src === 'string') { image = this._imageRegistry[src]; } if (!image) { if (src.width && src.height) { image = src; } else { image = this.openImage(src); } } if (!image.obj) { image.embed(this); } if (this.page.xobjects[image.label] == null) { this.page.xobjects[image.label] = image.obj; } var w = options.width || image.width; var h = options.height || image.height; if (options.width && !options.height) { var wp = w / image.width; w = image.width * wp; h = image.height * wp; } else if (options.height && !options.width) { var hp = h / image.height; w = image.width * hp; h = image.height * hp; } else if (options.scale) { w = image.width * options.scale; h = image.height * options.scale; } else if (options.fit) { [bw, bh] = options.fit; bp = bw / bh; ip = image.width / image.height; if (ip > bp) { w = bw; h = bw / ip; } else { h = bh; w = bh * ip; } } else if (options.cover) { [bw, bh] = options.cover; bp = bw / bh; ip = image.width / image.height; if (ip > bp) { h = bh; w = bh * ip; } else { w = bw; h = bw / ip; } } if (options.fit || options.cover) { if (options.align === 'center') { x = x + bw / 2 - w / 2; } else if (options.align === 'right') { x = x + bw - w; } if (options.valign === 'center') { y = y + bh / 2 - h / 2; } else if (options.valign === 'bottom') { y = y + bh - h; } } // create link annotations if the link option is given if (options.link != null) { this.link(x, y, w, h, options.link); } if (options.goTo != null) { this.goTo(x, y, w, h, options.goTo); } if (options.destination != null) { this.addNamedDestination(options.destination, 'XYZ', x, y, null); } // Set the current y position to below the image if it is in the document flow if (this.y === y) { this.y += h; } this.save(); this.transform(w, 0, 0, -h, x, y + h); this.addContent("/".concat(image.label, " Do")); this.restore(); return this; }, openImage(src) { var image; if (typeof src === 'string') { image = this._imageRegistry[src]; } if (!image) { image = PDFImage.open(src, "I".concat(++this._imageCount)); if (typeof src === 'string') { this._imageRegistry[src] = image; } } return image; } }; var AnnotationsMixin = { annotate(x, y, w, h, options) { options.Type = 'Annot'; options.Rect = this._convertRect(x, y, w, h); options.Border = [0, 0, 0]; if (options.Subtype === 'Link' && typeof options.F === 'undefined') { options.F = 1 << 2; // Print Annotation Flag } if (options.Subtype !== 'Link') { if (options.C == null) { options.C = this._normalizeColor(options.color || [0, 0, 0]); } } // convert colors delete options.color; if (typeof options.Dest === 'string') { options.Dest = new String(options.Dest); } // Capitalize keys for (var key in options) { var val = options[key]; options[key[0].toUpperCase() + key.slice(1)] = val; } var ref = this.ref(options); this.page.annotations.push(ref); ref.end(); return this; }, note(x, y, w, h, contents) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; options.Subtype = 'Text'; options.Contents = new String(contents); options.Name = 'Comment'; if (options.color == null) { options.color = [243, 223, 92]; } return this.annotate(x, y, w, h, options); }, goTo(x, y, w, h, name) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; options.Subtype = 'Link'; options.A = this.ref({ S: 'GoTo', D: new String(name) }); options.A.end(); return this.annotate(x, y, w, h, options); }, link(x, y, w, h, url) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; options.Subtype = 'Link'; if (typeof url === 'number') { // Link to a page in the document (the page must already exist) var pages = this._root.data.Pages.data; if (url >= 0 && url < pages.Kids.length) { options.A = this.ref({ S: 'GoTo', D: [pages.Kids[url], 'XYZ', null, null, null] }); options.A.end(); } else { throw new Error("The document has no page ".concat(url)); } } else { // Link to an external url options.A = this.ref({ S: 'URI', URI: new String(url) }); options.A.end(); } return this.annotate(x, y, w, h, options); }, _markup(x, y, w, h) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var [x1, y1, x2, y2] = this._convertRect(x, y, w, h); options.QuadPoints = [x1, y2, x2, y2, x1, y1, x2, y1]; options.Contents = new String(); return this.annotate(x, y, w, h, options); }, highlight(x, y, w, h) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.Subtype = 'Highlight'; if (options.color == null) { options.color = [241, 238, 148]; } return this._markup(x, y, w, h, options); }, underline(x, y, w, h) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.Subtype = 'Underline'; return this._markup(x, y, w, h, options); }, strike(x, y, w, h) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.Subtype = 'StrikeOut'; return this._markup(x, y, w, h, options); }, lineAnnotation(x1, y1, x2, y2) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.Subtype = 'Line'; options.Contents = new String(); options.L = [x1, this.page.height - y1, x2, this.page.height - y2]; return this.annotate(x1, y1, x2, y2, options); }, rectAnnotation(x, y, w, h) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.Subtype = 'Square'; options.Contents = new String(); return this.annotate(x, y, w, h, options); }, ellipseAnnotation(x, y, w, h) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; options.Subtype = 'Circle'; options.Contents = new String(); return this.annotate(x, y, w, h, options); }, textAnnotation(x, y, w, h, text) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; options.Subtype = 'FreeText'; options.Contents = new String(text); options.DA = new String(); return this.annotate(x, y, w, h, options); }, fileAnnotation(x, y, w, h) { var file = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; // create hidden file var filespec = this.file(file.src, Object.assign({ hidden: true }, file)); options.Subtype = 'FileAttachment'; options.FS = filespec; // add description from filespec unless description (Contents) has already been set if (options.Contents) { options.Contents = new String(options.Contents); } else if (filespec.data.Desc) { options.Contents = filespec.data.Desc; } return this.annotate(x, y, w, h, options); }, _convertRect(x1, y1, w, h) { // flip y1 and y2 var y2 = y1; y1 += h; // make x2 var x2 = x1 + w; // apply current transformation matrix to points var [m0, m1, m2, m3, m4, m5] = this._ctm; x1 = m0 * x1 + m2 * y1 + m4; y1 = m1 * x1 + m3 * y1 + m5; x2 = m0 * x2 + m2 * y2 + m4; y2 = m1 * x2 + m3 * y2 + m5; return [x1, y1, x2, y2]; } }; class PDFOutline { constructor(document, parent, title, dest) { var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : { expanded: false }; this.document = document; this.options = options; this.outlineData = {}; if (dest !== null) { this.outlineData['Dest'] = [dest.dictionary, 'Fit']; } if (parent !== null) { this.outlineData['Parent'] = parent; } if (title !== null) { this.outlineData['Title'] = new String(title); } this.dictionary = this.document.ref(this.outlineData); this.children = []; } addItem(title) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { expanded: false }; var result = new PDFOutline(this.document, this.dictionary, title, this.document.page, options); this.children.push(result); return result; } endOutline() { if (this.children.length > 0) { if (this.options.expanded) { this.outlineData.Count = this.children.length; } var first = this.children[0], last = this.children[this.children.length - 1]; this.outlineData.First = first.dictionary; this.outlineData.Last = last.dictionary; for (var i = 0, len = this.children.length; i < len; i++) { var child = this.children[i]; if (i > 0) { child.outlineData.Prev = this.children[i - 1].dictionary; } if (i < this.children.length - 1) { child.outlineData.Next = this.children[i + 1].dictionary; } child.endOutline(); } } return this.dictionary.end(); } } var OutlineMixin = { initOutline() { return this.outline = new PDFOutline(this, null, null, null); }, endOutline() { this.outline.endOutline(); if (this.outline.children.length > 0) { this._root.data.Outlines = this.outline.dictionary; return this._root.data.PageMode = 'UseOutlines'; } } }; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } /* PDFStructureContent - a reference to a marked structure content By Ben Schmidt */ class PDFStructureContent { constructor(pageRef, mcid) { this.refs = [{ pageRef, mcid }]; } push(structContent) { structContent.refs.forEach(ref => this.refs.push(ref)); } } /* PDFStructureElement - represents an element in the PDF logical structure tree By Ben Schmidt */ class PDFStructureElement { constructor(document, type) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var children = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; this.document = document; this._attached = false; this._ended = false; this._flushed = false; this.dictionary = document.ref({ // Type: "StructElem", S: type }); var data = this.dictionary.data; if (Array.isArray(options) || this._isValidChild(options)) { children = options; options = {}; } if (typeof options.title !== 'undefined') { data.T = new String(options.title); } if (typeof options.lang !== 'undefined') { data.Lang = new String(options.lang); } if (typeof options.alt !== 'undefined') { data.Alt = new String(options.alt); } if (typeof options.expanded !== 'undefined') { data.E = new String(options.expanded); } if (typeof options.actual !== 'undefined') { data.ActualText = new String(options.actual); } this._children = []; if (children) { if (!Array.isArray(children)) { children = [children]; } children.forEach(child => this.add(child)); this.end(); } } add(child) { if (this._ended) { throw new Error("Cannot add child to already-ended structure element"); } if (!this._isValidChild(child)) { throw new Error("Invalid structure element child"); } if (child instanceof PDFStructureElement) { child.setParent(this.dictionary); if (this._attached) { child.setAttached(); } } if (child instanceof PDFStructureContent) { this._addContentToParentTree(child); } if (typeof child === 'function' && this._attached) { // _contentForClosure() adds the content to the parent tree child = this._contentForClosure(child); } this._children.push(child); return this; } _addContentToParentTree(content) { content.refs.forEach((_ref) => { var { pageRef, mcid } = _ref; var pageStructParents = this.document.getStructParentTree().get(pageRef.data.StructParents); pageStructParents[mcid] = this.dictionary; }); } setParent(parentRef) { if (this.dictionary.data.P) { throw new Error("Structure element added to more than one parent"); } this.dictionary.data.P = parentRef; this._flush(); } setAttached() { if (this._attached) { return; } this._children.forEach((child, index) => { if (child instanceof PDFStructureElement) { child.setAttached(); } if (typeof child === 'function') { this._children[index] = this._contentForClosure(child); } }); this._attached = true; this._flush(); } end() { if (this._ended) { return; } this._children.filter(child => child instanceof PDFStructureElement).forEach(child => child.end()); this._ended = true; this._flush(); } _isValidChild(child) { return child instanceof PDFStructureElement || child instanceof PDFStructureContent || typeof child === 'function'; } _contentForClosure(closure) { var content = this.document.markStructureContent(this.dictionary.data.S); closure(); this.document.endMarkedContent(); this._addContentToParentTree(content); return content; } _isFlushable() { if (!this.dictionary.data.P || !this._ended) { return false; } return this._children.every(child => { if (typeof child === 'function') { return false; } if (child instanceof PDFStructureElement) { return child._isFlushable(); } return true; }); } _flush() { if (this._flushed || !this._isFlushable()) { return; } this.dictionary.data.K = []; this._children.forEach(child => this._flushChild(child)); this.dictionary.end(); // free memory used by children; the dictionary itself may still be // referenced by a parent structure element or root, but we can // at least trim the tree here this._children = []; this.dictionary.data.K = null; this._flushed = true; } _flushChild(child) { if (child instanceof PDFStructureElement) { this.dictionary.data.K.push(child.dictionary); } if (child instanceof PDFStructureContent) { child.refs.forEach((_ref2) => { var { pageRef, mcid } = _ref2; if (!this.dictionary.data.Pg) { this.dictionary.data.Pg = pageRef; } if (this.dictionary.data.Pg === pageRef) { this.dictionary.data.K.push(mcid); } else { this.dictionary.data.K.push({ Type: "MCR", Pg: pageRef, MCID: mcid }); } }); } } } /* PDFNumberTree - represents a number tree object */ class PDFNumberTree extends PDFTree { _compareKeys(a, b) { return parseInt(a) - parseInt(b); } _keysName() { return "Nums"; } _dataForKey(k) { return parseInt(k); } } var MarkingsMixin = { initMarkings(options) { this.structChildren = []; if (options.tagged) { this.getMarkInfoDictionary().data.Marked = true; this.getStructTreeRoot(); } }, markContent(tag) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; if (tag === 'Artifact' || options && options.mcid) { var toClose = 0; this.page.markings.forEach(marking => { if (toClose || marking.structContent || marking.tag === 'Artifact') { toClose++; } }); while (toClose--) { this.endMarkedContent(); } } if (!options) { this.page.markings.push({ tag }); this.addContent("/".concat(tag, " BMC")); return this; } this.page.markings.push({ tag, options }); var dictionary = {}; if (typeof options.mcid !== 'undefined') { dictionary.MCID = options.mcid; } if (tag === 'Artifact') { if (typeof options.type === 'string') { dictionary.Type = options.type; } if (Array.isArray(options.bbox)) { dictionary.BBox = [options.bbox[0], this.page.height - options.bbox[3], options.bbox[2], this.page.height - options.bbox[1]]; } if (Array.isArray(options.attached) && options.attached.every(val => typeof val === 'string')) { dictionary.Attached = options.attached; } } if (tag === 'Span') { if (options.lang) { dictionary.Lang = new String(options.lang); } if (options.alt) { dictionary.Alt = new String(options.alt); } if (options.expanded) { dictionary.E = new String(options.expanded); } if (options.actual) { dictionary.ActualText = new String(options.actual); } } this.addContent("/".concat(tag, " ").concat(PDFObject.convert(dictionary), " BDC")); return this; }, markStructureContent(tag) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var pageStructParents = this.getStructParentTree().get(this.page.structParentTreeKey); var mcid = pageStructParents.length; pageStructParents.push(null); this.markContent(tag, _objectSpread2(_objectSpread2({}, options), {}, { mcid })); var structContent = new PDFStructureContent(this.page.dictionary, mcid); this.page.markings.slice(-1)[0].structContent = structContent; return structContent; }, endMarkedContent() { this.page.markings.pop(); this.addContent('EMC'); return this; }, struct(type) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var children = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; return new PDFStructureElement(this, type, options, children); }, addStructure(structElem) { var structTreeRoot = this.getStructTreeRoot(); structElem.setParent(structTreeRoot); structElem.setAttached(); this.structChildren.push(structElem); if (!structTreeRoot.data.K) { structTreeRoot.data.K = []; } structTreeRoot.data.K.push(structElem.dictionary); return this; }, initPageMarkings(pageMarkings) { pageMarkings.forEach(marking => { if (marking.structContent) { var structContent = marking.structContent; var newStructContent = this.markStructureContent(marking.tag, marking.options); structContent.push(newStructContent); this.page.markings.slice(-1)[0].structContent = structContent; } else { this.markContent(marking.tag, marking.options); } }); }, endPageMarkings(page) { var pageMarkings = page.markings; pageMarkings.forEach(() => page.write('EMC')); page.markings = []; return pageMarkings; }, getMarkInfoDictionary() { if (!this._root.data.MarkInfo) { this._root.data.MarkInfo = this.ref({}); } return this._root.data.MarkInfo; }, getStructTreeRoot() { if (!this._root.data.StructTreeRoot) { this._root.data.StructTreeRoot = this.ref({ Type: 'StructTreeRoot', ParentTree: new PDFNumberTree(), ParentTreeNextKey: 0 }); } return this._root.data.StructTreeRoot; }, getStructParentTree() { return this.getStructTreeRoot().data.ParentTree; }, createStructParentTreeNextKey() { // initialise the MarkInfo dictionary this.getMarkInfoDictionary(); var structTreeRoot = this.getStructTreeRoot(); var key = structTreeRoot.data.ParentTreeNextKey++; structTreeRoot.data.ParentTree.add(key, []); return key; }, endMarkings() { var structTreeRoot = this._root.data.StructTreeRoot; if (structTreeRoot) { structTreeRoot.end(); this.structChildren.forEach(structElem => structElem.end()); } if (this._root.data.MarkInfo) { this._root.data.MarkInfo.end(); } } }; var FIELD_FLAGS = { readOnly: 1, required: 2, noExport: 4, multiline: 0x1000, password: 0x2000, toggleToOffButton: 0x4000, radioButton: 0x8000, pushButton: 0x10000, combo: 0x20000, edit: 0x40000, sort: 0x80000, multiSelect: 0x200000, noSpell: 0x400000 }; var FIELD_JUSTIFY = { left: 0, center: 1, right: 2 }; var VALUE_MAP = { value: 'V', defaultValue: 'DV' }; var FORMAT_SPECIAL = { zip: '0', zipPlus4: '1', zip4: '1', phone: '2', ssn: '3' }; var FORMAT_DEFAULT = { number: { nDec: 0, sepComma: false, negStyle: 'MinusBlack', currency: '', currencyPrepend: true }, percent: { nDec: 0, sepComma: false } }; var AcroFormMixin = { /** * Must call if adding AcroForms to a document. Must also call font() before * this method to set the default font. */ initForm() { if (!this._font) { throw new Error('Must set a font before calling initForm method'); } this._acroform = { fonts: {}, defaultFont: this._font.name }; this._acroform.fonts[this._font.id] = this._font.ref(); var data = { Fields: [], NeedAppearances: true, DA: new String("/".concat(this._font.id, " 0 Tf 0 g")), DR: { Font: {} } }; data.DR.Font[this._font.id] = this._font.ref(); var AcroForm = this.ref(data); this._root.data.AcroForm = AcroForm; return this; }, /** * Called automatically by document.js */ endAcroForm() { if (this._root.data.AcroForm) { if (!Object.keys(this._acroform.fonts).length && !this._acroform.defaultFont) { throw new Error('No fonts specified for PDF form'); } var fontDict = this._root.data.AcroForm.data.DR.Font; Object.keys(this._acroform.fonts).forEach(name => { fontDict[name] = this._acroform.fonts[name]; }); this._root.data.AcroForm.data.Fields.forEach(fieldRef => { this._endChild(fieldRef); }); this._root.data.AcroForm.end(); } return this; }, _endChild(ref) { if (Array.isArray(ref.data.Kids)) { ref.data.Kids.forEach(childRef => { this._endChild(childRef); }); ref.end(); } return this; }, /** * Creates and adds a form field to the document. Form fields are intermediate * nodes in a PDF form that are used to specify form name heirarchy and form * value defaults. * @param {string} name - field name (T attribute in field dictionary) * @param {object} options - other attributes to include in field dictionary */ formField(name) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var fieldDict = this._fieldDict(name, null, options); var fieldRef = this.ref(fieldDict); this._addToParent(fieldRef); return fieldRef; }, /** * Creates and adds a Form Annotation to the document. Form annotations are * called Widget annotations internally within a PDF file. * @param {string} name - form field name (T attribute of widget annotation * dictionary) * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @param {object} options */ formAnnotation(name, type, x, y, w, h) { var options = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {}; var fieldDict = this._fieldDict(name, type, options); fieldDict.Subtype = 'Widget'; if (fieldDict.F === undefined) { fieldDict.F = 4; // print the annotation } // Add Field annot to page, and get it's ref this.annotate(x, y, w, h, fieldDict); var annotRef = this.page.annotations[this.page.annotations.length - 1]; return this._addToParent(annotRef); }, formText(name, x, y, w, h) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return this.formAnnotation(name, 'text', x, y, w, h, options); }, formPushButton(name, x, y, w, h) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return this.formAnnotation(name, 'pushButton', x, y, w, h, options); }, formCombo(name, x, y, w, h) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return this.formAnnotation(name, 'combo', x, y, w, h, options); }, formList(name, x, y, w, h) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return this.formAnnotation(name, 'list', x, y, w, h, options); }, formRadioButton(name, x, y, w, h) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return this.formAnnotation(name, 'radioButton', x, y, w, h, options); }, formCheckbox(name, x, y, w, h) { var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; return this.formAnnotation(name, 'checkbox', x, y, w, h, options); }, _addToParent(fieldRef) { var parent = fieldRef.data.Parent; if (parent) { if (!parent.data.Kids) { parent.data.Kids = []; } parent.data.Kids.push(fieldRef); } else { this._root.data.AcroForm.data.Fields.push(fieldRef); } return this; }, _fieldDict(name, type) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (!this._acroform) { throw new Error('Call document.initForms() method before adding form elements to document'); } var opts = Object.assign({}, options); if (type !== null) { opts = this._resolveType(type, options); } opts = this._resolveFlags(opts); opts = this._resolveJustify(opts); opts = this._resolveFont(opts); opts = this._resolveStrings(opts); opts = this._resolveColors(opts); opts = this._resolveFormat(opts); opts.T = new String(name); if (opts.parent) { opts.Parent = opts.parent; delete opts.parent; } return opts; }, _resolveType(type, opts) { if (type === 'text') { opts.FT = 'Tx'; } else if (type === 'pushButton') { opts.FT = 'Btn'; opts.pushButton = true; } else if (type === 'radioButton') { opts.FT = 'Btn'; opts.radioButton = true; } else if (type === 'checkbox') { opts.FT = 'Btn'; } else if (type === 'combo') { opts.FT = 'Ch'; opts.combo = true; } else if (type === 'list') { opts.FT = 'Ch'; } else { throw new Error("Invalid form annotation type '".concat(type, "'")); } return opts; }, _resolveFormat(opts) { var f = opts.format; if (f && f.type) { var fnKeystroke; var fnFormat; var params = ''; if (FORMAT_SPECIAL[f.type] !== undefined) { fnKeystroke = "AFSpecial_Keystroke"; fnFormat = "AFSpecial_Format"; params = FORMAT_SPECIAL[f.type]; } else { var format = f.type.charAt(0).toUpperCase() + f.type.slice(1); fnKeystroke = "AF".concat(format, "_Keystroke"); fnFormat = "AF".concat(format, "_Format"); if (f.type === 'date') { fnKeystroke += 'Ex'; params = String(f.param); } else if (f.type === 'time') { params = String(f.param); } else if (f.type === 'number') { var p = Object.assign({}, FORMAT_DEFAULT.number, f); params = String([String(p.nDec), p.sepComma ? '0' : '1', '"' + p.negStyle + '"', 'null', '"' + p.currency + '"', String(p.currencyPrepend)].join(',')); } else if (f.type === 'percent') { var _p = Object.assign({}, FORMAT_DEFAULT.percent, f); params = String([String(_p.nDec), _p.sepComma ? '0' : '1'].join(',')); } } opts.AA = opts.AA ? opts.AA : {}; opts.AA.K = { S: 'JavaScript', JS: new String("".concat(fnKeystroke, "(").concat(params, ");")) }; opts.AA.F = { S: 'JavaScript', JS: new String("".concat(fnFormat, "(").concat(params, ");")) }; } delete opts.format; return opts; }, _resolveColors(opts) { var color = this._normalizeColor(opts.backgroundColor); if (color) { if (!opts.MK) { opts.MK = {}; } opts.MK.BG = color; } color = this._normalizeColor(opts.borderColor); if (color) { if (!opts.MK) { opts.MK = {}; } opts.MK.BC = color; } delete opts.backgroundColor; delete opts.borderColor; return opts; }, _resolveFlags(options) { var result = 0; Object.keys(options).forEach(key => { if (FIELD_FLAGS[key]) { result |= FIELD_FLAGS[key]; delete options[key]; } }); if (result !== 0) { options.Ff = options.Ff ? options.Ff : 0; options.Ff |= result; } return options; }, _resolveJustify(options) { var result = 0; if (options.align !== undefined) { if (typeof FIELD_JUSTIFY[options.align] === 'number') { result = FIELD_JUSTIFY[options.align]; } delete options.align; } if (result !== 0) { options.Q = result; // default } return options; }, _resolveFont(options) { // add current font to document-level AcroForm dict if necessary if (this._acroform.fonts[this._font.id] === null) { this._acroform.fonts[this._font.id] = this._font.ref(); } // add current font to field's resource dict (RD) if not the default acroform font if (this._acroform.defaultFont !== this._font.name) { options.DR = { Font: {} }; // Get the fontSize option. If not set use auto sizing var fontSize = options.fontSize || 0; options.DR.Font[this._font.id] = this._font.ref(); options.DA = new String("/".concat(this._font.id, " ").concat(fontSize, " Tf 0 g")); } return options; }, _resolveStrings(options) { var select = []; function appendChoices(a) { if (Array.isArray(a)) { for (var idx = 0; idx < a.length; idx++) { if (typeof a[idx] === 'string') { select.push(new String(a[idx])); } else { select.push(a[idx]); } } } } appendChoices(options.Opt); if (options.select) { appendChoices(options.select); delete options.select; } if (select.length) { options.Opt = select; } Object.keys(VALUE_MAP).forEach(key => { if (options[key] !== undefined) { options[VALUE_MAP[key]] = options[key]; delete options[key]; } }); ['V', 'DV'].forEach(key => { if (typeof options[key] === 'string') { options[key] = new String(options[key]); } }); if (options.MK && options.MK.CA) { options.MK.CA = new String(options.MK.CA); } if (options.label) { options.MK = options.MK ? options.MK : {}; options.MK.CA = new String(options.label); delete options.label; } return options; } }; var AttachmentsMixin = { /** * Embed contents of `src` in PDF * @param {Buffer | ArrayBuffer | string} src input Buffer, ArrayBuffer, base64 encoded string or path to file * @param {object} options * * options.name: filename to be shown in PDF, will use `src` if none set * * options.type: filetype to be shown in PDF * * options.description: description to be shown in PDF * * options.hidden: if true, do not add attachment to EmbeddedFiles dictionary. Useful for file attachment annotations * * options.creationDate: override creation date * * options.modifiedDate: override modified date * @returns filespec reference */ file(src) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; options.name = options.name || src; var refBody = { Type: 'EmbeddedFile', Params: {} }; var data; if (!src) { throw new Error('No src specified'); } if (Buffer.isBuffer(src)) { data = src; } else if (src instanceof ArrayBuffer) { data = Buffer.from(new Uint8Array(src)); } else { var match; if (match = /^data:(.*);base64,(.*)$/.exec(src)) { if (match[1]) { refBody.Subtype = match[1].replace('/', '#2F'); } data = Buffer.from(match[2], 'base64'); } else { data = fs.readFileSync(src); if (!data) { throw new Error("Could not read contents of file at filepath ".concat(src)); } // update CreationDate and ModDate var { birthtime, ctime } = fs.statSync(src); refBody.Params.CreationDate = birthtime; refBody.Params.ModDate = ctime; } } // override creation date and modified date if (options.creationDate instanceof Date) { refBody.Params.CreationDate = options.creationDate; } if (options.modifiedDate instanceof Date) { refBody.Params.ModDate = options.modifiedDate; } // add optional subtype if (options.type) { refBody.Subtype = options.type.replace('/', '#2F'); } // add checksum and size information var checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(new Uint8Array(data))); refBody.Params.CheckSum = new String(checksum); refBody.Params.Size = data.byteLength; // save some space when embedding the same file again // if a file with the same name and metadata exists, reuse its reference var ref; if (!this._fileRegistry) this._fileRegistry = {}; var file = this._fileRegistry[options.name]; if (file && isEqual(refBody, file)) { ref = file.ref; } else { ref = this.ref(refBody); ref.end(data); this._fileRegistry[options.name] = _objectSpread2(_objectSpread2({}, refBody), {}, { ref }); } // add filespec for embedded file var fileSpecBody = { Type: 'Filespec', F: new String(options.name), EF: { F: ref }, UF: new String(options.name) }; if (options.description) { fileSpecBody.Desc = new String(options.description); } var filespec = this.ref(fileSpecBody); filespec.end(); if (!options.hidden) { this.addNamedEmbeddedFile(options.name, filespec); } return filespec; } }; /** check two embedded file metadata objects for equality */ function isEqual(a, b) { return a.Subtype === b.Subtype && a.Params.CheckSum.toString() === b.Params.CheckSum.toString() && a.Params.Size === b.Params.Size && a.Params.CreationDate === b.Params.CreationDate && a.Params.ModDate === b.Params.ModDate; } /* PDFDocument - represents an entire PDF document By Devon Govett */ class PDFDocument extends stream.Readable { constructor() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(options); this.options = options; // PDF version switch (options.pdfVersion) { case '1.4': this.version = 1.4; break; case '1.5': this.version = 1.5; break; case '1.6': this.version = 1.6; break; case '1.7': case '1.7ext3': this.version = 1.7; break; default: this.version = 1.3; break; } // Whether streams should be compressed this.compress = this.options.compress != null ? this.options.compress : true; this._pageBuffer = []; this._pageBufferStart = 0; // The PDF object store this._offsets = []; this._waiting = 0; this._ended = false; this._offset = 0; var Pages = this.ref({ Type: 'Pages', Count: 0, Kids: [] }); var Names = this.ref({ Dests: new PDFNameTree() }); this._root = this.ref({ Type: 'Catalog', Pages, Names }); if (this.options.lang) { this._root.data.Lang = new String(this.options.lang); } // The current page this.page = null; // Initialize mixins this.initColor(); this.initVector(); this.initFonts(options.font); this.initText(); this.initImages(); this.initOutline(); this.initMarkings(options); // Initialize the metadata this.info = { Producer: 'PDFKit', Creator: 'PDFKit', CreationDate: new Date() }; if (this.options.info) { for (var key in this.options.info) { var val = this.options.info[key]; this.info[key] = val; } } if (this.options.displayTitle) { this._root.data.ViewerPreferences = this.ref({ DisplayDocTitle: true }); } // Generate file ID this._id = PDFSecurity.generateFileID(this.info); // Initialize security settings this._security = PDFSecurity.create(this, options); // Write the header // PDF version this._write("%PDF-".concat(this.version)); // 4 binary chars, as recommended by the spec this._write('%\xFF\xFF\xFF\xFF'); // Add the first page if (this.options.autoFirstPage !== false) { this.addPage(); } } addPage(options) { if (options == null) { ({ options } = this); } // end the current page if needed if (!this.options.bufferPages) { this.flushPages(); } // create a page object this.page = new PDFPage(this, options); this._pageBuffer.push(this.page); // add the page to the object store var pages = this._root.data.Pages.data; pages.Kids.push(this.page.dictionary); pages.Count++; // reset x and y coordinates this.x = this.page.margins.left; this.y = this.page.margins.top; // flip PDF coordinate system so that the origin is in // the top left rather than the bottom left this._ctm = [1, 0, 0, 1, 0, 0]; this.transform(1, 0, 0, -1, 0, this.page.height); this.emit('pageAdded'); return this; } continueOnNewPage(options) { var pageMarkings = this.endPageMarkings(this.page); this.addPage(options); this.initPageMarkings(pageMarkings); return this; } bufferedPageRange() { return { start: this._pageBufferStart, count: this._pageBuffer.length }; } switchToPage(n) { var page; if (!(page = this._pageBuffer[n - this._pageBufferStart])) { throw new Error("switchToPage(".concat(n, ") out of bounds, current buffer covers pages ").concat(this._pageBufferStart, " to ").concat(this._pageBufferStart + this._pageBuffer.length - 1)); } return this.page = page; } flushPages() { // this local variable exists so we're future-proof against // reentrant calls to flushPages. var pages = this._pageBuffer; this._pageBuffer = []; this._pageBufferStart += pages.length; for (var page of pages) { this.endPageMarkings(page); page.end(); } } addNamedDestination(name) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } if (args.length === 0) { args = ['XYZ', null, null, null]; } if (args[0] === 'XYZ' && args[2] !== null) { args[2] = this.page.height - args[2]; } args.unshift(this.page.dictionary); this._root.data.Names.data.Dests.add(name, args); } addNamedEmbeddedFile(name, ref) { if (!this._root.data.Names.data.EmbeddedFiles) { // disabling /Limits for this tree fixes attachments not showing in Adobe Reader this._root.data.Names.data.EmbeddedFiles = new PDFNameTree({ limits: false }); } // add filespec to EmbeddedFiles this._root.data.Names.data.EmbeddedFiles.add(name, ref); } addNamedJavaScript(name, js) { if (!this._root.data.Names.data.JavaScript) { this._root.data.Names.data.JavaScript = new PDFNameTree(); } var data = { JS: new String(js), S: 'JavaScript' }; this._root.data.Names.data.JavaScript.add(name, data); } ref(data) { var ref = new PDFReference(this, this._offsets.length + 1, data); this._offsets.push(null); // placeholder for this object's offset once it is finalized this._waiting++; return ref; } _read() {} // do nothing, but this method is required by node _write(data) { if (!Buffer.isBuffer(data)) { data = Buffer.from(data + '\n', 'binary'); } this.push(data); return this._offset += data.length; } addContent(data) { this.page.write(data); return this; } _refEnd(ref) { this._offsets[ref.id - 1] = ref.offset; if (--this._waiting === 0 && this._ended) { this._finalize(); return this._ended = false; } } write(filename, fn) { // print a deprecation warning with a stacktrace var err = new Error("PDFDocument#write is deprecated, and will be removed in a future version of PDFKit. Please pipe the document into a Node stream."); console.warn(err.stack); this.pipe(fs.createWriteStream(filename)); this.end(); return this.once('end', fn); } end() { this.flushPages(); this._info = this.ref(); for (var key in this.info) { var val = this.info[key]; if (typeof val === 'string') { val = new String(val); } var entry = this.ref(val); entry.end(); this._info.data[key] = entry; } this._info.end(); for (var name in this._fontFamilies) { var font = this._fontFamilies[name]; font.finalize(); } this.endOutline(); this.endMarkings(); this._root.end(); this._root.data.Pages.end(); this._root.data.Names.end(); this.endAcroForm(); if (this._root.data.ViewerPreferences) { this._root.data.ViewerPreferences.end(); } if (this._security) { this._security.end(); } if (this._waiting === 0) { return this._finalize(); } else { return this._ended = true; } } _finalize() { // generate xref var xRefOffset = this._offset; this._write('xref'); this._write("0 ".concat(this._offsets.length + 1)); this._write('0000000000 65535 f '); for (var offset of this._offsets) { offset = "0000000000".concat(offset).slice(-10); this._write(offset + ' 00000 n '); } // trailer var trailer = { Size: this._offsets.length + 1, Root: this._root, Info: this._info, ID: [this._id, this._id] }; if (this._security) { trailer.Encrypt = this._security.dictionary; } this._write('trailer'); this._write(PDFObject.convert(trailer)); this._write('startxref'); this._write("".concat(xRefOffset)); this._write('%%EOF'); // end the stream return this.push(null); } toString() { return '[object PDFDocument]'; } } var mixin = methods => { Object.assign(PDFDocument.prototype, methods); }; mixin(ColorMixin); mixin(VectorMixin); mixin(FontsMixin); mixin(TextMixin); mixin(ImagesMixin); mixin(AnnotationsMixin); mixin(OutlineMixin); mixin(MarkingsMixin); mixin(AcroFormMixin); mixin(AttachmentsMixin); PDFDocument.LineWrapper = LineWrapper; export default PDFDocument; //# sourceMappingURL=pdfkit.esnext.js.map