From da59adc820dc192bb51a7c9aa394f9bf2f895b58 Mon Sep 17 00:00:00 2001
From: wangxiao1427 <464339371@qq.com>
Date: Tue, 24 Jul 2018 09:54:53 +0800
Subject: [PATCH 1/5] init lib
---
libs/jspdf/dist/jspdf.debug.js | 20722 +++++++++++++++++++++++++++++++
1 file changed, 20722 insertions(+)
create mode 100644 libs/jspdf/dist/jspdf.debug.js
diff --git a/libs/jspdf/dist/jspdf.debug.js b/libs/jspdf/dist/jspdf.debug.js
new file mode 100644
index 000000000..b92f65180
--- /dev/null
+++ b/libs/jspdf/dist/jspdf.debug.js
@@ -0,0 +1,20722 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.jsPDF = factory());
+}(this, (function () { 'use strict';
+
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ /** @preserve
+ * jsPDF - PDF Document creation from JavaScript
+ * Version 1.4.0 Built on 2018-05-21T16:49:17.000Z
+ * CommitID 48c1917315
+ *
+ * Copyright (c) 2010-2016 James Hall , 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
+ */
+
+ /**
+ * 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")
+ * Can also be an options object.
+ * @param unit {String} Measurement unit to be used when coordinates are specified.
+ * Possible values are "pt" (points), "mm" (Default), "cm", "in" or "px".
+ * @param format {String/Array} The format of the first page. Can be
a0 - a10
b0 - b10
c0 - c10
c0 - c10
dl
letter
government-letter
legal
junior-legal
ledger
tabloid
credit-card
+ * 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}
+ * @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) {
+
+ var pdfVersion = '1.3',
+ pageFormats = { // Size in pt of various paper formats
+ '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]
+ };
+
+ /**
+ * jsPDF's Internal PubSub Implementation.
+ * See mrrio.github.io/jsPDF/doc/symbols/PubSub.html
+ * Backward compatible rewritten on 2014 by
+ * Diego Casorran, https://github.com/diegocr
+ *
+ * @class
+ * @name PubSub
+ * @ignore This should not be in the public docs.
+ */
+ function PubSub(context) {
+ var topics = {};
+
+ this.subscribe = function (topic, callback, once) {
+ if (typeof callback !== 'function') {
+ return false;
+ }
+
+ if (!topics.hasOwnProperty(topic)) {
+ topics[topic] = {};
+ }
+
+ var id = Math.random().toString(35);
+ topics[topic][id] = [callback, !!once];
+
+ return id;
+ };
+
+ this.unsubscribe = function (token) {
+ for (var topic in topics) {
+ if (topics[topic][token]) {
+ delete topics[topic][token];
+ return true;
+ }
+ }
+ return false;
+ };
+
+ this.publish = function (topic) {
+ if (topics.hasOwnProperty(topic)) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ idr = [];
+
+ for (var id in topics[topic]) {
+ var sub = topics[topic][id];
+ try {
+ sub[0].apply(context, args);
+ } catch (ex) {
+ if (global.console) {
+ console.error('jsPDF PubSub Error', ex.message, ex);
+ }
+ }
+ if (sub[1]) idr.push(id);
+ }
+ if (idr.length) idr.forEach(this.unsubscribe);
+ }
+ };
+ }
+
+ /**
+ * @constructor
+ * @private
+ */
+ function jsPDF(orientation, unit, format, compressPdf) {
+ var options = {};
+
+ if ((typeof orientation === 'undefined' ? 'undefined' : _typeof(orientation)) === 'object') {
+ options = orientation;
+
+ orientation = options.orientation;
+ unit = options.unit || unit;
+ format = options.format || format;
+ compressPdf = options.compress || options.compressPdf || compressPdf;
+ }
+
+ // Default options
+ unit = unit || 'mm';
+ format = format || 'a4';
+ orientation = ('' + (orientation || 'P')).toLowerCase();
+
+ var format_as_string = ('' + format).toLowerCase(),
+ compress = !!compressPdf && typeof Uint8Array === 'function',
+ textColor = options.textColor || '0 g',
+ drawColor = options.drawColor || '0 G',
+ activeFontSize = options.fontSize || 16,
+ activeCharSpace = options.charSpace || 0,
+ R2L = options.R2L || false,
+ lineHeightProportion = options.lineHeight || 1.15,
+ lineWidth = options.lineWidth || 0.200025,
+ // 2mm
+ fileId = '00000000000000000000000000000000',
+ objectNumber = 2,
+ // 'n' Current object number
+ outToPages = !1,
+ // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
+ offsets = [],
+ // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
+ fonts = {},
+ // collection of font objects, where key is fontKey - a dynamically created label for a given font.
+ fontmap = {},
+ // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
+ activeFontKey,
+ // will be string representing the KEY of the font as combination of fontName + fontStyle
+ k,
+ // Scale factor
+ tmp,
+ page = 0,
+ currentPage,
+ pages = [],
+ pagesContext = [],
+ // same index as pages and pagedim
+ pagedim = [],
+ content = [],
+ additionalObjects = [],
+ lineCapID = 0,
+ lineJoinID = 0,
+ content_length = 0,
+ pageWidth,
+ pageHeight,
+ pageMode,
+ zoomMode,
+ layoutMode,
+ creationDate,
+ documentProperties = {
+ 'title': '',
+ 'subject': '',
+ 'author': '',
+ 'keywords': '',
+ 'creator': ''
+ },
+ API = {},
+ events = new PubSub(API),
+ hotfixes = options.hotfixes || [],
+
+
+ /////////////////////
+ // Private functions
+ /////////////////////
+ generateColorString = function generateColorString(options) {
+ var color;
+
+ 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'];
+
+ var cssColorNames = { "aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "aqua": "#00ffff", "aquamarine": "#7fffd4", "azure": "#f0ffff",
+ "beige": "#f5f5dc", "bisque": "#ffe4c4", "black": "#000000", "blanchedalmond": "#ffebcd", "blue": "#0000ff", "blueviolet": "#8a2be2", "brown": "#a52a2a", "burlywood": "#deb887",
+ "cadetblue": "#5f9ea0", "chartreuse": "#7fff00", "chocolate": "#d2691e", "coral": "#ff7f50", "cornflowerblue": "#6495ed", "cornsilk": "#fff8dc", "crimson": "#dc143c", "cyan": "#00ffff",
+ "darkblue": "#00008b", "darkcyan": "#008b8b", "darkgoldenrod": "#b8860b", "darkgray": "#a9a9a9", "darkgreen": "#006400", "darkkhaki": "#bdb76b", "darkmagenta": "#8b008b", "darkolivegreen": "#556b2f",
+ "darkorange": "#ff8c00", "darkorchid": "#9932cc", "darkred": "#8b0000", "darksalmon": "#e9967a", "darkseagreen": "#8fbc8f", "darkslateblue": "#483d8b", "darkslategray": "#2f4f4f", "darkturquoise": "#00ced1",
+ "darkviolet": "#9400d3", "deeppink": "#ff1493", "deepskyblue": "#00bfff", "dimgray": "#696969", "dodgerblue": "#1e90ff",
+ "firebrick": "#b22222", "floralwhite": "#fffaf0", "forestgreen": "#228b22", "fuchsia": "#ff00ff",
+ "gainsboro": "#dcdcdc", "ghostwhite": "#f8f8ff", "gold": "#ffd700", "goldenrod": "#daa520", "gray": "#808080", "green": "#008000", "greenyellow": "#adff2f",
+ "honeydew": "#f0fff0", "hotpink": "#ff69b4",
+ "indianred ": "#cd5c5c", "indigo": "#4b0082", "ivory": "#fffff0", "khaki": "#f0e68c",
+ "lavender": "#e6e6fa", "lavenderblush": "#fff0f5", "lawngreen": "#7cfc00", "lemonchiffon": "#fffacd", "lightblue": "#add8e6", "lightcoral": "#f08080", "lightcyan": "#e0ffff", "lightgoldenrodyellow": "#fafad2",
+ "lightgrey": "#d3d3d3", "lightgreen": "#90ee90", "lightpink": "#ffb6c1", "lightsalmon": "#ffa07a", "lightseagreen": "#20b2aa", "lightskyblue": "#87cefa", "lightslategray": "#778899", "lightsteelblue": "#b0c4de", "lightyellow": "#ffffe0", "lime": "#00ff00", "limegreen": "#32cd32", "linen": "#faf0e6",
+ "magenta": "#ff00ff", "maroon": "#800000", "mediumaquamarine": "#66cdaa", "mediumblue": "#0000cd", "mediumorchid": "#ba55d3", "mediumpurple": "#9370d8", "mediumseagreen": "#3cb371", "mediumslateblue": "#7b68ee", "mediumspringgreen": "#00fa9a", "mediumturquoise": "#48d1cc", "mediumvioletred": "#c71585", "midnightblue": "#191970", "mintcream": "#f5fffa", "mistyrose": "#ffe4e1", "moccasin": "#ffe4b5",
+ "navajowhite": "#ffdead", "navy": "#000080",
+ "oldlace": "#fdf5e6", "olive": "#808000", "olivedrab": "#6b8e23", "orange": "#ffa500", "orangered": "#ff4500", "orchid": "#da70d6",
+ "palegoldenrod": "#eee8aa", "palegreen": "#98fb98", "paleturquoise": "#afeeee", "palevioletred": "#d87093", "papayawhip": "#ffefd5", "peachpuff": "#ffdab9", "peru": "#cd853f", "pink": "#ffc0cb", "plum": "#dda0dd", "powderblue": "#b0e0e6", "purple": "#800080",
+ "rebeccapurple": "#663399", "red": "#ff0000", "rosybrown": "#bc8f8f", "royalblue": "#4169e1",
+ "saddlebrown": "#8b4513", "salmon": "#fa8072", "sandybrown": "#f4a460", "seagreen": "#2e8b57", "seashell": "#fff5ee", "sienna": "#a0522d", "silver": "#c0c0c0", "skyblue": "#87ceeb", "slateblue": "#6a5acd", "slategray": "#708090", "snow": "#fffafa", "springgreen": "#00ff7f", "steelblue": "#4682b4",
+ "tan": "#d2b48c", "teal": "#008080", "thistle": "#d8bfd8", "tomato": "#ff6347", "turquoise": "#40e0d0",
+ "violet": "#ee82ee",
+ "wheat": "#f5deb3", "white": "#ffffff", "whitesmoke": "#f5f5f5",
+ "yellow": "#ffff00", "yellowgreen": "#9acd32" };
+
+ if (typeof ch1 === "string" && cssColorNames.hasOwnProperty(ch1)) {
+ ch1 = cssColorNames[ch1];
+ }
+
+ //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 === 'undefined' ? 'undefined' : _typeof(ch4)) === "object") {
+ // 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(" ");
+ }
+ }
+ // assume RGBA
+ if (ch4 && ch4.a === 0) {
+ //TODO Implement transparency.
+ //WORKAROUND use white for now
+ color = ['255', '255', '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), f2(ch2), f2(ch3), f2(ch4), letterArray[2]].join(" ");
+ break;
+ case 3:
+ default:
+ color = [f3(ch1), f3(ch2), f3(ch3), f3(ch4), letterArray[2]].join(" ");
+ }
+ }
+ }
+ return color;
+ },
+ convertDateToPDFDate = function convertDateToPDFDate(parmDate) {
+ var padd2 = function padd2(number) {
+ return ('0' + parseInt(number)).slice(-2);
+ };
+ 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;
+ },
+ convertPDFDateToDate = function convertPDFDateToDate(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;
+ },
+ setCreationDate = function setCreationDate(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' ? 'undefined' : _typeof(date)) === undefined) {
+ date = new Date();
+ }
+
+ if ((typeof date === 'undefined' ? 'undefined' : _typeof(date)) === "object" && Object.prototype.toString.call(date) === "[object Date]") {
+ tmpCreationDateString = convertDateToPDFDate(date);
+ } else if (regexPDFCreationDate.test(date)) {
+ tmpCreationDateString = date;
+ } else {
+ tmpCreationDateString = convertDateToPDFDate(new Date());
+ }
+ creationDate = tmpCreationDateString;
+ return creationDate;
+ },
+ getCreationDate = function getCreationDate(type) {
+ var result = creationDate;
+ if (type === "jsDate") {
+ result = convertPDFDateToDate(creationDate);
+ }
+ return result;
+ },
+ setFileId = function setFileId(value) {
+ value = value || "12345678901234567890123456789012".split('').map(function () {
+ return "ABCDEF0123456789".charAt(Math.floor(Math.random() * 16));
+ }).join('');
+ fileId = value;
+ return fileId;
+ },
+ getFileId = function getFileId() {
+ return fileId;
+ },
+ f2 = function f2(number) {
+ return number.toFixed(2); // Ie, %.2f
+ },
+ f3 = function f3(number) {
+ return number.toFixed(3); // Ie, %.3f
+ },
+ out = function out(string) {
+ string = typeof string === "string" ? string : string.toString();
+ if (outToPages) {
+ /* set by beginPage */
+ pages[currentPage].push(string);
+ } else {
+ // +1 for '\n' that will be used to join 'content'
+ content_length += string.length + 1;
+ content.push(string);
+ }
+ },
+ newObject = function newObject() {
+ // Begin a new object
+ objectNumber++;
+ offsets[objectNumber] = content_length;
+ out(objectNumber + ' 0 obj');
+ return objectNumber;
+ },
+
+ // 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.
+ newAdditionalObject = function newAdditionalObject() {
+ var objId = pages.length * 2 + 1;
+ objId += additionalObjects.length;
+ var obj = {
+ objId: objId,
+ content: ''
+ };
+ additionalObjects.push(obj);
+ return obj;
+ },
+
+ // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
+ newObjectDeferred = function newObjectDeferred() {
+ objectNumber++;
+ offsets[objectNumber] = function () {
+ return content_length;
+ };
+ return objectNumber;
+ },
+ newObjectDeferredBegin = function newObjectDeferredBegin(oid) {
+ offsets[oid] = content_length;
+ },
+ putStream = function putStream(str) {
+ out('stream');
+ out(str);
+ out('endstream');
+ },
+ putPages = function putPages() {
+ var n,
+ p,
+ arr,
+ i,
+ deflater,
+ adler32,
+ adler32cs,
+ wPt,
+ hPt,
+ pageObjectNumbers = [];
+
+ adler32cs = global.adler32cs || jsPDF.API.adler32cs;
+ if (compress && typeof adler32cs === 'undefined') {
+ compress = false;
+ }
+
+ // outToPages = false as set in endDocument(). out() writes to content.
+
+ for (n = 1; n <= page; n++) {
+ pageObjectNumbers.push(newObject());
+ wPt = (pageWidth = pagedim[n].width) * k;
+ hPt = (pageHeight = pagedim[n].height) * k;
+ out('<>');
+ out('endobj');
+
+ // Page content
+ p = pages[n].join('\n');
+ newObject();
+ if (compress) {
+ arr = [];
+ i = p.length;
+ while (i--) {
+ arr[i] = p.charCodeAt(i);
+ }
+ adler32 = adler32cs.from(p);
+ deflater = new Deflater(6);
+ deflater.append(new Uint8Array(arr));
+ p = deflater.flush();
+ arr = new Uint8Array(p.length + 6);
+ arr.set(new Uint8Array([120, 156])), arr.set(p, 2);
+ arr.set(new Uint8Array([adler32 & 0xFF, adler32 >> 8 & 0xFF, adler32 >> 16 & 0xFF, adler32 >> 24 & 0xFF]), p.length + 2);
+ p = String.fromCharCode.apply(null, arr);
+ out('<>');
+ } else {
+ out('<>');
+ }
+ putStream(p);
+ out('endobj');
+ }
+ offsets[1] = content_length;
+ out('1 0 obj');
+ out('<>');
+ out('endobj');
+ events.publish('postPutPages');
+ },
+ putFont = function putFont(font) {
+
+ events.publish('putFont', {
+ font: font,
+ out: out,
+ newObject: newObject
+ });
+ 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');
+ }
+ },
+ putFonts = function putFonts() {
+ for (var fontKey in fonts) {
+ if (fonts.hasOwnProperty(fontKey)) {
+ putFont(fonts[fontKey]);
+ }
+ }
+ },
+ putXobjectDict = function putXobjectDict() {
+ // Loop through images, or other data objects
+ events.publish('putXobjectDict');
+ },
+ 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)) {
+ out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
+ }
+ }
+ out('>>');
+ out('/XObject <<');
+ putXobjectDict();
+ out('>>');
+ },
+ putResources = function putResources() {
+ putFonts();
+ events.publish('putResources');
+ // Resource dictionary
+ offsets[2] = content_length;
+ out('2 0 obj');
+ out('<<');
+ putResourceDictionary();
+ out('>>');
+ out('endobj');
+ events.publish('postPutResources');
+ },
+ putAdditionalObjects = function putAdditionalObjects() {
+ events.publish('putAdditionalObjects');
+ for (var i = 0; i < additionalObjects.length; i++) {
+ var obj = additionalObjects[i];
+ offsets[obj.objId] = content_length;
+ out(obj.objId + ' 0 obj');
+ out(obj.content); out('endobj');
+ }
+ objectNumber += additionalObjects.length;
+ events.publish('postPutAdditionalObjects');
+ },
+ 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;
+ },
+
+ /**
+ * FontObject describes a particular font as member of an instnace of jsPDF
+ *
+ * It's a collection of properties like 'id' (to be used in PDF stream),
+ * 'fontName' (font's family name), 'fontStyle' (font's style variant label)
+ *
+ * @class
+ * @public
+ * @property id {String} PDF-document-instance-specific label assinged to the font.
+ * @property postScriptName {String} PDF specification full name for the font
+ * @property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
+ * @name FontObject
+ * @ignore This should not be in the public docs.
+ */
+ addFont = function addFont(postScriptName, fontName, fontStyle, encoding) {
+ var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10),
+
+ // This is FontObject
+ font = fonts[fontKey] = {
+ 'id': fontKey,
+ 'postScriptName': postScriptName,
+ 'fontName': fontName,
+ 'fontStyle': fontStyle,
+ 'encoding': encoding,
+ 'metadata': {}
+ };
+ addToFontDictionary(fontKey, fontName, fontStyle);
+ events.publish('addFont', font);
+
+ return fontKey;
+ },
+ addFonts = function addFonts() {
+
+ var HELVETICA = "helvetica",
+ TIMES = "times",
+ COURIER = "courier",
+ NORMAL = "normal",
+ BOLD = "bold",
+ ITALIC = "italic",
+ BOLD_ITALIC = "bolditalic",
+ ZAPF = "zapfdingbats",
+ SYMBOL = "symbol",
+ standardFonts = [['Helvetica', HELVETICA, NORMAL, 'WinAnsiEncoding'], ['Helvetica-Bold', HELVETICA, BOLD, 'WinAnsiEncoding'], ['Helvetica-Oblique', HELVETICA, ITALIC, 'WinAnsiEncoding'], ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC, 'WinAnsiEncoding'], ['Courier', COURIER, NORMAL, 'WinAnsiEncoding'], ['Courier-Bold', COURIER, BOLD, 'WinAnsiEncoding'], ['Courier-Oblique', COURIER, ITALIC, 'WinAnsiEncoding'], ['Courier-BoldOblique', COURIER, BOLD_ITALIC, 'WinAnsiEncoding'], ['Times-Roman', TIMES, NORMAL, 'WinAnsiEncoding'], ['Times-Bold', TIMES, BOLD, 'WinAnsiEncoding'], ['Times-Italic', TIMES, ITALIC, 'WinAnsiEncoding'], ['Times-BoldItalic', TIMES, BOLD_ITALIC, 'WinAnsiEncoding'], ['ZapfDingbats', ZAPF, NORMAL, null], ['Symbol', SYMBOL, NORMAL, null]];
+
+ for (var i = 0, l = standardFonts.length; i < l; i++) {
+ var fontKey = addFont(standardFonts[i][0], standardFonts[i][1], standardFonts[i][2], standardFonts[i][3]);
+
+ // adding aliases for standard fonts, this time matching the capitalization
+ var parts = standardFonts[i][0].split('-');
+ addToFontDictionary(fontKey, parts[0], parts[1] || '');
+ }
+ events.publish('addFonts', {
+ fonts: fonts,
+ dictionary: fontmap
+ });
+ },
+ 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;
+ },
+ 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);
+ },
+ pdfEscape = function pdfEscape(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, '\\)');
+ },
+ putInfo = function putInfo() {
+ 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 + ')');
+ },
+ putCatalog = function putCatalog() {
+ out('/Type /Catalog');
+ out('/Pages 1 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]');
+ break;
+ default:
+ var pcn = '' + zoomMode;
+ if (pcn.substr(pcn.length - 1) === '%') zoomMode = parseInt(zoomMode) / 100;
+ if (typeof zoomMode === 'number') {
+ out('/OpenAction [3 0 R /XYZ null null ' + f2(zoomMode) + ']');
+ }
+ }
+ if (!layoutMode) layoutMode = 'continuous';
+ switch (layoutMode) {
+ case 'continuous':
+ out('/PageLayout /OneColumn');
+ break;
+ case 'single':
+ out('/PageLayout /SinglePage');
+ break;
+ case 'two':
+ case 'twoleft':
+ out('/PageLayout /TwoColumnLeft');
+ break;
+ case 'tworight':
+ out('/PageLayout /TwoColumnRight');
+ break;
+ }
+ if (pageMode) {
+ /**
+ * A name object specifying how the document should be displayed when opened:
+ * UseNone : Neither document outline nor thumbnail images visible -- DEFAULT
+ * UseOutlines : Document outline visible
+ * UseThumbs : Thumbnail images visible
+ * FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible
+ */
+ out('/PageMode /' + pageMode);
+ }
+ events.publish('putCatalog');
+ },
+ putTrailer = function putTrailer() {
+ out('/Size ' + (objectNumber + 1));
+ out('/Root ' + objectNumber + ' 0 R');
+ out('/Info ' + (objectNumber - 1) + ' 0 R');
+ out("/ID [ <" + fileId + "> <" + fileId + "> ]");
+ },
+ beginPage = function beginPage(width, height) {
+ // Dimensions are stored as user units and converted to points on output
+ var orientation = typeof height === 'string' && height.toLowerCase();
+ if (typeof width === 'string') {
+ var format = width.toLowerCase();
+ if (pageFormats.hasOwnProperty(format)) {
+ width = pageFormats[format][0] / k;
+ height = pageFormats[format][1] / k;
+ }
+ }
+ if (Array.isArray(width)) {
+ height = width[1];
+ width = width[0];
+ }
+ 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;
+ }
+ }
+ outToPages = true;
+ pages[++page] = [];
+ pagedim[page] = {
+ width: Number(width) || pageWidth,
+ height: Number(height) || pageHeight
+ };
+ pagesContext[page] = {};
+ _setPage(page);
+ },
+ _addPage = function _addPage() {
+ beginPage.apply(this, arguments);
+ // Set line width
+ out(f2(lineWidth * k) + ' w');
+ // Set draw color
+ out(drawColor);
+ // resurrecting non-default line caps, joins
+ if (lineCapID !== 0) {
+ out(lineCapID + ' J');
+ }
+ if (lineJoinID !== 0) {
+ out(lineJoinID + ' j');
+ }
+ events.publish('addPage', {
+ pageNumber: page
+ });
+ },
+ _deletePage = function _deletePage(n) {
+ if (n > 0 && n <= page) {
+ pages.splice(n, 1);
+ pagedim.splice(n, 1);
+ page--;
+ if (currentPage > page) {
+ currentPage = page;
+ }
+ this.setPage(currentPage);
+ }
+ },
+ _setPage = function _setPage(n) {
+ if (n > 0 && n <= page) {
+ currentPage = n;
+ pageWidth = pagedim[n].width;
+ pageHeight = pagedim[n].height;
+ }
+ },
+
+ /**
+ * 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.
+ */
+ _getFont = function _getFont(fontName, fontStyle) {
+ var key, fontNameLowerCase;
+
+ 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 {
+ console.warn("Unable to look up font label for font '" + fontName + "', '" + fontStyle + "'. Refer to getFontList() for available fonts.");
+ }
+
+ if (!key) {
+ //throw new Error();
+ key = fontmap['times'][fontStyle];
+ if (key == null) {
+ key = fontmap['times']['normal'];
+ }
+ }
+ return key;
+ },
+ buildDocument = function buildDocument() {
+ outToPages = false; // switches out() to content
+
+ objectNumber = 2;
+ content_length = 0;
+ content = [];
+ offsets = [];
+ additionalObjects = [];
+ // Added for AcroForm
+ events.publish('buildDocument');
+
+ // putHeader()
+ out('%PDF-' + pdfVersion);
+ out("%\xBA\xDF\xAC\xE0");
+
+ putPages();
+
+ // Must happen after putPages
+ // Modifies current object Id
+ putAdditionalObjects();
+
+ putResources();
+
+ // Info
+ newObject();
+ out('<<');
+ putInfo();
+ out('>>');
+ out('endobj');
+
+ // Catalog
+ newObject();
+ out('<<');
+ putCatalog();
+ out('>>');
+ out('endobj');
+
+ // Cross-ref
+ var o = content_length,
+ i,
+ p = "0000000000";
+ out('xref');
+ out('0 ' + (objectNumber + 1));
+ out(p + ' 65535 f ');
+ for (i = 1; i <= objectNumber; i++) {
+ var offset = offsets[i];
+ if (typeof offset === 'function') {
+ out((p + offsets[i]()).slice(-10) + ' 00000 n ');
+ } else {
+ out((p + offsets[i]).slice(-10) + ' 00000 n ');
+ }
+ }
+ // Trailer
+ out('trailer');
+ out('<<');
+ putTrailer();
+ out('>>');
+ out('startxref');
+ out('' + o);
+ out('%%EOF');
+
+ outToPages = true;
+
+ return content.join('\n');
+ },
+ getStyle = function getStyle(style) {
+ // see path-painting operators in PDF spec
+ var op = 'S'; // stroke
+ if (style === 'F') {
+ op = 'f'; // fill
+ } else if (style === 'FD' || style === 'DF') {
+ op = 'B'; // both
+ } else if (style === 'f' || style === 'f*' || style === 'B' || style === 'B*') {
+ /*
+ Allow direct use of these PDF path-painting operators:
+ - f fill using nonzero winding number rule
+ - f* fill using even-odd rule
+ - B fill then stroke with fill using non-zero winding number rule
+ - B* fill then stroke with fill using even-odd rule
+ */
+ op = style;
+ }
+ return op;
+ },
+ getArrayBuffer = function getArrayBuffer() {
+ var data = buildDocument(),
+ len = data.length,
+ ab = new ArrayBuffer(len),
+ u8 = new Uint8Array(ab);
+
+ while (len--) {
+ u8[len] = data.charCodeAt(len);
+ }return ab;
+ },
+ getBlob = function getBlob() {
+ return new Blob([getArrayBuffer()], {
+ type: "application/pdf"
+ });
+ },
+
+ /**
+ * Generates the PDF document.
+ *
+ * If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
+ *
+ * @param {String} type A string identifying one of the possible output types.
+ * @param {Object} options An object providing some additional signalling to PDF generator.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name output
+ */
+ _output = SAFE(function (type, options) {
+ var datauri = ('' + type).substr(0, 6) === 'dataur' ? 'data:application/pdf;base64,' + btoa(buildDocument()) : 0;
+
+ switch (type) {
+ case undefined:
+ return buildDocument();
+ case 'save':
+ if ((typeof navigator === 'undefined' ? 'undefined' : _typeof(navigator)) === "object" && navigator.getUserMedia) {
+ if (global.URL === undefined || global.URL.createObjectURL === undefined) {
+ return API.output('dataurlnewwindow');
+ }
+ }
+ saveAs(getBlob(), options);
+ if (typeof saveAs.unload === 'function') {
+ if (global.setTimeout) {
+ setTimeout(saveAs.unload, 911);
+ }
+ }
+ break;
+ case 'arraybuffer':
+ return getArrayBuffer();
+ case 'blob':
+ return getBlob();
+ case 'bloburi':
+ case 'bloburl':
+ // User is responsible of calling revokeObjectURL
+ return global.URL && global.URL.createObjectURL(getBlob()) || void 0;
+ case 'datauristring':
+ case 'dataurlstring':
+ return datauri;
+ case 'dataurlnewwindow':
+ var nW = global.open(datauri);
+ if (nW || typeof safari === "undefined") return nW;
+ /* pass through */
+ case 'datauri':
+ case 'dataurl':
+ return global.document.location.href = datauri;
+ default:
+ throw new Error('Output type "' + type + '" is not supported.');
+ }
+ // @TODO: Add different output options
+ }),
+
+
+ /**
+ * Used to see if a supplied hotfix was requested when the pdf instance was created.
+ * @param {String} hotfixName - The name of the hotfix to check.
+ * @returns {boolean}
+ */
+ hasHotfix = function hasHotfix(hotfixName) {
+ return Array.isArray(hotfixes) === true && hotfixes.indexOf(hotfixName) > -1;
+ };
+
+ switch (unit) {
+ case 'pt':
+ k = 1;
+ break;
+ case 'mm':
+ k = 72 / 25.4;
+ break;
+ case 'cm':
+ k = 72 / 2.54;
+ break;
+ case 'in':
+ k = 72;
+ break;
+ case 'px':
+ if (hasHotfix('px_scaling') == true) {
+ k = 72 / 96;
+ } else {
+ k = 96 / 72;
+ }
+ break;
+ case 'pc':
+ k = 12;
+ break;
+ case 'em':
+ k = 12;
+ break;
+ case 'ex':
+ k = 6;
+ break;
+ default:
+ throw 'Invalid unit: ' + unit;
+ }
+
+ setCreationDate();
+ setFileId();
+
+ //---------------------------------------
+ // Public API
+
+ /**
+ * Object exposing internal API to plugins
+ * @public
+ */
+ API.internal = {
+ 'pdfEscape': pdfEscape,
+ 'getStyle': getStyle,
+ /**
+ * Returns {FontObject} describing a particular font.
+ * @public
+ * @function
+ * @param fontName {String} (Optional) Font's family name
+ * @param fontStyle {String} (Optional) Font's style variation name (Example:"Italic")
+ * @returns {FontObject}
+ */
+ 'getFont': function getFont() {
+ return fonts[_getFont.apply(API, arguments)];
+ },
+ 'getFontSize': function getFontSize() {
+ return activeFontSize;
+ },
+ 'getCharSpace': function getCharSpace() {
+ return activeCharSpace;
+ },
+ 'getTextColor': function getTextColor() {
+ var colorEncoded = textColor.split(' ');
+ if (colorEncoded.length === 2 && 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 colorAsHex = '#';
+ for (var i = 0; i < 3; i++) {
+ colorAsHex += ('0' + Math.floor(parseFloat(colorEncoded[i]) * 255).toString(16)).slice(-2);
+ }
+ return colorAsHex;
+ },
+ 'getLineHeight': function getLineHeight() {
+ return activeFontSize * lineHeightProportion;
+ },
+ 'write': function write(string1 /*, string2, string3, etc */) {
+ out(arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' '));
+ },
+ 'getCoordinateString': function getCoordinateString(value) {
+ return f2(value * k);
+ },
+ 'getVerticalCoordinateString': function getVerticalCoordinateString(value) {
+ return f2((pageHeight - value) * k);
+ },
+ 'collections': {},
+ 'newObject': newObject,
+ 'newAdditionalObject': newAdditionalObject,
+ 'newObjectDeferred': newObjectDeferred,
+ 'newObjectDeferredBegin': newObjectDeferredBegin,
+ 'putStream': putStream,
+ 'events': events,
+ // ratio that you use in multiplication of a given "size" number to arrive to 'point'
+ // units of measurement.
+ // scaleFactor is set at initialization of the document and calculated against the stated
+ // default measurement units for the document.
+ // If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
+ // through multiplication.
+ 'scaleFactor': k,
+ 'pageSize': {
+ getWidth: function getWidth() {
+ return pageWidth;
+ },
+ getHeight: function getHeight() {
+ return pageHeight;
+ }
+ },
+ 'output': function output(type, options) {
+ return _output(type, options);
+ },
+ 'getNumberOfPages': function getNumberOfPages() {
+ return pages.length - 1;
+ },
+ 'pages': pages,
+ 'out': out,
+ 'f2': f2,
+ 'getPageInfo': function getPageInfo(pageNumberOneBased) {
+ var objId = (pageNumberOneBased - 1) * 2 + 3;
+ return {
+ objId: objId,
+ pageNumber: pageNumberOneBased,
+ pageContext: pagesContext[pageNumberOneBased]
+ };
+ },
+ 'getCurrentPageInfo': function getCurrentPageInfo() {
+ var objId = (currentPage - 1) * 2 + 3;
+ return {
+ objId: objId,
+ pageNumber: currentPage,
+ pageContext: pagesContext[currentPage]
+ };
+ },
+ 'getPDFVersion': function getPDFVersion() {
+ return pdfVersion;
+ },
+ 'hasHotfix': hasHotfix //Expose the hasHotfix check so plugins can also check them.
+ };
+
+ /**
+ * Adds (and transfers the focus to) new page to the PDF document.
+ * @param format {String/Array} The format of the new page. Can be
a0 - a10
b0 - b10
c0 - c10
c0 - c10
dl
letter
government-letter
legal
junior-legal
ledger
tabloid
credit-card
+ * 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]
+ * @param orientation {String} Orientation of the new page. Possible values are "portrait" or "landscape" (or shortcuts "p" (Default), "l")
+ * @function
+ * @returns {jsPDF}
+ *
+ * @methodOf jsPDF#
+ * @name addPage
+ */
+ API.addPage = function () {
+ _addPage.apply(this, arguments);
+ return this;
+ };
+ /**
+ * Adds (and transfers the focus to) new page to the PDF document.
+ * @function
+ * @returns {jsPDF}
+ *
+ * @methodOf jsPDF#
+ * @name setPage
+ * @param {Number} page Switch the active page to the page number specified
+ * @example
+ * doc = jsPDF()
+ * doc.addPage()
+ * doc.addPage()
+ * doc.text('I am on page 3', 10, 10)
+ * doc.setPage(1)
+ * doc.text('I am on page 1', 10, 10)
+ */
+ API.setPage = function () {
+ _setPage.apply(this, arguments);
+ return this;
+ };
+ API.insertPage = function (beforePage) {
+ this.addPage();
+ this.movePage(currentPage, beforePage);
+ return this;
+ };
+ API.movePage = function (targetPage, beforePage) {
+ if (targetPage > beforePage) {
+ var tmpPages = pages[targetPage];
+ var tmpPagedim = pagedim[targetPage];
+ var tmpPagesContext = pagesContext[targetPage];
+ for (var i = targetPage; i > beforePage; i--) {
+ pages[i] = pages[i - 1];
+ pagedim[i] = pagedim[i - 1];
+ pagesContext[i] = pagesContext[i - 1];
+ }
+ pages[beforePage] = tmpPages;
+ pagedim[beforePage] = tmpPagedim;
+ pagesContext[beforePage] = tmpPagesContext;
+ this.setPage(beforePage);
+ } else if (targetPage < beforePage) {
+ var tmpPages = pages[targetPage];
+ var tmpPagedim = pagedim[targetPage];
+ var tmpPagesContext = pagesContext[targetPage];
+ for (var i = targetPage; i < beforePage; i++) {
+ pages[i] = pages[i + 1];
+ pagedim[i] = pagedim[i + 1];
+ pagesContext[i] = pagesContext[i + 1];
+ }
+ pages[beforePage] = tmpPages;
+ pagedim[beforePage] = tmpPagedim;
+ pagesContext[beforePage] = tmpPagesContext;
+ this.setPage(beforePage);
+ }
+ return this;
+ };
+
+ API.deletePage = function () {
+ _deletePage.apply(this, arguments);
+ return this;
+ };
+
+ API.setCreationDate = function (date) {
+ setCreationDate(date);
+ return this;
+ };
+
+ API.getCreationDate = function (type) {
+ return getCreationDate(type);
+ };
+
+ API.setFileId = function (value) {
+ setFileId(value);
+ return this;
+ };
+
+ API.getFileId = function () {
+ return getFileId();
+ };
+
+ /**
+ * Set the display mode options of the page like zoom and layout.
+ *
+ * @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.
+ *
+ * @function
+ * @returns {jsPDF}
+ * @name setDisplayMode
+ */
+ API.setDisplayMode = function (zoom, layout, pmode) {
+ zoomMode = zoom;
+ layoutMode = layout;
+ pageMode = 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.');
+ }
+ return this;
+ };
+
+ /**
+ * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
+ *
+ * @function
+ * @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down per font, spacing settings declared before this call.
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Object} options Collection of settings signalling how the text must be encoded. Defaults are sane. If you think you want to pass some flags, you likely can read the source.
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name text
+ */
+ API.text = function (text, x, y, options) {
+ /**
+ * Inserts something like this into PDF
+ * BT
+ * /F1 16 Tf % Font name + size
+ * 16 TL % How many units down for next line in multiline text
+ * 0 g % color
+ * 28.35 813.54 Td % position
+ * (line one) Tj
+ * T* (line two) Tj
+ * T* (line three) Tj
+ * ET
+ */
+
+ var xtra = '';
+ var isHex = false;
+ var lineHeight = lineHeightProportion;
+
+ function ESC(s) {
+ s = s.split("\t").join(Array(options.TabLen || 9).join(" "));
+ return pdfEscape(s, flags);
+ }
+
+ function transformTextToSpecialArray(text) {
+ //we don't want to destroy original text array, so cloning it
+ var sa = text.concat();
+ var da = [];
+ var len = sa.length;
+ var curDa;
+ //we do array.join('text that must not be PDFescaped")
+ //thus, pdfEscape each component separately
+ while (len--) {
+ curDa = sa.shift();
+ if (typeof curDa === "string") {
+ da.push(curDa);
+ } else {
+ if (Object.prototype.toString.call(text) === '[object Array]' && curDa.length === 1) {
+ da.push(curDa[0]);
+ } else {
+ da.push([curDa[0], curDa[1], curDa[2]]);
+ }
+ }
+ }
+ return da;
+ }
+
+ function processTextByFunction(text, processingFunction) {
+ var result;
+ if (typeof text === 'string') {
+ result = processingFunction(text)[0];
+ } else if (Object.prototype.toString.call(text) === '[object Array]') {
+ //we don't want to destroy original text array, so cloning it
+ var sa = text.concat();
+ var da = [];
+ var len = sa.length;
+ var curDa;
+ var tmpResult;
+ //we do array.join('text that must not be PDFescaped")
+ //thus, pdfEscape each component separately
+ while (len--) {
+ curDa = sa.shift();
+ if (typeof curDa === "string") {
+ da.push(processingFunction(curDa)[0]);
+ } else if (Object.prototype.toString.call(curDa) === '[object Array]' && curDa[0] === "string") {
+ tmpResult = processingFunction(curDa[0], curDa[1], curDa[2]);
+ da.push([tmpResult[0], tmpResult[1], tmpResult[2]]);
+ }
+ }
+ result = da;
+ }
+ return result;
+ }
+ /**
+ Returns a widths of string in a given font, if the font size is set as 1 point.
+ In other words, this is "proportional" value. For 1 unit of font size, the length
+ of the string will be that much.
+ Multiply by font size to get actual width in *points*
+ Then divide by 72 to get inches or divide by (72/25.6) to get 'mm' etc.
+ @public
+ @function
+ @param
+ @returns {Type}
+ */
+ var getStringUnitWidth = function getStringUnitWidth(text, options) {
+ var result = 0;
+ if (typeof options.font.metadata.widthOfString === "function") {
+ result = options.font.metadata.widthOfString(text, options.fontSize, options.charSpace);
+ } else {
+ result = getArraySum(getCharWidthsArray(text, options)) * options.fontSize;
+ }
+ return result;
+ };
+
+ /**
+ Returns an array of length matching length of the 'word' string, with each
+ cell ocupied by the width of the char in that position.
+ @function
+ @param word {String}
+ @param widths {Object}
+ @param kerning {Object}
+ @returns {Array}
+ */
+ function getCharWidthsArray(text, options) {
+ options = options || {};
+
+ var widths = options.widths ? options.widths : options.font.metadata.Unicode.widths;
+ var widthsFractionOf = widths.fof ? widths.fof : 1;
+ var kerning = options.kerning ? options.kerning : options.font.metadata.Unicode.kerning;
+ var kerningFractionOf = kerning.fof ? kerning.fof : 1;
+
+ var i;
+ var l;
+ var char_code;
+ var prior_char_code = 0; //for kerning
+ var default_char_width = widths[0] || widthsFractionOf;
+ var output = [];
+
+ for (i = 0, l = text.length; i < l; i++) {
+ char_code = text.charCodeAt(i);
+ output.push((widths[char_code] || default_char_width) / widthsFractionOf + (kerning[char_code] && kerning[char_code][prior_char_code] || 0) / kerningFractionOf);
+ prior_char_code = char_code;
+ }
+
+ return output;
+ }
+
+ var getArraySum = function getArraySum(array) {
+ var i = array.length;
+ var output = 0;
+
+ while (i) {
+ i--;
+ output += array[i];
+ }
+
+ return output;
+ };
+
+ //backwardsCompatibility
+ var tmp;
+
+ // Pre-August-2012 the order of arguments was function(x, y, text, flags)
+ // in effort to make all calls have similar signature like
+ // function(data, coordinates... , miscellaneous)
+ // this method had its args flipped.
+ // code below allows backward compatibility with old arg order.
+ if (typeof text === 'number') {
+ tmp = y;
+ y = x;
+ x = text;
+ text = tmp;
+ }
+
+ var flags = arguments[3];
+ var angle = arguments[4];
+ var align = arguments[5];
+
+ if ((typeof flags === 'undefined' ? 'undefined' : _typeof(flags)) !== "object" || flags === null) {
+ if (typeof angle === 'string') {
+ align = angle;
+ angle = null;
+ }
+ if (typeof flags === 'string') {
+ align = flags;
+ flags = null;
+ }
+ if (typeof flags === 'number') {
+ angle = flags;
+ flags = null;
+ }
+ options = { flags: flags, angle: angle, align: align };
+ }
+
+ //Check if text is of type String
+ var textIsOfTypeString = false;
+ var tmpTextIsOfTypeString = true;
+
+ if (typeof text === 'string') {
+ textIsOfTypeString = true;
+ } else if (Object.prototype.toString.call(text) === '[object Array]') {
+ //we don't want to destroy original text array, so cloning it
+ var sa = text.concat();
+ var da = [];
+ var len = sa.length;
+ var curDa;
+ //we do array.join('text that must not be PDFescaped")
+ //thus, pdfEscape each component separately
+ while (len--) {
+ curDa = sa.shift();
+ if (typeof curDa !== "string" || Object.prototype.toString.call(curDa) === '[object Array]' && typeof curDa[0] !== "string") {
+ tmpTextIsOfTypeString = false;
+ }
+ }
+ textIsOfTypeString = tmpTextIsOfTypeString;
+ }
+ if (textIsOfTypeString === false) {
+ throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.');
+ }
+
+ //Escaping
+ var activeFontEncoding = fonts[activeFontKey].encoding;
+
+ if (activeFontEncoding === "WinAnsiEncoding" || activeFontEncoding === "StandardEncoding") {
+ text = processTextByFunction(text, function (text, posX, posY) {
+ return [ESC(text), posX, posY];
+ });
+ }
+ //If there are any newlines in text, we assume
+ //the user wanted to print multiple lines, so break the
+ //text up into an array. If the text is already an array,
+ //we assume the user knows what they are doing.
+ //Convert text into an array anyway to simplify
+ //later code.
+
+ if (typeof text === 'string') {
+ if (text.match(/[\r?\n]/)) {
+ text = text.split(/\r\n|\r|\n/g);
+ } else {
+ text = [text];
+ }
+ }
+
+ //multiline
+ var maxWidth = options.maxWidth || 0;
+ var algorythm = options.maxWidthAlgorythm || "first-fit";
+
+ lineHeight = options.lineHeight || lineHeightProportion;
+ var leading = activeFontSize * lineHeight;
+ var activeFont = fonts[activeFontKey];
+ var k = this.internal.scaleFactor;
+ var charSpace = options.charSpace || activeCharSpace;
+
+ var widthOfSpace = getStringUnitWidth(" ", { font: activeFont, charSpace: charSpace, fontSize: activeFontSize }) / k;
+ var splitByMaxWidth = function splitByMaxWidth(value, maxWidth) {
+ var i = 0;
+ var lastBreak = 0;
+ var currentWidth = 0;
+ var resultingChunks = [];
+ var widthOfEachWord = [];
+ var currentChunk = [];
+
+ var listOfWords = [];
+ var result = [];
+
+ listOfWords = value.split(/ /g);
+
+ for (i = 0; i < listOfWords.length; i += 1) {
+ widthOfEachWord.push(getStringUnitWidth(listOfWords[i], { font: activeFont, charSpace: charSpace, fontSize: activeFontSize }) / k);
+ }
+ for (i = 0; i < listOfWords.length; i += 1) {
+ currentChunk = widthOfEachWord.slice(lastBreak, i);
+ currentWidth = getArraySum(currentChunk) + widthOfSpace * (currentChunk.length - 1);
+ if (currentWidth >= maxWidth) {
+ resultingChunks.push(listOfWords.slice(lastBreak, i !== 0 ? i - 1 : 0).join(" "));
+ lastBreak = i !== 0 ? i - 1 : 0;
+ i -= 1;
+ } else if (i === widthOfEachWord.length - 1) {
+ resultingChunks.push(listOfWords.slice(lastBreak, widthOfEachWord.length).join(" "));
+ }
+ }
+ result = [];
+ for (i = 0; i < resultingChunks.length; i += 1) {
+ result = result.concat(resultingChunks[i]);
+ }
+ return result;
+ };
+ var firstFitMethod = function firstFitMethod(value, maxWidth) {
+ var j = 0;
+ var tmpText = [];
+ for (j = 0; j < value.length; j += 1) {
+ tmpText = tmpText.concat(splitByMaxWidth(value[j], maxWidth));
+ }
+ return tmpText;
+ };
+ if (maxWidth > 0) {
+ switch (algorythm) {
+ case "first-fit":
+ default:
+ text = firstFitMethod(text, maxWidth);
+ break;
+ }
+ }
+
+ //creating Payload-Object to make text byRef
+ var payload = {
+ text: text,
+ x: x,
+ y: y,
+ options: options,
+ mutex: {
+ pdfEscape: pdfEscape,
+ activeFontKey: activeFontKey,
+ fonts: fonts,
+ activeFontSize: activeFontSize
+ }
+ };
+ events.publish('preProcessText', payload);
+
+ text = payload.text;
+ options = payload.options;
+ //angle
+
+ var angle = options.angle;
+ var k = this.internal.scaleFactor;
+ var curY = (this.internal.pageSize.getHeight() - y) * k;
+ var transformationMatrix = [];
+
+ if (angle) {
+ angle *= Math.PI / 180;
+ var c = Math.cos(angle),
+ s = Math.sin(angle);
+ var f2 = function f2(number) {
+ return number.toFixed(2);
+ };
+ transformationMatrix = [f2(c), f2(s), f2(s * -1), f2(c)];
+ }
+
+ //charSpace
+
+ var charSpace = options.charSpace;
+
+ if (charSpace !== undefined) {
+ xtra += charSpace + " Tc\n";
+ }
+
+ //lang
+
+ var lang = options.lang;
+ var tmpRenderingMode = -1;
+ var parmRenderingMode = options.renderingMode || options.stroke;
+ var pageContext = this.internal.getCurrentPageInfo().pageContext;
+
+ switch (parmRenderingMode) {
+ case 0:
+ case false:
+ case 'fill':
+ tmpRenderingMode = 0;
+ break;
+ case 1:
+ case true:
+ case 'stroke':
+ tmpRenderingMode = 1;
+ break;
+ case 2:
+ case 'fillThenStroke':
+ tmpRenderingMode = 2;
+ break;
+ case 3:
+ case 'invisible':
+ tmpRenderingMode = 3;
+ break;
+ case 4:
+ case 'fillAndAddForClipping':
+ tmpRenderingMode = 4;
+ break;
+ case 5:
+ case 'strokeAndAddPathForClipping':
+ tmpRenderingMode = 5;
+ break;
+ case 6:
+ case 'fillThenStrokeAndAddToPathForClipping':
+ tmpRenderingMode = 6;
+ break;
+ case 7:
+ case 'addToPathForClipping':
+ tmpRenderingMode = 7;
+ break;
+ }
+
+ var usedRenderingMode = pageContext.usedRenderingMode || -1;
+
+ //if the coder wrote it explicitly to use a specific
+ //renderingMode, then use it
+ if (tmpRenderingMode !== -1) {
+ xtra += tmpRenderingMode + " Tr\n";
+ //otherwise check if we used the rendering Mode already
+ //if so then set the rendering Mode...
+ } else if (usedRenderingMode !== -1) {
+ xtra += "0 Tr\n";
+ }
+
+ if (tmpRenderingMode !== -1) {
+ pageContext.usedRenderingMode = tmpRenderingMode;
+ }
+
+ //align
+
+ var align = options.align || 'left';
+ var leading = activeFontSize * lineHeight;
+ var pageHeight = this.internal.pageSize.getHeight();
+ var pageWidth = this.internal.pageSize.getWidth();
+ var k = this.internal.scaleFactor;
+ var activeFont = fonts[activeFontKey];
+ var charSpace = options.charSpace || activeCharSpace;
+ var maxWidth = options.maxWidth || 0;
+
+ var lineWidths;
+ var flags = {};
+ var wordSpacingPerLine = [];
+
+ if (Object.prototype.toString.call(text) === '[object Array]') {
+ var da = transformTextToSpecialArray(text);
+ var newY;
+ var maxLineLength;
+ var lineWidths;
+ if (align !== "left") {
+ lineWidths = da.map(function (v) {
+ return getStringUnitWidth(v, { font: activeFont, charSpace: charSpace, fontSize: activeFontSize }) / k;
+ });
+ }
+ var maxLineLength = Math.max.apply(Math, lineWidths);
+ //The first line uses the "main" Td setting,
+ //and the subsequent lines are offset by the
+ //previous line's x coordinate.
+ var prevWidth = 0;
+ var delta;
+ var newX;
+ if (align === "right") {
+ x -= lineWidths[0];
+ text = [];
+ for (var i = 0, len = da.length; i < len; i++) {
+ delta = maxLineLength - lineWidths[i];
+ if (i === 0) {
+ newX = x * k;
+ newY = (pageHeight - y) * k;
+ } else {
+ newX = (prevWidth - lineWidths[i]) * k;
+ newY = -leading;
+ }
+ text.push([da[i], newX, newY]);
+ prevWidth = lineWidths[i];
+ }
+ } else if (align === "center") {
+ x -= lineWidths[0] / 2;
+ text = [];
+ for (var i = 0, len = da.length; i < len; i++) {
+ delta = (maxLineLength - lineWidths[i]) / 2;
+ if (i === 0) {
+ newX = x * k;
+ newY = (pageHeight - y) * k;
+ } else {
+ newX = (prevWidth - lineWidths[i]) / 2 * k;
+ newY = -leading;
+ }
+ text.push([da[i], newX, newY]);
+ prevWidth = lineWidths[i];
+ }
+ } else if (align === "left") {
+ text = [];
+ for (var i = 0, len = da.length; i < len; i++) {
+ newY = i === 0 ? (pageHeight - y) * k : -leading;
+ newX = i === 0 ? x * k : 0;
+ //text.push([da[i], newX, newY]);
+ text.push(da[i]);
+ }
+ } else if (align === "justify") {
+ text = [];
+ var maxWidth = maxWidth !== 0 ? maxWidth : pageWidth;
+
+ for (var i = 0, len = da.length; i < len; i++) {
+ newY = i === 0 ? (pageHeight - y) * k : -leading;
+ newX = i === 0 ? x * k : 0;
+ if (i < len - 1) {
+ wordSpacingPerLine.push(((maxWidth - lineWidths[i]) / (da[i].split(" ").length - 1) * k).toFixed(2));
+ }
+ text.push([da[i], newX, newY]);
+ }
+ } else {
+ throw new Error('Unrecognized alignment option, use "left", "center", "right" or "justify".');
+ }
+ }
+
+ //R2L
+ var doReversing = typeof options.R2L === "boolean" ? options.R2L : R2L;
+ if (doReversing === true) {
+ text = processTextByFunction(text, function (text, posX, posY) {
+ return [text.split("").reverse().join(""), posX, posY];
+ });
+ }
+
+ //creating Payload-Object to make text byRef
+ var payload = {
+ text: text,
+ x: x,
+ y: y,
+ options: options,
+ mutex: {
+ pdfEscape: pdfEscape,
+ activeFontKey: activeFontKey,
+ fonts: fonts,
+ activeFontSize: activeFontSize
+ }
+ };
+ events.publish('postProcessText', payload);
+
+ text = payload.text;
+ isHex = payload.mutex.isHex;
+
+ var da = transformTextToSpecialArray(text);
+
+ text = [];
+ var variant = 0;
+ var len = da.length;
+ var posX;
+ var posY;
+ var content;
+ var wordSpacing = '';
+
+ for (var i = 0; i < len; i++) {
+
+ wordSpacing = '';
+ if (Object.prototype.toString.call(da[i]) !== '[object Array]') {
+ posX = parseFloat(x * k).toFixed(2);
+ posY = parseFloat((pageHeight - y) * k).toFixed(2);
+ content = (isHex ? "<" : "(") + da[i] + (isHex ? ">" : ")");
+ } else if (Object.prototype.toString.call(da[i]) === '[object Array]') {
+ posX = parseFloat(da[i][1]).toFixed(2);
+ posY = parseFloat(da[i][2]).toFixed(2);
+ content = (isHex ? "<" : "(") + da[i][0] + (isHex ? ">" : ")");
+ variant = 1;
+ }
+ if (wordSpacingPerLine !== undefined && wordSpacingPerLine[i] !== undefined) {
+ wordSpacing = wordSpacingPerLine[i] + " Tw\n";
+ }
+ //TODO: Kind of a hack?
+ if (transformationMatrix.length !== 0 && i === 0) {
+ text.push(wordSpacing + transformationMatrix.join(" ") + " " + posX + " " + posY + " Tm\n" + content);
+ } else if (variant === 1 || variant === 0 && i === 0) {
+ text.push(wordSpacing + posX + " " + posY + " Td\n" + content);
+ } else {
+ text.push(wordSpacing + content);
+ }
+ }
+ if (variant === 0) {
+ text = text.join(" Tj\nT* ");
+ } else {
+ text = text.join(" Tj\n");
+ }
+
+ text += " Tj\n";
+
+ var result = 'BT\n/' + activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size
+ (activeFontSize * lineHeight).toFixed(2) + ' TL\n' + // line spacing
+ textColor + '\n';
+ result += xtra;
+ result += text;
+ result += "ET";
+
+ out(result);
+ return this;
+ };
+
+ /**
+ * Letter spacing method to print text with gaps
+ *
+ * @function
+ * @param {String|Array} text String to be added to the page.
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} spacing Spacing (in units declared at inception)
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name lstext
+ * @deprecated We'll be removing this function. It doesn't take character width into account.
+ */
+ API.lstext = function (text, x, y, spacing) {
+ console.warn('jsPDF.lstext is deprecated');
+ for (var i = 0, len = text.length; i < len; i++, x += spacing) {
+ this.text(text[i], x, y);
+ }return this;
+ };
+
+ API.line = function (x1, y1, x2, y2) {
+ return this.lines([[x2 - x1, y2 - y1]], x1, y1);
+ };
+
+ API.clip = function () {
+ // By patrick-roberts, github.com/MrRio/jsPDF/issues/328
+ // Call .clip() after calling .rect() with a style argument of null
+ out('W'); // clip
+ out('S'); // stroke path; necessary for clip to work
+ };
+
+ /**
+ * This fixes the previous function clip(). Perhaps the 'stroke path' hack was due to the missing 'n' instruction?
+ * We introduce the fixed version so as to not break API.
+ * @param fillRule
+ */
+ API.clip_fixed = function (fillRule) {
+ // Call .clip() after calling drawing ops with a style argument of null
+ // W is the PDF clipping op
+ if ('evenodd' === fillRule) {
+ out('W*');
+ } else {
+ out('W');
+ }
+ // End the path object without filling or stroking it.
+ // This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path
+ // (see Section 4.4.3, “Clipping Path Operators”)
+ out('n');
+ };
+
+ /**
+ * Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
+ * All data points in `lines` are relative to last line origin.
+ * `x`, `y` become x1,y1 for first line / curve in the set.
+ * For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
+ * For bezier curves you need to specify [x2,y2,x3,y3,x4,y4] - vectors to control points 1, 2, ending point. All vectors are against the start of the curve - x1,y1.
+ *
+ * @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
+ * @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} scale (Defaults to [1.0,1.0]) x,y Scaling factor for all vectors. Elements can be any floating number Sub-one makes drawing smaller. Over-one grows the drawing. Negative flips the direction.
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
+ * @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name lines
+ */
+ API.lines = function (lines, x, y, scale, style, closed) {
+ var scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4;
+
+ // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
+ // in effort to make all calls have similar signature like
+ // function(content, coordinateX, coordinateY , miscellaneous)
+ // this method had its args flipped.
+ // code below allows backward compatibility with old arg order.
+ if (typeof lines === 'number') {
+ tmp = y;
+ y = x;
+ x = lines;
+ lines = tmp;
+ }
+
+ scale = scale || [1, 1];
+
+ // starting point
+ out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m ');
+
+ scalex = scale[0];
+ scaley = scale[1];
+ l = lines.length;
+ //, x2, y2 // bezier only. In page default measurement "units", *after* scaling
+ //, x3, y3 // bezier only. In page default measurement "units", *after* scaling
+ // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
+ x4 = x; // last / ending point = starting point for first item.
+ y4 = y; // last / ending point = starting point for first item.
+
+ for (i = 0; i < l; i++) {
+ leg = lines[i];
+ if (leg.length === 2) {
+ // simple line
+ x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
+ y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
+ out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l');
+ } else {
+ // bezier curve
+ x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
+ y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
+ x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
+ y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
+ x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
+ y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
+ out(f3(x2 * k) + ' ' + f3((pageHeight - y2) * k) + ' ' + f3(x3 * k) + ' ' + f3((pageHeight - y3) * k) + ' ' + f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' c');
+ }
+ }
+
+ if (closed) {
+ out(' h');
+ }
+
+ // stroking / filling / both the path
+ if (style !== null) {
+ out(getStyle(style));
+ }
+ return this;
+ };
+
+ /**
+ * Adds a rectangle to PDF
+ *
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} w Width (in units declared at inception of PDF document)
+ * @param {Number} h Height (in units declared at inception of PDF document)
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name rect
+ */
+ API.rect = function (x, y, w, h, style) {
+ var op = getStyle(style);
+ out([f2(x * k), f2((pageHeight - y) * k), f2(w * k), f2(-h * k), 're'].join(' '));
+
+ if (style !== null) {
+ out(getStyle(style));
+ }
+
+ return this;
+ };
+
+ /**
+ * Adds a triangle to PDF
+ *
+ * @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name triangle
+ */
+ API.triangle = function (x1, y1, x2, y2, x3, y3, style) {
+ this.lines([[x2 - x1, y2 - y1], // vector to point 2
+ [x3 - x2, y3 - y2], // vector to point 3
+ [x1 - x3, y1 - y3] // closing vector back to point 1
+ ], x1, y1, // start of path
+ [1, 1], style, true);
+ return this;
+ };
+
+ /**
+ * Adds a rectangle with rounded corners to PDF
+ *
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} w Width (in units declared at inception of PDF document)
+ * @param {Number} h Height (in units declared at inception of PDF document)
+ * @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
+ * @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name roundedRect
+ */
+ API.roundedRect = function (x, y, w, h, rx, ry, style) {
+ var MyArc = 4 / 3 * (Math.SQRT2 - 1);
+ this.lines([[w - 2 * rx, 0], [rx * MyArc, 0, rx, ry - ry * MyArc, rx, ry], [0, h - 2 * ry], [0, ry * MyArc, -(rx * MyArc), ry, -rx, ry], [-w + 2 * rx, 0], [-(rx * MyArc), 0, -rx, -(ry * MyArc), -rx, -ry], [0, -h + 2 * ry], [0, -(ry * MyArc), rx * MyArc, -ry, rx, -ry]], x + rx, y, // start of path
+ [1, 1], style);
+ return this;
+ };
+
+ /**
+ * Adds an ellipse to PDF
+ *
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
+ * @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name ellipse
+ */
+ API.ellipse = function (x, y, rx, ry, style) {
+ var lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
+ ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
+
+ out([f2((x + rx) * k), f2((pageHeight - y) * k), 'm', f2((x + rx) * k), f2((pageHeight - (y - ly)) * k), f2((x + lx) * k), f2((pageHeight - (y - ry)) * k), f2(x * k), f2((pageHeight - (y - ry)) * k), 'c'].join(' '));
+ out([f2((x - lx) * k), f2((pageHeight - (y - ry)) * k), f2((x - rx) * k), f2((pageHeight - (y - ly)) * k), f2((x - rx) * k), f2((pageHeight - y) * k), 'c'].join(' '));
+ out([f2((x - rx) * k), f2((pageHeight - (y + ly)) * k), f2((x - lx) * k), f2((pageHeight - (y + ry)) * k), f2(x * k), f2((pageHeight - (y + ry)) * k), 'c'].join(' '));
+ out([f2((x + lx) * k), f2((pageHeight - (y + ry)) * k), f2((x + rx) * k), f2((pageHeight - (y + ly)) * k), f2((x + rx) * k), f2((pageHeight - y) * k), 'c'].join(' '));
+
+ if (style !== null) {
+ out(getStyle(style));
+ }
+
+ return this;
+ };
+
+ /**
+ * Adds an circle to PDF
+ *
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
+ * @param {Number} r Radius (in units declared at inception of PDF document)
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name circle
+ */
+ API.circle = function (x, y, r, style) {
+ return this.ellipse(x, y, r, r, style);
+ };
+
+ /**
+ * Adds a properties to the PDF document
+ *
+ * @param {Object} A property_name-to-property_value object structure.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setProperties
+ */
+ API.setProperties = 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;
+ };
+
+ /**
+ * Sets font size for upcoming text elements.
+ *
+ * @param {Number} size Font size in points.
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setFontSize
+ */
+ API.setFontSize = function (size) {
+ activeFontSize = size;
+ return this;
+ };
+
+ /**
+ * Sets text font face, variant for upcoming text elements.
+ * See output of jsPDF.getFontList() for possible font names, styles.
+ *
+ * @param {String} fontName Font name or family. Example: "times"
+ * @param {String} fontStyle Font style or variant. Example: "italic"
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setFont
+ */
+ API.setFont = function (fontName, fontStyle) {
+ activeFontKey = _getFont(fontName, fontStyle);
+ // if font is not found, the above line blows up and we never go further
+ return this;
+ };
+
+ /**
+ * Switches font style or variant for upcoming text elements,
+ * while keeping the font face or family same.
+ * See output of jsPDF.getFontList() for possible font names, styles.
+ *
+ * @param {String} style Font style or variant. Example: "italic"
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setFontStyle
+ */
+ API.setFontStyle = API.setFontType = function (style) {
+ activeFontKey = _getFont(undefined, style);
+ // if font is not found, the above line blows up and we never go further
+ return this;
+ };
+
+ /**
+ * Returns an object - a tree of fontName to fontStyle relationships available to
+ * active PDF document.
+ *
+ * @public
+ * @function
+ * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
+ * @methodOf jsPDF#
+ * @name getFontList
+ */
+ API.getFontList = function () {
+ // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
+ var list = {},
+ fontName,
+ fontStyle,
+ tmp;
+
+ for (fontName in fontmap) {
+ if (fontmap.hasOwnProperty(fontName)) {
+ list[fontName] = tmp = [];
+ for (fontStyle in fontmap[fontName]) {
+ if (fontmap[fontName].hasOwnProperty(fontStyle)) {
+ tmp.push(fontStyle);
+ }
+ }
+ }
+ }
+
+ return list;
+ };
+
+ /**
+ * Add a custom font.
+ *
+ * @param {String} Postscript name of the Font. Example: "Menlo-Regular"
+ * @param {String} Name of font-family from @font-face definition. Example: "Menlo Regular"
+ * @param {String} Font style. Example: "normal"
+ * @function
+ * @returns the {fontKey} (same as the internal method)
+ * @methodOf jsPDF#
+ * @name addFont
+ */
+ API.addFont = function (postScriptName, fontName, fontStyle, encoding) {
+ encoding = encoding || 'Identity-H';
+ addFont(postScriptName, fontName, fontStyle, encoding);
+ };
+
+ /**
+ * Sets line width for upcoming lines.
+ *
+ * @param {Number} width Line width (in units declared at inception of PDF document)
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setLineWidth
+ */
+ API.setLineWidth = function (width) {
+ out((width * k).toFixed(2) + ' w');
+ return this;
+ };
+
+ /**
+ * Sets the stroke color for upcoming elements.
+ *
+ * Depending on the number of arguments given, Gray, RGB, or CMYK
+ * color space is implied.
+ *
+ * When only ch1 is given, "Gray" color space is implied and it
+ * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
+ * if values are communicated as String types, or in range from 0 (black)
+ * to 255 (white) if communicated as Number type.
+ * The RGB-like 0-255 range is provided for backward compatibility.
+ *
+ * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
+ * value must be in the range from 0.00 (minimum intensity) to to 1.00
+ * (max intensity) if values are communicated as String types, or
+ * from 0 (min intensity) to to 255 (max intensity) if values are communicated
+ * as Number types.
+ * The RGB-like 0-255 range is provided for backward compatibility.
+ *
+ * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
+ * value must be a in the range from 0.00 (0% concentration) to to
+ * 1.00 (100% concentration)
+ *
+ * Because JavaScript treats fixed point numbers badly (rounds to
+ * floating point nearest to binary representation) it is highly advised to
+ * communicate the fractional numbers as String types, not JavaScript Number type.
+ *
+ * @param {Number|String} ch1 Color channel value or {String} ch1 color value in hexadecimal, example: '#FFFFFF'
+ * @param {Number|String} ch2 Color channel value
+ * @param {Number|String} ch3 Color channel value
+ * @param {Number|String} ch4 Color channel value
+ *
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setDrawColor
+ */
+ API.setDrawColor = function (ch1, ch2, ch3, ch4) {
+ var options = {
+ "ch1": ch1,
+ "ch2": ch2,
+ "ch3": ch3,
+ "ch4": ch4,
+ "pdfColorType": "draw",
+ "precision": 2
+ };
+
+ out(generateColorString(options));
+ return this;
+ };
+
+ /**
+ * Sets the fill color for upcoming elements.
+ *
+ * Depending on the number of arguments given, Gray, RGB, or CMYK
+ * color space is implied.
+ *
+ * When only ch1 is given, "Gray" color space is implied and it
+ * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
+ * if values are communicated as String types, or in range from 0 (black)
+ * to 255 (white) if communicated as Number type.
+ * The RGB-like 0-255 range is provided for backward compatibility.
+ *
+ * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
+ * value must be in the range from 0.00 (minimum intensity) to to 1.00
+ * (max intensity) if values are communicated as String types, or
+ * from 0 (min intensity) to to 255 (max intensity) if values are communicated
+ * as Number types.
+ * The RGB-like 0-255 range is provided for backward compatibility.
+ *
+ * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
+ * value must be a in the range from 0.00 (0% concentration) to to
+ * 1.00 (100% concentration)
+ *
+ * Because JavaScript treats fixed point numbers badly (rounds to
+ * floating point nearest to binary representation) it is highly advised to
+ * communicate the fractional numbers as String types, not JavaScript Number type.
+ *
+ * @param {Number|String} ch1 Color channel value or {String} ch1 color value in hexadecimal, example: '#FFFFFF'
+ * @param {Number|String} ch2 Color channel value
+ * @param {Number|String} ch3 Color channel value
+ * @param {Number|String} ch4 Color channel value
+ *
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setFillColor
+ */
+
+ API.setFillColor = function (ch1, ch2, ch3, ch4) {
+ var options = {
+ "ch1": ch1,
+ "ch2": ch2,
+ "ch3": ch3,
+ "ch4": ch4,
+ "pdfColorType": "fill",
+ "precision": 2
+ };
+
+ out(generateColorString(options));
+ return this;
+ };
+
+ /**
+ * Sets the text color for upcoming elements.
+ *
+ * Depending on the number of arguments given, Gray, RGB, or CMYK
+ * color space is implied.
+ *
+ * When only ch1 is given, "Gray" color space is implied and it
+ * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
+ * if values are communicated as String types, or in range from 0 (black)
+ * to 255 (white) if communicated as Number type.
+ * The RGB-like 0-255 range is provided for backward compatibility.
+ *
+ * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
+ * value must be in the range from 0.00 (minimum intensity) to to 1.00
+ * (max intensity) if values are communicated as String types, or
+ * from 0 (min intensity) to to 255 (max intensity) if values are communicated
+ * as Number types.
+ * The RGB-like 0-255 range is provided for backward compatibility.
+ *
+ * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
+ * value must be a in the range from 0.00 (0% concentration) to to
+ * 1.00 (100% concentration)
+ *
+ * Because JavaScript treats fixed point numbers badly (rounds to
+ * floating point nearest to binary representation) it is highly advised to
+ * communicate the fractional numbers as String types, not JavaScript Number type.
+ *
+ * @param {Number|String} ch1 Color channel value or {String} ch1 color value in hexadecimal, example: '#FFFFFF'
+ * @param {Number|String} ch2 Color channel value
+ * @param {Number|String} ch3 Color channel value
+ * @param {Number|String} ch4 Color channel value
+ *
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setTextColor
+ */
+ API.setTextColor = function (ch1, ch2, ch3, ch4) {
+ var options = {
+ "ch1": ch1,
+ "ch2": ch2,
+ "ch3": ch3,
+ "ch4": ch4,
+ "pdfColorType": "text",
+ "precision": 3
+ };
+ textColor = generateColorString(options);
+
+ return this;
+ };
+
+ /**
+ * Initializes the default character set that the user wants to be global..
+ *
+ * @param {Number} charSpace
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setCharSpace
+ */
+
+ API.setCharSpace = function (charSpace) {
+ activeCharSpace = charSpace;
+ return this;
+ };
+
+ /**
+ * Initializes the default character set that the user wants to be global..
+ *
+ * @param {Boolean} boolean
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setR2L
+ */
+
+ API.setR2L = function (boolean) {
+ R2L = boolean;
+ return this;
+ };
+
+ /**
+ * Is an Object providing a mapping from human-readable to
+ * integer flag values designating the varieties of line cap
+ * and join styles.
+ *
+ * @returns {Object}
+ * @fieldOf jsPDF#
+ * @name CapJoinStyles
+ */
+ API.CapJoinStyles = {
+ 0: 0,
+ 'butt': 0,
+ 'but': 0,
+ 'miter': 0,
+ 1: 1,
+ 'round': 1,
+ 'rounded': 1,
+ 'circle': 1,
+ 2: 2,
+ 'projecting': 2,
+ 'project': 2,
+ 'square': 2,
+ 'bevel': 2
+ };
+
+ /**
+ * Sets the line cap styles
+ * See {jsPDF.CapJoinStyles} for variants
+ *
+ * @param {String|Number} style A string or number identifying the type of line cap
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setLineCap
+ */
+ API.setLineCap = function (style) {
+ var id = this.CapJoinStyles[style];
+ if (id === undefined) {
+ throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
+ }
+ lineCapID = id;
+ out(id + ' J');
+
+ return this;
+ };
+
+ /**
+ * Sets the line join styles
+ * See {jsPDF.CapJoinStyles} for variants
+ *
+ * @param {String|Number} style A string or number identifying the type of line join
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name setLineJoin
+ */
+ API.setLineJoin = function (style) {
+ var id = this.CapJoinStyles[style];
+ if (id === undefined) {
+ throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
+ }
+ lineJoinID = id;
+ out(id + ' j');
+
+ return this;
+ };
+
+ // Output is both an internal (for plugins) and external function
+ API.output = _output;
+
+ /**
+ * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
+ * @param {String} filename The filename including extension.
+ *
+ * @function
+ * @returns {jsPDF}
+ * @methodOf jsPDF#
+ * @name save
+ */
+ API.save = function (filename) {
+ API.output('save', filename);
+ };
+
+ // applying plugins (more methods) ON TOP of built-in API.
+ // this is intentional as we allow plugins to override
+ // built-ins
+ for (var plugin in jsPDF.API) {
+ if (jsPDF.API.hasOwnProperty(plugin)) {
+ if (plugin === 'events' && jsPDF.API.events.length) {
+ (function (events, newEvents) {
+
+ // jsPDF.API.events is a JS Array of Arrays
+ // where each Array is a pair of event name, handler
+ // Events were added by plugins to the jsPDF instantiator.
+ // These are always added to the new instance and some ran
+ // during instantiation.
+ var eventname, handler_and_args, i;
+
+ for (i = newEvents.length - 1; i !== -1; i--) {
+ // subscribe takes 3 args: 'topic', function, runonce_flag
+ // if undefined, runonce is false.
+ // users can attach callback directly,
+ // or they can attach an array with [callback, runonce_flag]
+ // that's what the "apply" magic is for below.
+ eventname = newEvents[i][0];
+ handler_and_args = newEvents[i][1];
+ events.subscribe.apply(events, [eventname].concat(typeof handler_and_args === 'function' ? [handler_and_args] : handler_and_args));
+ }
+ })(events, jsPDF.API.events);
+ } else {
+ API[plugin] = jsPDF.API[plugin];
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////
+ // continuing initialization of jsPDF Document object
+ //////////////////////////////////////////////////////
+ // Add the first page automatically
+ addFonts();
+ activeFontKey = 'F1';
+ _addPage(format, orientation);
+
+ events.publish('initialized');
+ return API;
+ }
+
+ /**
+ * jsPDF.API is a STATIC property of jsPDF class.
+ * jsPDF.API is an object you can add methods and properties to.
+ * The methods / properties you add will show up in new jsPDF objects.
+ *
+ * One property is prepopulated. It is the 'events' Object. Plugin authors can add topics,
+ * callbacks to this object. These will be reassigned to all new instances of jsPDF.
+ * Examples:
+ * jsPDF.API.events['initialized'] = function(){ 'this' is API object }
+ * jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
+ *
+ * @static
+ * @public
+ * @memberOf jsPDF
+ * @name API
+ *
+ * @example
+ * jsPDF.API.mymethod = function(){
+ * // 'this' will be ref to internal API object. see jsPDF source
+ * // , so you can refer to built-in methods like so:
+ * // this.line(....)
+ * // this.text(....)
+ * }
+ * var pdfdoc = new jsPDF()
+ * pdfdoc.mymethod() // <- !!!!!!
+ */
+ jsPDF.API = {
+ events: []
+ };
+ jsPDF.version = "0.0.0";
+
+ if (typeof define === 'function' && define.amd) {
+ define('jsPDF', function () {
+ return jsPDF;
+ });
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = jsPDF;
+ module.exports.jsPDF = jsPDF;
+ } else {
+ global.jsPDF = jsPDF;
+ }
+ return jsPDF;
+ }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || typeof global !== "undefined" && global || Function('return typeof this === "object" && this.content')() || Function('return this')());
+ // `self` is undefined in Firefox for Android content script context
+ // while `this` is nsIContentFrameMessageManager
+ // with an attribute `content` that corresponds to the window
+
+
+ /**
+ * jsPDF AcroForm Plugin
+ * Copyright (c) 2016 Alexander Weidt, https://github.com/BiggA94
+ *
+ * Licensed under the MIT License.
+ * http://opensource.org/licenses/mit-license
+ */
+
+ (function (jsPDFAPI, globalObj) {
+
+ var scope;
+ var pageHeight;
+ var scaleFactor = 1;
+ var inherit = function inherit(child, parent) {
+ child.prototype = Object.create(parent.prototype);
+ child.prototype.constructor = child;
+ };
+ var scale = function scale(x) {
+ return x * (scaleFactor / 1); // 1 = (96 / 72)
+ };
+
+ /**
+ Returns a widths of string in a given font, if the font size is set as 1 point.
+ In other words, this is "proportional" value. For 1 unit of font size, the length
+ of the string will be that much.
+ Multiply by font size to get actual width in *points*
+ Then divide by 72 to get inches or divide by (72/25.6) to get 'mm' etc.
+ @public
+ @function
+ @param
+ @returns {Type}
+ */
+ var getStringUnitWidth = function getStringUnitWidth(text, options) {
+ var result = 0;
+ if (typeof options.font.metadata.widthOfString === "function") {
+ result = options.font.metadata.widthOfString(text, options.fontSize, options.charSpace);
+ } else {
+ result = getArraySum(getCharWidthsArray(text, options)) * options.fontSize;
+ }
+ return result;
+ };
+
+ /**
+ Returns an array of length matching length of the 'word' string, with each
+ cell ocupied by the width of the char in that position.
+ @function
+ @param word {String}
+ @param widths {Object}
+ @param kerning {Object}
+ @returns {Array}
+ */
+ function getCharWidthsArray(text, options) {
+ options = options || {};
+
+ var widths = options.widths ? options.widths : options.font.metadata.Unicode.widths;
+ var widthsFractionOf = widths.fof ? widths.fof : 1;
+ var kerning = options.kerning ? options.kerning : options.font.metadata.Unicode.kerning;
+ var kerningFractionOf = kerning.fof ? kerning.fof : 1;
+
+ var i;
+ var l;
+ var char_code;
+ var prior_char_code = 0; //for kerning
+ var default_char_width = widths[0] || widthsFractionOf;
+ var output = [];
+
+ for (i = 0, l = text.length; i < l; i++) {
+ char_code = text.charCodeAt(i);
+ output.push((widths[char_code] || default_char_width) / widthsFractionOf + (kerning[char_code] && kerning[char_code][prior_char_code] || 0) / kerningFractionOf);
+ prior_char_code = char_code;
+ }
+
+ return output;
+ }
+
+ var getArraySum = function getArraySum(array) {
+ var i = array.length;
+ var output = 0;
+
+ while (i) {
+ i--;
+ output += array[i];
+ }
+
+ return output;
+ };
+
+ var createFormXObject = function createFormXObject(formObject) {
+ var xobj = new AcroFormXObject();
+ var height = AcroFormAppearance.internal.getHeight(formObject) || 0;
+ var width = AcroFormAppearance.internal.getWidth(formObject) || 0;
+ xobj.BBox = [0, 0, width.toFixed(2), height.toFixed(2)];
+ return xobj;
+ };
+
+ var setBitPosition = function setBitPosition(variable, position, value) {
+ variable = variable || 0;
+ value = value || 1;
+
+ var bitMask = 1;
+ bitMask = bitMask << position - 1;
+
+ if (value == 1) {
+ // Set the Bit to 1
+ var variable = variable | bitMask;
+ } else {
+ // Set the Bit to 0
+ var variable = variable & ~bitMask;
+ }
+
+ return variable;
+ };
+
+ /**
+ * Calculating the Ff entry:
+ *
+ * The Ff entry contains flags, that have to be set bitwise
+ * In the Following the number in the Comment is the BitPosition
+ */
+ var calculateFlagsOnOptions = function calculateFlagsOnOptions(flags, opts, PDFVersion) {
+ var PDFVersion = PDFVersion || 1.3;
+ var flags = flags || 0;
+
+ // 1, readOnly
+ if (opts.readOnly == true) {
+ flags = setBitPosition(flags, 1);
+ }
+
+ // 2, required
+ if (opts.required == true) {
+ flags = setBitPosition(flags, 2);
+ }
+
+ // 4, noExport
+ if (opts.noExport == true) {
+ flags = setBitPosition(flags, 3);
+ }
+
+ // 13, multiline
+ if (opts.multiline == true) {
+ flags = setBitPosition(flags, 13);
+ }
+
+ // 14, Password
+ if (opts.password) {
+ flags = setBitPosition(flags, 14);
+ }
+
+ // 15, NoToggleToOff (Radio buttons only
+ if (opts.noToggleToOff) {
+ flags = setBitPosition(flags, 15);
+ }
+
+ //16, Radio
+ if (opts.radio) {
+ flags = setBitPosition(flags, 16);
+ }
+
+ // 17, Pushbutton
+ if (opts.pushbutton) {
+ flags = setBitPosition(flags, 17);
+ }
+
+ // 18, Combo (If not set, the choiceField is a listBox!!)
+ if (opts.combo) {
+ flags = setBitPosition(flags, 18);
+ }
+
+ // 19, Edit
+ if (opts.edit) {
+ flags = setBitPosition(flags, 19);
+ }
+
+ // 20, Sort
+ if (opts.sort) {
+ flags = setBitPosition(flags, 20);
+ }
+
+ // 21, FileSelect, PDF 1.4...
+ if (opts.fileSelect && PDFVersion >= 1.4) {
+ flags = setBitPosition(flags, 21);
+ }
+
+ // 22, MultiSelect (PDF 1.4)
+ if (opts.multiSelect && PDFVersion >= 1.4) {
+ flags = setBitPosition(flags, 22);
+ }
+
+ // 23, DoNotSpellCheck (PDF 1.4)
+ if (opts.doNotSpellCheck && PDFVersion >= 1.4) {
+ flags = setBitPosition(flags, 23);
+ }
+
+ // 24, DoNotScroll (PDF 1.4)
+ if (opts.doNotScroll == true && PDFVersion >= 1.4) {
+ flags = setBitPosition(flags, 24);
+ }
+
+ // 25, RichText (PDF 1.4)
+ if (opts.richText && PDFVersion >= 1.4) {
+ flags = setBitPosition(flags, 25);
+ }
+
+ return flags;
+ };
+
+ var calculateCoordinates = function calculateCoordinates(args) {
+ var x = args[0];
+ var y = args[1];
+ var w = args[2];
+ var h = args[3];
+
+ var coordinates = {};
+
+ if (Array.isArray(x)) {
+ x[0] = scale(x[0]);
+ x[1] = scale(x[1]);
+ x[2] = scale(x[2]);
+ x[3] = scale(x[3]);
+ } else {
+ x = scale(x);
+ y = scale(y);
+ w = scale(w);
+ h = scale(h);
+ }
+ coordinates.lowerLeft_X = x || 0;
+ coordinates.lowerLeft_Y = scale(pageHeight) - y - h || 0;
+ coordinates.upperRight_X = x + w || 0;
+ coordinates.upperRight_Y = scale(pageHeight) - y || 0;
+
+ return [coordinates.lowerLeft_X.toFixed(2), coordinates.lowerLeft_Y.toFixed(2), coordinates.upperRight_X.toFixed(2), coordinates.upperRight_Y.toFixed(2)];
+ };
+
+ var calculateAppearanceStream = function calculateAppearanceStream(formObject) {
+ if (formObject.appearanceStreamContent) {
+ // If appearanceStream is already set, use it
+ return formObject.appearanceStreamContent;
+ }
+
+ if (!formObject.V && !formObject.DV) {
+ return;
+ }
+
+ // else calculate it
+
+ var stream = [];
+ var text = formObject.V || formObject.DV;
+ var calcRes = calculateX(formObject, text);
+
+ stream.push('/Tx BMC');
+ stream.push('q');
+ stream.push('/F1 ' + calcRes.fontSize.toFixed(2) + ' Tf');
+ stream.push('1 0 0 1 0 0 Tm'); // Text Matrix
+
+ stream.push('BT'); // Begin Text
+ stream.push(calcRes.text);
+
+ stream.push('ET'); // End Text
+ stream.push('Q');
+ stream.push('EMC');
+
+ var appearanceStreamContent = new createFormXObject(formObject);
+ appearanceStreamContent.stream = stream.join("\n");
+
+ return appearanceStreamContent;
+ };
+
+ var calculateX = function calculateX(formObject, text, font, maxFontSize) {
+ var maxFontSize = maxFontSize || 12;
+ var font = font || "helvetica";
+ var returnValue = {
+ text: "",
+ fontSize: ""
+ };
+ // Remove Brackets
+ text = text.substr(0, 1) == '(' ? text.substr(1) : text;
+ text = text.substr(text.length - 1) == ')' ? text.substr(0, text.length - 1) : text;
+ // split into array of words
+ var textSplit = text.split(' ');
+ var fontSize = maxFontSize; // The Starting fontSize (The Maximum)
+ var lineSpacing = 2;
+ var borderPadding = 2;
+
+ var height = AcroFormAppearance.internal.getHeight(formObject) || 0;
+ height = height < 0 ? -height : height;
+ var width = AcroFormAppearance.internal.getWidth(formObject) || 0;
+ width = width < 0 ? -width : width;
+
+ var isSmallerThanWidth = function isSmallerThanWidth(i, lastLine, fontSize) {
+ if (i + 1 < textSplit.length) {
+ var tmp = lastLine + " " + textSplit[i + 1];
+ var TextWidth = calculateFontSpace(tmp, fontSize + "px", font).width;
+ var FieldWidth = width - 2 * borderPadding;
+ return TextWidth <= FieldWidth;
+ } else {
+ return false;
+ }
+ };
+
+ fontSize++;
+ FontSize: while (true) {
+ var text = "";
+ fontSize--;
+ var textHeight = calculateFontSpace("3", fontSize + "px", font).height;
+ var startY = formObject.multiline ? height - fontSize : (height - textHeight) / 2;
+ startY += lineSpacing;
+ var startX = -borderPadding;
+
+ var lastY = startY;
+ var firstWordInLine = 0,
+ lastWordInLine = 0;
+ var lastLength = 0;
+ if (fontSize <= 0) {
+ // In case, the Text doesn't fit at all
+ fontSize = 12;
+ text = "(...) Tj\n";
+ text += "% Width of Text: " + calculateFontSpace(text, "1px").width + ", FieldWidth:" + width + "\n";
+ break;
+ }
+
+ lastLength = calculateFontSpace(textSplit[0] + " ", fontSize + "px", font).width;
+
+ var lastLine = "";
+ var lineCount = 0;
+ Line: for (var i in textSplit) {
+ lastLine += textSplit[i] + " ";
+ // Remove last blank
+ lastLine = lastLine.substr(lastLine.length - 1) == " " ? lastLine.substr(0, lastLine.length - 1) : lastLine;
+ var key = parseInt(i);
+ lastLength = calculateFontSpace(lastLine + " ", fontSize + "px", font).width;
+ var nextLineIsSmaller = isSmallerThanWidth(key, lastLine, fontSize);
+ var isLastWord = i >= textSplit.length - 1;
+ if (nextLineIsSmaller && !isLastWord) {
+ lastLine += " ";
+ continue; // Line
+ } else if (!nextLineIsSmaller && !isLastWord) {
+ if (!formObject.multiline) {
+ continue FontSize;
+ } else {
+ if ((textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) {
+ // If the Text is higher than the FieldObject
+ continue FontSize;
+ }
+ lastWordInLine = key;
+ // go on
+ }
+ } else if (isLastWord) {
+ lastWordInLine = key;
+ } else {
+ if (formObject.multiline && (textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) {
+ // If the Text is higher than the FieldObject
+ continue FontSize;
+ }
+ }
+
+ var line = '';
+
+ for (var x = firstWordInLine; x <= lastWordInLine; x++) {
+ line += textSplit[x] + ' ';
+ }
+
+ // Remove last blank
+ line = line.substr(line.length - 1) == " " ? line.substr(0, line.length - 1) : line;
+ //lastLength -= blankSpace.width;
+ lastLength = calculateFontSpace(line, fontSize + "px", font).width;
+
+ // Calculate startX
+ switch (formObject.Q) {
+ case 2:
+ // Right justified
+ startX = width - lastLength - borderPadding;
+ break;
+ case 1:
+ // Q = 1 := Text-Alignment: Center
+ startX = (width - lastLength) / 2;
+ break;
+ case 0:
+ default:
+ startX = borderPadding;
+ break;
+ }
+ text += startX.toFixed(2) + ' ' + lastY.toFixed(2) + ' Td\n';
+ text += '(' + line + ') Tj\n';
+ // reset X in PDF
+ text += -startX.toFixed(2) + ' 0 Td\n';
+
+ // After a Line, adjust y position
+ lastY = -(fontSize + lineSpacing);
+
+ // Reset for next iteration step
+ lastLength = 0;
+ firstWordInLine = lastWordInLine + 1;
+ lineCount++;
+
+ lastLine = "";
+ continue Line;
+ }
+ break;
+ }
+
+ returnValue.text = text;
+ returnValue.fontSize = fontSize;
+
+ return returnValue;
+ };
+ /*
+ * small workaround for calculating the TextMetric approximately
+ * @param text
+ * @param fontsize
+ * @returns {TextMetrics} (Has Height and Width)
+ */
+ var calculateFontSpace = function calculateFontSpace(text, fontsize, fonttype) {
+ var fonttype = fonttype || "helvetica";
+ var font = scope.internal.getFont(fonttype);
+ var width = getStringUnitWidth(text, { font: font, fontSize: parseFloat(fontsize), charSpace: 0 });
+ var height = getStringUnitWidth("3", { font: font, fontSize: parseFloat(fontsize), charSpace: 0 }) * 1.5;
+ var result = { height: height, width: width };
+ return result;
+ };
+
+ var acroformPluginTemplate = {
+ fields: [],
+ xForms: [],
+ /**
+ * acroFormDictionaryRoot contains information about the AcroForm Dictionary
+ * 0: The Event-Token, the AcroFormDictionaryCallback has
+ * 1: The Object ID of the Root
+ */
+ acroFormDictionaryRoot: null,
+ /**
+ * After the PDF gets evaluated, the reference to the root has to be reset,
+ * this indicates, whether the root has already been printed out
+ */
+ printedOut: false,
+ internal: null,
+ isInitialized: false
+ };
+
+ var annotReferenceCallback = function annotReferenceCallback() {
+ for (var i in scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields) {
+ var formObject = scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields[i];
+ // add Annot Reference!
+ if (formObject.hasAnnotation) {
+ // If theres an Annotation Widget in the Form Object, put the Reference in the /Annot array
+ createAnnotationReference.call(scope, formObject);
+ }
+ }
+ };
+
+ var putForm = function putForm(formObject) {
+ if (scope.internal.acroformPlugin.printedOut) {
+ scope.internal.acroformPlugin.printedOut = false;
+ scope.internal.acroformPlugin.acroFormDictionaryRoot = null;
+ }
+ if (!scope.internal.acroformPlugin.acroFormDictionaryRoot) {
+ initializeAcroForm.call(scope);
+ }
+ scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields.push(formObject);
+ };
+ /**
+ * Create the Reference to the widgetAnnotation, so that it gets referenced in the Annot[] int the+
+ * (Requires the Annotation Plugin)
+ */
+ var createAnnotationReference = function createAnnotationReference(object) {
+ var options = {
+ type: 'reference',
+ object: object
+ };
+ scope.annotationPlugin.annotations[scope.internal.getPageInfo(object.page).pageNumber].push(options);
+ };
+
+ // Callbacks
+
+ var putCatalogCallback = function putCatalogCallback() {
+ //Put reference to AcroForm to DocumentCatalog
+ if (typeof scope.internal.acroformPlugin.acroFormDictionaryRoot != 'undefined') {
+ // for safety, shouldn't normally be the case
+ scope.internal.write('/AcroForm ' + scope.internal.acroformPlugin.acroFormDictionaryRoot.objId + ' ' + 0 + ' R');
+ } else {
+ console.log('Root missing...');
+ }
+ };
+
+ /**
+ * Adds /Acroform X 0 R to Document Catalog,
+ * and creates the AcroForm Dictionary
+ */
+ var AcroFormDictionaryCallback = function AcroFormDictionaryCallback() {
+ // Remove event
+ scope.internal.events.unsubscribe(scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID);
+
+ delete scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID;
+
+ scope.internal.acroformPlugin.printedOut = true;
+ };
+
+ /**
+ * Creates the single Fields and writes them into the Document
+ *
+ * If fieldArray is set, use the fields that are inside it instead of the fields from the AcroRoot
+ * (for the FormXObjects...)
+ */
+ var createFieldCallback = function createFieldCallback(fieldArray) {
+ var standardFields = !fieldArray;
+
+ if (!fieldArray) {
+ // in case there is no fieldArray specified, we want to print out the Fields of the AcroForm
+ // Print out Root
+ scope.internal.newObjectDeferredBegin(scope.internal.acroformPlugin.acroFormDictionaryRoot.objId);
+ scope.internal.out(scope.internal.acroformPlugin.acroFormDictionaryRoot.getString());
+ }
+
+ var fieldArray = fieldArray || scope.internal.acroformPlugin.acroFormDictionaryRoot.Kids;
+
+ for (var i in fieldArray) {
+ var form = fieldArray[i];
+
+ var oldRect = form.Rect;
+
+ if (form.Rect) {
+ form.Rect = calculateCoordinates.call(this, form.Rect);
+ }
+
+ // Start Writing the Object
+ scope.internal.newObjectDeferredBegin(form.objId);
+
+ var content = form.objId + " 0 obj\n<<\n";
+
+ if ((typeof form === 'undefined' ? 'undefined' : _typeof(form)) === "object" && typeof form.getContent === "function") {
+ content += form.getContent();
+ }
+
+ form.Rect = oldRect;
+
+ if (form.hasAppearanceStream && !form.appearanceStreamContent) {
+ // Calculate Appearance
+ var appearance = calculateAppearanceStream.call(this, form);
+ content += "/AP << /N " + appearance + " >>\n";
+
+ scope.internal.acroformPlugin.xForms.push(appearance);
+ }
+
+ // Assume AppearanceStreamContent is a Array with N,R,D (at least one of them!)
+ if (form.appearanceStreamContent) {
+ content += "/AP << ";
+ // Iterate over N,R and D
+ for (var k in form.appearanceStreamContent) {
+ var value = form.appearanceStreamContent[k];
+ content += "/" + k + " ";
+ content += "<< ";
+ if (Object.keys(value).length >= 1 || Array.isArray(value)) {
+ // appearanceStream is an Array or Object!
+ for (var i in value) {
+ var obj = value[i];
+ if (typeof obj === 'function') {
+ // if Function is referenced, call it in order to get the FormXObject
+ obj = obj.call(this, form);
+ }
+ content += "/" + i + " " + obj + " ";
+
+ // In case the XForm is already used, e.g. OffState of CheckBoxes, don't add it
+ if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0)) scope.internal.acroformPlugin.xForms.push(obj);
+ }
+ } else {
+ var obj = value;
+ if (typeof obj === 'function') {
+ // if Function is referenced, call it in order to get the FormXObject
+ obj = obj.call(this, form);
+ }
+ content += "/" + i + " " + obj + " \n";
+ if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0)) scope.internal.acroformPlugin.xForms.push(obj);
+ }
+ content += " >>\n";
+ }
+
+ // appearance stream is a normal Object..
+ content += ">>\n";
+ }
+
+ content += ">>\nendobj\n";
+
+ scope.internal.out(content);
+ }
+ if (standardFields) {
+ createXFormObjectCallback.call(this, scope.internal.acroformPlugin.xForms);
+ }
+ };
+
+ var createXFormObjectCallback = function createXFormObjectCallback(fieldArray) {
+ for (var i in fieldArray) {
+ var key = i;
+ var form = fieldArray[i];
+ // Start Writing the Object
+ scope.internal.newObjectDeferredBegin(form && form.objId);
+
+ var content = "";
+ if ((typeof form === 'undefined' ? 'undefined' : _typeof(form)) === "object" && typeof form.getString === "function") {
+ content = form.getString();
+ }
+ scope.internal.out(content);
+
+ delete fieldArray[key];
+ }
+ };
+ var initializeAcroForm = function initializeAcroForm() {
+ if (this.internal !== undefined && (this.internal.acroformPlugin === undefined || this.internal.acroformPlugin.isInitialized === false)) {
+
+ scope = this;
+
+ AcroFormField.FieldNum = 0;
+ this.internal.acroformPlugin = JSON.parse(JSON.stringify(acroformPluginTemplate));
+ if (this.internal.acroformPlugin.acroFormDictionaryRoot) {
+ //return;
+ throw new Error("Exception while creating AcroformDictionary");
+ }
+ scaleFactor = scope.internal.scaleFactor;
+ pageHeight = scope.internal.pageSize.getHeight();
+
+ // The Object Number of the AcroForm Dictionary
+ scope.internal.acroformPlugin.acroFormDictionaryRoot = new AcroFormDictionary();
+
+ // add Callback for creating the AcroForm Dictionary
+ scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID = scope.internal.events.subscribe('postPutResources', AcroFormDictionaryCallback);
+
+ scope.internal.events.subscribe('buildDocument', annotReferenceCallback); //buildDocument
+
+ // Register event, that is triggered when the DocumentCatalog is written, in order to add /AcroForm
+ scope.internal.events.subscribe('putCatalog', putCatalogCallback);
+
+ // Register event, that creates all Fields
+ scope.internal.events.subscribe('postPutPages', createFieldCallback);
+
+ scope.internal.acroformPlugin.isInitialized = true;
+ }
+ };
+
+ // ### Handy Functions:
+
+ var arrayToPdfArray = function arrayToPdfArray(array) {
+ if (Array.isArray(array)) {
+ var content = ' [';
+ for (var i in array) {
+ var element = array[i].toString();
+ content += element;
+ content += i < array.length - 1 ? ' ' : '';
+ }
+ content += ']';
+
+ return content;
+ }
+ };
+
+ var toPdfString = function toPdfString(string) {
+ string = string || "";
+
+ // put Bracket at the Beginning of the String
+ if (string.indexOf('(') !== 0) {
+ string = '(' + string;
+ }
+
+ if (string.substring(string.length - 1) != ')') {
+ string += ')';
+ }
+ return string;
+ };
+
+ // ##########################
+ // Classes
+ // ##########################
+
+ var AcroFormPDFObject = function AcroFormPDFObject() {
+ // The Object ID in the PDF Object Model
+ // todo
+ var _objId;
+ Object.defineProperty(this, 'objId', {
+ get: function get$$1() {
+ if (!_objId) {
+ _objId = scope.internal.newObjectDeferred();
+ }
+ if (!_objId) {
+ console.log("Couldn't create Object ID");
+ }
+ return _objId;
+ },
+ configurable: false
+ });
+ };
+
+ AcroFormPDFObject.prototype.toString = function () {
+ return this.objId + " 0 R";
+ };
+
+ AcroFormPDFObject.prototype.getString = function () {
+ var res = this.objId + " 0 obj\n<<";
+ var content = this.getContent();
+
+ res += content + ">>\n";
+ if (this.stream) {
+ res += "stream\n";
+ res += this.stream;
+ res += "\nendstream\n";
+ }
+ res += "endobj\n";
+ return res;
+ };
+
+ AcroFormPDFObject.prototype.getContent = function () {
+ /**
+ * Prints out all enumerable Variables from the Object
+ * @param fieldObject
+ * @returns {string}
+ */
+ var createContentFromFieldObject = function createContentFromFieldObject(fieldObject) {
+ var content = '';
+
+ var keys = Object.keys(fieldObject).filter(function (key) {
+ return key != 'content' && key != 'appearanceStreamContent' && key.substring(0, 1) != "_";
+ });
+
+ for (var i in keys) {
+ var key = keys[i];
+ var value = fieldObject[key];
+
+ /*if (key == 'Rect' && value) {
+ value = AcroForm.internal.calculateCoordinates.call(jsPDF.API.acroformPlugin.internal, value);
+ }*/
+
+ if (value) {
+ if (Array.isArray(value)) {
+ content += '/' + key + ' ' + arrayToPdfArray(value) + "\n";
+ } else if (value instanceof AcroFormPDFObject) {
+ // In case it is a reference to another PDFObject, take the referennce number
+ content += '/' + key + ' ' + value.objId + " 0 R" + "\n";
+ } else {
+ content += '/' + key + ' ' + value + '\n';
+ }
+ }
+ }
+ return content;
+ };
+
+ var object = "";
+
+ object += createContentFromFieldObject(this);
+ return object;
+ };
+
+ var AcroFormXObject = function AcroFormXObject() {
+ AcroFormPDFObject.call(this);
+ this.Type = "/XObject";
+ this.Subtype = "/Form";
+ this.FormType = 1;
+ this.BBox;
+ this.Matrix;
+ this.Resources = "2 0 R";
+ this.PieceInfo;
+ var _stream;
+ Object.defineProperty(this, 'Length', {
+ enumerable: true,
+ get: function get$$1() {
+ return _stream !== undefined ? _stream.length : 0;
+ }
+ });
+ Object.defineProperty(this, 'stream', {
+ enumerable: false,
+ set: function set$$1(val) {
+ _stream = val.trim();
+ },
+ get: function get$$1() {
+ if (_stream) {
+ return _stream;
+ } else {
+ return null;
+ }
+ }
+ });
+ };
+
+ inherit(AcroFormXObject, AcroFormPDFObject);
+ // ##### The Objects, the User can Create:
+
+ var AcroFormDictionary = function AcroFormDictionary() {
+ AcroFormPDFObject.call(this);
+ var _Kids = [];
+ Object.defineProperty(this, 'Kids', {
+ enumerable: false,
+ configurable: true,
+ get: function get$$1() {
+ if (_Kids.length > 0) {
+ return _Kids;
+ } else {
+ return;
+ }
+ }
+ });
+ Object.defineProperty(this, 'Fields', {
+ enumerable: true,
+ configurable: true,
+ get: function get$$1() {
+ return _Kids;
+ }
+ });
+ // Default Appearance
+ this.DA;
+ };
+
+ inherit(AcroFormDictionary, AcroFormPDFObject);
+
+ // The Field Object contains the Variables, that every Field needs
+ // Rectangle for Appearance: lower_left_X, lower_left_Y, width, height
+ var AcroFormField = function AcroFormField() {
+
+ AcroFormPDFObject.call(this);
+
+ var _Rect;
+ Object.defineProperty(this, 'Rect', {
+ enumerable: true,
+ configurable: false,
+ get: function get$$1() {
+ if (!_Rect) {
+ return;
+ }
+ var tmp = _Rect;
+ //var calculatedRes = AcroForm.internal.calculateCoordinates(_Rect); // do later!
+ return tmp;
+ },
+ set: function set$$1(val) {
+ _Rect = val;
+ }
+ });
+
+ var _FT = "";
+ Object.defineProperty(this, 'FT', {
+ enumerable: true,
+ set: function set$$1(val) {
+ _FT = val;
+ },
+ get: function get$$1() {
+ return _FT;
+ }
+ });
+ /**
+ * The Partial name of the Field Object.
+ * It has to be unique.
+ */
+ var _T;
+
+ Object.defineProperty(this, 'T', {
+ enumerable: true,
+ configurable: false,
+ set: function set$$1(val) {
+ _T = val;
+ },
+ get: function get$$1() {
+ if (!_T || _T.length < 1) {
+ if (this instanceof AcroFormChildClass) {
+ // In case of a Child from a Radio´Group, you don't need a FieldName!!!
+ return;
+ }
+ return "(FieldObject" + AcroFormField.FieldNum++ + ")";
+ }
+ if (_T.substring(0, 1) == "(" && _T.substring(_T.length - 1)) {
+ return _T;
+ }
+ return "(" + _T + ")";
+ }
+ });
+
+ var _DA;
+ // Defines the default appearance (Needed for variable Text)
+ Object.defineProperty(this, 'DA', {
+ enumerable: true,
+ get: function get$$1() {
+ if (!_DA) {
+ return;
+ }
+ return '(' + _DA + ')';
+ },
+ set: function set$$1(val) {
+ _DA = val;
+ }
+ });
+
+ var _DV;
+ // Defines the default value
+ Object.defineProperty(this, 'DV', {
+ enumerable: true,
+ configurable: true,
+ get: function get$$1() {
+ if (!_DV) {
+ return;
+ }
+ return _DV;
+ },
+ set: function set$$1(val) {
+ _DV = val;
+ }
+ });
+
+ var _V;
+ // Defines the default value
+ Object.defineProperty(this, 'V', {
+ enumerable: true,
+ configurable: true,
+ get: function get$$1() {
+ if (!_V) {
+ return;
+ }
+ return _V;
+ },
+ set: function set$$1(val) {
+ _V = val;
+ }
+ });
+
+ //this.Type = "/Annot";
+ //this.Subtype = "/Widget";
+ Object.defineProperty(this, 'Type', {
+ enumerable: true,
+ get: function get$$1() {
+ return this.hasAnnotation ? "/Annot" : null;
+ }
+ });
+
+ Object.defineProperty(this, 'Subtype', {
+ enumerable: true,
+ get: function get$$1() {
+ return this.hasAnnotation ? "/Widget" : null;
+ }
+ });
+
+ /**
+ *
+ * @type {Array}
+ */
+ this.BG;
+
+ Object.defineProperty(this, 'hasAnnotation', {
+ enumerable: false,
+ get: function get$$1() {
+ if (this.Rect || this.BC || this.BG) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Object.defineProperty(this, 'hasAppearanceStream', {
+ enumerable: false,
+ configurable: true,
+ writable: true
+ });
+
+ Object.defineProperty(this, 'page', {
+ enumerable: false,
+ configurable: true,
+ writable: true
+ });
+ };
+
+ inherit(AcroFormField, AcroFormPDFObject);
+
+ var AcroFormChoiceField = function AcroFormChoiceField() {
+ AcroFormField.call(this);
+ // Field Type = Choice Field
+ this.FT = "/Ch";
+ // options
+ this.Opt = [];
+ this.V = '()';
+ // Top Index
+ this.TI = 0;
+ /**
+ * Defines, whether the
+ * @type {boolean}
+ */
+
+ var _combo = false;
+
+ Object.defineProperty(this, 'combo', {
+ enumerable: false,
+ get: function get$$1() {
+ return _combo;
+ },
+ set: function set$$1(val) {
+ _combo = val;
+ }
+ });
+ /**
+ * Defines, whether the Choice Field is an Edit Field.
+ * An Edit Field is automatically an Combo Field.
+ */
+ Object.defineProperty(this, 'edit', {
+ enumerable: true,
+ set: function set$$1(val) {
+ if (val == true) {
+ this._edit = true;
+ // ComboBox has to be true
+ this.combo = true;
+ } else {
+ this._edit = false;
+ }
+ },
+ get: function get$$1() {
+ if (!this._edit) {
+ return false;
+ }
+ return this._edit;
+ },
+ configurable: false
+ });
+ this.hasAppearanceStream = true;
+ };
+ inherit(AcroFormChoiceField, AcroFormField);
+
+ var AcroFormListBox = function AcroFormListBox() {
+ AcroFormChoiceField.call(this);
+ this.combo = false;
+ };
+ inherit(AcroFormListBox, AcroFormChoiceField);
+
+ var AcroFormComboBox = function AcroFormComboBox() {
+ AcroFormListBox.call(this);
+ this.combo = true;
+ };
+ inherit(AcroFormComboBox, AcroFormListBox);
+
+ var AcroFormEditBox = function AcroFormEditBox() {
+ AcroFormComboBox.call(this);
+ this.edit = true;
+ };
+ inherit(AcroFormEditBox, AcroFormComboBox);
+
+ var AcroFormButton = function AcroFormButton() {
+ AcroFormField.call(this);
+ this.FT = "/Btn";
+ //this.hasAnnotation = true;
+ };
+ inherit(AcroFormButton, AcroFormField);
+
+ var AcroFormPushButton = function AcroFormPushButton() {
+ AcroFormButton.call(this);
+
+ var _pushbutton = true;
+ Object.defineProperty(this, 'pushbutton', {
+ enumerable: false,
+ get: function get$$1() {
+ return _pushbutton;
+ },
+ set: function set$$1(val) {
+ _pushbutton = val;
+ }
+ });
+ };
+ inherit(AcroFormPushButton, AcroFormButton);
+
+ var AcroFormRadioButton = function AcroFormRadioButton() {
+ AcroFormButton.call(this);
+
+ var _radio = true;
+ Object.defineProperty(this, 'radio', {
+ enumerable: false,
+ get: function get$$1() {
+ return _radio;
+ },
+ set: function set$$1(val) {
+ _radio = val;
+ }
+ });
+
+ var _Kids = [];
+ Object.defineProperty(this, 'Kids', {
+ enumerable: true,
+ get: function get$$1() {
+ if (_Kids.length > 0) {
+ return _Kids;
+ }
+ }
+ });
+
+ Object.defineProperty(this, '__Kids', {
+ get: function get$$1() {
+ return _Kids;
+ }
+ });
+
+ var _noToggleToOff;
+
+ Object.defineProperty(this, 'noToggleToOff', {
+ enumerable: false,
+ get: function get$$1() {
+ return _noToggleToOff;
+ },
+ set: function set$$1(val) {
+ _noToggleToOff = val;
+ }
+ });
+
+ //this.hasAnnotation = false;
+ };
+ inherit(AcroFormRadioButton, AcroFormButton);
+
+ /*
+ * The Child classs of a RadioButton (the radioGroup)
+ * -> The single Buttons
+ */
+ var AcroFormChildClass = function AcroFormChildClass(parent, name) {
+ AcroFormField.call(this);
+ this.Parent = parent;
+
+ // todo: set AppearanceType as variable that can be set from the outside...
+ this._AppearanceType = AcroFormAppearance.RadioButton.Circle; // The Default appearanceType is the Circle
+ this.appearanceStreamContent = this._AppearanceType.createAppearanceStream(name);
+
+ // Set Print in the Annot Flag
+ this.F = setBitPosition(this.F, 3, 1);
+
+ // Set AppearanceCharacteristicsDictionary with default appearance if field is not interacting with user
+ this.MK = this._AppearanceType.createMK(); // (8) -> Cross, (1)-> Circle, ()-> nothing
+
+ // Default Appearance is Off
+ this.AS = "/Off"; // + name;
+
+ this._Name = name;
+ };
+ inherit(AcroFormChildClass, AcroFormField);
+
+ AcroFormRadioButton.prototype.setAppearance = function (appearance) {
+ if (!('createAppearanceStream' in appearance && 'createMK' in appearance)) {
+ console.log("Couldn't assign Appearance to RadioButton. Appearance was Invalid!");
+ return;
+ }
+ for (var i in this.__Kids) {
+ var child = this.__Kids[i];
+
+ child.appearanceStreamContent = appearance.createAppearanceStream(child._Name);
+ child.MK = appearance.createMK();
+ }
+ };
+
+ AcroFormRadioButton.prototype.createOption = function (name) {
+ var parent = this;
+ var kidCount = this.__Kids.length;
+
+ // Create new Child for RadioGroup
+ var child = new AcroFormChildClass(parent, name);
+ // Add to Parent
+ this.__Kids.push(child);
+
+ jsPDFAPI.addField(child);
+
+ return child;
+ };
+
+ var AcroFormCheckBox = function AcroFormCheckBox() {
+ AcroFormButton.call(this);
+ this.appearanceStreamContent = AcroFormAppearance.CheckBox.createAppearanceStream();
+ this.MK = AcroFormAppearance.CheckBox.createMK();
+ this.AS = "/On";
+ this.V = "/On";
+ };
+ inherit(AcroFormCheckBox, AcroFormButton);
+
+ var AcroFormTextField = function AcroFormTextField() {
+ AcroFormField.call(this);
+ this.DA = AcroFormAppearance.createDefaultAppearanceStream();
+ this.F = 4;
+ var _V;
+ Object.defineProperty(this, 'V', {
+ get: function get$$1() {
+ if (_V) {
+ return toPdfString(_V);
+ } else {
+ return _V;
+ }
+ },
+ enumerable: true,
+ set: function set$$1(val) {
+ _V = val;
+ }
+ });
+
+ var _DV;
+ Object.defineProperty(this, 'DV', {
+ get: function get$$1() {
+ if (_DV) {
+ return toPdfString(_DV);
+ } else {
+ return _DV;
+ }
+ },
+ enumerable: true,
+ set: function set$$1(val) {
+ _DV = val;
+ }
+ });
+
+ var _multiline = false;
+ Object.defineProperty(this, 'multiline', {
+ enumerable: false,
+ get: function get$$1() {
+ return _multiline;
+ },
+ set: function set$$1(val) {
+ _multiline = val;
+ }
+ });
+
+ /**
+ * For PDF 1.4
+ * @type {boolean}
+ */
+ var _fileSelect = false;
+ Object.defineProperty(this, 'fileSelect', {
+ enumerable: false,
+ get: function get$$1() {
+ return _fileSelect;
+ },
+ set: function set$$1(val) {
+ _fileSelect = val;
+ }
+ });
+ /**
+ * For PDF 1.4
+ * @type {boolean}
+ */
+ var _doNotSpellCheck = false;
+ Object.defineProperty(this, 'doNotSpellCheck', {
+ enumerable: false,
+ get: function get$$1() {
+ return _doNotSpellCheck;
+ },
+ set: function set$$1(val) {
+ _doNotSpellCheck = val;
+ }
+ });
+ /**
+ * For PDF 1.4
+ * @type {boolean}
+ */
+ var _doNotScroll = false;
+ Object.defineProperty(this, 'doNotScroll', {
+ enumerable: false,
+ get: function get$$1() {
+ return _doNotScroll;
+ },
+ set: function set$$1(val) {
+ _doNotScroll = val;
+ }
+ });
+
+ var _MaxLen = false;
+ Object.defineProperty(this, 'MaxLen', {
+ enumerable: true,
+ get: function get$$1() {
+ return _MaxLen;
+ },
+ set: function set$$1(val) {
+ _MaxLen = val;
+ }
+ });
+
+ Object.defineProperty(this, 'hasAppearanceStream', {
+ enumerable: false,
+ get: function get$$1() {
+ return this.V || this.DV;
+ }
+ });
+ };
+ inherit(AcroFormTextField, AcroFormField);
+
+ var AcroFormPasswordField = function AcroFormPasswordField() {
+ AcroFormTextField.call(this);
+
+ var _password = true;
+ Object.defineProperty(this, 'password', {
+ enumerable: false,
+ get: function get$$1() {
+ return _password;
+ },
+ set: function set$$1(val) {
+ _password = val;
+ }
+ });
+ };
+ inherit(AcroFormPasswordField, AcroFormTextField);
+
+ // Contains Methods for creating standard appearances
+ var AcroFormAppearance = {
+ CheckBox: {
+ createAppearanceStream: function createAppearanceStream() {
+ var appearance = {
+ N: {
+ On: AcroFormAppearance.CheckBox.YesNormal
+ },
+ D: {
+ On: AcroFormAppearance.CheckBox.YesPushDown,
+ Off: AcroFormAppearance.CheckBox.OffPushDown
+ }
+ };
+
+ return appearance;
+ },
+ /**
+ * If any other icons are needed, the number between the brackets can be changed
+ * @returns {string}
+ */
+ createMK: function createMK() {
+ return "<< /CA (3)>>";
+ },
+ /**
+ * Returns the standard On Appearance for a CheckBox
+ * @returns {AcroFormXObject}
+ */
+ YesPushDown: function YesPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ var zapfDingbatsId = scope.internal.getFont("zapfdingbats", "normal").id;
+ formObject.Q = 1; // set text-alignment as centered
+ var calcRes = calculateX(formObject, "3", "ZapfDingbats", 50);
+ stream.push("0.749023 g");
+ stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re");
+ stream.push("f");
+ stream.push("BMC");
+ stream.push("q");
+ stream.push("0 0 1 rg");
+ stream.push("/" + zapfDingbatsId + " " + calcRes.fontSize.toFixed(2) + " Tf 0 g");
+ stream.push("BT");
+ stream.push(calcRes.text);
+ stream.push("ET");
+ stream.push("Q");
+ stream.push("EMC");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ },
+
+ YesNormal: function YesNormal(formObject) {
+ var xobj = createFormXObject(formObject);
+ var zapfDingbatsId = scope.internal.getFont("zapfdingbats", "normal").id;
+ var stream = [];
+ formObject.Q = 1; // set text-alignment as centered
+ var height = AcroFormAppearance.internal.getHeight(formObject);
+ var width = AcroFormAppearance.internal.getWidth(formObject);
+ var calcRes = calculateX(formObject, "3", "ZapfDingbats", height * 0.9);
+ stream.push("1 g");
+ stream.push("0 0 " + width.toFixed(2) + " " + height.toFixed(2) + " re");
+ stream.push("f");
+ stream.push("q");
+ stream.push("0 0 1 rg");
+ stream.push("0 0 " + (width - 1).toFixed(2) + " " + (height - 1).toFixed(2) + " re");
+ stream.push("W");
+ stream.push("n");
+ stream.push("0 g");
+ stream.push("BT");
+ stream.push("/" + zapfDingbatsId + " " + calcRes.fontSize.toFixed(2) + " Tf 0 g");
+ stream.push(calcRes.text);
+ stream.push("ET");
+ stream.push("Q");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ },
+
+ /**
+ * Returns the standard Off Appearance for a CheckBox
+ * @returns {AcroFormXObject}
+ */
+ OffPushDown: function OffPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ stream.push("0.749023 g");
+ stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re");
+ stream.push("f");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ }
+ },
+
+ RadioButton: {
+ Circle: {
+ createAppearanceStream: function createAppearanceStream(name) {
+ var appearanceStreamContent = {
+ D: {
+ 'Off': AcroFormAppearance.RadioButton.Circle.OffPushDown
+ },
+ N: {}
+ };
+ appearanceStreamContent.N[name] = AcroFormAppearance.RadioButton.Circle.YesNormal;
+ appearanceStreamContent.D[name] = AcroFormAppearance.RadioButton.Circle.YesPushDown;
+ return appearanceStreamContent;
+ },
+ createMK: function createMK() {
+ return "<< /CA (l)>>";
+ },
+
+ YesNormal: function YesNormal(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ // Make the Radius of the Circle relative to min(height, width) of formObject
+ var DotRadius = AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4;
+ // The Borderpadding...
+ DotRadius *= 0.9;
+ var c = AcroFormAppearance.internal.Bezier_C;
+ /*
+ The Following is a Circle created with Bezier-Curves.
+ */
+ stream.push("q");
+ stream.push("1 0 0 1 " + AcroFormAppearance.internal.getWidth(formObject) / 2 + " " + AcroFormAppearance.internal.getHeight(formObject) / 2 + " cm");
+ stream.push(DotRadius + " 0 m");
+ stream.push(DotRadius + " " + DotRadius * c + " " + DotRadius * c + " " + DotRadius + " 0 " + DotRadius + " c");
+ stream.push("-" + DotRadius * c + " " + DotRadius + " -" + DotRadius + " " + DotRadius * c + " -" + DotRadius + " 0 c");
+ stream.push("-" + DotRadius + " -" + DotRadius * c + " -" + DotRadius * c + " -" + DotRadius + " 0 -" + DotRadius + " c");
+ stream.push(DotRadius * c + " -" + DotRadius + " " + DotRadius + " -" + DotRadius * c + " " + DotRadius + " 0 c");
+ stream.push("f");
+ stream.push("Q");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ },
+ YesPushDown: function YesPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ var DotRadius = AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4;
+ // The Borderpadding...
+ DotRadius *= 0.9;
+ // Save results for later use; no need to waste processor ticks on doing math
+ var k = DotRadius * 2;
+ // var c = AcroFormAppearance.internal.Bezier_C;
+ var kc = k * AcroFormAppearance.internal.Bezier_C;
+ var dc = DotRadius * AcroFormAppearance.internal.Bezier_C;
+
+ stream.push("0.749023 g");
+ stream.push("q");
+ stream.push("1 0 0 1 " + (AcroFormAppearance.internal.getWidth(formObject) / 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) / 2).toFixed(2) + " cm");
+ stream.push(k + " 0 m");
+ stream.push(k + " " + kc + " " + kc + " " + k + " 0 " + k + " c");
+ stream.push("-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c");
+ stream.push("-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c");
+ stream.push(kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c");
+ stream.push("f");
+ stream.push("Q");
+ stream.push("0 g");
+ stream.push("q");
+ stream.push("1 0 0 1 " + (AcroFormAppearance.internal.getWidth(formObject) / 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) / 2).toFixed(2) + " cm");
+ stream.push(DotRadius + " 0 m");
+ stream.push("" + DotRadius + " " + dc + " " + dc + " " + DotRadius + " 0 " + DotRadius + " c");
+ stream.push("-" + dc + " " + DotRadius + " -" + DotRadius + " " + dc + " -" + DotRadius + " 0 c");
+ stream.push("-" + DotRadius + " -" + dc + " -" + dc + " -" + DotRadius + " 0 -" + DotRadius + " c");
+ stream.push(dc + " -" + DotRadius + " " + DotRadius + " -" + dc + " " + DotRadius + " 0 c");
+ stream.push("f");
+ stream.push("Q");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ },
+ OffPushDown: function OffPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ var DotRadius = AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4;
+ // The Borderpadding...
+ DotRadius *= 0.9;
+ // Save results for later use; no need to waste processor ticks on doing math
+ var k = DotRadius * 2;
+ // var c = AcroFormAppearance.internal.Bezier_C;
+ var kc = k * AcroFormAppearance.internal.Bezier_C;
+
+ stream.push("0.749023 g");
+ stream.push("q");
+ stream.push("1 0 0 1 " + (AcroFormAppearance.internal.getWidth(formObject) / 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) / 2).toFixed(2) + " cm");
+ stream.push(k + " 0 m");
+ stream.push(k + " " + kc + " " + kc + " " + k + " 0 " + k + " c");
+ stream.push("-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c");
+ stream.push("-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c");
+ stream.push(kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c");
+ stream.push("f");
+ stream.push("Q");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ }
+ },
+
+ Cross: {
+ /**
+ * Creates the Actual AppearanceDictionary-References
+ * @param name
+ * @returns
+ */
+ createAppearanceStream: function createAppearanceStream(name) {
+ var appearanceStreamContent = {
+ D: {
+ 'Off': AcroFormAppearance.RadioButton.Cross.OffPushDown
+ },
+ N: {}
+ };
+ appearanceStreamContent.N[name] = AcroFormAppearance.RadioButton.Cross.YesNormal;
+ appearanceStreamContent.D[name] = AcroFormAppearance.RadioButton.Cross.YesPushDown;
+ return appearanceStreamContent;
+ },
+ createMK: function createMK() {
+ return "<< /CA (8)>>";
+ },
+
+ YesNormal: function YesNormal(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ var cross = AcroFormAppearance.internal.calculateCross(formObject);
+ stream.push("q");
+ stream.push("1 1 " + (AcroFormAppearance.internal.getWidth(formObject) - 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) - 2).toFixed(2) + " re");
+ stream.push("W");
+ stream.push("n");
+ stream.push(cross.x1.x.toFixed(2) + " " + cross.x1.y.toFixed(2) + " m");
+ stream.push(cross.x2.x.toFixed(2) + " " + cross.x2.y.toFixed(2) + " l");
+ stream.push(cross.x4.x.toFixed(2) + " " + cross.x4.y.toFixed(2) + " m");
+ stream.push(cross.x3.x.toFixed(2) + " " + cross.x3.y.toFixed(2) + " l");
+ stream.push("s");
+ stream.push("Q");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ },
+ YesPushDown: function YesPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var cross = AcroFormAppearance.internal.calculateCross(formObject);
+ var stream = [];
+ stream.push("0.749023 g");
+ stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re");
+ stream.push("f");
+ stream.push("q");
+ stream.push("1 1 " + (AcroFormAppearance.internal.getWidth(formObject) - 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) - 2).toFixed(2) + " re");
+ stream.push("W");
+ stream.push("n");
+ stream.push(cross.x1.x.toFixed(2) + " " + cross.x1.y.toFixed(2) + " m");
+ stream.push(cross.x2.x.toFixed(2) + " " + cross.x2.y.toFixed(2) + " l");
+ stream.push(cross.x4.x.toFixed(2) + " " + cross.x4.y.toFixed(2) + " m");
+ stream.push(cross.x3.x.toFixed(2) + " " + cross.x3.y.toFixed(2) + " l");
+ stream.push("s");
+ stream.push("Q");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ },
+ OffPushDown: function OffPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ stream.push("0.749023 g");
+ stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re");
+ stream.push("f");
+ xobj.stream = stream.join("\n");
+ return xobj;
+ }
+ }
+ },
+
+ /**
+ * Returns the standard Appearance
+ * @returns {AcroFormXObject}
+ */
+ createDefaultAppearanceStream: function createDefaultAppearanceStream(formObject) {
+ // Set Helvetica to Standard Font (size: auto)
+ // Color: Black
+ return "/F1 0 Tf 0 g";
+ }
+ };
+
+ AcroFormAppearance.internal = {
+ Bezier_C: 0.551915024494,
+
+ calculateCross: function calculateCross(formObject) {
+ var min = function min(x, y) {
+ return x > y ? y : x;
+ };
+
+ var width = AcroFormAppearance.internal.getWidth(formObject);
+ var height = AcroFormAppearance.internal.getHeight(formObject);
+ var a = min(width, height);
+
+
+ var cross = {
+ x1: { // upperLeft
+ x: (width - a) / 2,
+ y: (height - a) / 2 + a //height - borderPadding
+ },
+ x2: { // lowerRight
+ x: (width - a) / 2 + a,
+ y: (height - a) / 2 //borderPadding
+ },
+ x3: { // lowerLeft
+ x: (width - a) / 2,
+ y: (height - a) / 2 //borderPadding
+ },
+ x4: { // upperRight
+ x: (width - a) / 2 + a,
+ y: (height - a) / 2 + a //height - borderPadding
+ }
+ };
+
+ return cross;
+ }
+ };
+ AcroFormAppearance.internal.getWidth = function (formObject) {
+ var result = 0;
+ if ((typeof formObject === 'undefined' ? 'undefined' : _typeof(formObject)) === "object") {
+ result = scale(formObject.Rect[2]); //(formObject.Rect[2] - formObject.Rect[0]) || 0;
+ }
+ return result;
+ };
+ AcroFormAppearance.internal.getHeight = function (formObject) {
+ var result = 0;
+ if ((typeof formObject === 'undefined' ? 'undefined' : _typeof(formObject)) === "object") {
+ result = scale(formObject.Rect[3]); //(formObject.Rect[1] - formObject.Rect[3]) || 0;
+ }
+ return result;
+ };
+
+ // Public:
+
+ jsPDFAPI.addField = function (fieldObject) {
+ initializeAcroForm.call(this);
+ //var opt = parseOptions(fieldObject);
+ if (fieldObject instanceof AcroFormTextField) {
+ this.addTextField.call(this, fieldObject);
+ } else if (fieldObject instanceof AcroFormChoiceField) {
+ this.addChoiceField.call(this, fieldObject);
+ } else if (fieldObject instanceof AcroFormButton) {
+ this.addButton.call(this, fieldObject);
+ } else if (fieldObject instanceof AcroFormChildClass) {
+ putForm.call(this, fieldObject);
+ } else if (fieldObject) {
+ // try to put..
+ putForm.call(this, fieldObject);
+ }
+ fieldObject.page = scope.internal.getCurrentPageInfo().pageNumber;
+ return this;
+ };
+
+ /**
+ * Button
+ * FT = Btn
+ */
+ jsPDFAPI.addButton = function (opts) {
+ initializeAcroForm.call(this);
+ var options = opts || new AcroFormField();
+
+ options.FT = '/Btn';
+ options.Ff = calculateFlagsOnOptions(options.Ff, opts, scope.internal.getPDFVersion());
+
+ putForm.call(this, options);
+ };
+
+ jsPDFAPI.addTextField = function (opts) {
+ initializeAcroForm.call(this);
+ var options = opts || new AcroFormField();
+
+ options.FT = '/Tx';
+
+ options.Ff = calculateFlagsOnOptions(options.Ff, opts, scope.internal.getPDFVersion());
+
+ // Add field
+ putForm.call(this, options);
+ };
+
+ jsPDFAPI.addChoiceField = function (opts) {
+ initializeAcroForm.call(this);
+ var options = opts || new AcroFormField();
+
+ options.FT = '/Ch';
+
+ options.Ff = calculateFlagsOnOptions(options.Ff, opts, scope.internal.getPDFVersion());
+ //options.hasAnnotation = true;
+
+ // Add field
+ putForm.call(this, options);
+ };
+
+ if ((typeof globalObj === 'undefined' ? 'undefined' : _typeof(globalObj)) == "object") {
+ globalObj["ChoiceField"] = AcroFormChoiceField;
+ globalObj["ListBox"] = AcroFormListBox;
+ globalObj["ComboBox"] = AcroFormComboBox;
+ globalObj["EditBox"] = AcroFormEditBox;
+ globalObj["Button"] = AcroFormButton;
+ globalObj["PushButton"] = AcroFormPushButton;
+ globalObj["RadioButton"] = AcroFormRadioButton;
+ globalObj["CheckBox"] = AcroFormCheckBox;
+ globalObj["TextField"] = AcroFormTextField;
+ globalObj["PasswordField"] = AcroFormPasswordField;
+
+ //backwardsCompatibility
+ globalObj["AcroForm"] = { Appearance: AcroFormAppearance };
+ }
+ })(jsPDF.API, typeof window !== "undefined" && window || typeof global !== "undefined" && global);
+
+ /**
+ * jsPDF addHTML PlugIn
+ * Copyright (c) 2014 Diego Casorran
+ *
+ * Licensed under the MIT License.
+ * http://opensource.org/licenses/mit-license
+ */
+
+ (function (jsPDFAPI) {
+
+ /**
+ * Renders an HTML element to canvas object which added to the PDF
+ *
+ * This feature requires [html2canvas](https://github.com/niklasvh/html2canvas)
+ * or [rasterizeHTML](https://github.com/cburgmer/rasterizeHTML.js)
+ *
+ * @returns {jsPDF}
+ * @name addHTML
+ * @param element {Mixed} HTML Element, or anything supported by html2canvas.
+ * @param x {Number} starting X coordinate in jsPDF instance's declared units.
+ * @param y {Number} starting Y coordinate in jsPDF instance's declared units.
+ * @param options {Object} Additional options, check the code below.
+ * @param callback {Function} to call when the rendering has finished.
+ * NOTE: Every parameter is optional except 'element' and 'callback', in such
+ * case the image is positioned at 0x0 covering the whole PDF document
+ * size. Ie, to easily take screenshots of webpages saving them to PDF.
+ * @deprecated This is being replace with a vector-supporting API. See
+ * [this link](https://cdn.rawgit.com/MrRio/jsPDF/master/examples/html2pdf/showcase_supported_html.html)
+ */
+
+ jsPDFAPI.addHTML = function (element, x, y, options, callback) {
+
+ if (typeof html2canvas === 'undefined' && typeof rasterizeHTML === 'undefined') throw new Error('You need either ' + 'https://github.com/niklasvh/html2canvas' + ' or https://github.com/cburgmer/rasterizeHTML.js');
+
+ if (typeof x !== 'number') {
+ options = x;
+ callback = y;
+ }
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ if (typeof callback !== 'function') {
+ callback = function callback() {};
+ }
+
+ var I = this.internal,
+ K = I.scaleFactor,
+ W = I.pageSize.getWidth(),
+ H = I.pageSize.getHeight();
+
+ options = options || {};
+ options.onrendered = function (obj) {
+ x = parseInt(x) || 0;
+ y = parseInt(y) || 0;
+ var dim = options.dim || {};
+ var margin = Object.assign({ top: 0, right: 0, bottom: 0, left: 0, useFor: 'content' }, options.margin);
+ var h = dim.h || Math.min(H, obj.height / K);
+ var w = dim.w || Math.min(W, obj.width / K) - x;
+
+ var format = options.format || 'JPEG';
+ var imageCompression = options.imageCompression || 'SLOW';
+
+ var notFittingHeight = obj.height > H - margin.top - margin.bottom;
+
+ if (notFittingHeight && options.pagesplit) {
+ var cropArea = function cropArea(parmObj, parmX, parmY, parmWidth, parmHeight) {
+ var canvas = document.createElement('canvas');
+ canvas.height = parmHeight;
+ canvas.width = parmWidth;
+ var ctx = canvas.getContext('2d');
+ ctx.mozImageSmoothingEnabled = false;
+ ctx.webkitImageSmoothingEnabled = false;
+ ctx.msImageSmoothingEnabled = false;
+ ctx.imageSmoothingEnabled = false;
+ ctx.fillStyle = options.backgroundColor || '#ffffff';
+ ctx.fillRect(0, 0, parmWidth, parmHeight);
+ ctx.drawImage(parmObj, parmX, parmY, parmWidth, parmHeight, 0, 0, parmWidth, parmHeight);
+ return canvas;
+ };
+ var crop = function () {
+ var cy = 0;
+ var cx = 0;
+ var position = {};
+ var isOverWide = false;
+ var width;
+ var height;
+ while (1) {
+ cx = 0;
+ position.top = cy !== 0 ? margin.top : y;
+ position.left = cy !== 0 ? margin.left : x;
+ isOverWide = (W - margin.left - margin.right) * K < obj.width;
+ if (margin.useFor === "content") {
+ if (cy === 0) {
+ width = Math.min((W - margin.left) * K, obj.width);
+ height = Math.min((H - margin.top) * K, obj.height - cy);
+ } else {
+ width = Math.min(W * K, obj.width);
+ height = Math.min(H * K, obj.height - cy);
+ position.top = 0;
+ }
+ } else {
+ width = Math.min((W - margin.left - margin.right) * K, obj.width);
+ height = Math.min((H - margin.bottom - margin.top) * K, obj.height - cy);
+ }
+ if (isOverWide) {
+ while (1) {
+ if (margin.useFor === "content") {
+ if (cx === 0) {
+ width = Math.min((W - margin.left) * K, obj.width);
+ } else {
+ width = Math.min(W * K, obj.width - cx);
+ position.left = 0;
+ }
+ }
+ var canvas = cropArea(obj, cx, cy, width, height);
+ var args = [canvas, position.left, position.top, canvas.width / K, canvas.height / K, format, null, imageCompression];
+ this.addImage.apply(this, args);
+ cx += width;
+ if (cx >= obj.width) {
+ break;
+ }
+ this.addPage();
+ }
+ } else {
+ var canvas = cropArea(obj, 0, cy, width, height);
+ var args = [canvas, position.left, position.top, canvas.width / K, canvas.height / K, format, null, imageCompression];
+ this.addImage.apply(this, args);
+ }
+ cy += height;
+ if (cy >= obj.height) {
+ break;
+ }
+ this.addPage();
+ }
+ callback(w, cy, null, args);
+ }.bind(this);
+ if (obj.nodeName === 'CANVAS') {
+ var img = new Image();
+ img.onload = crop;
+ img.src = obj.toDataURL("image/png");
+ obj = img;
+ } else {
+ crop();
+ }
+ } else {
+ var alias = Math.random().toString(35);
+ var args = [obj, x, y, w, h, format, alias, imageCompression];
+
+ this.addImage.apply(this, args);
+
+ callback(w, h, alias, args);
+ }
+ }.bind(this);
+
+ if (typeof html2canvas !== 'undefined' && !options.rstz) {
+ return html2canvas(element, options);
+ }
+
+ if (typeof rasterizeHTML !== 'undefined') {
+ var meth = 'drawDocument';
+ if (typeof element === 'string') {
+ meth = /^http/.test(element) ? 'drawURL' : 'drawHTML';
+ }
+ options.width = options.width || W * K;
+ return rasterizeHTML[meth](element, void 0, options).then(function (r) {
+ options.onrendered(r.image);
+ }, function (e) {
+ callback(null, e);
+ });
+ }
+
+ return null;
+ };
+ })(jsPDF.API);
+
+ /** @preserve
+ * jsPDF addImage plugin
+ * Copyright (c) 2012 Jason Siefken, https://github.com/siefkenj/
+ * 2013 Chris Dowling, https://github.com/gingerchris
+ * 2013 Trinh Ho, https://github.com/ineedfat
+ * 2013 Edwin Alejandro Perez, https://github.com/eaparango
+ * 2013 Norah Smith, https://github.com/burnburnrocket
+ * 2014 Diego Casorran, https://github.com/diegocr
+ * 2014 James Robb, https://github.com/jamesbrobb
+ *
+ *
+ */
+ (function (jsPDFAPI) {
+
+ var namespace = 'addImage_';
+
+ var imageFileTypeHeaders = {
+ PNG: [[0x89, 0x50, 0x4e, 0x47]],
+ TIFF: [[0x4D, 0x4D, 0x00, 0x2A], //Motorola
+ [0x49, 0x49, 0x2A, 0x00] //Intel
+ ],
+ JPEG: [[0xFF, 0xD8, 0xFF, 0xE0, undefined, undefined, 0x4A, 0x46, 0x49, 0x46, 0x00], //JFIF
+ [0xFF, 0xD8, 0xFF, 0xE1, undefined, undefined, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00] //Exif
+ ],
+ JPEG2000: [[0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20]],
+ GIF87a: [[0x47, 0x49, 0x46, 0x38, 0x37, 0x61]],
+ GIF89a: [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61]],
+ BMP: [[0x42, 0x4D], //BM - Windows 3.1x, 95, NT, ... etc.
+ [0x42, 0x41], //BA - OS/2 struct bitmap array
+ [0x43, 0x49], //CI - OS/2 struct color icon
+ [0x43, 0x50], //CP - OS/2 const color pointer
+ [0x49, 0x43], //IC - OS/2 struct icon
+ [0x50, 0x54] //PT - OS/2 pointer
+ ]
+ };
+
+ jsPDFAPI.getImageFileTypeByImageData = function (imageData) {
+ var i;
+ var j;
+ var result = 'UNKNOWN';
+ var headerSchemata;
+ var compareResult;
+ var fileType;
+
+ for (fileType in imageFileTypeHeaders) {
+ headerSchemata = imageFileTypeHeaders[fileType];
+ for (i = 0; i < headerSchemata.length; i += 1) {
+ compareResult = true;
+ for (j = 0; j < headerSchemata[i].length; j += 1) {
+ if (headerSchemata[i][j] === undefined) {
+ continue;
+ }
+ if (headerSchemata[i][j] !== imageData.charCodeAt(j)) {
+ compareResult = false;
+ break;
+ }
+ }
+ if (compareResult === true) {
+ result = fileType;
+ break;
+ }
+ }
+ }
+ return result;
+ };
+
+ // Image functionality ported from pdf.js
+ var putImage = function putImage(img) {
+
+ var objectNumber = this.internal.newObject(),
+ out = this.internal.write,
+ putStream = this.internal.putStream;
+
+ img['n'] = objectNumber;
+
+ out('<>');
+ }
+ if ('trns' in img && img['trns'].constructor == Array) {
+ var trns = '',
+ i = 0,
+ len = img['trns'].length;
+ for (; i < len; i++) {
+ trns += img['trns'][i] + ' ' + img['trns'][i] + ' ';
+ }out('/Mask [' + trns + ']');
+ }
+ if ('smask' in img) {
+ out('/SMask ' + (objectNumber + 1) + ' 0 R');
+ }
+ out('/Length ' + img['data'].length + '>>');
+
+ putStream(img['data']);
+
+ out('endobj');
+
+ // Soft mask
+ if ('smask' in img) {
+ var dp = '/Predictor ' + img['p'] + ' /Colors 1 /BitsPerComponent ' + img['bpc'] + ' /Columns ' + img['w'];
+ var smask = { 'w': img['w'], 'h': img['h'], 'cs': 'DeviceGray', 'bpc': img['bpc'], 'dp': dp, 'data': img['smask'] };
+ if ('f' in img) smask.f = img['f'];
+ putImage.call(this, smask);
+ }
+
+ //Palette
+ if (img['cs'] === this.color_spaces.INDEXED) {
+
+ this.internal.newObject();
+ //out('<< /Filter / ' + img['f'] +' /Length ' + img['pal'].length + '>>');
+ //putStream(zlib.compress(img['pal']));
+ out('<< /Length ' + img['pal'].length + '>>');
+ putStream(this.arrayBufferToBinaryString(new Uint8Array(img['pal'])));
+ out('endobj');
+ }
+ },
+ putResourcesCallback = function putResourcesCallback() {
+ var images = this.internal.collections[namespace + 'images'];
+ for (var i in images) {
+ putImage.call(this, images[i]);
+ }
+ },
+ putXObjectsDictCallback = function putXObjectsDictCallback() {
+ var images = this.internal.collections[namespace + 'images'],
+ out = this.internal.write,
+ image;
+ for (var i in images) {
+ image = images[i];
+ out('/I' + image['i'], image['n'], '0', 'R');
+ }
+ },
+ checkCompressValue = function checkCompressValue(value) {
+ if (value && typeof value === 'string') value = value.toUpperCase();
+ return value in jsPDFAPI.image_compression ? value : jsPDFAPI.image_compression.NONE;
+ },
+ getImages = function getImages() {
+ var images = this.internal.collections[namespace + 'images'];
+ //first run, so initialise stuff
+ if (!images) {
+ this.internal.collections[namespace + 'images'] = images = {};
+ this.internal.events.subscribe('putResources', putResourcesCallback);
+ this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback);
+ }
+
+ return images;
+ },
+ getImageIndex = function getImageIndex(images) {
+ var imageIndex = 0;
+
+ if (images) {
+ // this is NOT the first time this method is ran on this instance of jsPDF object.
+ imageIndex = Object.keys ? Object.keys(images).length : function (o) {
+ var i = 0;
+ for (var e in o) {
+ if (o.hasOwnProperty(e)) {
+ i++;
+ }
+ }
+ return i;
+ }(images);
+ }
+
+ return imageIndex;
+ },
+ notDefined = function notDefined(value) {
+ return typeof value === 'undefined' || value === null || value.length === 0;
+ },
+ generateAliasFromData = function generateAliasFromData(data) {
+ return typeof data === 'string' && jsPDFAPI.sHashCode(data);
+ },
+ isImageTypeSupported = function isImageTypeSupported(type) {
+ return typeof jsPDFAPI["process" + type.toUpperCase()] === "function";
+ },
+ isDOMElement = function isDOMElement(object) {
+ return (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && object.nodeType === 1;
+ },
+ createDataURIFromElement = function createDataURIFromElement(element, format) {
+
+ //if element is an image which uses data url definition, just return the dataurl
+ if (element.nodeName === 'IMG' && element.hasAttribute('src')) {
+ var src = '' + element.getAttribute('src');
+ if (src.indexOf('data:image/') === 0) return src;
+
+ // only if the user doesn't care about a format
+ if (!format && /\.png(?:[?#].*)?$/i.test(src)) format = 'png';
+ }
+
+ if (element.nodeName === 'CANVAS') {
+ var canvas = element;
+ } else {
+ var canvas = document.createElement('canvas');
+ canvas.width = element.clientWidth || element.width;
+ canvas.height = element.clientHeight || element.height;
+
+ var ctx = canvas.getContext('2d');
+ if (!ctx) {
+ throw 'addImage requires canvas to be supported by browser.';
+ }
+ ctx.drawImage(element, 0, 0, canvas.width, canvas.height);
+ }
+ return canvas.toDataURL(('' + format).toLowerCase() == 'png' ? 'image/png' : 'image/jpeg');
+ },
+ checkImagesForAlias = function checkImagesForAlias(alias, images) {
+ var cached_info;
+ if (images) {
+ for (var e in images) {
+ if (alias === images[e].alias) {
+ cached_info = images[e];
+ break;
+ }
+ }
+ }
+ return cached_info;
+ },
+ determineWidthAndHeight = function determineWidthAndHeight(w, h, info) {
+ if (!w && !h) {
+ w = -96;
+ h = -96;
+ }
+ if (w < 0) {
+ w = -1 * info['w'] * 72 / w / this.internal.scaleFactor;
+ }
+ if (h < 0) {
+ h = -1 * info['h'] * 72 / h / this.internal.scaleFactor;
+ }
+ if (w === 0) {
+ w = h * info['w'] / info['h'];
+ }
+ if (h === 0) {
+ h = w * info['h'] / info['w'];
+ }
+
+ return [w, h];
+ },
+ writeImageToPDF = function writeImageToPDF(x, y, w, h, info, index, images, rotation) {
+ var dims = determineWidthAndHeight.call(this, w, h, info),
+ coord = this.internal.getCoordinateString,
+ vcoord = this.internal.getVerticalCoordinateString;
+
+ w = dims[0];
+ h = dims[1];
+
+ images[index] = info;
+
+ if (rotation) {
+ rotation *= Math.PI / 180;
+ var c = Math.cos(rotation);
+ var s = Math.sin(rotation);
+ //like in pdf Reference do it 4 digits instead of 2
+ var f4 = function f4(number) {
+ return number.toFixed(4);
+ };
+ var rotationTransformationMatrix = [f4(c), f4(s), f4(s * -1), f4(c), 0, 0, 'cm'];
+ }
+ this.internal.write('q'); //Save graphics state
+ if (rotation) {
+ this.internal.write([1, '0', '0', 1, coord(x), vcoord(y + h), 'cm'].join(' ')); //Translate
+ this.internal.write(rotationTransformationMatrix.join(' ')); //Rotate
+ this.internal.write([coord(w), '0', '0', coord(h), '0', '0', 'cm'].join(' ')); //Scale
+ } else {
+ this.internal.write([coord(w), '0', '0', coord(h), coord(x), vcoord(y + h), 'cm'].join(' ')); //Translate and Scale
+ }
+ this.internal.write('/I' + info['i'] + ' Do'); //Paint Image
+ this.internal.write('Q'); //Restore graphics state
+ };
+
+ /**
+ * COLOR SPACES
+ */
+ jsPDFAPI.color_spaces = {
+ DEVICE_RGB: 'DeviceRGB',
+ DEVICE_GRAY: 'DeviceGray',
+ DEVICE_CMYK: 'DeviceCMYK',
+ CAL_GREY: 'CalGray',
+ CAL_RGB: 'CalRGB',
+ LAB: 'Lab',
+ ICC_BASED: 'ICCBased',
+ INDEXED: 'Indexed',
+ PATTERN: 'Pattern',
+ SEPARATION: 'Separation',
+ DEVICE_N: 'DeviceN'
+ };
+
+ /**
+ * DECODE METHODS
+ */
+ jsPDFAPI.decode = {
+ DCT_DECODE: 'DCTDecode',
+ FLATE_DECODE: 'FlateDecode',
+ LZW_DECODE: 'LZWDecode',
+ JPX_DECODE: 'JPXDecode',
+ JBIG2_DECODE: 'JBIG2Decode',
+ ASCII85_DECODE: 'ASCII85Decode',
+ ASCII_HEX_DECODE: 'ASCIIHexDecode',
+ RUN_LENGTH_DECODE: 'RunLengthDecode',
+ CCITT_FAX_DECODE: 'CCITTFaxDecode'
+ };
+
+ /**
+ * IMAGE COMPRESSION TYPES
+ */
+ jsPDFAPI.image_compression = {
+ NONE: 'NONE',
+ FAST: 'FAST',
+ MEDIUM: 'MEDIUM',
+ SLOW: 'SLOW'
+ };
+
+ jsPDFAPI.sHashCode = function (str) {
+ str = str || "";
+ return Array.prototype.reduce && str.split("").reduce(function (a, b) {
+ a = (a << 5) - a + b.charCodeAt(0);return a & a;
+ }, 0);
+ };
+
+ jsPDFAPI.isString = function (object) {
+ return typeof object === 'string';
+ };
+
+ /**
+ * Regex taken from https://github.com/kevva/base64-regex
+ **/
+ jsPDFAPI.validateStringAsBase64 = function (possibleBase64String) {
+ possibleBase64String = possibleBase64String || '';
+ var base64Regex = new RegExp('(?:^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$)');
+ return base64Regex.test(possibleBase64String);
+ };
+
+ /**
+ * Strips out and returns info from a valid base64 data URI
+ * @param {String[dataURI]} a valid data URI of format 'data:[][;base64],'
+ * @returns an Array containing the following
+ * [0] the complete data URI
+ * [1]
+ * [2] format - the second part of the mime-type i.e 'png' in 'image/png'
+ * [4]
+ */
+ jsPDFAPI.extractInfoFromBase64DataURI = function (dataURI) {
+ return (/^data:([\w]+?\/([\w]+?));base64,(.+?)$/g.exec(dataURI)
+ );
+ };
+
+ /**
+ * Check to see if ArrayBuffer is supported
+ */
+ jsPDFAPI.supportsArrayBuffer = function () {
+ return typeof ArrayBuffer !== 'undefined' && typeof Uint8Array !== 'undefined';
+ };
+
+ /**
+ * Tests supplied object to determine if ArrayBuffer
+ * @param {Object[object]}
+ */
+ jsPDFAPI.isArrayBuffer = function (object) {
+ if (!this.supportsArrayBuffer()) return false;
+ return object instanceof ArrayBuffer;
+ };
+
+ /**
+ * Tests supplied object to determine if it implements the ArrayBufferView (TypedArray) interface
+ * @param {Object[object]}
+ */
+ jsPDFAPI.isArrayBufferView = function (object) {
+ if (!this.supportsArrayBuffer()) return false;
+ if (typeof Uint32Array === 'undefined') return false;
+ return object instanceof Int8Array || object instanceof Uint8Array || typeof Uint8ClampedArray !== 'undefined' && object instanceof Uint8ClampedArray || object instanceof Int16Array || object instanceof Uint16Array || object instanceof Int32Array || object instanceof Uint32Array || object instanceof Float32Array || object instanceof Float64Array;
+ };
+
+ /**
+ * Exactly what it says on the tin
+ */
+ jsPDFAPI.binaryStringToUint8Array = function (binary_string) {
+ /*
+ * not sure how efficient this will be will bigger files. Is there a native method?
+ */
+ var len = binary_string.length;
+ var bytes = new Uint8Array(len);
+ for (var i = 0; i < len; i++) {
+ bytes[i] = binary_string.charCodeAt(i);
+ }
+ return bytes;
+ };
+
+ /**
+ * Convert the Buffer to a Binary String
+ */
+ jsPDFAPI.arrayBufferToBinaryString = function (buffer) {
+
+ if (typeof atob === "function") {
+ return atob(this.arrayBufferToBase64(buffer));
+ }
+
+ if (typeof TextDecoder === "function") {
+ var decoder = new TextDecoder('ascii');
+ // test if the encoding is supported
+ if (decoder.encoding === 'ascii') {
+ return decoder.decode(buffer);
+ }
+ }
+
+ //Fallback-solution
+ var data = this.isArrayBuffer(buffer) ? buffer : new Uint8Array(buffer);
+ var chunkSizeForSlice = 0x5000;
+ var binary_string = '';
+ var slicesCount = Math.ceil(data.byteLength / chunkSizeForSlice);
+ for (var i = 0; i < slicesCount; i++) {
+ binary_string += String.fromCharCode.apply(null, data.slice(i * chunkSizeForSlice, i * chunkSizeForSlice + chunkSizeForSlice));
+ }
+ return binary_string;
+ };
+
+ /**
+ * Converts an ArrayBuffer directly to base64
+ *
+ * Taken from here
+ *
+ * http://jsperf.com/encoding-xhr-image-data/31
+ *
+ * Need to test if this is a better solution for larger files
+ *
+ */
+ jsPDFAPI.arrayBufferToBase64 = function (arrayBuffer) {
+ var base64 = '';
+ var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+ var bytes = new Uint8Array(arrayBuffer);
+ var byteLength = bytes.byteLength;
+ var byteRemainder = byteLength % 3;
+ var mainLength = byteLength - byteRemainder;
+
+ var a, b, c, d;
+ var chunk;
+
+ // Main loop deals with bytes in chunks of 3
+ for (var i = 0; i < mainLength; i = i + 3) {
+ // Combine the three bytes into a single integer
+ chunk = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
+
+ // Use bitmasks to extract 6-bit segments from the triplet
+ a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
+ b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
+ c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
+ d = chunk & 63; // 63 = 2^6 - 1
+
+ // Convert the raw binary segments to the appropriate ASCII encoding
+ base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
+ }
+
+ // Deal with the remaining bytes and padding
+ if (byteRemainder == 1) {
+ chunk = bytes[mainLength];
+
+ a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
+
+ // Set the 4 least significant bits to zero
+ b = (chunk & 3) << 4; // 3 = 2^2 - 1
+
+ base64 += encodings[a] + encodings[b] + '==';
+ } else if (byteRemainder == 2) {
+ chunk = bytes[mainLength] << 8 | bytes[mainLength + 1];
+
+ a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
+ b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
+
+ // Set the 2 least significant bits to zero
+ c = (chunk & 15) << 2; // 15 = 2^4 - 1
+
+ base64 += encodings[a] + encodings[b] + encodings[c] + '=';
+ }
+
+ return base64;
+ };
+
+ jsPDFAPI.createImageInfo = function (data, wd, ht, cs, bpc, f, imageIndex, alias, dp, trns, pal, smask, p) {
+ var info = {
+ alias: alias,
+ w: wd,
+ h: ht,
+ cs: cs,
+ bpc: bpc,
+ i: imageIndex,
+ data: data
+ // n: objectNumber will be added by putImage code
+ };
+
+ if (f) info.f = f;
+ if (dp) info.dp = dp;
+ if (trns) info.trns = trns;
+ if (pal) info.pal = pal;
+ if (smask) info.smask = smask;
+ if (p) info.p = p; // predictor parameter for PNG compression
+
+ return info;
+ };
+
+ jsPDFAPI.addImage = function (imageData, format, x, y, w, h, alias, compression, rotation) {
+
+ var tmpImageData = '';
+
+ if (typeof format !== 'string') {
+ var tmp = h;
+ h = w;
+ w = y;
+ y = x;
+ x = format;
+ format = tmp;
+ }
+
+ if ((typeof imageData === 'undefined' ? 'undefined' : _typeof(imageData)) === 'object' && !isDOMElement(imageData) && "imageData" in imageData) {
+ var options = imageData;
+
+ imageData = options.imageData;
+ format = options.format || format;
+ x = options.x || x || 0;
+ y = options.y || y || 0;
+ w = options.w || w;
+ h = options.h || h;
+ alias = options.alias || alias;
+ compression = options.compression || compression;
+ rotation = options.rotation || options.angle || rotation;
+ }
+
+ if (isNaN(x) || isNaN(y)) {
+ console.error('jsPDF.addImage: Invalid coordinates', arguments);
+ throw new Error('Invalid coordinates passed to jsPDF.addImage');
+ }
+
+ var images = getImages.call(this),
+ info;
+
+ if (!(info = checkImagesForAlias(imageData, images))) {
+ var dataAsBinaryString;
+
+ if (isDOMElement(imageData)) imageData = createDataURIFromElement(imageData, format);
+
+ if (notDefined(alias)) alias = generateAliasFromData(imageData);
+
+ if (!(info = checkImagesForAlias(alias, images))) {
+ if (this.isString(imageData)) {
+ tmpImageData = this.convertStringToImageData(imageData);
+
+ if (tmpImageData !== '') {
+ imageData = tmpImageData;
+ } else {
+ tmpImageData = this.loadImageFile(imageData);
+ if (tmpImageData !== undefined) {
+ imageData = tmpImageData;
+ }
+ }
+ }
+ format = this.getImageFileTypeByImageData(imageData);
+
+ if (!isImageTypeSupported(format)) throw new Error('addImage does not support files of type \'' + format + '\', please ensure that a plugin for \'' + format + '\' support is added.');
+
+ /**
+ * need to test if it's more efficient to convert all binary strings
+ * to TypedArray - or should we just leave and process as string?
+ */
+ if (this.supportsArrayBuffer()) {
+ // no need to convert if imageData is already uint8array
+ if (!(imageData instanceof Uint8Array)) {
+ dataAsBinaryString = imageData;
+ imageData = this.binaryStringToUint8Array(imageData);
+ }
+ }
+
+ info = this['process' + format.toUpperCase()](imageData, getImageIndex(images), alias, checkCompressValue(compression), dataAsBinaryString);
+
+ if (!info) throw new Error('An unkwown error occurred whilst processing the image');
+ }
+ }
+ writeImageToPDF.call(this, x, y, w, h, info, info.i, images, rotation);
+
+ return this;
+ };
+
+ jsPDFAPI.convertStringToImageData = function (stringData) {
+ var base64Info;
+ var imageData = '';
+ if (this.isString(stringData)) {
+ var base64Info = this.extractInfoFromBase64DataURI(stringData);
+
+ if (base64Info !== null) {
+ if (jsPDFAPI.validateStringAsBase64(base64Info[3])) {
+ imageData = atob(base64Info[3]); //convert to binary string
+ }
+ } else if (jsPDFAPI.validateStringAsBase64(stringData)) {
+ imageData = atob(stringData);
+ }
+ }
+ return imageData;
+ };
+ /**
+ * JPEG SUPPORT
+ **/
+
+ //takes a string imgData containing the raw bytes of
+ //a jpeg image and returns [width, height]
+ //Algorithm from: http://www.64lines.com/jpeg-width-height
+ var getJpegSize = function getJpegSize(imgData) {
+
+ var width, height, numcomponents;
+ // Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00
+ if (!imgData.charCodeAt(0) === 0xff || !imgData.charCodeAt(1) === 0xd8 || !imgData.charCodeAt(2) === 0xff || !imgData.charCodeAt(3) === 0xe0 || !imgData.charCodeAt(6) === 'J'.charCodeAt(0) || !imgData.charCodeAt(7) === 'F'.charCodeAt(0) || !imgData.charCodeAt(8) === 'I'.charCodeAt(0) || !imgData.charCodeAt(9) === 'F'.charCodeAt(0) || !imgData.charCodeAt(10) === 0x00) {
+ throw new Error('getJpegSize requires a binary string jpeg file');
+ }
+ var blockLength = imgData.charCodeAt(4) * 256 + imgData.charCodeAt(5);
+ var i = 4,
+ len = imgData.length;
+ while (i < len) {
+ i += blockLength;
+ if (imgData.charCodeAt(i) !== 0xff) {
+ throw new Error('getJpegSize could not find the size of the image');
+ }
+ if (imgData.charCodeAt(i + 1) === 0xc0 || //(SOF) Huffman - Baseline DCT
+ imgData.charCodeAt(i + 1) === 0xc1 || //(SOF) Huffman - Extended sequential DCT
+ imgData.charCodeAt(i + 1) === 0xc2 || // Progressive DCT (SOF2)
+ imgData.charCodeAt(i + 1) === 0xc3 || // Spatial (sequential) lossless (SOF3)
+ imgData.charCodeAt(i + 1) === 0xc4 || // Differential sequential DCT (SOF5)
+ imgData.charCodeAt(i + 1) === 0xc5 || // Differential progressive DCT (SOF6)
+ imgData.charCodeAt(i + 1) === 0xc6 || // Differential spatial (SOF7)
+ imgData.charCodeAt(i + 1) === 0xc7) {
+ height = imgData.charCodeAt(i + 5) * 256 + imgData.charCodeAt(i + 6);
+ width = imgData.charCodeAt(i + 7) * 256 + imgData.charCodeAt(i + 8);
+ numcomponents = imgData.charCodeAt(i + 9);
+ return [width, height, numcomponents];
+ } else {
+ i += 2;
+ blockLength = imgData.charCodeAt(i) * 256 + imgData.charCodeAt(i + 1);
+ }
+ }
+ },
+ getJpegSizeFromBytes = function getJpegSizeFromBytes(data) {
+
+ var hdr = data[0] << 8 | data[1];
+
+ if (hdr !== 0xFFD8) throw new Error('Supplied data is not a JPEG');
+
+ var len = data.length,
+ block = (data[4] << 8) + data[5],
+ pos = 4,
+ bytes,
+ width,
+ height,
+ numcomponents;
+
+ while (pos < len) {
+ pos += block;
+ bytes = readBytes(data, pos);
+ block = (bytes[2] << 8) + bytes[3];
+ if ((bytes[1] === 0xC0 || bytes[1] === 0xC2) && bytes[0] === 0xFF && block > 7) {
+ bytes = readBytes(data, pos + 5);
+ width = (bytes[2] << 8) + bytes[3];
+ height = (bytes[0] << 8) + bytes[1];
+ numcomponents = bytes[4];
+ return { width: width, height: height, numcomponents: numcomponents };
+ }
+
+ pos += 2;
+ }
+
+ throw new Error('getJpegSizeFromBytes could not find the size of the image');
+ },
+ readBytes = function readBytes(data, offset) {
+ return data.subarray(offset, offset + 5);
+ };
+
+ jsPDFAPI.processJPEG = function (data, index, alias, compression, dataAsBinaryString, colorSpace) {
+
+ var filter = this.decode.DCT_DECODE,
+ bpc = 8,
+ dims;
+
+ if (!this.isString(data) && !this.isArrayBuffer(data) && !this.isArrayBufferView(data)) {
+ return null;
+ }
+
+ if (this.isString(data)) {
+ dims = getJpegSize(data);
+ }
+
+ if (this.isArrayBuffer(data)) {
+ data = new Uint8Array(data);
+ }
+ if (this.isArrayBufferView(data)) {
+
+ dims = getJpegSizeFromBytes(data);
+
+ // if we already have a stored binary string rep use that
+ data = dataAsBinaryString || this.arrayBufferToBinaryString(data);
+ }
+
+ if (colorSpace === undefined) {
+ switch (dims.numcomponents) {
+ case 1:
+ colorSpace = this.color_spaces.DEVICE_GRAY;
+ break;
+ case 4:
+ colorSpace = this.color_spaces.DEVICE_CMYK;
+ break;
+ default:
+ case 3:
+ colorSpace = this.color_spaces.DEVICE_RGB;
+ break;
+ }
+ }
+
+ return this.createImageInfo(data, dims.width, dims.height, colorSpace, bpc, filter, index, alias);
+ };
+
+ jsPDFAPI.processJPG = function () /*data, index, alias, compression, dataAsBinaryString*/{
+ return this.processJPEG.apply(this, arguments);
+ };
+
+ jsPDFAPI.loadImageFile = function (path, sync, callback) {
+ sync = sync || true;
+ callback = callback || function () {};
+ var isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
+
+ var xhrMethod = function xhrMethod(url, sync, callback) {
+ var req = new XMLHttpRequest();
+ var byteArray = [];
+ var i = 0;
+
+ var sanitizeUnicode = function sanitizeUnicode(data) {
+ var dataLength = data.length;
+ var StringFromCharCode = String.fromCharCode;
+
+ //Transform Unicode to ASCII
+ for (i = 0; i < dataLength; i += 1) {
+ byteArray.push(StringFromCharCode(data.charCodeAt(i) & 0xff));
+ }
+ return byteArray.join("");
+ };
+
+ req.open('GET', url, !sync);
+ // XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
+ req.overrideMimeType('text\/plain; charset=x-user-defined');
+
+ if (sync === false) {
+ req.onload = function () {
+ return sanitizeUnicode(this.responseText);
+ };
+ }
+ req.send(null);
+
+ if (req.status !== 200) {
+ console.warn('Unable to load file "' + url + '"');
+ return;
+ }
+
+ if (sync) {
+ return sanitizeUnicode(req.responseText);
+ }
+ };
+
+ var nodeJSMethod = function nodeJSMethod(path, sync, callback) {
+ sync = sync || true;
+ var fs = require('fs');
+ if (sync === true) {
+ var data = fs.readFileSync(path).toString();
+ return data;
+ } else {
+ fs.readFile('image.jpg', function (err, data) {
+ callback(data);
+ });
+ }
+ };
+
+ //we have a browser and probably no CORS-Problem
+ if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== undefined && (typeof location === 'undefined' ? 'undefined' : _typeof(location)) === "object" && location.protocol.substr(0, 4) === "http") {
+ return xhrMethod(path, sync, callback);
+ } else if (isNode) {
+ return nodeJSMethod(path, sync, callback);
+ }
+ };
+
+ jsPDFAPI.getImageProperties = function (imageData) {
+ var info;
+ var tmpImageData = '';
+ var format;
+
+ if (isDOMElement(imageData)) {
+ imageData = createDataURIFromElement(imageData);
+ }
+
+ if (this.isString(imageData)) {
+ tmpImageData = this.convertStringToImageData(imageData);
+
+ if (tmpImageData !== '') {
+ imageData = tmpImageData;
+ } else {
+ tmpImageData = this.loadImageFile(imageData);
+ if (tmpImageData !== undefined) {
+ imageData = tmpImageData;
+ }
+ }
+ }
+ format = this.getImageFileTypeByImageData(imageData);
+
+ if (!isImageTypeSupported(format)) throw new Error('addImage does not support files of type \'' + format + '\', please ensure that a plugin for \'' + format + '\' support is added.');
+
+ /**
+ * need to test if it's more efficient to convert all binary strings
+ * to TypedArray - or should we just leave and process as string?
+ */
+ if (this.supportsArrayBuffer()) {
+ // no need to convert if imageData is already uint8array
+ if (!(imageData instanceof Uint8Array)) {
+ imageData = this.binaryStringToUint8Array(imageData);
+ }
+ }
+
+ info = this['process' + format.toUpperCase()](imageData);
+
+ if (!info) {
+ throw new Error('An unkwown error occurred whilst processing the image');
+ }
+
+ return {
+ fileType: format,
+ width: info.w,
+ height: info.h,
+ colorSpace: info.cs,
+ compressionMode: info.f,
+ bitsPerComponent: info.bpc
+ };
+ };
+ })(jsPDF.API);
+
+ /**
+ * jsPDF Annotations PlugIn
+ * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
+ *
+ * Licensed under the MIT License.
+ * http://opensource.org/licenses/mit-license
+ */
+
+ /**
+ * There are many types of annotations in a PDF document. Annotations are placed
+ * on a page at a particular location. They are not 'attached' to an object.
+ *
+ * This plugin current supports
+ *
Goto Page (set pageNumber and top in options)
+ *
Goto Name (set name and top in options)
+ *
Goto URL (set url in options)
+ *
+ * The destination magnification factor can also be specified when goto is a page number or a named destination. (see documentation below)
+ * (set magFactor in options). XYZ is the default.
+ *
+ *
+ * Links, Text, Popup, and FreeText are supported.
+ *
+ *
+ * Options In PDF spec Not Implemented Yet
+ *
link border
+ *
named target
+ *
page coordinates
+ *
destination page scaling and layout
+ *
actions other than URL and GotoPage
+ *
background / hover actions
+ *
+ */
+
+ /*
+ Destination Magnification Factors
+ See PDF 1.3 Page 386 for meanings and options
+
+ [supported]
+ XYZ (options; left top zoom)
+ Fit (no options)
+ FitH (options: top)
+ FitV (options: left)
+
+ [not supported]
+ FitR
+ FitB
+ FitBH
+ FitBV
+ */
+
+ (function (jsPDFAPI) {
+
+ var annotationPlugin = {
+
+ /**
+ * An array of arrays, indexed by pageNumber.
+ */
+ annotations: [],
+
+ f2: function f2(number) {
+ return number.toFixed(2);
+ },
+
+ notEmpty: function notEmpty(obj) {
+ if (typeof obj != 'undefined') {
+ if (obj != '') {
+ return true;
+ }
+ }
+ }
+ };
+
+ jsPDF.API.annotationPlugin = annotationPlugin;
+
+ jsPDF.API.events.push(['addPage', function (info) {
+ this.annotationPlugin.annotations[info.pageNumber] = [];
+ }]);
+
+ jsPDFAPI.events.push(['putPage', function (info) {
+ //TODO store annotations in pageContext so reorder/remove will not affect them.
+ var pageAnnos = this.annotationPlugin.annotations[info.pageNumber];
+
+ var found = false;
+ for (var a = 0; a < pageAnnos.length && !found; a++) {
+ var anno = pageAnnos[a];
+ switch (anno.type) {
+ case 'link':
+ if (annotationPlugin.notEmpty(anno.options.url) || annotationPlugin.notEmpty(anno.options.pageNumber)) {
+ found = true;
+ break;
+ }
+ case 'reference':
+ case 'text':
+ case 'freetext':
+ found = true;
+ break;
+ }
+ }
+ if (found == false) {
+ return;
+ }
+
+ this.internal.write("/Annots [");
+ var f2 = this.annotationPlugin.f2;
+ var k = this.internal.scaleFactor;
+ var pageHeight = this.internal.pageSize.getHeight();
+ var pageInfo = this.internal.getPageInfo(info.pageNumber);
+ for (var a = 0; a < pageAnnos.length; a++) {
+ var anno = pageAnnos[a];
+
+ switch (anno.type) {
+ case 'reference':
+ // References to Widget Anotations (for AcroForm Fields)
+ this.internal.write(' ' + anno.object.objId + ' 0 R ');
+ break;
+ case 'text':
+ // Create a an object for both the text and the popup
+ var objText = this.internal.newAdditionalObject();
+ var objPopup = this.internal.newAdditionalObject();
+
+ var title = anno.title || 'Note';
+ var rect = "/Rect [" + f2(anno.bounds.x * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + " " + f2((anno.bounds.x + anno.bounds.w) * k) + " " + f2((pageHeight - anno.bounds.y) * k) + "] ";
+ line = '<>';
+ objText.content = line;
+
+ var parent = objText.objId + ' 0 R';
+ var popoff = 30;
+ var rect = "/Rect [" + f2((anno.bounds.x + popoff) * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + " " + f2((anno.bounds.x + anno.bounds.w + popoff) * k) + " " + f2((pageHeight - anno.bounds.y) * k) + "] ";
+ //var rect2 = "/Rect [" + f2(anno.bounds.x * k) + " " + f2((pageHeight - anno.bounds.y) * k) + " " + f2(anno.bounds.x + anno.bounds.w * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + "] ";
+ line = '<>';
+ objPopup.content = line;
+
+ this.internal.write(objText.objId, '0 R', objPopup.objId, '0 R');
+
+ break;
+ case 'freetext':
+ var rect = "/Rect [" + f2(anno.bounds.x * k) + " " + f2((pageHeight - anno.bounds.y) * k) + " " + f2(anno.bounds.x + anno.bounds.w * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + "] ";
+ var color = anno.color || '#000000';
+ line = '<>';
+ this.internal.write(line);
+ break;
+ case 'link':
+ if (anno.options.name) {
+ var loc = this.annotations._nameMap[anno.options.name];
+ anno.options.pageNumber = loc.page;
+ anno.options.top = loc.y;
+ } else {
+ if (!anno.options.top) {
+ anno.options.top = 0;
+ }
+ }
+
+ var rect = "/Rect [" + f2(anno.x * k) + " " + f2((pageHeight - anno.y) * k) + " " + f2((anno.x + anno.w) * k) + " " + f2((pageHeight - (anno.y + anno.h)) * k) + "] ";
+
+ var line = '';
+ if (anno.options.url) {
+ line = '<>';
+ } else if (anno.options.pageNumber) {
+ // first page is 0
+ var info = this.internal.getPageInfo(anno.options.pageNumber);
+ line = '<>";
+ this.internal.write(line);
+ }
+ break;
+ }
+ }
+ this.internal.write("]");
+ }]);
+
+ jsPDFAPI.createAnnotation = function (options) {
+ switch (options.type) {
+ case 'link':
+ this.link(options.bounds.x, options.bounds.y, options.bounds.w, options.bounds.h, options);
+ break;
+ case 'text':
+ case 'freetext':
+ this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push(options);
+ break;
+ }
+ };
+
+ /**
+ * valid options
+ *
pageNumber or url [required]
+ *
If pageNumber is specified, top and zoom may also be specified
+ */
+ jsPDFAPI.link = function (x, y, w, h, options) {
+
+ this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push({
+ x: x,
+ y: y,
+ w: w,
+ h: h,
+ options: options,
+ type: 'link'
+ });
+ };
+
+ /**
+ * Currently only supports single line text.
+ * Returns the width of the text/link
+ */
+ jsPDFAPI.textWithLink = function (text, x, y, options) {
+
+ var width = this.getTextWidth(text);
+ var height = this.internal.getLineHeight() / this.internal.scaleFactor;
+ this.text(text, x, y);
+ //TODO We really need the text baseline height to do this correctly.
+ // Or ability to draw text on top, bottom, center, or baseline.
+ y += height * .2;
+ this.link(x, y - height, width, height, options);
+ return width;
+ };
+
+ //TODO move into external library
+ jsPDFAPI.getTextWidth = function (text) {
+
+ var fontSize = this.internal.getFontSize();
+ var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor;
+ return txtWidth;
+ };
+
+ //TODO move into external library
+ jsPDFAPI.getLineHeight = function () {
+ return this.internal.getLineHeight();
+ };
+
+ return this;
+ })(jsPDF.API);
+
+ (function (jsPDFAPI) {
+
+ var arLangCodes = {
+ "ar": "Arabic (Standard)",
+ "ar-DZ": "Arabic (Algeria)",
+ "ar-BH": "Arabic (Bahrain)",
+ "ar-EG": "Arabic (Egypt)",
+ "ar-IQ": "Arabic (Iraq)",
+ "ar-JO": "Arabic (Jordan)",
+ "ar-KW": "Arabic (Kuwait)",
+ "ar-LB": "Arabic (Lebanon)",
+ "ar-LY": "Arabic (Libya)",
+ "ar-MA": "Arabic (Morocco)",
+ "ar-OM": "Arabic (Oman)",
+ "ar-QA": "Arabic (Qatar)",
+ "ar-SA": "Arabic (Saudi Arabia)",
+ "ar-SY": "Arabic (Syria)",
+ "ar-TN": "Arabic (Tunisia)",
+ "ar-AE": "Arabic (U.A.E.)",
+ "ar-YE": "Arabic (Yemen)",
+ "fa": "Persian",
+ "fa-IR": "Persian/Iran",
+ "ur": "Urdu"
+ };
+
+ var arLangCodesKeys = Object.keys(arLangCodes);
+
+ /**
+ * Arabic shape substitutions: char code => (isolated, final, initial, medial).
+ */
+ var arabicSubst = {
+ 1569: [65152],
+ 1570: [65153, 65154, 65153, 65154],
+ 1571: [65155, 65156, 65155, 65156],
+ 1572: [65157, 65158],
+ 1573: [65159, 65160, 65159, 65160],
+ 1574: [65161, 65162, 65163, 65164],
+ 1575: [65165, 65166, 65165, 65166],
+ 1576: [65167, 65168, 65169, 65170],
+ 1577: [65171, 65172],
+ 1578: [65173, 65174, 65175, 65176],
+ 1579: [65177, 65178, 65179, 65180],
+ 1580: [65181, 65182, 65183, 65184],
+ 1581: [65185, 65186, 65187, 65188],
+ 1582: [65189, 65190, 65191, 65192],
+ 1583: [65193, 65194, 65193, 65194],
+ 1584: [65195, 65196, 65195, 65196],
+ 1585: [65197, 65198, 65197, 65198],
+ 1586: [65199, 65200, 65199, 65200],
+ 1587: [65201, 65202, 65203, 65204],
+ 1588: [65205, 65206, 65207, 65208],
+ 1589: [65209, 65210, 65211, 65212],
+ 1590: [65213, 65214, 65215, 65216],
+ 1591: [65217, 65218, 65219, 65220],
+ 1592: [65221, 65222, 65223, 65224],
+ 1593: [65225, 65226, 65227, 65228],
+ 1594: [65229, 65230, 65231, 65232],
+ 1601: [65233, 65234, 65235, 65236],
+ 1602: [65237, 65238, 65239, 65240],
+ 1603: [65241, 65242, 65243, 65244],
+ 1604: [65245, 65246, 65247, 65248],
+ 1605: [65249, 65250, 65251, 65252],
+ 1606: [65253, 65254, 65255, 65256],
+ 1607: [65257, 65258, 65259, 65260],
+ 1608: [65261, 65262, 65261, 65262],
+ 1609: [65263, 65264, 64488, 64489],
+ 1610: [65265, 65266, 65267, 65268],
+ 1649: [64336, 64337],
+ 1655: [64477],
+ 1657: [64358, 64359, 64360, 64361],
+ 1658: [64350, 64351, 64352, 64353],
+ 1659: [64338, 64339, 64340, 64341],
+ 1662: [64342, 64343, 64344, 64345],
+ 1663: [64354, 64355, 64356, 64357],
+ 1664: [64346, 64347, 64348, 64349],
+ 1667: [64374, 64375, 64376, 64377],
+ 1668: [64370, 64371, 64372, 64373],
+ 1670: [64378, 64379, 64380, 64381],
+ 1671: [64382, 64383, 64384, 64385],
+ 1672: [64392, 64393],
+ 1676: [64388, 64389],
+ 1677: [64386, 64387],
+ 1678: [64390, 64391],
+ 1681: [64396, 64397],
+ 1688: [64394, 64395, 64394, 64395],
+ 1700: [64362, 64363, 64364, 64365],
+ 1702: [64366, 64367, 64368, 64369],
+ 1705: [64398, 64399, 64400, 64401],
+ 1709: [64467, 64468, 64469, 64470],
+ 1711: [64402, 64403, 64404, 64405],
+ 1713: [64410, 64411, 64412, 64413],
+ 1715: [64406, 64407, 64408, 64409],
+ 1722: [64414, 64415],
+ 1723: [64416, 64417, 64418, 64419],
+ 1726: [64426, 64427, 64428, 64429],
+ 1728: [64420, 64421],
+ 1729: [64422, 64423, 64424, 64425],
+ 1733: [64480, 64481],
+ 1734: [64473, 64474],
+ 1735: [64471, 64472],
+ 1736: [64475, 64476],
+ 1737: [64482, 64483],
+ 1739: [64478, 64479],
+ 1740: [64508, 64509, 64510, 64511],
+ 1744: [64484, 64485, 64486, 64487],
+ 1746: [64430, 64431],
+ 1747: [64432, 64433]
+ };
+ var arabiclaasubst = {
+ 1570: [65269, 65270, 65269, 65270],
+ 1571: [65271, 65272, 65271, 65272],
+ 1573: [65273, 65274, 65273, 65274],
+ 1575: [65275, 65276, 65275, 65276]
+ };
+ var arabicorigsubst = {
+ 1570: [65153, 65154, 65153, 65154],
+ 1571: [65155, 65156, 65155, 65156],
+ 1573: [65159, 65160, 65159, 65160],
+ 1575: [65165, 65166, 65165, 65166]
+ };
+ var arabic_diacritics = {
+ 1612: 64606, // Shadda + Dammatan
+ 1613: 64607, // Shadda + Kasratan
+ 1614: 64608, // Shadda + Fatha
+ 1615: 64609, // Shadda + Damma
+ 1616: 64610 // Shadda + Kasra
+ };
+
+ var alfletter = [1570, 1571, 1573, 1575];
+ var endedletter = [1569, 1570, 1571, 1572, 1573, 1575, 1577, 1583, 1584, 1585, 1586, 1608, 1688];
+
+ var isolatedForm = 0;
+ var finalForm = 1;
+ var initialForm = 2;
+ var medialForm = 3;
+
+ //private
+ function isArabicLetter(letter) {
+ return letter !== undefined && arabicSubst[letter.charCodeAt(0)] !== undefined;
+ }
+
+ function isArabicEndLetter(letter) {
+ return letter !== undefined && endedletter.indexOf(letter.charCodeAt(0)) >= 0;
+ }
+
+ function isArabicAlfLetter(letter) {
+ return letter !== undefined && alfletter.indexOf(letter.charCodeAt(0)) >= 0;
+ }
+
+ function arabicLetterHasFinalForm(letter) {
+ return isArabicLetter(letter) && arabicSubst[letter.charCodeAt(0)].length >= 2;
+ }
+
+ function arabicLetterHasMedialForm(letter) {
+ return isArabicLetter(letter) && arabicSubst[letter.charCodeAt(0)].length == 4;
+ }
+
+ function isArabicDiacritic(letter) {
+ return letter !== undefined && arabic_diacritics[letter.charCodeAt(0)] !== undefined;
+ }
+
+ function getCorrectForm(currentChar, beforeChar, nextChar, arabicSubstition) {
+ if (!isArabicLetter(currentChar)) {
+ return -1;
+ }
+
+ arabicSubstition = arabicSubstition || {};
+ arabicSubst = Object.assign(arabicSubst, arabicSubstition);
+
+ if (!arabicLetterHasFinalForm(currentChar) || !isArabicLetter(beforeChar) && !isArabicLetter(nextChar) || !isArabicLetter(nextChar) && isArabicEndLetter(beforeChar) || isArabicEndLetter(currentChar) && !isArabicLetter(beforeChar) || isArabicEndLetter(currentChar) && isArabicAlfLetter(beforeChar)) {
+ arabicSubst = Object.assign(arabicSubst, arabicorigsubst);
+ return isolatedForm;
+ }
+
+ if (arabicLetterHasMedialForm(currentChar) && isArabicLetter(beforeChar) && !isArabicEndLetter(beforeChar) && isArabicLetter(nextChar) && arabicLetterHasFinalForm(nextChar)) {
+ arabicSubst = Object.assign(arabicSubst, arabicorigsubst);
+ return medialForm;
+ }
+
+ if (isArabicEndLetter(currentChar) || !isArabicLetter(nextChar)) {
+ arabicSubst = Object.assign(arabicSubst, arabicorigsubst);
+ return finalForm;
+ }
+
+ arabicSubst = Object.assign(arabicSubst, arabicorigsubst);
+ return initialForm;
+ }
+
+ function processArabic(text) {
+ text = text || "";
+ var result = "";
+ var i = 0;
+ var position = 0;
+ var currentLetter = "";
+ var prevLetter = "";
+ var nextLetter = "";
+ var resultingLetter;
+
+ var localPrevLetter;
+ var localCurrentLetter;
+ var localNextLetter;
+
+ for (i = 0; i < text.length; i += 1) {
+ currentLetter = text[i];
+ prevLetter = text[i - 1];
+ nextLetter = text[i + 1];
+ if (!isArabicLetter(currentLetter)) {
+ result += currentLetter;
+ } else {
+ if (prevLetter !== undefined && prevLetter.charCodeAt(0) === 1604 && isArabicAlfLetter(currentLetter)) {
+ localPrevLetter = text[i - 2];
+ localCurrentLetter = currentLetter;
+ localNextLetter = text[i + 1];
+ position = getCorrectForm(localCurrentLetter, localPrevLetter, localNextLetter, arabiclaasubst);
+ resultingLetter = String.fromCharCode(arabiclaasubst[currentLetter.charCodeAt(0)][position]);
+ result = result.substr(0, result.length - 1) + resultingLetter;
+ } else if (prevLetter !== undefined && prevLetter.charCodeAt(0) === 1617 && isArabicDiacritic(currentLetter)) {
+ localPrevLetter = text[i - 2];
+ localCurrentLetter = currentLetter;
+ localNextLetter = text[i + 1];
+ position = getCorrectForm(localCurrentLetter, localPrevLetter, localNextLetter, arabicorigsubst);
+ resultingLetter = String.fromCharCode(arabic_diacritics[currentLetter.charCodeAt(0)][position]);
+ result = result.substr(0, result.length - 1) + resultingLetter;
+ } else {
+ position = getCorrectForm(currentLetter, prevLetter, nextLetter, arabicorigsubst);
+ result += String.fromCharCode(arabicSubst[currentLetter.charCodeAt(0)][position]);
+ }
+ }
+ }
+ return result.split("").reverse().join("");
+ }
+
+ var arabicParserFunction = function arabicParserFunction(args) {
+ var text = args.text;
+ var x = args.x;
+ var y = args.y;
+ var options = args.options || {};
+ var mutex = args.mutex || {};
+ var lang = options.lang;
+ var tmpText = [];
+
+ if (arLangCodesKeys.indexOf(lang) >= 0) {
+ if (Object.prototype.toString.call(text) === '[object Array]') {
+ var i = 0;
+ tmpText = [];
+ for (i = 0; i < text.length; i += 1) {
+ if (Object.prototype.toString.call(text[i]) === '[object Array]') {
+ tmpText.push([processArabic(text[i][0]), text[i][1], text[i][2]]);
+ } else {
+ tmpText.push([processArabic(text[i])]);
+ }
+ }
+ args.text = tmpText;
+ } else {
+ args.text = processArabic(text);
+ }
+ //force charSpace if not given.
+ if (options.charSpace === undefined) {
+ args.options.charSpace = 0;
+ }
+ //if R2L is true, set it false.
+ if (options.R2L === true) {
+ args.options.R2L = false;
+ }
+ }
+ };
+
+ jsPDFAPI.events.push(['preProcessText', arabicParserFunction]);
+ })(jsPDF.API);
+
+ /**
+ * jsPDF Autoprint Plugin
+ *
+ * Licensed under the MIT License.
+ * http://opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Makes the PDF automatically print. This works in Chrome, Firefox, Acrobat
+ * Reader.
+ *
+ * @returns {jsPDF}
+ * @name autoPrint
+ * @example
+ * var doc = new jsPDF()
+ * doc.text(10, 10, 'This is a test')
+ * doc.autoPrint()
+ * doc.save('autoprint.pdf')
+ */
+
+ (function (jsPDFAPI) {
+
+ jsPDFAPI.autoPrint = function (options) {
+
+ var refAutoPrintTag;
+ options = options || {};
+ options.variant = options.variant || 'non-conform';
+
+ switch (options.variant) {
+ case 'javascript':
+ //https://github.com/Rob--W/pdf.js/commit/c676ecb5a0f54677b9f3340c3ef2cf42225453bb
+ this.addJS('print({});');
+ break;
+ case 'non-conform':
+ default:
+ this.internal.events.subscribe('postPutResources', function () {
+ refAutoPrintTag = this.internal.newObject();
+ this.internal.out("<<");
+ this.internal.out("/S /Named");
+ this.internal.out("/Type /Action");
+ this.internal.out("/N /Print");
+ this.internal.out(">>");
+ this.internal.out("endobj");
+ });
+
+ this.internal.events.subscribe("putCatalog", function () {
+ this.internal.out("/OpenAction " + refAutoPrintTag + " 0 R");
+ });
+ break;
+ }
+ return this;
+ };
+ })(jsPDF.API);
+
+ /**
+ * jsPDF Canvas PlugIn
+ * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
+ *
+ * Licensed under the MIT License.
+ * http://opensource.org/licenses/mit-license
+ */
+
+ /**
+ * This plugin mimics the HTML5 Canvas
+ *
+ * The goal is to provide a way for current canvas users to print directly to a PDF.
+ */
+
+ (function (jsPDFAPI) {
+
+ jsPDFAPI.events.push(['initialized', function () {
+ this.canvas.pdf = this;
+ }]);
+
+ jsPDFAPI.canvas = {
+ getContext: function getContext(name) {
+ this.pdf.context2d._canvas = this;
+ return this.pdf.context2d;
+ },
+ style: {}
+ };
+
+ Object.defineProperty(jsPDFAPI.canvas, 'width', {
+ get: function get() {
+ return this._width;
+ },
+ set: function set(value) {
+ this._width = value;
+ this.getContext('2d').pageWrapX = value + 1;
+ }
+ });
+
+ Object.defineProperty(jsPDFAPI.canvas, 'height', {
+ get: function get() {
+ return this._height;
+ },
+ set: function set(value) {
+ this._height = value;
+ this.getContext('2d').pageWrapY = value + 1;
+ }
+ });
+
+ return this;
+ })(jsPDF.API);
+
+ /** ====================================================================
+ * jsPDF Cell plugin
+ * Copyright (c) 2013 Youssef Beddad, youssef.beddad@gmail.com
+ * 2013 Eduardo Menezes de Morais, eduardo.morais@usp.br
+ * 2013 Lee Driscoll, https://github.com/lsdriscoll
+ * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
+ * 2014 James Hall, james@parall.ax
+ * 2014 Diego Casorran, https://github.com/diegocr
+ *
+ *
+ * ====================================================================
+ */
+
+ (function (jsPDFAPI) {
+ /*jslint browser:true */
+ /*global document: false, jsPDF */
+
+ var fontName,
+ fontSize,
+ fontStyle,
+ padding = 3,
+ margin = 13,
+ headerFunction,
+ lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined },
+ pages = 1,
+ setLastCellPosition = function setLastCellPosition(x, y, w, h, ln) {
+ lastCellPos = { 'x': x, 'y': y, 'w': w, 'h': h, 'ln': ln };
+ },
+ getLastCellPosition = function getLastCellPosition() {
+ return lastCellPos;
+ },
+ NO_MARGINS = { left: 0, top: 0, bottom: 0 };
+
+ jsPDFAPI.setHeaderFunction = function (func) {
+ headerFunction = func;
+ };
+
+ jsPDFAPI.getTextDimensions = function (txt) {
+ fontName = this.internal.getFont().fontName;
+ fontSize = this.table_font_size || this.internal.getFontSize();
+ fontStyle = this.internal.getFont().fontStyle;
+ // 1 pixel = 0.264583 mm and 1 mm = 72/25.4 point
+ var px2pt = 0.264583 * 72 / 25.4,
+ dimensions,
+ text;
+
+ text = document.createElement('font');
+ text.id = "jsPDFCell";
+
+ try {
+ text.style.fontStyle = fontStyle;
+ } catch (e) {
+ text.style.fontWeight = fontStyle;
+ }
+
+ text.style.fontSize = fontSize + 'pt';
+ text.style.fontFamily = fontName;
+ try {
+ text.textContent = txt;
+ } catch (e) {
+ text.innerText = txt;
+ }
+
+ document.body.appendChild(text);
+
+ dimensions = { w: (text.offsetWidth + 1) * px2pt, h: (text.offsetHeight + 1) * px2pt };
+
+ document.body.removeChild(text);
+
+ return dimensions;
+ };
+
+ jsPDFAPI.cellAddPage = function () {
+ var margins = this.margins || NO_MARGINS;
+
+ this.addPage();
+
+ setLastCellPosition(margins.left, margins.top, undefined, undefined);
+ //setLastCellPosition(undefined, undefined, undefined, undefined, undefined);
+ pages += 1;
+ };
+
+ jsPDFAPI.cellInitialize = function () {
+ lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined };
+ pages = 1;
+ };
+
+ jsPDFAPI.cell = function (x, y, w, h, txt, ln, align) {
+ var curCell = getLastCellPosition();
+ var pgAdded = false;
+
+ // If this is not the first cell, we must change its position
+ if (curCell.ln !== undefined) {
+ if (curCell.ln === ln) {
+ //Same line
+ x = curCell.x + curCell.w;
+ y = curCell.y;
+ } else {
+ //New line
+ var margins = this.margins || NO_MARGINS;
+ if (curCell.y + curCell.h + h + margin >= this.internal.pageSize.getHeight() - margins.bottom) {
+ this.cellAddPage();
+ pgAdded = true;
+ if (this.printHeaders && this.tableHeaderRow) {
+ this.printHeaderRow(ln, true);
+ }
+ }
+ //We ignore the passed y: the lines may have different heights
+ y = getLastCellPosition().y + getLastCellPosition().h;
+ if (pgAdded) y = margin + 10;
+ }
+ }
+
+ if (txt[0] !== undefined) {
+ if (this.printingHeaderRow) {
+ this.rect(x, y, w, h, 'FD');
+ } else {
+ this.rect(x, y, w, h);
+ }
+ if (align === 'right') {
+ if (!(txt instanceof Array)) {
+ txt = [txt];
+ }
+ for (var i = 0; i < txt.length; i++) {
+ var currentLine = txt[i];
+ var textSize = this.getStringUnitWidth(currentLine) * this.internal.getFontSize();
+ this.text(currentLine, x + w - textSize - padding, y + this.internal.getLineHeight() * (i + 1));
+ }
+ } else {
+ this.text(txt, x + padding, y + this.internal.getLineHeight());
+ }
+ }
+ setLastCellPosition(x, y, w, h, ln);
+ return this;
+ };
+
+ /**
+ * Return the maximum value from an array
+ * @param array
+ * @param comparisonFn
+ * @returns {*}
+ */
+ jsPDFAPI.arrayMax = function (array, comparisonFn) {
+ var max = array[0],
+ i,
+ ln,
+ item;
+
+ for (i = 0, ln = array.length; i < ln; i += 1) {
+ item = array[i];
+
+ if (comparisonFn) {
+ if (comparisonFn(max, item) === -1) {
+ max = item;
+ }
+ } else {
+ if (item > max) {
+ max = item;
+ }
+ }
+ }
+
+ return max;
+ };
+
+ /**
+ * Create a table from a set of data.
+ * @param {Integer} [x] : left-position for top-left corner of table
+ * @param {Integer} [y] top-position for top-left corner of table
+ * @param {Object[]} [data] As array of objects containing key-value pairs corresponding to a row of data.
+ * @param {String[]} [headers] Omit or null to auto-generate headers at a performance cost
+ * @param {Object} [config.printHeaders] True to print column headers at the top of every page
+ * @param {Object} [config.autoSize] True to dynamically set the column widths to match the widest cell value
+ * @param {Object} [config.margins] margin values for left, top, bottom, and width
+ * @param {Object} [config.fontSize] Integer fontSize to use (optional)
+ */
+
+ jsPDFAPI.table = function (x, y, data, headers, config) {
+ if (!data) {
+ throw 'No data for PDF table';
+ }
+
+ var headerNames = [],
+ headerPrompts = [],
+ header,
+ i,
+ ln,
+ cln,
+ columnMatrix = {},
+ columnWidths = {},
+ columnData,
+ column,
+ columnMinWidths = [],
+ j,
+ tableHeaderConfigs = [],
+ model,
+ jln,
+ func,
+
+
+ //set up defaults. If a value is provided in config, defaults will be overwritten:
+ autoSize = false,
+ printHeaders = true,
+ fontSize = 12,
+ margins = NO_MARGINS;
+
+ margins.width = this.internal.pageSize.getWidth();
+
+ if (config) {
+ //override config defaults if the user has specified non-default behavior:
+ if (config.autoSize === true) {
+ autoSize = true;
+ }
+ if (config.printHeaders === false) {
+ printHeaders = false;
+ }
+ if (config.fontSize) {
+ fontSize = config.fontSize;
+ }
+ if (config.css && typeof config.css['font-size'] !== "undefined") {
+ fontSize = config.css['font-size'] * 16;
+ }
+ if (config.margins) {
+ margins = config.margins;
+ }
+ }
+
+ /**
+ * @property {Number} lnMod
+ * Keep track of the current line number modifier used when creating cells
+ */
+ this.lnMod = 0;
+ lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }, pages = 1;
+
+ this.printHeaders = printHeaders;
+ this.margins = margins;
+ this.setFontSize(fontSize);
+ this.table_font_size = fontSize;
+
+ // Set header values
+ if (headers === undefined || headers === null) {
+ // No headers defined so we derive from data
+ headerNames = Object.keys(data[0]);
+ } else if (headers[0] && typeof headers[0] !== 'string') {
+ var px2pt = 0.264583 * 72 / 25.4;
+
+ // Split header configs into names and prompts
+ for (i = 0, ln = headers.length; i < ln; i += 1) {
+ header = headers[i];
+ headerNames.push(header.name);
+ headerPrompts.push(header.prompt);
+ columnWidths[header.name] = header.width * px2pt;
+ }
+ } else {
+ headerNames = headers;
+ }
+
+ if (autoSize) {
+ // Create a matrix of columns e.g., {column_title: [row1_Record, row2_Record]}
+ func = function func(rec) {
+ return rec[header];
+ };
+
+ for (i = 0, ln = headerNames.length; i < ln; i += 1) {
+ header = headerNames[i];
+
+ columnMatrix[header] = data.map(func);
+
+ // get header width
+ columnMinWidths.push(this.getTextDimensions(headerPrompts[i] || header).w);
+ column = columnMatrix[header];
+
+ // get cell widths
+ for (j = 0, cln = column.length; j < cln; j += 1) {
+ columnData = column[j];
+ columnMinWidths.push(this.getTextDimensions(columnData).w);
+ }
+
+ // get final column width
+ columnWidths[header] = jsPDFAPI.arrayMax(columnMinWidths);
+
+ //have to reset
+ columnMinWidths = [];
+ }
+ }
+
+ // -- Construct the table
+
+ if (printHeaders) {
+ var lineHeight = this.calculateLineHeight(headerNames, columnWidths, headerPrompts.length ? headerPrompts : headerNames);
+
+ // Construct the header row
+ for (i = 0, ln = headerNames.length; i < ln; i += 1) {
+ header = headerNames[i];
+ tableHeaderConfigs.push([x, y, columnWidths[header], lineHeight, String(headerPrompts.length ? headerPrompts[i] : header)]);
+ }
+
+ // Store the table header config
+ this.setTableHeaderRow(tableHeaderConfigs);
+
+ // Print the header for the start of the table
+ this.printHeaderRow(1, false);
+ }
+
+ // Construct the data rows
+ for (i = 0, ln = data.length; i < ln; i += 1) {
+ var lineHeight;
+ model = data[i];
+ lineHeight = this.calculateLineHeight(headerNames, columnWidths, model);
+
+ for (j = 0, jln = headerNames.length; j < jln; j += 1) {
+ header = headerNames[j];
+ this.cell(x, y, columnWidths[header], lineHeight, model[header], i + 2, header.align);
+ }
+ }
+ this.lastCellPos = lastCellPos;
+ this.table_x = x;
+ this.table_y = y;
+ return this;
+ };
+ /**
+ * Calculate the height for containing the highest column
+ * @param {String[]} headerNames is the header, used as keys to the data
+ * @param {Integer[]} columnWidths is size of each column
+ * @param {Object[]} model is the line of data we want to calculate the height of
+ */
+ jsPDFAPI.calculateLineHeight = function (headerNames, columnWidths, model) {
+ var header,
+ lineHeight = 0;
+ for (var j = 0; j < headerNames.length; j++) {
+ header = headerNames[j];
+ model[header] = this.splitTextToSize(String(model[header]), columnWidths[header] - padding);
+ var h = this.internal.getLineHeight() * model[header].length + padding;
+ if (h > lineHeight) lineHeight = h;
+ }
+ return lineHeight;
+ };
+
+ /**
+ * Store the config for outputting a table header
+ * @param {Object[]} config
+ * An array of cell configs that would define a header row: Each config matches the config used by jsPDFAPI.cell
+ * except the ln parameter is excluded
+ */
+ jsPDFAPI.setTableHeaderRow = function (config) {
+ this.tableHeaderRow = config;
+ };
+
+ /**
+ * Output the store header row
+ * @param lineNumber The line number to output the header at
+ */
+ jsPDFAPI.printHeaderRow = function (lineNumber, new_page) {
+ if (!this.tableHeaderRow) {
+ throw 'Property tableHeaderRow does not exist.';
+ }
+
+ var tableHeaderCell, tmpArray, i, ln;
+
+ this.printingHeaderRow = true;
+ if (headerFunction !== undefined) {
+ var position = headerFunction(this, pages);
+ setLastCellPosition(position[0], position[1], position[2], position[3], -1);
+ }
+ this.setFontStyle('bold');
+ var tempHeaderConf = [];
+ for (i = 0, ln = this.tableHeaderRow.length; i < ln; i += 1) {
+ this.setFillColor(200, 200, 200);
+
+ tableHeaderCell = this.tableHeaderRow[i];
+ if (new_page) {
+ this.margins.top = margin;
+ tableHeaderCell[1] = this.margins && this.margins.top || 0;
+ tempHeaderConf.push(tableHeaderCell);
+ }
+ tmpArray = [].concat(tableHeaderCell);
+ this.cell.apply(this, tmpArray.concat(lineNumber));
+ }
+ if (tempHeaderConf.length > 0) {
+ this.setTableHeaderRow(tempHeaderConf);
+ }
+ this.setFontStyle('normal');
+ this.printingHeaderRow = false;
+ };
+ })(jsPDF.API);
+
+ /**
+ * jsPDF Context2D PlugIn Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
+ *
+ * Licensed under the MIT License. http://opensource.org/licenses/mit-license
+ */
+
+ /**
+ * This plugin mimics the HTML5 Canvas's context2d.
+ *
+ * The goal is to provide a way for current canvas implementations to print directly to a PDF.
+ */
+
+ /**
+ * TODO implement stroke opacity (refactor from fill() method )
+ * TODO transform angle and radii parameters
+ */
+
+ /**
+ * require('jspdf.js'); require('lib/css_colors.js');
+ */
+
+ (function (jsPDFAPI) {
+
+ jsPDFAPI.events.push(['initialized', function () {
+ this.context2d.pdf = this;
+ this.context2d.internal.pdf = this;
+ this.context2d.ctx = new context();
+ this.context2d.ctxStack = [];
+ this.context2d.path = [];
+ }]);
+
+ jsPDFAPI.context2d = {
+ pageWrapXEnabled: false,
+ pageWrapYEnabled: false,
+ pageWrapX: 9999999,
+ pageWrapY: 9999999,
+ ctx: new context(),
+ f2: function f2(number) {
+ return number.toFixed(2);
+ },
+
+ fillRect: function fillRect(x, y, w, h) {
+ if (this._isFillTransparent()) {
+ return;
+ }
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h });
+ this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "f");
+ },
+
+ strokeRect: function strokeRect(x, y, w, h) {
+ if (this._isStrokeTransparent()) {
+ return;
+ }
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h });
+ this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "s");
+ },
+
+ /**
+ * We cannot clear PDF commands that were already written to PDF, so we use white instead.
+ * As a special case, read a special flag (ignoreClearRect) and do nothing if it is set.
+ * This results in all calls to clearRect() to do nothing, and keep the canvas transparent.
+ * This flag is stored in the save/restore context and is managed the same way as other drawing states.
+ * @param x
+ * @param y
+ * @param w
+ * @param h
+ */
+ clearRect: function clearRect(x, y, w, h) {
+ if (this.ctx.ignoreClearRect) {
+ return;
+ }
+
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h });
+ this.save();
+ this.setFillStyle('#ffffff');
+ //TODO This is hack to fill with white.
+ this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "f");
+ this.restore();
+ },
+
+ save: function save() {
+ this.ctx._fontSize = this.pdf.internal.getFontSize();
+ var ctx = new context();
+ ctx.copy(this.ctx);
+ this.ctxStack.push(this.ctx);
+ this.ctx = ctx;
+ },
+
+ restore: function restore() {
+ this.ctx = this.ctxStack.pop();
+ this.setFillStyle(this.ctx.fillStyle);
+ this.setStrokeStyle(this.ctx.strokeStyle);
+ this.setFont(this.ctx.font);
+ this.pdf.setFontSize(this.ctx._fontSize);
+ this.setLineCap(this.ctx.lineCap);
+ this.setLineWidth(this.ctx.lineWidth);
+ this.setLineJoin(this.ctx.lineJoin);
+ },
+
+ rect: function rect(x, y, w, h) {
+ this.moveTo(x, y);
+ this.lineTo(x + w, y);
+ this.lineTo(x + w, y + h);
+ this.lineTo(x, y + h);
+ this.lineTo(x, y); //TODO not needed
+ this.closePath();
+ },
+
+ beginPath: function beginPath() {
+ this.path = [];
+ },
+
+ closePath: function closePath() {
+ this.path.push({
+ type: 'close'
+ });
+ },
+
+ _getRGBA: function _getRGBA(style) {
+ // get the decimal values of r, g, and b;
+ var r, g, b, a;
+ if (!style) {
+ return { r: 0, g: 0, b: 0, a: 0, style: style };
+ }
+
+ if (this.internal.rxTransparent.test(style)) {
+ r = 0;
+ g = 0;
+ b = 0;
+ a = 0;
+ } else {
+ var m = this.internal.rxRgb.exec(style);
+ if (m != null) {
+ r = parseInt(m[1]);
+ g = parseInt(m[2]);
+ b = parseInt(m[3]);
+ a = 1;
+ } else {
+ m = this.internal.rxRgba.exec(style);
+ if (m != null) {
+ r = parseInt(m[1]);
+ g = parseInt(m[2]);
+ b = parseInt(m[3]);
+ a = parseFloat(m[4]);
+ } else {
+ a = 1;
+ if (style.charAt(0) != '#') {
+ style = CssColors.colorNameToHex(style);
+ if (!style) {
+ style = '#000000';
+ }
+ }
+
+ if (style.length === 4) {
+ r = style.substring(1, 2);
+ r += r;
+ g = style.substring(2, 3);
+ g += g;
+ b = style.substring(3, 4);
+ b += b;
+ } else {
+ r = style.substring(1, 3);
+ g = style.substring(3, 5);
+ b = style.substring(5, 7);
+ }
+ r = parseInt(r, 16);
+ g = parseInt(g, 16);
+ b = parseInt(b, 16);
+ }
+ }
+ }
+ return { r: r, g: g, b: b, a: a, style: style };
+ },
+
+ setFillStyle: function setFillStyle(style) {
+ var rgba = this._getRGBA(style);
+
+ this.ctx.fillStyle = style;
+ this.ctx._isFillTransparent = rgba.a === 0;
+ this.ctx._fillOpacity = rgba.a;
+
+ this.pdf.setFillColor(rgba.r, rgba.g, rgba.b, {
+ a: rgba.a
+ });
+ this.pdf.setTextColor(rgba.r, rgba.g, rgba.b, {
+ a: rgba.a
+ });
+ },
+
+ setStrokeStyle: function setStrokeStyle(style) {
+ var rgba = this._getRGBA(style);
+
+ this.ctx.strokeStyle = rgba.style;
+ this.ctx._isStrokeTransparent = rgba.a === 0;
+ this.ctx._strokeOpacity = rgba.a;
+
+ //TODO jsPDF to handle rgba
+ if (rgba.a === 0) {
+ this.pdf.setDrawColor(255, 255, 255);
+ } else if (rgba.a === 1) {
+ this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b);
+ } else {
+ //this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b, {a: rgba.a});
+ this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b);
+ }
+ },
+
+ fillText: function fillText(text, x, y, maxWidth) {
+ if (this._isFillTransparent()) {
+ return;
+ }
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+ var rads = this._matrix_rotation(this.ctx._transform);
+ var degs = rads * 57.2958;
+
+ //TODO only push the clip if it has not been applied to the current PDF context
+ if (this.ctx._clip_path.length > 0) {
+ var lines;
+ if (window.outIntercept) {
+ lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept;
+ } else {
+ lines = this.internal.getCurrentPage();
+ }
+ lines.push("q");
+ var origPath = this.path;
+ this.path = this.ctx._clip_path;
+ this.ctx._clip_path = [];
+ this._fill(null, true);
+ this.ctx._clip_path = this.path;
+ this.path = origPath;
+ }
+
+ // We only use X axis as scale hint
+ var scale = 1;
+ try {
+ scale = this._matrix_decompose(this._getTransform()).scale[0];
+ } catch (e) {
+ console.warn(e);
+ }
+
+ // In some cases the transform was very small (5.715760606202283e-17). Most likely a canvg rounding error.
+ if (scale < 0.01) {
+ this.pdf.text(text, x, this._getBaseline(y), null, degs);
+ } else {
+ var oldSize = this.pdf.internal.getFontSize();
+ this.pdf.setFontSize(oldSize * scale);
+ this.pdf.text(text, x, this._getBaseline(y), null, degs);
+ this.pdf.setFontSize(oldSize);
+ }
+
+ if (this.ctx._clip_path.length > 0) {
+ lines.push('Q');
+ }
+ },
+
+ strokeText: function strokeText(text, x, y, maxWidth) {
+ if (this._isStrokeTransparent()) {
+ return;
+ }
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+ var rads = this._matrix_rotation(this.ctx._transform);
+ var degs = rads * 57.2958;
+
+ //TODO only push the clip if it has not been applied to the current PDF context
+ if (this.ctx._clip_path.length > 0) {
+ var lines;
+ if (window.outIntercept) {
+ lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept;
+ } else {
+ lines = this.internal.getCurrentPage();
+ }
+ lines.push("q");
+ var origPath = this.path;
+ this.path = this.ctx._clip_path;
+ this.ctx._clip_path = [];
+ this._fill(null, true);
+ this.ctx._clip_path = this.path;
+ this.path = origPath;
+ }
+
+ var scale = 1;
+ // We only use the X axis as scale hint
+ try {
+ scale = this._matrix_decompose(this._getTransform()).scale[0];
+ } catch (e) {
+ console.warn(e);
+ }
+
+ if (scale === 1) {
+ this.pdf.text(text, x, this._getBaseline(y), {
+ stroke: true
+ }, degs);
+ } else {
+ var oldSize = this.pdf.internal.getFontSize();
+ this.pdf.setFontSize(oldSize * scale);
+ this.pdf.text(text, x, this._getBaseline(y), {
+ stroke: true
+ }, degs);
+ this.pdf.setFontSize(oldSize);
+ }
+
+ if (this.ctx._clip_path.length > 0) {
+ lines.push('Q');
+ }
+ },
+
+ setFont: function setFont(font) {
+ this.ctx.font = font;
+
+ //var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+([\d\.]+)(px|pt|em)\s+["']?(\w+)['"]?/;
+ var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+([\d\.]+)(px|pt|em)\s+(.*)?/;
+ m = rx.exec(font);
+ if (m != null) {
+ var fontStyle = m[1];
+ var fontVariant = m[2];
+ var fontWeight = m[3];
+ var fontSize = m[4];
+ var fontSizeUnit = m[5];
+ var fontFamily = m[6];
+
+ if ('px' === fontSizeUnit) {
+ fontSize = Math.floor(parseFloat(fontSize));
+ // fontSize = fontSize * 1.25;
+ } else if ('em' === fontSizeUnit) {
+ fontSize = Math.floor(parseFloat(fontSize) * this.pdf.getFontSize());
+ } else {
+ fontSize = Math.floor(parseFloat(fontSize));
+ }
+
+ this.pdf.setFontSize(fontSize);
+
+ if (fontWeight === 'bold' || fontWeight === '700') {
+ this.pdf.setFontStyle('bold');
+ } else {
+ if (fontStyle === 'italic') {
+ this.pdf.setFontStyle('italic');
+ } else {
+ this.pdf.setFontStyle('normal');
+ }
+ }
+
+ var name = fontFamily;
+ var parts = name.toLowerCase().split(/\s*,\s*/);
+ var jsPdfFontName;
+
+ if (parts.indexOf('arial') != -1) {
+ jsPdfFontName = 'Arial';
+ } else if (parts.indexOf('verdana') != -1) {
+ jsPdfFontName = 'Verdana';
+ } else if (parts.indexOf('helvetica') != -1) {
+ jsPdfFontName = 'Helvetica';
+ } else if (parts.indexOf('sans-serif') != -1) {
+ jsPdfFontName = 'sans-serif';
+ } else if (parts.indexOf('fixed') != -1) {
+ jsPdfFontName = 'Fixed';
+ } else if (parts.indexOf('monospace') != -1) {
+ jsPdfFontName = 'Monospace';
+ } else if (parts.indexOf('terminal') != -1) {
+ jsPdfFontName = 'Terminal';
+ } else if (parts.indexOf('courier') != -1) {
+ jsPdfFontName = 'Courier';
+ } else if (parts.indexOf('times') != -1) {
+ jsPdfFontName = 'Times';
+ } else if (parts.indexOf('cursive') != -1) {
+ jsPdfFontName = 'Cursive';
+ } else if (parts.indexOf('fantasy') != -1) {
+ jsPdfFontName = 'Fantasy';
+ } else if (parts.indexOf('serif') != -1) {
+ jsPdfFontName = 'Serif';
+ } else {
+ jsPdfFontName = 'Serif';
+ }
+
+ //TODO check more cases
+ var style;
+ if ('bold' === fontWeight) {
+ style = 'bold';
+ } else {
+ style = 'normal';
+ }
+
+ this.pdf.setFont(jsPdfFontName, style);
+ } else {
+ var rx = /\s*(\d+)(pt|px|em)\s+([\w "]+)\s*([\w "]+)?/;
+ var m = rx.exec(font);
+ if (m != null) {
+ var size = m[1];
+ var unit = m[2];
+ var name = m[3];
+ var style = m[4];
+ if (!style) {
+ style = 'normal';
+ }
+ if ('em' === fontSizeUnit) {
+ size = Math.floor(parseFloat(fontSize) * this.pdf.getFontSize());
+ } else {
+ size = Math.floor(parseFloat(size));
+ }
+ this.pdf.setFontSize(size);
+ this.pdf.setFont(name, style);
+ }
+ }
+ },
+
+ setTextBaseline: function setTextBaseline(baseline) {
+ this.ctx.textBaseline = baseline;
+ },
+
+ getTextBaseline: function getTextBaseline() {
+ return this.ctx.textBaseline;
+ },
+
+ //TODO implement textAlign
+ setTextAlign: function setTextAlign(align) {
+ this.ctx.textAlign = align;
+ },
+
+ getTextAlign: function getTextAlign() {
+ return this.ctx.textAlign;
+ },
+
+ setLineWidth: function setLineWidth(width) {
+ this.ctx.lineWidth = width;
+ this.pdf.setLineWidth(width);
+ },
+
+ setLineCap: function setLineCap(style) {
+ this.ctx.lineCap = style;
+ this.pdf.setLineCap(style);
+ },
+
+ setLineJoin: function setLineJoin(style) {
+ this.ctx.lineJoin = style;
+ this.pdf.setLineJoin(style);
+ },
+
+ moveTo: function moveTo(x, y) {
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+
+ var obj = {
+ type: 'mt',
+ x: x,
+ y: y
+ };
+ this.path.push(obj);
+ },
+
+ _wrapX: function _wrapX(x) {
+ if (this.pageWrapXEnabled) {
+ return x % this.pageWrapX;
+ } else {
+ return x;
+ }
+ },
+
+ _wrapY: function _wrapY(y) {
+ if (this.pageWrapYEnabled) {
+ this._gotoPage(this._page(y));
+ return (y - this.lastBreak) % this.pageWrapY;
+ } else {
+ return y;
+ }
+ },
+
+ transform: function transform(a, b, c, d, e, f) {
+ //TODO apply to current transformation instead of replacing
+ this.ctx._transform = [a, b, c, d, e, f];
+ },
+
+ setTransform: function setTransform(a, b, c, d, e, f) {
+ this.ctx._transform = [a, b, c, d, e, f];
+ },
+
+ _getTransform: function _getTransform() {
+ return this.ctx._transform;
+ },
+
+ lastBreak: 0,
+ // Y Position of page breaks.
+ pageBreaks: [],
+ // returns: One-based Page Number
+ // Should only be used if pageWrapYEnabled is true
+ _page: function _page(y) {
+ if (this.pageWrapYEnabled) {
+ this.lastBreak = 0;
+ var manualBreaks = 0;
+ var autoBreaks = 0;
+ for (var i = 0; i < this.pageBreaks.length; i++) {
+ if (y >= this.pageBreaks[i]) {
+ manualBreaks++;
+ if (this.lastBreak === 0) {
+ autoBreaks++;
+ }
+ var spaceBetweenLastBreak = this.pageBreaks[i] - this.lastBreak;
+ this.lastBreak = this.pageBreaks[i];
+ var pagesSinceLastBreak = Math.floor(spaceBetweenLastBreak / this.pageWrapY);
+ autoBreaks += pagesSinceLastBreak;
+ }
+ }
+ if (this.lastBreak === 0) {
+ var pagesSinceLastBreak = Math.floor(y / this.pageWrapY) + 1;
+ autoBreaks += pagesSinceLastBreak;
+ }
+ return autoBreaks + manualBreaks;
+ } else {
+ return this.pdf.internal.getCurrentPageInfo().pageNumber;
+ }
+ },
+
+ _gotoPage: function _gotoPage(pageOneBased) {
+ // This is a stub to be overriden if needed
+ },
+
+ lineTo: function lineTo(x, y) {
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+
+ var obj = {
+ type: 'lt',
+ x: x,
+ y: y
+ };
+ this.path.push(obj);
+ },
+
+ bezierCurveTo: function bezierCurveTo(x1, y1, x2, y2, x, y) {
+ x1 = this._wrapX(x1);
+ y1 = this._wrapY(y1);
+ x2 = this._wrapX(x2);
+ y2 = this._wrapY(y2);
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xpt;
+ xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+ xpt = this._matrix_map_point(this.ctx._transform, [x1, y1]);
+ x1 = xpt[0];
+ y1 = xpt[1];
+ xpt = this._matrix_map_point(this.ctx._transform, [x2, y2]);
+ x2 = xpt[0];
+ y2 = xpt[1];
+
+ var obj = {
+ type: 'bct',
+ x1: x1,
+ y1: y1,
+ x2: x2,
+ y2: y2,
+ x: x,
+ y: y
+ };
+ this.path.push(obj);
+ },
+
+ quadraticCurveTo: function quadraticCurveTo(x1, y1, x, y) {
+ x1 = this._wrapX(x1);
+ y1 = this._wrapY(y1);
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xpt;
+ xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+ xpt = this._matrix_map_point(this.ctx._transform, [x1, y1]);
+ x1 = xpt[0];
+ y1 = xpt[1];
+
+ var obj = {
+ type: 'qct',
+ x1: x1,
+ y1: y1,
+ x: x,
+ y: y
+ };
+ this.path.push(obj);
+ },
+
+ arc: function arc(x, y, radius, startAngle, endAngle, anticlockwise) {
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ if (!this._matrix_is_identity(this.ctx._transform)) {
+ var xpt = this._matrix_map_point(this.ctx._transform, [x, y]);
+ x = xpt[0];
+ y = xpt[1];
+
+ var x_radPt0 = this._matrix_map_point(this.ctx._transform, [0, 0]);
+ var x_radPt = this._matrix_map_point(this.ctx._transform, [0, radius]);
+ radius = Math.sqrt(Math.pow(x_radPt[0] - x_radPt0[0], 2) + Math.pow(x_radPt[1] - x_radPt0[1], 2));
+
+ //TODO angles need to be transformed
+ }
+
+ var obj = {
+ type: 'arc',
+ x: x,
+ y: y,
+ radius: radius,
+ startAngle: startAngle,
+ endAngle: endAngle,
+ anticlockwise: anticlockwise
+ };
+ this.path.push(obj);
+ },
+
+ drawImage: function drawImage(img, x, y, w, h, x2, y2, w2, h2) {
+ if (x2 !== undefined) {
+ x = x2;
+ y = y2;
+ w = w2;
+ h = h2;
+ }
+ x = this._wrapX(x);
+ y = this._wrapY(y);
+
+ var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h });
+ var xRect2 = this._matrix_map_rect(this.ctx._transform, { x: x2, y: y2, w: w2, h: h2 });
+
+ // TODO implement source clipping and image scaling
+ var format;
+ var rx = /data:image\/(\w+).*/i;
+ var m = rx.exec(img);
+ if (m != null) {
+ format = m[1];
+ } else {
+ // format = "jpeg";
+ format = "png";
+ }
+
+ this.pdf.addImage(img, format, xRect.x, xRect.y, xRect.w, xRect.h);
+ },
+
+ /**
+ * Multiply the first matrix by the second
+ * @param m1
+ * @param m2
+ * @returns {*[]}
+ * @private
+ */
+ _matrix_multiply: function _matrix_multiply(m2, m1) {
+ var sx = m1[0];
+ var shy = m1[1];
+ var shx = m1[2];
+ var sy = m1[3];
+ var tx = m1[4];
+ var ty = m1[5];
+
+ var t0 = sx * m2[0] + shy * m2[2];
+ var t2 = shx * m2[0] + sy * m2[2];
+ var t4 = tx * m2[0] + ty * m2[2] + m2[4];
+ shy = sx * m2[1] + shy * m2[3];
+ sy = shx * m2[1] + sy * m2[3];
+ ty = tx * m2[1] + ty * m2[3] + m2[5];
+ sx = t0;
+ shx = t2;
+ tx = t4;
+
+ return [sx, shy, shx, sy, tx, ty];
+ },
+
+ _matrix_rotation: function _matrix_rotation(m) {
+ return Math.atan2(m[2], m[0]);
+ },
+
+ _matrix_decompose: function _matrix_decompose(matrix) {
+
+ var a = matrix[0];
+ var b = matrix[1];
+ var c = matrix[2];
+ var d = matrix[3];
+
+ var scaleX = Math.sqrt(a * a + b * b);
+ a /= scaleX;
+ b /= scaleX;
+
+ var shear = a * c + b * d;
+ c -= a * shear;
+ d -= b * shear;
+
+ var scaleY = Math.sqrt(c * c + d * d);
+ c /= scaleY;
+ d /= scaleY;
+ shear /= scaleY;
+
+ if (a * d < b * c) {
+ a = -a;
+ b = -b;
+ shear = -shear;
+ scaleX = -scaleX;
+ }
+
+ return {
+ scale: [scaleX, 0, 0, scaleY, 0, 0],
+ translate: [1, 0, 0, 1, matrix[4], matrix[5]],
+ rotate: [a, b, -b, a, 0, 0],
+ skew: [1, 0, shear, 1, 0, 0]
+ };
+ },
+
+ _matrix_map_point: function _matrix_map_point(m1, pt) {
+ var sx = m1[0];
+ var shy = m1[1];
+ var shx = m1[2];
+ var sy = m1[3];
+ var tx = m1[4];
+ var ty = m1[5];
+
+ var px = pt[0];
+ var py = pt[1];
+
+ var x = px * sx + py * shx + tx;
+ var y = px * shy + py * sy + ty;
+ return [x, y];
+ },
+
+ _matrix_map_point_obj: function _matrix_map_point_obj(m1, pt) {
+ var xpt = this._matrix_map_point(m1, [pt.x, pt.y]);
+ return { x: xpt[0], y: xpt[1] };
+ },
+
+ _matrix_map_rect: function _matrix_map_rect(m1, rect) {
+ var p1 = this._matrix_map_point(m1, [rect.x, rect.y]);
+ var p2 = this._matrix_map_point(m1, [rect.x + rect.w, rect.y + rect.h]);
+ return { x: p1[0], y: p1[1], w: p2[0] - p1[0], h: p2[1] - p1[1] };
+ },
+
+ _matrix_is_identity: function _matrix_is_identity(m1) {
+ if (m1[0] != 1) {
+ return false;
+ }
+ if (m1[1] != 0) {
+ return false;
+ }
+ if (m1[2] != 0) {
+ return false;
+ }
+ if (m1[3] != 1) {
+ return false;
+ }
+ if (m1[4] != 0) {
+ return false;
+ }
+ if (m1[5] != 0) {
+ return false;
+ }
+ return true;
+ },
+
+ rotate: function rotate(angle) {
+ var matrix = [Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0.0, 0.0];
+ this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix);
+ },
+
+ scale: function scale(sx, sy) {
+ var matrix = [sx, 0.0, 0.0, sy, 0.0, 0.0];
+ this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix);
+ },
+
+ translate: function translate(x, y) {
+ var matrix = [1.0, 0.0, 0.0, 1.0, x, y];
+ this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix);
+ },
+
+ stroke: function stroke() {
+ if (this.ctx._clip_path.length > 0) {
+
+ var lines;
+ if (window.outIntercept) {
+ lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept;
+ } else {
+ lines = this.internal.getCurrentPage();
+ }
+ lines.push("q");
+
+ var origPath = this.path;
+ this.path = this.ctx._clip_path;
+ this.ctx._clip_path = [];
+ this._stroke(true);
+
+ this.ctx._clip_path = this.path;
+ this.path = origPath;
+ this._stroke(false);
+
+ lines.push("Q");
+ } else {
+ this._stroke(false);
+ }
+ },
+
+ _stroke: function _stroke(isClip) {
+ if (!isClip && this._isStrokeTransparent()) {
+ return;
+ }
+
+ //TODO opacity
+
+ var moves = [];
+
+ var xPath = this.path;
+
+ for (var i = 0; i < xPath.length; i++) {
+ var pt = xPath[i];
+ switch (pt.type) {
+ case 'mt':
+ moves.push({ start: pt, deltas: [], abs: [] });
+ break;
+ case 'lt':
+ var delta = [pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y];
+ moves[moves.length - 1].deltas.push(delta);
+ moves[moves.length - 1].abs.push(pt);
+ break;
+ case 'bct':
+ var delta = [pt.x1 - xPath[i - 1].x, pt.y1 - xPath[i - 1].y, pt.x2 - xPath[i - 1].x, pt.y2 - xPath[i - 1].y, pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y];
+ moves[moves.length - 1].deltas.push(delta);
+ break;
+ case 'qct':
+ // convert to bezier
+ var x1 = xPath[i - 1].x + 2.0 / 3.0 * (pt.x1 - xPath[i - 1].x);
+ var y1 = xPath[i - 1].y + 2.0 / 3.0 * (pt.y1 - xPath[i - 1].y);
+ var x2 = pt.x + 2.0 / 3.0 * (pt.x1 - pt.x);
+ var y2 = pt.y + 2.0 / 3.0 * (pt.y1 - pt.y);
+ var x3 = pt.x;
+ var y3 = pt.y;
+ var delta = [x1 - xPath[i - 1].x, y1 - xPath[i - 1].y, x2 - xPath[i - 1].x, y2 - xPath[i - 1].y, x3 - xPath[i - 1].x, y3 - xPath[i - 1].y];
+ moves[moves.length - 1].deltas.push(delta);
+ break;
+ case 'arc':
+ //TODO this was hack to avoid out-of-bounds issue
+ // No move-to before drawing the arc
+ if (moves.length == 0) {
+ moves.push({ start: { x: 0, y: 0 }, deltas: [], abs: [] });
+ }
+ moves[moves.length - 1].arc = true;
+ moves[moves.length - 1].abs.push(pt);
+ break;
+ case 'close':
+ break;
+ }
+ }
+
+ for (var i = 0; i < moves.length; i++) {
+ var style;
+ if (i == moves.length - 1) {
+ style = 's';
+ } else {
+ style = null;
+ }
+ if (moves[i].arc) {
+ var arcs = moves[i].abs;
+ for (var ii = 0; ii < arcs.length; ii++) {
+ var arc = arcs[ii];
+ var start = arc.startAngle * 360 / (2 * Math.PI);
+ var end = arc.endAngle * 360 / (2 * Math.PI);
+ var x = arc.x;
+ var y = arc.y;
+ this.internal.arc2(this, x, y, arc.radius, start, end, arc.anticlockwise, style, isClip);
+ }
+ } else {
+ var x = moves[i].start.x;
+ var y = moves[i].start.y;
+ if (!isClip) {
+ this.pdf.lines(moves[i].deltas, x, y, null, style);
+ } else {
+ this.pdf.lines(moves[i].deltas, x, y, null, null);
+ this.pdf.clip_fixed();
+ }
+ }
+ }
+ },
+
+ _isFillTransparent: function _isFillTransparent() {
+ return this.ctx._isFillTransparent || this.globalAlpha == 0;
+ },
+
+ _isStrokeTransparent: function _isStrokeTransparent() {
+ return this.ctx._isStrokeTransparent || this.globalAlpha == 0;
+ },
+
+ fill: function fill(fillRule) {
+ //evenodd or nonzero (default)
+ if (this.ctx._clip_path.length > 0) {
+
+ var lines;
+ if (window.outIntercept) {
+ lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept;
+ } else {
+ lines = this.internal.getCurrentPage();
+ }
+ lines.push("q");
+
+ var origPath = this.path;
+ this.path = this.ctx._clip_path;
+ this.ctx._clip_path = [];
+ this._fill(fillRule, true);
+
+ this.ctx._clip_path = this.path;
+ this.path = origPath;
+ this._fill(fillRule, false);
+
+ lines.push('Q');
+ } else {
+ this._fill(fillRule, false);
+ }
+ },
+
+ _fill: function _fill(fillRule, isClip) {
+ if (this._isFillTransparent()) {
+ return;
+ }
+ var v2Support = typeof this.pdf.internal.newObject2 === 'function';
+
+ var lines;
+ if (window.outIntercept) {
+ lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept;
+ } else {
+ lines = this.internal.getCurrentPage();
+ }
+
+ // if (this.ctx._clip_path.length > 0) {
+ // lines.push('q');
+ // var oldPath = this.path;
+ // this.path = this.ctx._clip_path;
+ // this.ctx._clip_path = [];
+ // this._fill(fillRule, true);
+ // this.ctx._clip_path = this.path;
+ // this.path = oldPath;
+ // }
+
+ var moves = [];
+ var outInterceptOld = window.outIntercept;
+
+ if (v2Support) {
+ // Blend and Mask
+ switch (this.ctx.globalCompositeOperation) {
+ case 'normal':
+ case 'source-over':
+ break;
+ case 'destination-in':
+ case 'destination-out':
+ //TODO this need to be added to the current group or page
+ // define a mask stream
+ var obj = this.pdf.internal.newStreamObject();
+
+ // define a mask state
+ var obj2 = this.pdf.internal.newObject2();
+ obj2.push('<>'); // /S /Luminosity will need to define color space
+ obj2.push('>>');
+
+ // add mask to page resources
+ var gsName = 'MASK' + obj2.objId;
+ this.pdf.internal.addGraphicsState(gsName, obj2.objId);
+
+ var instruction = '/' + gsName + ' gs';
+ // add mask to page, group, or stream
+ lines.splice(0, 0, 'q');
+ lines.splice(1, 0, instruction);
+ lines.push('Q');
+
+ window.outIntercept = obj;
+ break;
+ default:
+ var dictionaryEntry = '/' + this.pdf.internal.blendModeMap[this.ctx.globalCompositeOperation.toUpperCase()];
+ if (dictionaryEntry) {
+ this.pdf.internal.out(dictionaryEntry + ' gs');
+ }
+ break;
+ }
+ }
+
+ var alpha = this.ctx.globalAlpha;
+ if (this.ctx._fillOpacity < 1) {
+ // TODO combine this with global opacity
+ alpha = this.ctx._fillOpacity;
+ }
+
+ //TODO check for an opacity graphics state that was already created
+ //TODO do not set opacity if current value is already active
+ if (v2Support) {
+ var objOpac = this.pdf.internal.newObject2();
+ objOpac.push('<>');
+ var gsName = 'GS_O_' + objOpac.objId;
+ this.pdf.internal.addGraphicsState(gsName, objOpac.objId);
+ this.pdf.internal.out('/' + gsName + ' gs');
+ }
+
+ var xPath = this.path;
+
+ for (var i = 0; i < xPath.length; i++) {
+ var pt = xPath[i];
+ switch (pt.type) {
+ case 'mt':
+ moves.push({ start: pt, deltas: [], abs: [] });
+ break;
+ case 'lt':
+ var delta = [pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y];
+ moves[moves.length - 1].deltas.push(delta);
+ moves[moves.length - 1].abs.push(pt);
+ break;
+ case 'bct':
+ var delta = [pt.x1 - xPath[i - 1].x, pt.y1 - xPath[i - 1].y, pt.x2 - xPath[i - 1].x, pt.y2 - xPath[i - 1].y, pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y];
+ moves[moves.length - 1].deltas.push(delta);
+ break;
+ case 'qct':
+ // convert to bezier
+ var x1 = xPath[i - 1].x + 2.0 / 3.0 * (pt.x1 - xPath[i - 1].x);
+ var y1 = xPath[i - 1].y + 2.0 / 3.0 * (pt.y1 - xPath[i - 1].y);
+ var x2 = pt.x + 2.0 / 3.0 * (pt.x1 - pt.x);
+ var y2 = pt.y + 2.0 / 3.0 * (pt.y1 - pt.y);
+ var x3 = pt.x;
+ var y3 = pt.y;
+ var delta = [x1 - xPath[i - 1].x, y1 - xPath[i - 1].y, x2 - xPath[i - 1].x, y2 - xPath[i - 1].y, x3 - xPath[i - 1].x, y3 - xPath[i - 1].y];
+ moves[moves.length - 1].deltas.push(delta);
+ break;
+ case 'arc':
+ //TODO this was hack to avoid out-of-bounds issue when drawing circle
+ // No move-to before drawing the arc
+ if (moves.length === 0) {
+ moves.push({ deltas: [], abs: [] });
+ }
+ moves[moves.length - 1].arc = true;
+ moves[moves.length - 1].abs.push(pt);
+ break;
+ case 'close':
+ moves.push({ close: true });
+ break;
+ }
+ }
+
+ for (var i = 0; i < moves.length; i++) {
+ var style;
+ if (i == moves.length - 1) {
+ style = 'f';
+ if (fillRule === 'evenodd') {
+ style += '*';
+ }
+ } else {
+ style = null;
+ }
+
+ if (moves[i].close) {
+ this.pdf.internal.out('h');
+ if (style) {
+ // only fill at final path move
+ this.pdf.internal.out(style);
+ }
+ } else if (moves[i].arc) {
+ if (moves[i].start) {
+ this.internal.move2(this, moves[i].start.x, moves[i].start.y);
+ }
+ var arcs = moves[i].abs;
+ for (var ii = 0; ii < arcs.length; ii++) {
+ var arc = arcs[ii];
+ //TODO lines deltas were getting in here
+ if (typeof arc.startAngle !== 'undefined') {
+ var start = arc.startAngle * 360 / (2 * Math.PI);
+ var end = arc.endAngle * 360 / (2 * Math.PI);
+ var x = arc.x;
+ var y = arc.y;
+ if (ii === 0) {
+ this.internal.move2(this, x, y);
+ }
+ this.internal.arc2(this, x, y, arc.radius, start, end, arc.anticlockwise, null, isClip);
+ if (ii === arcs.length - 1) {
+ // The original arc move did not occur because of the algorithm
+ if (moves[i].start) {
+ var x = moves[i].start.x;
+ var y = moves[i].start.y;
+ this.internal.line2(c2d, x, y);
+ }
+ }
+ } else {
+ this.internal.line2(c2d, arc.x, arc.y);
+ }
+ }
+ } else {
+ var x = moves[i].start.x;
+ var y = moves[i].start.y;
+ if (!isClip) {
+ this.pdf.lines(moves[i].deltas, x, y, null, style);
+ } else {
+ this.pdf.lines(moves[i].deltas, x, y, null, null);
+ this.pdf.clip_fixed();
+ }
+ }
+ }
+
+ window.outIntercept = outInterceptOld;
+
+ // if (this.ctx._clip_path.length > 0) {
+ // lines.push('Q');
+ // }
+ },
+
+ pushMask: function pushMask() {
+ var v2Support = typeof this.pdf.internal.newObject2 === 'function';
+
+ if (!v2Support) {
+ console.log('jsPDF v2 not enabled');
+ return;
+ }
+
+ // define a mask stream
+ var obj = this.pdf.internal.newStreamObject();
+
+ // define a mask state
+ var obj2 = this.pdf.internal.newObject2();
+ obj2.push('<>'); // /S /Luminosity will need to define color space
+ obj2.push('>>');
+
+ // add mask to page resources
+ var gsName = 'MASK' + obj2.objId;
+ this.pdf.internal.addGraphicsState(gsName, obj2.objId);
+
+ var instruction = '/' + gsName + ' gs';
+ this.pdf.internal.out(instruction);
+ },
+
+ clip: function clip() {
+ //TODO do we reset the path, or just copy it?
+ if (this.ctx._clip_path.length > 0) {
+ for (var i = 0; i < this.path.length; i++) {
+ this.ctx._clip_path.push(this.path[i]);
+ }
+ } else {
+ this.ctx._clip_path = this.path;
+ }
+ this.path = [];
+ },
+
+ measureText: function measureText(text) {
+ var pdf = this.pdf;
+ return {
+ getWidth: function getWidth() {
+ var fontSize = pdf.internal.getFontSize();
+ var txtWidth = pdf.getStringUnitWidth(text) * fontSize / pdf.internal.scaleFactor;
+ // Convert points to pixels
+ txtWidth *= 1.3333;
+ return txtWidth;
+ },
+
+ get width() {
+ return this.getWidth(text);
+ }
+ };
+ },
+ _getBaseline: function _getBaseline(y) {
+ var height = parseInt(this.pdf.internal.getFontSize());
+ // TODO Get descent from font descriptor
+ var descent = height * 0.25;
+ switch (this.ctx.textBaseline) {
+ case 'bottom':
+ return y - descent;
+ case 'top':
+ return y + height;
+ case 'hanging':
+ return y + height - descent;
+ case 'middle':
+ return y + height / 2 - descent;
+ case 'ideographic':
+ // TODO not implemented
+ return y;
+ case 'alphabetic':
+ default:
+ return y;
+ }
+ }
+ };
+
+ var c2d = jsPDFAPI.context2d;
+
+ // accessor methods
+ Object.defineProperty(c2d, 'fillStyle', {
+ set: function set(value) {
+ this.setFillStyle(value);
+ },
+ get: function get() {
+ return this.ctx.fillStyle;
+ }
+ });
+ Object.defineProperty(c2d, 'strokeStyle', {
+ set: function set(value) {
+ this.setStrokeStyle(value);
+ },
+ get: function get() {
+ return this.ctx.strokeStyle;
+ }
+ });
+ Object.defineProperty(c2d, 'lineWidth', {
+ set: function set(value) {
+ this.setLineWidth(value);
+ },
+ get: function get() {
+ return this.ctx.lineWidth;
+ }
+ });
+ Object.defineProperty(c2d, 'lineCap', {
+ set: function set(val) {
+ this.setLineCap(val);
+ },
+ get: function get() {
+ return this.ctx.lineCap;
+ }
+ });
+ Object.defineProperty(c2d, 'lineJoin', {
+ set: function set(val) {
+ this.setLineJoin(val);
+ },
+ get: function get() {
+ return this.ctx.lineJoin;
+ }
+ });
+ Object.defineProperty(c2d, 'miterLimit', {
+ set: function set(val) {
+ this.ctx.miterLimit = val;
+ },
+ get: function get() {
+ return this.ctx.miterLimit;
+ }
+ });
+ Object.defineProperty(c2d, 'textBaseline', {
+ set: function set(value) {
+ this.setTextBaseline(value);
+ },
+ get: function get() {
+ return this.getTextBaseline();
+ }
+ });
+ Object.defineProperty(c2d, 'textAlign', {
+ set: function set(value) {
+ this.setTextAlign(value);
+ },
+ get: function get() {
+ return this.getTextAlign();
+ }
+ });
+ Object.defineProperty(c2d, 'font', {
+ set: function set(value) {
+ this.setFont(value);
+ },
+ get: function get() {
+ return this.ctx.font;
+ }
+ });
+ Object.defineProperty(c2d, 'globalCompositeOperation', {
+ set: function set(value) {
+ this.ctx.globalCompositeOperation = value;
+ },
+ get: function get() {
+ return this.ctx.globalCompositeOperation;
+ }
+ });
+ Object.defineProperty(c2d, 'globalAlpha', {
+ set: function set(value) {
+ this.ctx.globalAlpha = value;
+ },
+ get: function get() {
+ return this.ctx.globalAlpha;
+ }
+ });
+ // Not HTML API
+ Object.defineProperty(c2d, 'ignoreClearRect', {
+ set: function set(value) {
+ this.ctx.ignoreClearRect = value;
+ },
+ get: function get() {
+ return this.ctx.ignoreClearRect;
+ }
+ });
+ // End Not HTML API
+
+ c2d.internal = {};
+
+ c2d.internal.rxRgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/;
+ c2d.internal.rxRgba = /rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/;
+ c2d.internal.rxTransparent = /transparent|rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*0+\s*\)/;
+
+ // http://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html
+ c2d.internal.arc = function (c2d, xc, yc, r, a1, a2, anticlockwise, style) {
+
+ var k = this.pdf.internal.scaleFactor;
+ var pageHeight = this.pdf.internal.pageSize.getHeight();
+ var f2 = this.pdf.internal.f2;
+
+ var a1r = a1 * (Math.PI / 180);
+ var a2r = a2 * (Math.PI / 180);
+ var curves = this.createArc(r, a1r, a2r, anticlockwise);
+
+ for (var i = 0; i < curves.length; i++) {
+ var curve = curves[i];
+ if (i === 0) {
+ this.pdf.internal.out([f2((curve.x1 + xc) * k), f2((pageHeight - (curve.y1 + yc)) * k), 'm', f2((curve.x2 + xc) * k), f2((pageHeight - (curve.y2 + yc)) * k), f2((curve.x3 + xc) * k), f2((pageHeight - (curve.y3 + yc)) * k), f2((curve.x4 + xc) * k), f2((pageHeight - (curve.y4 + yc)) * k), 'c'].join(' '));
+ } else {
+ this.pdf.internal.out([f2((curve.x2 + xc) * k), f2((pageHeight - (curve.y2 + yc)) * k), f2((curve.x3 + xc) * k), f2((pageHeight - (curve.y3 + yc)) * k), f2((curve.x4 + xc) * k), f2((pageHeight - (curve.y4 + yc)) * k), 'c'].join(' '));
+ }
+
+ //c2d._lastPoint = {x: curve.x1 + xc, y: curve.y1 + yc};
+ c2d._lastPoint = { x: xc, y: yc };
+ // f2((curve.x1 + xc) * k), f2((pageHeight - (curve.y1 + yc)) * k), 'm', f2((curve.x2 + xc) * k), f2((pageHeight - (curve.y2 + yc)) * k), f2((curve.x3 + xc) * k), f2((pageHeight - (curve.y3 + yc)) * k), f2((curve.x4 + xc) * k), f2((pageHeight - (curve.y4 + yc)) * k), 'c'
+ }
+
+ if (style !== null) {
+ this.pdf.internal.out(this.pdf.internal.getStyle(style));
+ }
+ };
+
+ /**
+ *
+ * @param x Edge point X
+ * @param y Edge point Y
+ * @param r Radius
+ * @param a1 start angle
+ * @param a2 end angle
+ * @param anticlockwise
+ * @param style
+ * @param isClip
+ */
+ c2d.internal.arc2 = function (c2d, x, y, r, a1, a2, anticlockwise, style, isClip) {
+ // we need to convert from cartesian to polar here methinks.
+ var centerX = x; // + r;
+ var centerY = y;
+
+ if (!isClip) {
+ this.arc(c2d, centerX, centerY, r, a1, a2, anticlockwise, style);
+ } else {
+ this.arc(c2d, centerX, centerY, r, a1, a2, anticlockwise, null);
+ this.pdf.clip_fixed();
+ }
+ };
+
+ c2d.internal.move2 = function (c2d, x, y) {
+ var k = this.pdf.internal.scaleFactor;
+ var pageHeight = this.pdf.internal.pageSize.getHeight();
+ var f2 = this.pdf.internal.f2;
+
+ this.pdf.internal.out([f2(x * k), f2((pageHeight - y) * k), 'm'].join(' '));
+ c2d._lastPoint = { x: x, y: y };
+ };
+
+ c2d.internal.line2 = function (c2d, dx, dy) {
+ var k = this.pdf.internal.scaleFactor;
+ var pageHeight = this.pdf.internal.pageSize.getHeight();
+ var f2 = this.pdf.internal.f2;
+
+ //var pt = {x: c2d._lastPoint.x + dx, y: c2d._lastPoint.y + dy};
+ var pt = { x: dx, y: dy };
+
+ this.pdf.internal.out([f2(pt.x * k), f2((pageHeight - pt.y) * k), 'l'].join(' '));
+ //this.pdf.internal.out('f');
+ c2d._lastPoint = pt;
+ };
+
+ /**
+ * Return a array of objects that represent bezier curves which approximate the circular arc centered at the origin, from startAngle to endAngle (radians) with the specified radius.
+ *
+ * Each bezier curve is an object with four points, where x1,y1 and x4,y4 are the arc's end points and x2,y2 and x3,y3 are the cubic bezier's control points.
+ */
+
+ c2d.internal.createArc = function (radius, startAngle, endAngle, anticlockwise) {
+ var EPSILON = 0.00001; // Roughly 1/1000th of a degree, see below
+ var twoPI = Math.PI * 2;
+ var piOverTwo = Math.PI / 2.0;
+
+ // normalize startAngle, endAngle to [0, 2PI]
+ var startAngleN = startAngle;
+ if (startAngleN < twoPI || startAngleN > twoPI) {
+ startAngleN = startAngleN % twoPI;
+ }
+ if (startAngleN < 0) {
+ startAngleN = twoPI + startAngleN;
+ }
+
+ while (startAngle > endAngle) {
+ startAngle = startAngle - twoPI;
+ }
+ var totalAngle = Math.abs(endAngle - startAngle);
+ if (totalAngle < twoPI) {
+ if (anticlockwise) {
+ totalAngle = twoPI - totalAngle;
+ }
+ }
+
+ // Compute the sequence of arc curves, up to PI/2 at a time.
+ var curves = [];
+ var sgn = anticlockwise ? -1 : +1;
+
+ var a1 = startAngleN;
+ for (; totalAngle > EPSILON;) {
+ var remain = sgn * Math.min(totalAngle, piOverTwo);
+ var a2 = a1 + remain;
+ curves.push(this.createSmallArc(radius, a1, a2));
+ totalAngle -= Math.abs(a2 - a1);
+ a1 = a2;
+ }
+
+ return curves;
+ };
+
+ c2d.internal.getCurrentPage = function () {
+ return this.pdf.internal.pages[this.pdf.internal.getCurrentPageInfo().pageNumber];
+ };
+
+ /**
+ * Cubic bezier approximation of a circular arc centered at the origin, from (radians) a1 to a2, where a2-a1 < pi/2. The arc's radius is r.
+ *
+ * Returns an object with four points, where x1,y1 and x4,y4 are the arc's end points and x2,y2 and x3,y3 are the cubic bezier's control points.
+ *
+ * This algorithm is based on the approach described in: A. Riškus, "Approximation of a Cubic Bezier Curve by Circular Arcs and Vice Versa," Information Technology and Control, 35(4), 2006 pp. 371-378.
+ */
+
+ c2d.internal.createSmallArc = function (r, a1, a2) {
+ // Compute all four points for an arc that subtends the same total angle
+ // but is centered on the X-axis
+
+ var a = (a2 - a1) / 2.0;
+
+ var x4 = r * Math.cos(a);
+ var y4 = r * Math.sin(a);
+ var x1 = x4;
+ var y1 = -y4;
+
+ var q1 = x1 * x1 + y1 * y1;
+ var q2 = q1 + x1 * x4 + y1 * y4;
+ var k2 = 4 / 3 * (Math.sqrt(2 * q1 * q2) - q2) / (x1 * y4 - y1 * x4);
+
+ var x2 = x1 - k2 * y1;
+ var y2 = y1 + k2 * x1;
+ var x3 = x2;
+ var y3 = -y2;
+
+ // Find the arc points' actual locations by computing x1,y1 and x4,y4
+ // and rotating the control points by a + a1
+
+ var ar = a + a1;
+ var cos_ar = Math.cos(ar);
+ var sin_ar = Math.sin(ar);
+
+ return {
+ x1: r * Math.cos(a1),
+ y1: r * Math.sin(a1),
+ x2: x2 * cos_ar - y2 * sin_ar,
+ y2: x2 * sin_ar + y2 * cos_ar,
+ x3: x3 * cos_ar - y3 * sin_ar,
+ y3: x3 * sin_ar + y3 * cos_ar,
+ x4: r * Math.cos(a2),
+ y4: r * Math.sin(a2)
+ };
+ };
+
+ function context() {
+ this._isStrokeTransparent = false;
+ this._strokeOpacity = 1;
+ this.strokeStyle = '#000000';
+ this.fillStyle = '#000000';
+ this._isFillTransparent = false;
+ this._fillOpacity = 1;
+ this.font = "12pt times";
+ this.textBaseline = 'alphabetic'; // top,bottom,middle,ideographic,alphabetic,hanging
+ this.textAlign = 'start';
+ this.lineWidth = 1;
+ this.lineJoin = 'miter'; // round, bevel, miter
+ this.lineCap = 'butt'; // butt, round, square
+ this._transform = [1, 0, 0, 1, 0, 0]; // sx, shy, shx, sy, tx, ty
+ this.globalCompositeOperation = 'normal';
+ this.globalAlpha = 1.0;
+ this._clip_path = [];
+ // TODO miter limit //default 10
+
+ // Not HTML API
+ this.ignoreClearRect = false;
+
+ this.copy = function (ctx) {
+ this._isStrokeTransparent = ctx._isStrokeTransparent;
+ this._strokeOpacity = ctx._strokeOpacity;
+ this.strokeStyle = ctx.strokeStyle;
+ this._isFillTransparent = ctx._isFillTransparent;
+ this._fillOpacity = ctx._fillOpacity;
+ this.fillStyle = ctx.fillStyle;
+ this.font = ctx.font;
+ this.lineWidth = ctx.lineWidth;
+ this.lineJoin = ctx.lineJoin;
+ this.lineCap = ctx.lineCap;
+ this.textBaseline = ctx.textBaseline;
+ this.textAlign = ctx.textAlign;
+ this._fontSize = ctx._fontSize;
+ this._transform = ctx._transform.slice(0);
+ this.globalCompositeOperation = ctx.globalCompositeOperation;
+ this.globalAlpha = ctx.globalAlpha;
+ this._clip_path = ctx._clip_path.slice(0); //TODO deep copy?
+
+ // Not HTML API
+ this.ignoreClearRect = ctx.ignoreClearRect;
+ };
+ }
+
+ return this;
+ })(jsPDF.API);
+
+ /** @preserve
+ * jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser
+ * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
+ * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
+ * 2014 Diego Casorran, https://github.com/diegocr
+ * 2014 Daniel Husar, https://github.com/danielhusar
+ * 2014 Wolfgang Gassler, https://github.com/woolfg
+ * 2014 Steven Spungin, https://github.com/flamenco
+ *
+ *
+ * ====================================================================
+ */
+
+ (function (jsPDFAPI) {
+ var clone, _DrillForContent, FontNameDB, FontStyleMap, TextAlignMap, FontWeightMap, FloatMap, ClearMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, checkForFooter, process, tableToJson;
+ clone = function () {
+ return function (obj) {
+ Clone.prototype = obj;
+ return new Clone();
+ };
+ function Clone() {}
+ }();
+ PurgeWhiteSpace = function PurgeWhiteSpace(array) {
+ var fragment, i, l, lTrimmed, r, rTrimmed, trailingSpace;
+ i = 0;
+ l = array.length;
+ fragment = void 0;
+ lTrimmed = false;
+ rTrimmed = false;
+ while (!lTrimmed && i !== l) {
+ fragment = array[i] = array[i].trimLeft();
+ if (fragment) {
+ lTrimmed = true;
+ }
+ i++;
+ }
+ i = l - 1;
+ while (l && !rTrimmed && i !== -1) {
+ fragment = array[i] = array[i].trimRight();
+ if (fragment) {
+ rTrimmed = true;
+ }
+ i--;
+ }
+ r = /\s+$/g;
+ trailingSpace = true;
+ i = 0;
+ while (i !== l) {
+ // Leave the line breaks intact
+ if (array[i] != "\u2028") {
+ fragment = array[i].replace(/\s+/g, " ");
+ if (trailingSpace) {
+ fragment = fragment.trimLeft();
+ }
+ if (fragment) {
+ trailingSpace = r.test(fragment);
+ }
+ array[i] = fragment;
+ }
+ i++;
+ }
+ return array;
+ };
+ Renderer = function Renderer(pdf, x, y, settings) {
+ this.pdf = pdf;
+ this.x = x;
+ this.y = y;
+ this.settings = settings;
+ //list of functions which are called after each element-rendering process
+ this.watchFunctions = [];
+ this.init();
+ return this;
+ };
+ ResolveFont = function ResolveFont(css_font_family_string) {
+ var name, part, parts;
+ name = void 0;
+ parts = css_font_family_string.split(",");
+ part = parts.shift();
+ while (!name && part) {
+ name = FontNameDB[part.trim().toLowerCase()];
+ part = parts.shift();
+ }
+ return name;
+ };
+ ResolveUnitedNumber = function ResolveUnitedNumber(css_line_height_string) {
+
+ //IE8 issues
+ css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string;
+ if (css_line_height_string.indexOf("em") > -1 && !isNaN(Number(css_line_height_string.replace("em", "")))) {
+ css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px";
+ }
+ if (css_line_height_string.indexOf("pt") > -1 && !isNaN(Number(css_line_height_string.replace("pt", "")))) {
+ css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px";
+ }
+
+ var normal, undef, value;
+ undef = void 0;
+ normal = 16.00;
+ value = UnitedNumberMap[css_line_height_string];
+ if (value) {
+ return value;
+ }
+ value = {
+ "xx-small": 9,
+ "x-small": 11,
+ small: 13,
+ medium: 16,
+ large: 19,
+ "x-large": 23,
+ "xx-large": 28,
+ auto: 0
+ }[{ css_line_height_string: css_line_height_string }];
+
+ if (value !== undef) {
+ return UnitedNumberMap[css_line_height_string] = value / normal;
+ }
+ if (value = parseFloat(css_line_height_string)) {
+ return UnitedNumberMap[css_line_height_string] = value / normal;
+ }
+ value = css_line_height_string.match(/([\d\.]+)(px)/);
+ if (value.length === 3) {
+ return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal;
+ }
+ return UnitedNumberMap[css_line_height_string] = 1;
+ };
+ GetCSS = function GetCSS(element) {
+ var css, tmp, computedCSSElement;
+ computedCSSElement = function (el) {
+ var compCSS;
+ compCSS = function (el) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ return document.defaultView.getComputedStyle(el, null);
+ } else if (el.currentStyle) {
+ return el.currentStyle;
+ } else {
+ return el.style;
+ }
+ }(el);
+ return function (prop) {
+ prop = prop.replace(/-\D/g, function (match) {
+ return match.charAt(1).toUpperCase();
+ });
+ return compCSS[prop];
+ };
+ }(element);
+ css = {};
+ tmp = void 0;
+ css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times";
+ css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal";
+ css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left";
+ tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal";
+ if (tmp === "bold") {
+ if (css["font-style"] === "normal") {
+ css["font-style"] = tmp;
+ } else {
+ css["font-style"] = tmp + css["font-style"];
+ }
+ }
+ css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1;
+ css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1;
+ css["display"] = computedCSSElement("display") === "inline" ? "inline" : "block";
+
+ tmp = css["display"] === "block";
+ css["margin-top"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-top")) || 0;
+ css["margin-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0;
+ css["padding-top"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-top")) || 0;
+ css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0;
+ css["margin-left"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-left")) || 0;
+ css["margin-right"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-right")) || 0;
+ css["padding-left"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-left")) || 0;
+ css["padding-right"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-right")) || 0;
+
+ css["page-break-before"] = computedCSSElement("page-break-before") || "auto";
+
+ //float and clearing of floats
+ css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none";
+ css["clear"] = ClearMap[computedCSSElement("clear")] || "none";
+
+ css["color"] = computedCSSElement("color");
+
+ return css;
+ };
+ elementHandledElsewhere = function elementHandledElsewhere(element, renderer, elementHandlers) {
+ var handlers, i, isHandledElsewhere, l, classNames;
+ isHandledElsewhere = false;
+ i = void 0;
+ l = void 0;
+ handlers = elementHandlers["#" + element.id];
+ if (handlers) {
+ if (typeof handlers === "function") {
+ isHandledElsewhere = handlers(element, renderer);
+ } else {
+ i = 0;
+ l = handlers.length;
+ while (!isHandledElsewhere && i !== l) {
+ isHandledElsewhere = handlers[i](element, renderer);
+ i++;
+ }
+ }
+ }
+ handlers = elementHandlers[element.nodeName];
+ if (!isHandledElsewhere && handlers) {
+ if (typeof handlers === "function") {
+ isHandledElsewhere = handlers(element, renderer);
+ } else {
+ i = 0;
+ l = handlers.length;
+ while (!isHandledElsewhere && i !== l) {
+ isHandledElsewhere = handlers[i](element, renderer);
+ i++;
+ }
+ }
+ }
+
+ // Try class names
+ classNames = typeof element.className === 'string' ? element.className.split(' ') : [];
+ for (i = 0; i < classNames.length; i++) {
+ handlers = elementHandlers['.' + classNames[i]];
+ if (!isHandledElsewhere && handlers) {
+ if (typeof handlers === "function") {
+ isHandledElsewhere = handlers(element, renderer);
+ } else {
+ i = 0;
+ l = handlers.length;
+ while (!isHandledElsewhere && i !== l) {
+ isHandledElsewhere = handlers[i](element, renderer);
+ i++;
+ }
+ }
+ }
+ }
+
+ return isHandledElsewhere;
+ };
+ tableToJson = function tableToJson(table, renderer) {
+ var data, headers, i, j, rowData, tableRow, table_obj, table_with, cell, l;
+ data = [];
+ headers = [];
+ i = 0;
+ l = table.rows[0].cells.length;
+ table_with = table.clientWidth;
+ while (i < l) {
+ cell = table.rows[0].cells[i];
+ headers[i] = {
+ name: cell.textContent.toLowerCase().replace(/\s+/g, ''),
+ prompt: cell.textContent.replace(/\r?\n/g, ''),
+ width: cell.clientWidth / table_with * renderer.pdf.internal.pageSize.getWidth()
+ };
+ i++;
+ }
+ i = 1;
+ while (i < table.rows.length) {
+ tableRow = table.rows[i];
+ rowData = {};
+ j = 0;
+ while (j < tableRow.cells.length) {
+ rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, '');
+ j++;
+ }
+ data.push(rowData);
+ i++;
+ }
+ return table_obj = {
+ rows: data,
+ headers: headers
+ };
+ };
+ var SkipNode = {
+ SCRIPT: 1,
+ STYLE: 1,
+ NOSCRIPT: 1,
+ OBJECT: 1,
+ EMBED: 1,
+ SELECT: 1
+ };
+ var listCount = 1;
+ _DrillForContent = function DrillForContent(element, renderer, elementHandlers) {
+ var cn, cns, fragmentCSS, i, isBlock, l, table2json, cb;
+ cns = element.childNodes;
+ cn = void 0;
+ fragmentCSS = GetCSS(element);
+ isBlock = fragmentCSS.display === "block";
+ if (isBlock) {
+ renderer.setBlockBoundary();
+ renderer.setBlockStyle(fragmentCSS);
+ }
+ i = 0;
+ l = cns.length;
+ while (i < l) {
+ cn = cns[i];
+ if ((typeof cn === "undefined" ? "undefined" : _typeof(cn)) === "object") {
+
+ //execute all watcher functions to e.g. reset floating
+ renderer.executeWatchFunctions(cn);
+
+ /*** HEADER rendering **/
+ if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
+ var header = cn;
+ //store old top margin
+ var oldMarginTop = renderer.pdf.margins_doc.top;
+ //subscribe for new page event and render header first on every page
+ renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) {
+ //set current y position to old margin
+ renderer.y = oldMarginTop;
+ //render all child nodes of the header element
+ _DrillForContent(header, renderer, elementHandlers);
+ //set margin to old margin + rendered header + 10 space to prevent overlapping
+ //important for other plugins (e.g. table) to start rendering at correct position after header
+ renderer.pdf.margins_doc.top = renderer.y + 10;
+ renderer.y += 10;
+ }, false);
+ }
+
+ if (cn.nodeType === 8 && cn.nodeName === "#comment") {
+ if (~cn.textContent.indexOf("ADD_PAGE")) {
+ renderer.pdf.addPage();
+ renderer.y = renderer.pdf.margins_doc.top;
+ }
+ } else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
+ /*** IMAGE RENDERING ***/
+ var cached_image;
+ if (cn.nodeName === "IMG") {
+ var url = cn.getAttribute("src");
+ cached_image = images[renderer.pdf.sHashCode(url) || url];
+ }
+ if (cached_image) {
+ if (renderer.pdf.internal.pageSize.getHeight() - renderer.pdf.margins_doc.bottom < renderer.y + cn.height && renderer.y > renderer.pdf.margins_doc.top) {
+ renderer.pdf.addPage();
+ renderer.y = renderer.pdf.margins_doc.top;
+ //check if we have to set back some values due to e.g. header rendering for new page
+ renderer.executeWatchFunctions(cn);
+ }
+
+ var imagesCSS = GetCSS(cn);
+ var imageX = renderer.x;
+ var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor;
+
+ //define additional paddings, margins which have to be taken into account for margin calculations
+ var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"]) * fontToUnitRatio;
+ var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"]) * fontToUnitRatio;
+ var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"]) * fontToUnitRatio;
+ var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"]) * fontToUnitRatio;
+
+ //if float is set to right, move the image to the right border
+ //add space if margin is set
+ if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') {
+ imageX += renderer.settings.width - cn.width - additionalSpaceRight;
+ } else {
+ imageX += additionalSpaceLeft;
+ }
+
+ renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height);
+ cached_image = undefined;
+ //if the float prop is specified we have to float the text around the image
+ if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') {
+ //add functiont to set back coordinates after image rendering
+ renderer.watchFunctions.push(function (diffX, thresholdY, diffWidth, el) {
+ //undo drawing box adaptions which were set by floating
+ if (renderer.y >= thresholdY) {
+ renderer.x += diffX;
+ renderer.settings.width += diffWidth;
+ return true;
+ } else if (el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x + el.width > renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width) {
+ renderer.x += diffX;
+ renderer.y = thresholdY;
+ renderer.settings.width += diffWidth;
+ return true;
+ } else {
+ return false;
+ }
+ }.bind(this, imagesCSS['float'] === 'left' ? -cn.width - additionalSpaceLeft - additionalSpaceRight : 0, renderer.y + cn.height + additionalSpaceTop + additionalSpaceBottom, cn.width));
+ //reset floating by clear:both divs
+ //just set cursorY after the floating element
+ renderer.watchFunctions.push(function (yPositionAfterFloating, pages, el) {
+ if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) {
+ if (el.nodeType === 1 && GetCSS(el).clear === 'both') {
+ renderer.y = yPositionAfterFloating;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }.bind(this, renderer.y + cn.height, renderer.pdf.internal.getNumberOfPages()));
+
+ //if floating is set we decrease the available width by the image width
+ renderer.settings.width -= cn.width + additionalSpaceLeft + additionalSpaceRight;
+ //if left just add the image width to the X coordinate
+ if (imagesCSS['float'] === 'left') {
+ renderer.x += cn.width + additionalSpaceLeft + additionalSpaceRight;
+ }
+ } else {
+ //if no floating is set, move the rendering cursor after the image height
+ renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom;
+ }
+
+ /*** TABLE RENDERING ***/
+ } else if (cn.nodeName === "TABLE") {
+ table2json = tableToJson(cn, renderer);
+ renderer.y += 10;
+ renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, {
+ autoSize: false,
+ printHeaders: elementHandlers.printHeaders,
+ margins: renderer.pdf.margins_doc,
+ css: GetCSS(cn)
+ });
+ renderer.y = renderer.pdf.lastCellPos.y + renderer.pdf.lastCellPos.h + 20;
+ } else if (cn.nodeName === "OL" || cn.nodeName === "UL") {
+ listCount = 1;
+ if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
+ _DrillForContent(cn, renderer, elementHandlers);
+ }
+ renderer.y += 10;
+ } else if (cn.nodeName === "LI") {
+ var temp = renderer.x;
+ renderer.x += 20 / renderer.pdf.internal.scaleFactor;
+ renderer.y += 3;
+ if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
+ _DrillForContent(cn, renderer, elementHandlers);
+ }
+ renderer.x = temp;
+ } else if (cn.nodeName === "BR") {
+ renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor;
+ renderer.addText("\u2028", clone(fragmentCSS));
+ } else {
+ if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
+ _DrillForContent(cn, renderer, elementHandlers);
+ }
+ }
+ } else if (cn.nodeType === 3) {
+ var value = cn.nodeValue;
+ if (cn.nodeValue && cn.parentNode.nodeName === "LI") {
+ if (cn.parentNode.parentNode.nodeName === "OL") {
+ value = listCount++ + '. ' + value;
+ } else {
+ var fontSize = fragmentCSS["font-size"];
+ var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor;
+ var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor;
+ var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor;
+ cb = function cb(x, y) {
+ this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD');
+ };
+ }
+ }
+ // Only add the text if the text node is in the body element
+ // Add compatibility with IE11
+ if (!!(cn.ownerDocument.body.compareDocumentPosition(cn) & 16)) {
+ renderer.addText(value, fragmentCSS);
+ }
+ } else if (typeof cn === "string") {
+ renderer.addText(cn, fragmentCSS);
+ }
+ }
+ i++;
+ }
+ elementHandlers.outY = renderer.y;
+
+ if (isBlock) {
+ return renderer.setBlockBoundary(cb);
+ }
+ };
+ images = {};
+ loadImgs = function loadImgs(element, renderer, elementHandlers, cb) {
+ var imgs = element.getElementsByTagName('img'),
+ l = imgs.length,
+ found_images,
+ x = 0;
+ function done() {
+ renderer.pdf.internal.events.publish('imagesLoaded');
+ cb(found_images);
+ }
+ function loadImage(url, width, height) {
+ if (!url) return;
+ var img = new Image();
+ found_images = ++x;
+ img.crossOrigin = '';
+ img.onerror = img.onload = function () {
+ if (img.complete) {
+ //to support data urls in images, set width and height
+ //as those values are not recognized automatically
+ if (img.src.indexOf('data:image/') === 0) {
+ img.width = width || img.width || 0;
+ img.height = height || img.height || 0;
+ }
+ //if valid image add to known images array
+ if (img.width + img.height) {
+ var hash = renderer.pdf.sHashCode(url) || url;
+ images[hash] = images[hash] || img;
+ }
+ }
+ if (! --x) {
+ done();
+ }
+ };
+ img.src = url;
+ }
+ while (l--) {
+ loadImage(imgs[l].getAttribute("src"), imgs[l].width, imgs[l].height);
+ }return x || done();
+ };
+ checkForFooter = function checkForFooter(elem, renderer, elementHandlers) {
+ //check if we can found a