jspdf
Version:
PDF Document creation from JavaScript
1,499 lines (1,243 loc) • 730 kB
JavaScript
'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]');