UNPKG

jspdf

Version:

PDF Document creation from JavaScript

1,499 lines (1,243 loc) 730 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** @license * jsPDF - PDF Document creation from JavaScript * Version 1.5.2 Built on 2018-12-20T15:49:08.058Z * CommitID 81f5c40ca4 * * Copyright (c) 2010-2016 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF * 2010 Aaron Spike, https://github.com/acspike * 2012 Willow Systems Corporation, willow-systems.com * 2012 Pablo Hess, https://github.com/pablohess * 2012 Florian Jenett, https://github.com/fjenett * 2013 Warren Weckesser, https://github.com/warrenweckesser * 2013 Youssef Beddad, https://github.com/lifof * 2013 Lee Driscoll, https://github.com/lsdriscoll * 2013 Stefan Slonevskiy, https://github.com/stefslon * 2013 Jeremy Morel, https://github.com/jmorel * 2013 Christoph Hartmann, https://github.com/chris-rock * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria * 2014 James Makes, https://github.com/dollaruw * 2014 Diego Casorran, https://github.com/diegocr * 2014 Steven Spungin, https://github.com/Flamenco * 2014 Kenneth Glassey, https://github.com/Gavvers * * Licensed under the MIT License * * Contributor(s): * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, * kim3er, mfo, alnorth, Flamenco */ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } /** * Creates new jsPDF document object instance. * @name jsPDF * @class * @param orientation {string/Object} Orientation of the first page. Possible values are "portrait" or "landscape" (or shortcuts "p" (Default), "l").<br /> * Can also be an options object. * @param unit {string} Measurement unit to be used when coordinates are specified.<br /> * Possible values are "pt" (points), "mm" (Default), "cm", "in" or "px". * @param format {string/Array} The format of the first page. Can be:<ul><li>a0 - a10</li><li>b0 - b10</li><li>c0 - c10</li><li>dl</li><li>letter</li><li>government-letter</li><li>legal</li><li>junior-legal</li><li>ledger</li><li>tabloid</li><li>credit-card</li></ul><br /> * Default is "a4". If you want to use your own format just pass instead of one of the above predefined formats the size as an number-array, e.g. [595.28, 841.89] * @returns {jsPDF} jsPDF-instance * @description * If the first parameter (orientation) is an object, it will be interpreted as an object of named parameters * ``` * { * orientation: 'p', * unit: 'mm', * format: 'a4', * hotfixes: [] // an array of hotfix strings to enable * } * ``` */ var jsPDF = function (global) { /** * jsPDF's Internal PubSub Implementation. * Backward compatible rewritten on 2014 by * Diego Casorran, https://github.com/diegocr * * @class * @name PubSub * @ignore */ function PubSub(context) { if (_typeof(context) !== 'object') { throw new Error('Invalid Context passed to initialize PubSub (jsPDF-module)'); } var topics = {}; this.subscribe = function (topic, callback, once) { once = once || false; if (typeof topic !== 'string' || typeof callback !== 'function' || typeof once !== 'boolean') { throw new Error('Invalid arguments passed to PubSub.subscribe (jsPDF-module)'); } if (!topics.hasOwnProperty(topic)) { topics[topic] = {}; } var token = Math.random().toString(35); topics[topic][token] = [callback, !!once]; return token; }; this.unsubscribe = function (token) { for (var topic in topics) { if (topics[topic][token]) { delete topics[topic][token]; if (Object.keys(topics[topic]).length === 0) { delete topics[topic]; } return true; } } return false; }; this.publish = function (topic) { if (topics.hasOwnProperty(topic)) { var args = Array.prototype.slice.call(arguments, 1), tokens = []; for (var token in topics[topic]) { var sub = topics[topic][token]; try { sub[0].apply(context, args); } catch (ex) { if (global.console) { console.error('jsPDF PubSub Error', ex.message, ex); } } if (sub[1]) tokens.push(token); } if (tokens.length) tokens.forEach(this.unsubscribe); } }; this.getTopics = function () { return topics; }; } /** * @constructor * @private */ function jsPDF(orientation, unit, format, compressPdf) { var options = {}; var filters = []; var userUnit = 1.0; if (_typeof(orientation) === 'object') { options = orientation; orientation = options.orientation; unit = options.unit || unit; format = options.format || format; compressPdf = options.compress || options.compressPdf || compressPdf; filters = options.filters || (compressPdf === true ? ['FlateEncode'] : filters); userUnit = typeof options.userUnit === "number" ? Math.abs(options.userUnit) : 1.0; } unit = unit || 'mm'; orientation = ('' + (orientation || 'P')).toLowerCase(); var putOnlyUsedFonts = options.putOnlyUsedFonts || true; var usedFonts = {}; var API = { internal: {}, __private__: {} }; API.__private__.PubSub = PubSub; var pdfVersion = '1.3'; var getPdfVersion = API.__private__.getPdfVersion = function () { return pdfVersion; }; var setPdfVersion = API.__private__.setPdfVersion = function (value) { pdfVersion = value; }; // Size in pt of various paper formats var pageFormats = { '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.40, 209.76], 'a9': [104.88, 147.40], 'a10': [73.70, 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.90, 708.66], 'b6': [354.33, 498.90], '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], 'dl': [311.81, 623.62], 'letter': [612, 792], 'government-letter': [576, 756], 'legal': [612, 1008], 'junior-legal': [576, 360], 'ledger': [1224, 792], 'tabloid': [792, 1224], 'credit-card': [153, 243] }; var getPageFormats = API.__private__.getPageFormats = function () { return pageFormats; }; var getPageFormat = API.__private__.getPageFormat = function (value) { return pageFormats[value]; }; if (typeof format === "string") { format = getPageFormat(format); } format = format || getPageFormat('a4'); var f2 = API.f2 = API.__private__.f2 = function (number) { if (isNaN(number)) { throw new Error('Invalid argument passed to jsPDF.f2'); } return number.toFixed(2); // Ie, %.2f }; var f3 = API.__private__.f3 = function (number) { if (isNaN(number)) { throw new Error('Invalid argument passed to jsPDF.f3'); } return number.toFixed(3); // Ie, %.3f }; var fileId = '00000000000000000000000000000000'; var getFileId = API.__private__.getFileId = function () { return fileId; }; var setFileId = API.__private__.setFileId = function (value) { value = value || "12345678901234567890123456789012".split('').map(function () { return "ABCDEF0123456789".charAt(Math.floor(Math.random() * 16)); }).join(''); fileId = value; return fileId; }; /** * @name setFileId * @memberOf jsPDF * @function * @instance * @param {string} value GUID. * @returns {jsPDF} */ API.setFileId = function (value) { setFileId(value); return this; }; /** * @name getFileId * @memberOf jsPDF * @function * @instance * * @returns {string} GUID. */ API.getFileId = function () { return getFileId(); }; var creationDate; var convertDateToPDFDate = API.__private__.convertDateToPDFDate = function (parmDate) { var result = ''; var tzoffset = parmDate.getTimezoneOffset(), tzsign = tzoffset < 0 ? '+' : '-', tzhour = Math.floor(Math.abs(tzoffset / 60)), tzmin = Math.abs(tzoffset % 60), timeZoneString = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join(''); result = ['D:', parmDate.getFullYear(), padd2(parmDate.getMonth() + 1), padd2(parmDate.getDate()), padd2(parmDate.getHours()), padd2(parmDate.getMinutes()), padd2(parmDate.getSeconds()), timeZoneString].join(''); return result; }; var convertPDFDateToDate = API.__private__.convertPDFDateToDate = function (parmPDFDate) { var year = parseInt(parmPDFDate.substr(2, 4), 10); var month = parseInt(parmPDFDate.substr(6, 2), 10) - 1; var date = parseInt(parmPDFDate.substr(8, 2), 10); var hour = parseInt(parmPDFDate.substr(10, 2), 10); var minutes = parseInt(parmPDFDate.substr(12, 2), 10); var seconds = parseInt(parmPDFDate.substr(14, 2), 10); var timeZoneHour = parseInt(parmPDFDate.substr(16, 2), 10); var timeZoneMinutes = parseInt(parmPDFDate.substr(20, 2), 10); var resultingDate = new Date(year, month, date, hour, minutes, seconds, 0); return resultingDate; }; var setCreationDate = API.__private__.setCreationDate = function (date) { var tmpCreationDateString; var regexPDFCreationDate = /^D:(20[0-2][0-9]|203[0-7]|19[7-9][0-9])(0[0-9]|1[0-2])([0-2][0-9]|3[0-1])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(0[0-9]|[1-5][0-9])(\+0[0-9]|\+1[0-4]|\-0[0-9]|\-1[0-1])\'(0[0-9]|[1-5][0-9])\'?$/; if (typeof date === "undefined") { date = new Date(); } if (_typeof(date) === "object" && Object.prototype.toString.call(date) === "[object Date]") { tmpCreationDateString = convertDateToPDFDate(date); } else if (regexPDFCreationDate.test(date)) { tmpCreationDateString = date; } else { throw new Error('Invalid argument passed to jsPDF.setCreationDate'); } creationDate = tmpCreationDateString; return creationDate; }; var getCreationDate = API.__private__.getCreationDate = function (type) { var result = creationDate; if (type === "jsDate") { result = convertPDFDateToDate(creationDate); } return result; }; /** * @name setCreationDate * @memberOf jsPDF * @function * @instance * @param {Object} date * @returns {jsPDF} */ API.setCreationDate = function (date) { setCreationDate(date); return this; }; /** * @name getCreationDate * @memberOf jsPDF * @function * @instance * @param {Object} type * @returns {Object} */ API.getCreationDate = function (type) { return getCreationDate(type); }; var padd2 = API.__private__.padd2 = function (number) { return ('0' + parseInt(number)).slice(-2); }; var outToPages = !1; // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content var pages = []; var content = []; var currentPage; var content_length = 0; var customOutputDestination; var setOutputDestination = API.__private__.setCustomOutputDestination = function (destination) { customOutputDestination = destination; }; var resetOutputDestination = API.__private__.resetCustomOutputDestination = function (destination) { customOutputDestination = undefined; }; var out = API.__private__.out = function (string) { var writeArray; string = typeof string === "string" ? string : string.toString(); if (typeof customOutputDestination === "undefined") { writeArray = outToPages ? pages[currentPage] : content; } else { writeArray = customOutputDestination; } writeArray.push(string); if (!outToPages) { content_length += string.length + 1; } return writeArray; }; var write = API.__private__.write = function (value) { return out(arguments.length === 1 ? value.toString() : Array.prototype.join.call(arguments, ' ')); }; var getArrayBuffer = API.__private__.getArrayBuffer = function (data) { var len = data.length, ab = new ArrayBuffer(len), u8 = new Uint8Array(ab); while (len--) { u8[len] = data.charCodeAt(len); } return ab; }; var standardFonts = [['Helvetica', "helvetica", "normal", 'WinAnsiEncoding'], ['Helvetica-Bold', "helvetica", "bold", 'WinAnsiEncoding'], ['Helvetica-Oblique', "helvetica", "italic", 'WinAnsiEncoding'], ['Helvetica-BoldOblique', "helvetica", "bolditalic", 'WinAnsiEncoding'], ['Courier', "courier", "normal", 'WinAnsiEncoding'], ['Courier-Bold', "courier", "bold", 'WinAnsiEncoding'], ['Courier-Oblique', "courier", "italic", 'WinAnsiEncoding'], ['Courier-BoldOblique', "courier", "bolditalic", 'WinAnsiEncoding'], ['Times-Roman', "times", "normal", 'WinAnsiEncoding'], ['Times-Bold', "times", "bold", 'WinAnsiEncoding'], ['Times-Italic', "times", "italic", 'WinAnsiEncoding'], ['Times-BoldItalic', "times", "bolditalic", 'WinAnsiEncoding'], ['ZapfDingbats', "zapfdingbats", "normal", null], ['Symbol', "symbol", "normal", null]]; var getStandardFonts = API.__private__.getStandardFonts = function (data) { return standardFonts; }; var activeFontSize = options.fontSize || 16; /** * Sets font size for upcoming text elements. * * @param {number} size Font size in points. * @function * @instance * @returns {jsPDF} * @memberOf jsPDF * @name setFontSize */ var setFontSize = API.__private__.setFontSize = API.setFontSize = function (size) { activeFontSize = size; return this; }; /** * Gets the fontsize for upcoming text elements. * * @function * @instance * @returns {number} * @memberOf jsPDF * @name getFontSize */ var getFontSize = API.__private__.getFontSize = API.getFontSize = function () { return activeFontSize; }; var R2L = options.R2L || false; /** * Set value of R2L functionality. * * @param {boolean} value * @function * @instance * @returns {jsPDF} jsPDF-instance * @memberOf jsPDF * @name setR2L */ var setR2L = API.__private__.setR2L = API.setR2L = function (value) { R2L = value; return this; }; /** * Get value of R2L functionality. * * @function * @instance * @returns {boolean} jsPDF-instance * @memberOf jsPDF * @name getR2L */ var getR2L = API.__private__.getR2L = API.getR2L = function (value) { return R2L; }; var zoomMode; // default: 1; var setZoomMode = API.__private__.setZoomMode = function (zoom) { var validZoomModes = [undefined, null, 'fullwidth', 'fullheight', 'fullpage', 'original']; if (/^\d*\.?\d*\%$/.test(zoom)) { zoomMode = zoom; } else if (!isNaN(zoom)) { zoomMode = parseInt(zoom, 10); } else if (validZoomModes.indexOf(zoom) !== -1) { zoomMode = zoom; } else { throw new Error('zoom must be Integer (e.g. 2), a percentage Value (e.g. 300%) or fullwidth, fullheight, fullpage, original. "' + zoom + '" is not recognized.'); } }; var getZoomMode = API.__private__.getZoomMode = function () { return zoomMode; }; var pageMode; // default: 'UseOutlines'; var setPageMode = API.__private__.setPageMode = function (pmode) { var validPageModes = [undefined, null, 'UseNone', 'UseOutlines', 'UseThumbs', 'FullScreen']; if (validPageModes.indexOf(pmode) == -1) { throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "' + pmode + '" is not recognized.'); } pageMode = pmode; }; var getPageMode = API.__private__.getPageMode = function () { return pageMode; }; var layoutMode; // default: 'continuous'; var setLayoutMode = API.__private__.setLayoutMode = function (layout) { var validLayoutModes = [undefined, null, 'continuous', 'single', 'twoleft', 'tworight', 'two']; if (validLayoutModes.indexOf(layout) == -1) { throw new Error('Layout mode must be one of continuous, single, twoleft, tworight. "' + layout + '" is not recognized.'); } layoutMode = layout; }; var getLayoutMode = API.__private__.getLayoutMode = function () { return layoutMode; }; /** * Set the display mode options of the page like zoom and layout. * * @name setDisplayMode * @memberOf jsPDF * @function * @instance * @param {integer|String} zoom You can pass an integer or percentage as * a string. 2 will scale the document up 2x, '200%' will scale up by the * same amount. You can also set it to 'fullwidth', 'fullheight', * 'fullpage', or 'original'. * * Only certain PDF readers support this, such as Adobe Acrobat. * * @param {string} layout Layout mode can be: 'continuous' - this is the * default continuous scroll. 'single' - the single page mode only shows one * page at a time. 'twoleft' - two column left mode, first page starts on * the left, and 'tworight' - pages are laid out in two columns, with the * first page on the right. This would be used for books. * @param {string} pmode 'UseOutlines' - it shows the * outline of the document on the left. 'UseThumbs' - shows thumbnails along * the left. 'FullScreen' - prompts the user to enter fullscreen mode. * * @returns {jsPDF} */ var setDisplayMode = API.__private__.setDisplayMode = API.setDisplayMode = function (zoom, layout, pmode) { setZoomMode(zoom); setLayoutMode(layout); setPageMode(pmode); return this; }; var documentProperties = { 'title': '', 'subject': '', 'author': '', 'keywords': '', 'creator': '' }; var getDocumentProperty = API.__private__.getDocumentProperty = function (key) { if (Object.keys(documentProperties).indexOf(key) === -1) { throw new Error('Invalid argument passed to jsPDF.getDocumentProperty'); } return documentProperties[key]; }; var getDocumentProperties = API.__private__.getDocumentProperties = function (properties) { return documentProperties; }; /** * Adds a properties to the PDF document. * * @param {Object} A property_name-to-property_value object structure. * @function * @instance * @returns {jsPDF} * @memberOf jsPDF * @name setDocumentProperties */ var setDocumentProperties = API.__private__.setDocumentProperties = API.setProperties = API.setDocumentProperties = function (properties) { // copying only those properties we can render. for (var property in documentProperties) { if (documentProperties.hasOwnProperty(property) && properties[property]) { documentProperties[property] = properties[property]; } } return this; }; var setDocumentProperty = API.__private__.setDocumentProperty = function (key, value) { if (Object.keys(documentProperties).indexOf(key) === -1) { throw new Error('Invalid arguments passed to jsPDF.setDocumentProperty'); } return documentProperties[key] = value; }; var objectNumber = 0; // 'n' Current object number var offsets = []; // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes. var fonts = {}; // collection of font objects, where key is fontKey - a dynamically created label for a given font. var fontmap = {}; // mapping structure fontName > fontStyle > font key - performance layer. See addFont() var activeFontKey; // will be string representing the KEY of the font as combination of fontName + fontStyle var k; // Scale factor var page = 0; var pagesContext = []; var additionalObjects = []; var events = new PubSub(API); var hotfixes = options.hotfixes || []; var newObject = API.__private__.newObject = function () { var oid = newObjectDeferred(); newObjectDeferredBegin(oid, true); return oid; }; // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data var newObjectDeferred = API.__private__.newObjectDeferred = function () { objectNumber++; offsets[objectNumber] = function () { return content_length; }; return objectNumber; }; var newObjectDeferredBegin = function newObjectDeferredBegin(oid, doOutput) { doOutput = typeof doOutput === 'boolean' ? doOutput : false; offsets[oid] = content_length; if (doOutput) { out(oid + ' 0 obj'); } return oid; }; // Does not output the object until after the pages have been output. // Returns an object containing the objectId and content. // All pages have been added so the object ID can be estimated to start right after. // This does not modify the current objectNumber; It must be updated after the newObjects are output. var newAdditionalObject = API.__private__.newAdditionalObject = function () { var objId = newObjectDeferred(); var obj = { objId: objId, content: '' }; additionalObjects.push(obj); return obj; }; var rootDictionaryObjId = newObjectDeferred(); var resourceDictionaryObjId = newObjectDeferred(); ///////////////////// // Private functions ///////////////////// var decodeColorString = API.__private__.decodeColorString = function (color) { var colorEncoded = color.split(' '); if (colorEncoded.length === 2 && (colorEncoded[1] === 'g' || colorEncoded[1] === 'G')) { // convert grayscale value to rgb so that it can be converted to hex for consistency var floatVal = parseFloat(colorEncoded[0]); colorEncoded = [floatVal, floatVal, floatVal, 'r']; } var colorAsRGB = '#'; for (var i = 0; i < 3; i++) { colorAsRGB += ('0' + Math.floor(parseFloat(colorEncoded[i]) * 255).toString(16)).slice(-2); } return colorAsRGB; }; var encodeColorString = API.__private__.encodeColorString = function (options) { var color; if (typeof options === "string") { options = { ch1: options }; } var ch1 = options.ch1; var ch2 = options.ch2; var ch3 = options.ch3; var ch4 = options.ch4; var precision = options.precision; var letterArray = options.pdfColorType === "draw" ? ['G', 'RG', 'K'] : ['g', 'rg', 'k']; if (typeof ch1 === "string" && ch1.charAt(0) !== '#') { var rgbColor = new RGBColor(ch1); if (rgbColor.ok) { ch1 = rgbColor.toHex(); } else if (!/^\d*\.?\d*$/.test(ch1)) { throw new Error('Invalid color "' + ch1 + '" passed to jsPDF.encodeColorString.'); } } //convert short rgb to long form if (typeof ch1 === "string" && /^#[0-9A-Fa-f]{3}$/.test(ch1)) { ch1 = '#' + ch1[1] + ch1[1] + ch1[2] + ch1[2] + ch1[3] + ch1[3]; } if (typeof ch1 === "string" && /^#[0-9A-Fa-f]{6}$/.test(ch1)) { var hex = parseInt(ch1.substr(1), 16); ch1 = hex >> 16 & 255; ch2 = hex >> 8 & 255; ch3 = hex & 255; } if (typeof ch2 === "undefined" || typeof ch4 === "undefined" && ch1 === ch2 && ch2 === ch3) { // Gray color space. if (typeof ch1 === "string") { color = ch1 + " " + letterArray[0]; } else { switch (options.precision) { case 2: color = f2(ch1 / 255) + " " + letterArray[0]; break; case 3: default: color = f3(ch1 / 255) + " " + letterArray[0]; } } } else if (typeof ch4 === "undefined" || _typeof(ch4) === "object") { // assume RGBA if (ch4 && !isNaN(ch4.a)) { //TODO Implement transparency. //WORKAROUND use white for now, if transparent, otherwise handle as rgb if (ch4.a === 0) { color = ['1.000', '1.000', '1.000', letterArray[1]].join(" "); return color; } } // assume RGB if (typeof ch1 === "string") { color = [ch1, ch2, ch3, letterArray[1]].join(" "); } else { switch (options.precision) { case 2: color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), letterArray[1]].join(" "); break; default: case 3: color = [f3(ch1 / 255), f3(ch2 / 255), f3(ch3 / 255), letterArray[1]].join(" "); } } } else { // assume CMYK if (typeof ch1 === 'string') { color = [ch1, ch2, ch3, ch4, letterArray[2]].join(" "); } else { switch (options.precision) { case 2: color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), f2(ch4 / 255), letterArray[2]].join(" "); break; case 3: default: color = [f3(ch1 / 255), f3(ch2 / 255), f3(ch3 / 255), f3(ch4 / 255), letterArray[2]].join(" "); } } } return color; }; var getFilters = API.__private__.getFilters = function () { return filters; }; var putStream = API.__private__.putStream = function (options) { options = options || {}; var data = options.data || ''; var filters = options.filters || getFilters(); var alreadyAppliedFilters = options.alreadyAppliedFilters || []; var addLength1 = options.addLength1 || false; var valueOfLength1 = data.length; var processedData = {}; if (filters === true) { filters = ['FlateEncode']; } var keyValues = options.additionalKeyValues || []; if (typeof jsPDF.API.processDataByFilters !== 'undefined') { processedData = jsPDF.API.processDataByFilters(data, filters); } else { processedData = { data: data, reverseChain: [] }; } var filterAsString = processedData.reverseChain + (Array.isArray(alreadyAppliedFilters) ? alreadyAppliedFilters.join(' ') : alreadyAppliedFilters.toString()); if (processedData.data.length !== 0) { keyValues.push({ key: 'Length', value: processedData.data.length }); if (addLength1 === true) { keyValues.push({ key: 'Length1', value: valueOfLength1 }); } } if (filterAsString.length != 0) { //if (filters.length === 0 && alreadyAppliedFilters.length === 1 && typeof alreadyAppliedFilters !== "undefined") { if (filterAsString.split('/').length - 1 === 1) { keyValues.push({ key: 'Filter', value: filterAsString }); } else { keyValues.push({ key: 'Filter', value: '[' + filterAsString + ']' }); } } out('<<'); for (var i = 0; i < keyValues.length; i++) { out('/' + keyValues[i].key + ' ' + keyValues[i].value); } out('>>'); if (processedData.data.length !== 0) { out('stream'); out(processedData.data); out('endstream'); } }; var putPage = API.__private__.putPage = function (page) { var mediaBox = page.mediaBox; var pageNumber = page.number; var data = page.data; var pageObjectNumber = page.objId; var pageContentsObjId = page.contentsObjId; newObjectDeferredBegin(pageObjectNumber, true); var wPt = pagesContext[currentPage].mediaBox.topRightX - pagesContext[currentPage].mediaBox.bottomLeftX; var hPt = pagesContext[currentPage].mediaBox.topRightY - pagesContext[currentPage].mediaBox.bottomLeftY; out('<</Type /Page'); out('/Parent ' + page.rootDictionaryObjId + ' 0 R'); out('/Resources ' + page.resourceDictionaryObjId + ' 0 R'); out('/MediaBox [' + parseFloat(f2(page.mediaBox.bottomLeftX)) + ' ' + parseFloat(f2(page.mediaBox.bottomLeftY)) + ' ' + f2(pagesContext[currentPage].mediaBox.topRightX) + ' ' + f2(pagesContext[currentPage].mediaBox.topRightY) + ']'); if (page.cropBox !== null) { out('/CropBox [' + f2(page.cropBox.bottomLeftX) + ' ' + f2(page.cropBox.bottomLeftY) + ' ' + f2(page.cropBox.topRightX) + ' ' + f2(page.cropBox.topRightY) + ']'); } if (page.bleedBox !== null) { out('/BleedBox [' + f2(page.bleedBox.bottomLeftX) + ' ' + f2(page.bleedBox.bottomLeftY) + ' ' + f2(page.bleedBox.topRightX) + ' ' + f2(page.bleedBox.topRightY) + ']'); } if (page.trimBox !== null) { out('/TrimBox [' + f2(page.trimBox.bottomLeftX) + ' ' + f2(page.trimBox.bottomLeftY) + ' ' + f2(page.trimBox.topRightX) + ' ' + f2(page.trimBox.topRightY) + ']'); } if (page.artBox !== null) { out('/ArtBox [' + f2(page.artBox.bottomLeftX) + ' ' + f2(page.artBox.bottomLeftY) + ' ' + f2(page.artBox.topRightX) + ' ' + f2(page.artBox.topRightY) + ']'); } if (typeof page.userUnit === "number" && page.userUnit !== 1.0) { out('/UserUnit ' + page.userUnit); } events.publish('putPage', { objId: pageObjectNumber, pageContext: pagesContext[pageNumber], pageNumber: pageNumber, page: data }); out('/Contents ' + pageContentsObjId + ' 0 R'); out('>>'); out('endobj'); // Page content var pageContent = data.join('\n'); newObjectDeferredBegin(pageContentsObjId, true); putStream({ data: pageContent, filters: getFilters() }); out('endobj'); return pageObjectNumber; }; var putPages = API.__private__.putPages = function () { var n, i, pageObjectNumbers = []; for (n = 1; n <= page; n++) { pagesContext[n].objId = newObjectDeferred(); pagesContext[n].contentsObjId = newObjectDeferred(); } for (n = 1; n <= page; n++) { pageObjectNumbers.push(putPage({ number: n, data: pages[n], objId: pagesContext[n].objId, contentsObjId: pagesContext[n].contentsObjId, mediaBox: pagesContext[n].mediaBox, cropBox: pagesContext[n].cropBox, bleedBox: pagesContext[n].bleedBox, trimBox: pagesContext[n].trimBox, artBox: pagesContext[n].artBox, userUnit: pagesContext[n].userUnit, rootDictionaryObjId: rootDictionaryObjId, resourceDictionaryObjId: resourceDictionaryObjId })); } newObjectDeferredBegin(rootDictionaryObjId, true); out('<</Type /Pages'); var kids = '/Kids ['; for (i = 0; i < page; i++) { kids += pageObjectNumbers[i] + ' 0 R '; } out(kids + ']'); out('/Count ' + page); out('>>'); out('endobj'); events.publish('postPutPages'); }; var putFont = function putFont(font) { events.publish('putFont', { font: font, out: out, newObject: newObject, putStream: putStream }); if (font.isAlreadyPutted !== true) { font.objectNumber = newObject(); out('<<'); out('/Type /Font'); out('/BaseFont /' + font.postScriptName); out('/Subtype /Type1'); if (typeof font.encoding === 'string') { out('/Encoding /' + font.encoding); } out('/FirstChar 32'); out('/LastChar 255'); out('>>'); out('endobj'); } }; var putFonts = function putFonts() { for (var fontKey in fonts) { if (fonts.hasOwnProperty(fontKey)) { if (putOnlyUsedFonts === false || putOnlyUsedFonts === true && usedFonts.hasOwnProperty(fontKey)) { putFont(fonts[fontKey]); } } } }; var putResourceDictionary = function putResourceDictionary() { out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); out('/Font <<'); // Do this for each font, the '1' bit is the index of the font for (var fontKey in fonts) { if (fonts.hasOwnProperty(fontKey)) { if (putOnlyUsedFonts === false || putOnlyUsedFonts === true && usedFonts.hasOwnProperty(fontKey)) { out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R'); } } } out('>>'); out('/XObject <<'); events.publish('putXobjectDict'); out('>>'); }; var putResources = function putResources() { putFonts(); events.publish('putResources'); newObjectDeferredBegin(resourceDictionaryObjId, true); out('<<'); putResourceDictionary(); out('>>'); out('endobj'); events.publish('postPutResources'); }; var putAdditionalObjects = function putAdditionalObjects() { events.publish('putAdditionalObjects'); for (var i = 0; i < additionalObjects.length; i++) { var obj = additionalObjects[i]; newObjectDeferredBegin(obj.objId, true); out(obj.content); out('endobj'); } events.publish('postPutAdditionalObjects'); }; var addToFontDictionary = function addToFontDictionary(fontKey, fontName, fontStyle) { // this is mapping structure for quick font key lookup. // returns the KEY of the font (ex: "F1") for a given // pair of font name and type (ex: "Arial". "Italic") if (!fontmap.hasOwnProperty(fontName)) { fontmap[fontName] = {}; } fontmap[fontName][fontStyle] = fontKey; }; var addFont = function addFont(postScriptName, fontName, fontStyle, encoding, isStandardFont) { isStandardFont = isStandardFont || false; var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10), // This is FontObject font = { 'id': fontKey, 'postScriptName': postScriptName, 'fontName': fontName, 'fontStyle': fontStyle, 'encoding': encoding, 'isStandardFont': isStandardFont, 'metadata': {} }; var instance = this; events.publish('addFont', { font: font, instance: instance }); if (fontKey !== undefined) { fonts[fontKey] = font; addToFontDictionary(fontKey, fontName, fontStyle); } return fontKey; }; var addFonts = function addFonts(arrayOfFonts) { for (var i = 0, l = standardFonts.length; i < l; i++) { var fontKey = addFont(arrayOfFonts[i][0], arrayOfFonts[i][1], arrayOfFonts[i][2], standardFonts[i][3], true); usedFonts[fontKey] = true; // adding aliases for standard fonts, this time matching the capitalization var parts = arrayOfFonts[i][0].split('-'); addToFontDictionary(fontKey, parts[0], parts[1] || ''); } events.publish('addFonts', { fonts: fonts, dictionary: fontmap }); }; var SAFE = function __safeCall(fn) { fn.foo = function __safeCallWrapper() { try { return fn.apply(this, arguments); } catch (e) { var stack = e.stack || ''; if (~stack.indexOf(' at ')) stack = stack.split(" at ")[1]; var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message; if (global.console) { global.console.error(m, e); if (global.alert) alert(m); } else { throw new Error(m); } } }; fn.foo.bar = fn; return fn.foo; }; var to8bitStream = function to8bitStream(text, flags) { /** * PDF 1.3 spec: * "For text strings encoded in Unicode, the first two bytes must be 254 followed by * 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts * with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely * to be a meaningful beginning of a word or phrase.) The remainder of the * string consists of Unicode character codes, according to the UTF-16 encoding * specified in the Unicode standard, version 2.0. Commonly used Unicode values * are represented as 2 bytes per character, with the high-order byte appearing first * in the string." * * In other words, if there are chars in a string with char code above 255, we * recode the string to UCS2 BE - string doubles in length and BOM is prepended. * * HOWEVER! * Actual *content* (body) text (as opposed to strings used in document properties etc) * does NOT expect BOM. There, it is treated as a literal GID (Glyph ID) * * Because of Adobe's focus on "you subset your fonts!" you are not supposed to have * a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could * fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode * code page. There, however, all characters in the stream are treated as GIDs, * including BOM, which is the reason we need to skip BOM in content text (i.e. that * that is tied to a font). * * To signal this "special" PDFEscape / to8bitStream handling mode, * API.text() function sets (unless you overwrite it with manual values * given to API.text(.., flags) ) * flags.autoencode = true * flags.noBOM = true * * =================================================================================== * `flags` properties relied upon: * .sourceEncoding = string with encoding label. * "Unicode" by default. = encoding of the incoming text. * pass some non-existing encoding name * (ex: 'Do not touch my strings! I know what I am doing.') * to make encoding code skip the encoding step. * .outputEncoding = Either valid PDF encoding name * (must be supported by jsPDF font metrics, otherwise no encoding) * or a JS object, where key = sourceCharCode, value = outputCharCode * missing keys will be treated as: sourceCharCode === outputCharCode * .noBOM * See comment higher above for explanation for why this is important * .autoencode * See comment higher above for explanation for why this is important */ var i, l, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch; flags = flags || {}; sourceEncoding = flags.sourceEncoding || 'Unicode'; outputEncoding = flags.outputEncoding; // This 'encoding' section relies on font metrics format // attached to font objects by, among others, // "Willow Systems' standard_font_metrics plugin" // see jspdf.plugin.standard_font_metrics.js for format // of the font.metadata.encoding Object. // It should be something like // .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}} // .widths = {0:width, code:width, ..., 'fof':divisor} // .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...} if ((flags.autoencode || outputEncoding) && fonts[activeFontKey].metadata && fonts[activeFontKey].metadata[sourceEncoding] && fonts[activeFontKey].metadata[sourceEncoding].encoding) { encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding; // each font has default encoding. Some have it clearly defined. if (!outputEncoding && fonts[activeFontKey].encoding) { outputEncoding = fonts[activeFontKey].encoding; } // Hmmm, the above did not work? Let's try again, in different place. if (!outputEncoding && encodingBlock.codePages) { outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default } if (typeof outputEncoding === 'string') { outputEncoding = encodingBlock[outputEncoding]; } // we want output encoding to be a JS Object, where // key = sourceEncoding's character code and // value = outputEncoding's character code. if (outputEncoding) { isUnicode = false; newtext = []; for (i = 0, l = text.length; i < l; i++) { ch = outputEncoding[text.charCodeAt(i)]; if (ch) { newtext.push(String.fromCharCode(ch)); } else { newtext.push(text[i]); } // since we are looping over chars anyway, might as well // check for residual unicodeness if (newtext[i].charCodeAt(0) >> 8) { /* more than 255 */ isUnicode = true; } } text = newtext.join(''); } } i = text.length; // isUnicode may be set to false above. Hence the triple-equal to undefined while (isUnicode === undefined && i !== 0) { if (text.charCodeAt(i - 1) >> 8) { /* more than 255 */ isUnicode = true; } i--; } if (!isUnicode) { return text; } newtext = flags.noBOM ? [] : [254, 255]; for (i = 0, l = text.length; i < l; i++) { ch = text.charCodeAt(i); bch = ch >> 8; // divide by 256 if (bch >> 8) { /* something left after dividing by 256 second time */ throw new Error("Character at position " + i + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE"); } newtext.push(bch); newtext.push(ch - (bch << 8)); } return String.fromCharCode.apply(undefined, newtext); }; var pdfEscape = API.__private__.pdfEscape = API.pdfEscape = function (text, flags) { /** * Replace '/', '(', and ')' with pdf-safe versions * * Doing to8bitStream does NOT make this PDF display unicode text. For that * we also need to reference a unicode font and embed it - royal pain in the rear. * * There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars, * which JavaScript Strings are happy to provide. So, while we still cannot display * 2-byte characters property, at least CONDITIONALLY converting (entire string containing) * 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF * is still parseable. * This will allow immediate support for unicode in document properties strings. */ return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); }; var beginPage = API.__private__.beginPage = function (width, height) { var tmp; // Dimensions are stored as user units and converted to points on output var orientation = typeof height === 'string' && height.toLowerCase(); if (typeof width === 'string') { if (getPageFormat(width.toLowerCase())) { width = getPageFormat(width.toLowerCase())[0]; height = getPageFormat(width.toLowerCase())[1]; } } if (Array.isArray(width)) { height = width[1]; width = width[0]; } if (isNaN(width) || isNaN(height)) { width = format[0]; height = format[1]; } if (orientation) { switch (orientation.substr(0, 1)) { case 'l': if (height > width) orientation = 's'; break; case 'p': if (width > height) orientation = 's'; break; } if (orientation === 's') { tmp = width; width = height; height = tmp; } } if (width > 14400 || height > 14400) { console.warn('A page in a PDF can not be wider or taller than 14400 userUnit. jsPDF limits the width/height to 14400'); width = Math.min(14400, width); height = Math.min(14400, height); } format = [width, height]; outToPages = true; pages[++page] = []; pagesContext[page] = { objId: 0, contentsObjId: 0, userUnit: Number(userUnit), artBox: null, bleedBox: null, cropBox: null, trimBox: null, mediaBox: { bottomLeftX: 0, bottomLeftY: 0, topRightX: Number(width), topRightY: Number(height) } }; _setPage(page); }; var _addPage = function _addPage() { beginPage.apply(this, arguments); // Set line width setLineWidth(lineWidth); // Set draw color out(strokeColor); // resurrecting non-default line caps, joins if (lineCapID !== 0) { out(lineCapID + ' J'); } if (lineJoinID !== 0) { out(lineJoinID + ' j'); } events.publish('addPage', { pageNumber: page }); }; var _deletePage = function _deletePage(n) { if (n > 0 && n <= page) { pages.splice(n, 1); page--; if (currentPage > page) { currentPage = page; } this.setPage(currentPage); } }; var _setPage = function _setPage(n) { if (n > 0 && n <= page) { currentPage = n; } }; var getNumberOfPages = API.__private__.getNumberOfPages = API.getNumberOfPages = function () { return pages.length - 1; }; /** * Returns a document-specific font key - a label assigned to a * font name + font type combination at the time the font was added * to the font inventory. * * Font key is used as label for the desired font for a block of text * to be added to the PDF document stream. * @private * @function * @param fontName {string} can be undefined on "falthy" to indicate "use current" * @param fontStyle {string} can be undefined on "falthy" to indicate "use current" * @returns {string} Font key. * @ignore */ var _getFont = function getFont(fontName, fontStyle, options) { var key = undefined, fontNameLowerCase; options = options || {}; fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName; fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle; fontNameLowerCase = fontName.toLowerCase(); if (fontmap[fontNameLowerCase] !== undefined && fontmap[fontNameLowerCase][fontStyle] !== undefined) { key = fontmap[fontNameLowerCase][fontStyle]; } else if (fontmap[fontName] !== undefined && fontmap[fontName][fontStyle] !== undefined) { key = fontmap[fontName][fontStyle]; } else { if (options.disableWarning === false) { console.warn("Unable to look up font label for font '" + fontName + "', '" + fontStyle + "'. Refer to getFontList() for available fonts."); } } if (!key && !options.noFallback) { key = fontmap['times'][fontStyle]; if (key == null) { key = fontmap['times']['normal']; } } return key; }; var putInfo = API.__private__.putInfo = function () { newObject(); out('<<'); out('/Producer (jsPDF ' + jsPDF.version + ')'); for (var key in documentProperties) { if (documentProperties.hasOwnProperty(key) && documentProperties[key]) { out('/' + key.substr(0, 1).toUpperCase() + key.substr(1) + ' (' + pdfEscape(documentProperties[key]) + ')'); } } out('/CreationDate (' + creationDate + ')'); out('>>'); out('endobj'); }; var putCatalog = API.__private__.putCatalog = function (options) { options = options || {}; var tmpRootDictionaryObjId = options.rootDictionaryObjId || rootDictionaryObjId; newObject(); out('<<'); out('/Type /Catalog'); out('/Pages ' + tmpRootDictionaryObjId + ' 0 R'); // PDF13ref Section 7.2.1 if (!zoomMode) zoomMode = 'fullwidth'; switch (zoomMode) { case 'fullwidth': out('/OpenAction [3 0 R /FitH null]'); break; case 'fullheight': out('/OpenAction [3 0 R /FitV null]'); break; case 'fullpage': out('/OpenAction [3 0 R /Fit]'); break; case 'original': out('/OpenAction [3 0 R /XYZ null null 1]');