diff --git a/dist/jspdf.debug.js b/dist/jspdf.debug.js
index c911e7dca..ecc077101 100644
--- a/dist/jspdf.debug.js
+++ b/dist/jspdf.debug.js
@@ -1,158 +1,733 @@
(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.1 Built on 2018-06-06T07:49:28.721Z
- * CommitID 3233f44044
- *
- * Copyright (c) 2010-2016 James Hall
+ * 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
+ *
- * 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
- * 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
- * }
- * ```
+ function _typeof(obj) {
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+ _typeof = function (obj) {
+ return typeof obj;
+ };
+ } else {
+ _typeof = function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+ }
+
+ return _typeof(obj);
+ }
+
+ /**
+ * JavaScript Polyfill functions for jsPDF
+ * Collected from public resources by
+ * https://github.com/diegocr
*/
- var jsPDF = function (global) {
+ (function (global) {
+ if (_typeof(global.console) !== "object") {
+ // Console-polyfill. MIT license.
+ // https://github.com/paulmillr/console-polyfill
+ // Make it safe to do console.log() always.
+ global.console = {};
+ var con = global.console;
+ var prop, method;
+
+ var dummy = function dummy() {};
+
+ var properties = ['memory'];
+ var methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' + 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,' + 'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn').split(',');
+
+ while (prop = properties.pop()) {
+ if (!con[prop]) con[prop] = {};
+ }
+
+ while (method = methods.pop()) {
+ if (!con[method]) con[method] = dummy;
+ }
+ }
+
+ var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+
+ if (typeof global.btoa === 'undefined') {
+ global.btoa = function (data) {
+ // discuss at: http://phpjs.org/functions/base64_encode/
+ // original by: Tyler Akins (http://rumkin.com)
+ // improved by: Bayron Guevara
+ // improved by: Thunder.m
+ // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // improved by: Rafal Kukawski (http://kukawski.pl)
+ // bugfixed by: Pellentesque Malesuada
+ // example 1: base64_encode('Kevin van Zonneveld');
+ // returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
+ var o1,
+ o2,
+ o3,
+ h1,
+ h2,
+ h3,
+ h4,
+ bits,
+ i = 0,
+ ac = 0,
+ enc = '',
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ do {
+ // pack three octets into four hexets
+ o1 = data.charCodeAt(i++);
+ o2 = data.charCodeAt(i++);
+ o3 = data.charCodeAt(i++);
+ bits = o1 << 16 | o2 << 8 | o3;
+ h1 = bits >> 18 & 0x3f;
+ h2 = bits >> 12 & 0x3f;
+ h3 = bits >> 6 & 0x3f;
+ h4 = bits & 0x3f; // use hexets to index into b64, and append result to encoded string
+
+ tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+ } while (i < data.length);
+
+ enc = tmp_arr.join('');
+ var r = data.length % 3;
+ return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
+ };
+ }
+
+ if (typeof global.atob === 'undefined') {
+ global.atob = function (data) {
+ // discuss at: http://phpjs.org/functions/base64_decode/
+ // original by: Tyler Akins (http://rumkin.com)
+ // improved by: Thunder.m
+ // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // input by: Aman Gupta
+ // input by: Brett Zamir (http://brett-zamir.me)
+ // bugfixed by: Onno Marsman
+ // bugfixed by: Pellentesque Malesuada
+ // bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
+ // returns 1: 'Kevin van Zonneveld'
+ var o1,
+ o2,
+ o3,
+ h1,
+ h2,
+ h3,
+ h4,
+ bits,
+ i = 0,
+ ac = 0,
+ dec = '',
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ data += '';
+
+ do {
+ // unpack four hexets into three octets using index points in b64
+ h1 = b64.indexOf(data.charAt(i++));
+ h2 = b64.indexOf(data.charAt(i++));
+ h3 = b64.indexOf(data.charAt(i++));
+ h4 = b64.indexOf(data.charAt(i++));
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+ o1 = bits >> 16 & 0xff;
+ o2 = bits >> 8 & 0xff;
+ o3 = bits & 0xff;
+
+ if (h3 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1);
+ } else if (h4 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
+ } else {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
+ }
+ } while (i < data.length);
+
+ dec = tmp_arr.join('');
+ return dec;
+ };
+ }
+
+ if (!Array.prototype.map) {
+ Array.prototype.map = function (fun
+ /*, thisArg */
+ ) {
+ if (this === void 0 || this === null || typeof fun !== "function") throw new TypeError();
+ var t = Object(this),
+ len = t.length >>> 0,
+ res = new Array(len);
+ var thisArg = arguments.length > 1 ? arguments[1] : void 0;
+
+ for (var i = 0; i < len; i++) {
+ // NOTE: Absolute correctness would demand Object.defineProperty
+ // be used. But this method is fairly new, and failure is
+ // possible only if Object.prototype or Array.prototype
+ // has a property |i| (very unlikely), so use a less-correct
+ // but more portable alternative.
+ if (i in t) res[i] = fun.call(thisArg, t[i], i, t);
+ }
+
+ return res;
+ };
+ }
+
+ if (!Array.isArray) {
+ Array.isArray = function (arg) {
+ return Object.prototype.toString.call(arg) === '[object Array]';
+ };
+ }
+
+ if (!Array.prototype.forEach) {
+ Array.prototype.forEach = function (fun, thisArg) {
+
+ if (this === void 0 || this === null || typeof fun !== "function") throw new TypeError();
+ var t = Object(this),
+ len = t.length >>> 0;
+
+ for (var i = 0; i < len; i++) {
+ if (i in t) fun.call(thisArg, t[i], i, t);
+ }
+ };
+ } // https://tc39.github.io/ecma262/#sec-array.prototype.find
+
+
+ if (!Array.prototype.find) {
+ Object.defineProperty(Array.prototype, 'find', {
+ value: function value(predicate) {
+ // 1. Let O be ? ToObject(this value).
+ if (this == null) {
+ throw new TypeError('"this" is null or not defined');
+ }
+
+ var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")).
+
+ var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception.
+
+ if (typeof predicate !== 'function') {
+ throw new TypeError('predicate must be a function');
+ } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
+
+
+ var thisArg = arguments[1]; // 5. Let k be 0.
+
+ var k = 0; // 6. Repeat, while k < len
+
+ while (k < len) {
+ // a. Let Pk be ! ToString(k).
+ // b. Let kValue be ? Get(O, Pk).
+ // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
+ // d. If testResult is true, return kValue.
+ var kValue = o[k];
+
+ if (predicate.call(thisArg, kValue, k, o)) {
+ return kValue;
+ } // e. Increase k by 1.
+
+
+ k++;
+ } // 7. Return undefined.
- 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]
+
+ return undefined;
+ },
+ configurable: true,
+ writable: true
+ });
+ }
+
+ if (!Object.keys) {
+ Object.keys = function () {
+
+ var hasOwnProperty = Object.prototype.hasOwnProperty,
+ hasDontEnumBug = !{
+ toString: null
+ }.propertyIsEnumerable('toString'),
+ dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'],
+ dontEnumsLength = dontEnums.length;
+ return function (obj) {
+ if (_typeof(obj) !== 'object' && (typeof obj !== 'function' || obj === null)) {
+ throw new TypeError();
+ }
+
+ var result = [],
+ prop,
+ i;
+
+ for (prop in obj) {
+ if (hasOwnProperty.call(obj, prop)) {
+ result.push(prop);
+ }
+ }
+
+ if (hasDontEnumBug) {
+ for (i = 0; i < dontEnumsLength; i++) {
+ if (hasOwnProperty.call(obj, dontEnums[i])) {
+ result.push(dontEnums[i]);
+ }
+ }
+ }
+
+ return result;
+ };
+ }();
+ }
+
+ if (typeof Object.assign != 'function') {
+ Object.assign = function (target) {
+
+ if (target == null) {
+ throw new TypeError('Cannot convert undefined or null to object');
+ }
+
+ target = Object(target);
+
+ for (var index = 1; index < arguments.length; index++) {
+ var source = arguments[index];
+
+ if (source != null) {
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+ }
+
+ return target;
+ };
+ }
+
+ if (!String.prototype.trim) {
+ String.prototype.trim = function () {
+ return this.replace(/^\s+|\s+$/g, '');
+ };
+ }
+
+ if (!String.prototype.trimLeft) {
+ String.prototype.trimLeft = function () {
+ return this.replace(/^\s+/g, "");
+ };
+ }
+
+ if (!String.prototype.trimRight) {
+ String.prototype.trimRight = function () {
+ return this.replace(/\s+$/g, "");
+ };
+ }
+
+ Number.isInteger = Number.isInteger || function (value) {
+ return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;
};
+ })(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
+
+ (function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : factory();
+ })(window, function () {
+ /**
+ * @this {Promise}
+ */
+
+ function finallyConstructor(callback) {
+ var constructor = this.constructor;
+ return this.then(function (value) {
+ return constructor.resolve(callback()).then(function () {
+ return value;
+ });
+ }, function (reason) {
+ return constructor.resolve(callback()).then(function () {
+ return constructor.reject(reason);
+ });
+ });
+ } // Store setTimeout reference so promise-polyfill will be unaffected by
+ // other code modifying setTimeout (like sinon.useFakeTimers())
+
+
+ var setTimeoutFunc = setTimeout;
+
+ function noop() {} // Polyfill for Function.prototype.bind
+
+
+ function bind(fn, thisArg) {
+ return function () {
+ fn.apply(thisArg, arguments);
+ };
+ }
+ /**
+ * @constructor
+ * @param {Function} fn
+ */
+
+
+ function Promise(fn) {
+ if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new');
+ if (typeof fn !== 'function') throw new TypeError('not a function');
+ /** @type {!number} */
+ this._state = 0;
+ /** @type {!boolean} */
+
+ this._handled = false;
+ /** @type {Promise|undefined} */
+
+ this._value = undefined;
+ /** @type {!Array} */
+
+ this._deferreds = [];
+ doResolve(fn, this);
+ }
+
+ function handle(self, deferred) {
+ while (self._state === 3) {
+ self = self._value;
+ }
+
+ if (self._state === 0) {
+ self._deferreds.push(deferred);
+
+ return;
+ }
+
+ self._handled = true;
+
+ Promise._immediateFn(function () {
+ var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+
+ if (cb === null) {
+ (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+ return;
+ }
+
+ var ret;
+
+ try {
+ ret = cb(self._value);
+ } catch (e) {
+ reject(deferred.promise, e);
+ return;
+ }
+
+ resolve(deferred.promise, ret);
+ });
+ }
+
+ function resolve(self, newValue) {
+ try {
+ // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
+
+ if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
+ var then = newValue.then;
+
+ if (newValue instanceof Promise) {
+ self._state = 3;
+ self._value = newValue;
+ finale(self);
+ return;
+ } else if (typeof then === 'function') {
+ doResolve(bind(then, newValue), self);
+ return;
+ }
+ }
+
+ self._state = 1;
+ self._value = newValue;
+ finale(self);
+ } catch (e) {
+ reject(self, e);
+ }
+ }
+
+ function reject(self, newValue) {
+ self._state = 2;
+ self._value = newValue;
+ finale(self);
+ }
+
+ function finale(self) {
+ if (self._state === 2 && self._deferreds.length === 0) {
+ Promise._immediateFn(function () {
+ if (!self._handled) {
+ Promise._unhandledRejectionFn(self._value);
+ }
+ });
+ }
+
+ for (var i = 0, len = self._deferreds.length; i < len; i++) {
+ handle(self, self._deferreds[i]);
+ }
+
+ self._deferreds = null;
+ }
+ /**
+ * @constructor
+ */
+
+
+ function Handler(onFulfilled, onRejected, promise) {
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+ this.promise = promise;
+ }
/**
- * 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
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
*
- * @class
- * @name PubSub
- * @ignore This should not be in the public docs.
+ * Makes no guarantees about asynchrony.
+ */
+
+
+ function doResolve(fn, self) {
+ var done = false;
+
+ try {
+ fn(function (value) {
+ if (done) return;
+ done = true;
+ resolve(self, value);
+ }, function (reason) {
+ if (done) return;
+ done = true;
+ reject(self, reason);
+ });
+ } catch (ex) {
+ if (done) return;
+ done = true;
+ reject(self, ex);
+ }
+ }
+
+ Promise.prototype['catch'] = function (onRejected) {
+ return this.then(null, onRejected);
+ };
+
+ Promise.prototype.then = function (onFulfilled, onRejected) {
+ // @ts-ignore
+ var prom = new this.constructor(noop);
+ handle(this, new Handler(onFulfilled, onRejected, prom));
+ return prom;
+ };
+
+ Promise.prototype['finally'] = finallyConstructor;
+
+ Promise.all = function (arr) {
+ return new Promise(function (resolve, reject) {
+ if (!arr || typeof arr.length === 'undefined') throw new TypeError('Promise.all accepts an array');
+ var args = Array.prototype.slice.call(arr);
+ if (args.length === 0) return resolve([]);
+ var remaining = args.length;
+
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then;
+
+ if (typeof then === 'function') {
+ then.call(val, function (val) {
+ res(i, val);
+ }, reject);
+ return;
+ }
+ }
+
+ args[i] = val;
+
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex);
+ }
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i]);
+ }
+ });
+ };
+
+ Promise.resolve = function (value) {
+ if (value && typeof value === 'object' && value.constructor === Promise) {
+ return value;
+ }
+
+ return new Promise(function (resolve) {
+ resolve(value);
+ });
+ };
+
+ Promise.reject = function (value) {
+ return new Promise(function (resolve, reject) {
+ reject(value);
+ });
+ };
+
+ Promise.race = function (values) {
+ return new Promise(function (resolve, reject) {
+ for (var i = 0, len = values.length; i < len; i++) {
+ values[i].then(resolve, reject);
+ }
+ });
+ }; // Use polyfill for setImmediate for performance gains
+
+
+ Promise._immediateFn = typeof setImmediate === 'function' && function (fn) {
+ setImmediate(fn);
+ } || function (fn) {
+ setTimeoutFunc(fn, 0);
+ };
+
+ Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+ if (typeof console !== 'undefined' && console) {
+ console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+ }
+ };
+ /** @suppress {undefinedVars} */
+
+
+ var globalNS = function () {
+ // the only reliable means to get the global object is
+ // `Function('return this')()`
+ // However, this causes CSP violations in Chrome apps.
+ if (typeof self !== 'undefined') {
+ return self;
+ }
+
+ if (typeof window !== 'undefined') {
+ return window;
+ }
+
+ if (typeof global !== 'undefined') {
+ return global;
+ }
+
+ throw new Error('unable to locate global object');
+ }();
+
+ if (!('Promise' in globalNS)) {
+ globalNS['Promise'] = Promise;
+ } else if (!globalNS.Promise.prototype['finally']) {
+ globalNS.Promise.prototype['finally'] = finallyConstructor;
+ }
+ });
+
+ /**
+ * 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:
+ * Default is "a4". If you want to use your own format just pass instead of one of the above predefined formats the size as an number-array, e.g. [595.28, 841.89]
+ * @returns {jsPDF} jsPDF-instance
+ * @description
+ * If the first parameter (orientation) is an object, it will be interpreted as an object of named parameters
+ * ```
+ * {
+ * orientation: 'p',
+ * unit: 'mm',
+ * format: 'a4',
+ * hotfixes: [] // an array of hotfix strings to enable
+ * }
+ * ```
+ */
+ var jsPDF = function (global) {
+ /**
+ * jsPDF's Internal PubSub Implementation.
+ * Backward compatible rewritten on 2014 by
+ * Diego Casorran, https://github.com/diegocr
+ *
+ * @class
+ * @name PubSub
+ * @ignore
*/
+
function PubSub(context) {
+ if (_typeof(context) !== 'object') {
+ throw new Error('Invalid Context passed to initialize PubSub (jsPDF-module)');
+ }
+
var topics = {};
this.subscribe = function (topic, callback, once) {
- if (typeof callback !== 'function') {
- return false;
+ once = once || false;
+
+ if (typeof topic !== 'string' || typeof callback !== 'function' || typeof once !== 'boolean') {
+ throw new Error('Invalid arguments passed to PubSub.subscribe (jsPDF-module)');
}
if (!topics.hasOwnProperty(topic)) {
topics[topic] = {};
}
- var id = Math.random().toString(35);
- topics[topic][id] = [callback, !!once];
-
- return id;
+ var token = Math.random().toString(35);
+ topics[topic][token] = [callback, !!once];
+ return token;
};
this.unsubscribe = function (token) {
for (var topic in topics) {
if (topics[topic][token]) {
delete topics[topic][token];
+
+ if (Object.keys(topics[topic]).length === 0) {
+ delete topics[topic];
+ }
+
return true;
}
}
+
return false;
};
this.publish = function (topic) {
if (topics.hasOwnProperty(topic)) {
var args = Array.prototype.slice.call(arguments, 1),
- idr = [];
+ tokens = [];
+
+ for (var token in topics[topic]) {
+ var sub = topics[topic][token];
- for (var id in topics[topic]) {
- var sub = topics[topic][id];
try {
sub[0].apply(context, args);
} catch (ex) {
@@ -160,186 +735,188 @@
console.error('jsPDF PubSub Error', ex.message, ex);
}
}
- if (sub[1]) idr.push(id);
+
+ if (sub[1]) tokens.push(token);
}
- if (idr.length) idr.forEach(this.unsubscribe);
+
+ if (tokens.length) tokens.forEach(this.unsubscribe);
}
};
- }
- /**
- * @constructor
- * @private
+ this.getTopics = function () {
+ return topics;
+ };
+ }
+ /**
+ * @constructor
+ * @private
*/
- function jsPDF(orientation, unit, format, compressPdf) {
+
+
+ function jsPDF(orientation, unit, format, compressPdf) {
var options = {};
+ var filters = [];
+ var userUnit = 1.0;
- if ((typeof orientation === 'undefined' ? 'undefined' : _typeof(orientation)) === 'object') {
+ if (_typeof(orientation) === 'object') {
options = orientation;
-
orientation = options.orientation;
unit = options.unit || unit;
format = options.format || format;
compressPdf = options.compress || options.compressPdf || compressPdf;
+ filters = options.filters || (compressPdf === true ? ['FlateEncode'] : filters);
+ userUnit = typeof options.userUnit === "number" ? Math.abs(options.userUnit) : 1.0;
}
- // Default options
unit = unit || 'mm';
- format = format || 'a4';
orientation = ('' + (orientation || 'P')).toLowerCase();
+ var putOnlyUsedFonts = options.putOnlyUsedFonts || true;
+ var usedFonts = {};
+ var API = {
+ internal: {},
+ __private__: {}
+ };
+ API.__private__.PubSub = PubSub;
+ var pdfVersion = '1.3';
- 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 || [],
+ var getPdfVersion = API.__private__.getPdfVersion = function () {
+ return pdfVersion;
+ };
+ var setPdfVersion = API.__private__.setPdfVersion = function (value) {
+ pdfVersion = value;
+ }; // Size in pt of various paper formats
+
+
+ var pageFormats = {
+ 'a0': [2383.94, 3370.39],
+ 'a1': [1683.78, 2383.94],
+ 'a2': [1190.55, 1683.78],
+ 'a3': [841.89, 1190.55],
+ 'a4': [595.28, 841.89],
+ 'a5': [419.53, 595.28],
+ 'a6': [297.64, 419.53],
+ 'a7': [209.76, 297.64],
+ 'a8': [147.40, 209.76],
+ 'a9': [104.88, 147.40],
+ 'a10': [73.70, 104.88],
+ 'b0': [2834.65, 4008.19],
+ 'b1': [2004.09, 2834.65],
+ 'b2': [1417.32, 2004.09],
+ 'b3': [1000.63, 1417.32],
+ 'b4': [708.66, 1000.63],
+ 'b5': [498.90, 708.66],
+ 'b6': [354.33, 498.90],
+ 'b7': [249.45, 354.33],
+ 'b8': [175.75, 249.45],
+ 'b9': [124.72, 175.75],
+ 'b10': [87.87, 124.72],
+ 'c0': [2599.37, 3676.54],
+ 'c1': [1836.85, 2599.37],
+ 'c2': [1298.27, 1836.85],
+ 'c3': [918.43, 1298.27],
+ 'c4': [649.13, 918.43],
+ 'c5': [459.21, 649.13],
+ 'c6': [323.15, 459.21],
+ 'c7': [229.61, 323.15],
+ 'c8': [161.57, 229.61],
+ 'c9': [113.39, 161.57],
+ 'c10': [79.37, 113.39],
+ 'dl': [311.81, 623.62],
+ 'letter': [612, 792],
+ 'government-letter': [576, 756],
+ 'legal': [612, 1008],
+ 'junior-legal': [576, 360],
+ 'ledger': [1224, 792],
+ 'tabloid': [792, 1224],
+ 'credit-card': [153, 243]
+ };
- /////////////////////
- // Private functions
- /////////////////////
- generateColorString = function generateColorString(options) {
- var color;
+ var getPageFormats = API.__private__.getPageFormats = function () {
+ return pageFormats;
+ };
- 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 getPageFormat = API.__private__.getPageFormat = function (value) {
+ return pageFormats[value];
+ };
- if (typeof ch1 === "string" && ch1.charAt(0) !== '#') {
- var rgbColor = new RGBColor(ch1);
- if (rgbColor.ok) {
- ch1 = rgbColor.toHex();
- }
- }
- //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 format === "string") {
+ format = getPageFormat(format);
+ }
- 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;
+ format = format || getPageFormat('a4');
+
+ var f2 = API.f2 = API.__private__.f2 = function (number) {
+ if (isNaN(number)) {
+ throw new Error('Invalid argument passed to jsPDF.f2');
}
- 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 number.toFixed(2); // Ie, %.2f
+ };
+
+ var f3 = API.__private__.f3 = function (number) {
+ if (isNaN(number)) {
+ throw new Error('Invalid argument passed to jsPDF.f3');
}
- return color;
- },
- convertDateToPDFDate = function convertDateToPDFDate(parmDate) {
- var padd2 = function padd2(number) {
- return ('0' + parseInt(number)).slice(-2);
- };
+
+ return number.toFixed(3); // Ie, %.3f
+ };
+
+ var fileId = '00000000000000000000000000000000';
+
+ var getFileId = API.__private__.getFileId = function () {
+ return fileId;
+ };
+
+ var setFileId = API.__private__.setFileId = function (value) {
+ value = value || "12345678901234567890123456789012".split('').map(function () {
+ return "ABCDEF0123456789".charAt(Math.floor(Math.random() * 16));
+ }).join('');
+ fileId = value;
+ return fileId;
+ };
+ /**
+ * @name setFileId
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ * @param {string} value GUID.
+ * @returns {jsPDF}
+ */
+
+
+ API.setFileId = function (value) {
+ setFileId(value);
+ return this;
+ };
+ /**
+ * @name getFileId
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ *
+ * @returns {string} GUID.
+ */
+
+
+ API.getFileId = function () {
+ return getFileId();
+ };
+
+ var creationDate;
+
+ var convertDateToPDFDate = API.__private__.convertDateToPDFDate = function (parmDate) {
var result = '';
var tzoffset = parmDate.getTimezoneOffset(),
tzsign = tzoffset < 0 ? '+' : '-',
tzhour = Math.floor(Math.abs(tzoffset / 60)),
tzmin = Math.abs(tzoffset % 60),
timeZoneString = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join('');
-
result = ['D:', parmDate.getFullYear(), padd2(parmDate.getMonth() + 1), padd2(parmDate.getDate()), padd2(parmDate.getHours()), padd2(parmDate.getMinutes()), padd2(parmDate.getSeconds()), timeZoneString].join('');
return result;
- },
- convertPDFDateToDate = function convertPDFDateToDate(parmPDFDate) {
+ };
+
+ var convertPDFDateToDate = API.__private__.convertPDFDateToDate = function (parmPDFDate) {
var year = parseInt(parmPDFDate.substr(2, 4), 10);
var month = parseInt(parmPDFDate.substr(6, 2), 10) - 1;
var date = parseInt(parmPDFDate.substr(8, 2), 10);
@@ -348,21229 +925,23863 @@
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 setCreationDate = API.__private__.setCreationDate = function (date) {
var tmpCreationDateString;
var regexPDFCreationDate = /^D:(20[0-2][0-9]|203[0-7]|19[7-9][0-9])(0[0-9]|1[0-2])([0-2][0-9]|3[0-1])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(0[0-9]|[1-5][0-9])(\+0[0-9]|\+1[0-4]|\-0[0-9]|\-1[0-1])\'(0[0-9]|[1-5][0-9])\'?$/;
- if ((typeof date === 'undefined' ? 'undefined' : _typeof(date)) === undefined) {
+
+ if (typeof date === "undefined") {
date = new Date();
}
- if ((typeof date === 'undefined' ? 'undefined' : _typeof(date)) === "object" && Object.prototype.toString.call(date) === "[object Date]") {
+ if (_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());
+ throw new Error('Invalid argument passed to jsPDF.setCreationDate');
}
+
creationDate = tmpCreationDateString;
return creationDate;
- },
- getCreationDate = function getCreationDate(type) {
+ };
+
+ var getCreationDate = API.__private__.getCreationDate = function (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) {
+ };
+ /**
+ * @name setCreationDate
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ * @param {Object} date
+ * @returns {jsPDF}
+ */
+
+
+ API.setCreationDate = function (date) {
+ setCreationDate(date);
+ return this;
+ };
+ /**
+ * @name getCreationDate
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ * @param {Object} type
+ * @returns {Object}
+ */
+
+
+ API.getCreationDate = function (type) {
+ return getCreationDate(type);
+ };
+
+ var padd2 = API.__private__.padd2 = function (number) {
+ return ('0' + parseInt(number)).slice(-2);
+ };
+
+ var outToPages = !1; // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
+
+ var pages = [];
+ var content = [];
+ var currentPage;
+ var content_length = 0;
+ var customOutputDestination;
+
+ var setOutputDestination = API.__private__.setCustomOutputDestination = function (destination) {
+ customOutputDestination = destination;
+ };
+
+ var resetOutputDestination = API.__private__.resetCustomOutputDestination = function (destination) {
+ customOutputDestination = undefined;
+ };
+
+ var out = API.__private__.out = function (string) {
+ var writeArray;
string = typeof string === "string" ? string : string.toString();
- if (outToPages) {
- /* set by beginPage */
- pages[currentPage].push(string);
+
+ if (typeof customOutputDestination === "undefined") {
+ writeArray = outToPages ? pages[currentPage] : content;
} else {
- // +1 for '\n' that will be used to join 'content'
- content_length += string.length + 1;
- content.push(string);
+ writeArray = customOutputDestination;
}
- },
- 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 = [];
+ writeArray.push(string);
- adler32cs = global.adler32cs || jsPDF.API.adler32cs;
- if (compress && typeof adler32cs === 'undefined') {
- compress = false;
+ if (!outToPages) {
+ content_length += string.length + 1;
}
- // outToPages = false as set in endDocument(). out() writes to content.
+ return writeArray;
+ };
- for (n = 1; n <= page; n++) {
- pageObjectNumbers.push(newObject());
- wPt = (pageWidth = pagedim[n].width) * k;
- hPt = (pageHeight = pagedim[n].height) * k;
- out('<>');
- out('endobj');
+ var write = API.__private__.write = function (value) {
+ return out(arguments.length === 1 ? value.toString() : Array.prototype.join.call(arguments, ' '));
+ };
- // 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) {
+ var getArrayBuffer = API.__private__.getArrayBuffer = function (data) {
+ var len = data.length,
+ ab = new ArrayBuffer(len),
+ u8 = new Uint8Array(ab);
- 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]);
- }
+ while (len--) {
+ u8[len] = data.charCodeAt(len);
}
- },
- 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');
- }
+ return ab;
+ };
+
+ var standardFonts = [['Helvetica', "helvetica", "normal", 'WinAnsiEncoding'], ['Helvetica-Bold', "helvetica", "bold", 'WinAnsiEncoding'], ['Helvetica-Oblique', "helvetica", "italic", 'WinAnsiEncoding'], ['Helvetica-BoldOblique', "helvetica", "bolditalic", 'WinAnsiEncoding'], ['Courier', "courier", "normal", 'WinAnsiEncoding'], ['Courier-Bold', "courier", "bold", 'WinAnsiEncoding'], ['Courier-Oblique', "courier", "italic", 'WinAnsiEncoding'], ['Courier-BoldOblique', "courier", "bolditalic", 'WinAnsiEncoding'], ['Times-Roman', "times", "normal", 'WinAnsiEncoding'], ['Times-Bold', "times", "bold", 'WinAnsiEncoding'], ['Times-Italic', "times", "italic", 'WinAnsiEncoding'], ['Times-BoldItalic', "times", "bolditalic", 'WinAnsiEncoding'], ['ZapfDingbats', "zapfdingbats", "normal", null], ['Symbol', "symbol", "normal", null]];
+
+ var getStandardFonts = API.__private__.getStandardFonts = function (data) {
+ return standardFonts;
+ };
+
+ var activeFontSize = options.fontSize || 16;
+ /**
+ * Sets font size for upcoming text elements.
+ *
+ * @param {number} size Font size in points.
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setFontSize
+ */
+
+ var setFontSize = API.__private__.setFontSize = API.setFontSize = function (size) {
+ activeFontSize = size;
+ return this;
+ };
+ /**
+ * Gets the fontsize for upcoming text elements.
+ *
+ * @function
+ * @instance
+ * @returns {number}
+ * @memberOf jsPDF
+ * @name getFontSize
+ */
+
+
+ var getFontSize = API.__private__.getFontSize = API.getFontSize = function () {
+ return activeFontSize;
+ };
+
+ var R2L = options.R2L || false;
+ /**
+ * Set value of R2L functionality.
+ *
+ * @param {boolean} value
+ * @function
+ * @instance
+ * @returns {jsPDF} jsPDF-instance
+ * @memberOf jsPDF
+ * @name setR2L
+ */
+
+ var setR2L = API.__private__.setR2L = API.setR2L = function (value) {
+ R2L = value;
+ return this;
+ };
+ /**
+ * Get value of R2L functionality.
+ *
+ * @function
+ * @instance
+ * @returns {boolean} jsPDF-instance
+ * @memberOf jsPDF
+ * @name getR2L
+ */
+
+
+ var getR2L = API.__private__.getR2L = API.getR2L = function (value) {
+ return R2L;
+ };
+
+ var zoomMode; // default: 1;
+
+ var setZoomMode = API.__private__.setZoomMode = function (zoom) {
+ var validZoomModes = [undefined, null, 'fullwidth', 'fullheight', 'fullpage', 'original'];
+
+ if (/^\d*\.?\d*\%$/.test(zoom)) {
+ zoomMode = zoom;
+ } else if (!isNaN(zoom)) {
+ zoomMode = parseInt(zoom, 10);
+ } else if (validZoomModes.indexOf(zoom) !== -1) {
+ zoomMode = zoom;
+ } else {
+ throw new Error('zoom must be Integer (e.g. 2), a percentage Value (e.g. 300%) or fullwidth, fullheight, fullpage, original. "' + zoom + '" is not recognized.');
}
- 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');
+ };
+
+ var getZoomMode = API.__private__.getZoomMode = function () {
+ return zoomMode;
+ };
+
+ var pageMode; // default: 'UseOutlines';
+
+ var setPageMode = API.__private__.setPageMode = function (pmode) {
+ var validPageModes = [undefined, null, 'UseNone', 'UseOutlines', 'UseThumbs', 'FullScreen'];
+
+ if (validPageModes.indexOf(pmode) == -1) {
+ throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "' + pmode + '" is not recognized.');
}
- 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] = {};
+
+ pageMode = pmode;
+ };
+
+ var getPageMode = API.__private__.getPageMode = function () {
+ return pageMode;
+ };
+
+ var layoutMode; // default: 'continuous';
+
+ var setLayoutMode = API.__private__.setLayoutMode = function (layout) {
+ var validLayoutModes = [undefined, null, 'continuous', 'single', 'twoleft', 'tworight', 'two'];
+
+ if (validLayoutModes.indexOf(layout) == -1) {
+ throw new Error('Layout mode must be one of continuous, single, twoleft, tworight. "' + layout + '" is not recognized.');
}
- 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.
+ layoutMode = layout;
+ };
+
+ var getLayoutMode = API.__private__.getLayoutMode = function () {
+ return layoutMode;
+ };
+ /**
+ * Set the display mode options of the page like zoom and layout.
+ *
+ * @name setDisplayMode
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ * @param {integer|String} zoom You can pass an integer or percentage as
+ * a string. 2 will scale the document up 2x, '200%' will scale up by the
+ * same amount. You can also set it to 'fullwidth', 'fullheight',
+ * 'fullpage', or 'original'.
+ *
+ * Only certain PDF readers support this, such as Adobe Acrobat.
+ *
+ * @param {string} layout Layout mode can be: 'continuous' - this is the
+ * default continuous scroll. 'single' - the single page mode only shows one
+ * page at a time. 'twoleft' - two column left mode, first page starts on
+ * the left, and 'tworight' - pages are laid out in two columns, with the
+ * first page on the right. This would be used for books.
+ * @param {string} pmode 'UseOutlines' - it shows the
+ * outline of the document on the left. 'UseThumbs' - shows thumbnails along
+ * the left. 'FullScreen' - prompts the user to enter fullscreen mode.
+ *
+ * @returns {jsPDF}
*/
- 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]];
+ var setDisplayMode = API.__private__.setDisplayMode = API.setDisplayMode = function (zoom, layout, pmode) {
+ setZoomMode(zoom);
+ setLayoutMode(layout);
+ setPageMode(pmode);
+ return this;
+ };
- 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]);
+ var documentProperties = {
+ 'title': '',
+ 'subject': '',
+ 'author': '',
+ 'keywords': '',
+ 'creator': ''
+ };
- // adding aliases for standard fonts, this time matching the capitalization
- var parts = standardFonts[i][0].split('-');
- addToFontDictionary(fontKey, parts[0], parts[1] || '');
+ var getDocumentProperty = API.__private__.getDocumentProperty = function (key) {
+ if (Object.keys(documentProperties).indexOf(key) === -1) {
+ throw new Error('Invalid argument passed to jsPDF.getDocumentProperty');
}
- 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);
- }
+
+ return documentProperties[key];
+ };
+
+ var getDocumentProperties = API.__private__.getDocumentProperties = function (properties) {
+ return documentProperties;
+ };
+ /**
+ * Adds a properties to the PDF document.
+ *
+ * @param {Object} A property_name-to-property_value object structure.
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setDocumentProperties
+ */
+
+
+ var setDocumentProperties = API.__private__.setDocumentProperties = API.setProperties = API.setDocumentProperties = function (properties) {
+ // copying only those properties we can render.
+ for (var property in documentProperties) {
+ if (documentProperties.hasOwnProperty(property) && properties[property]) {
+ documentProperties[property] = properties[property];
}
+ }
+
+ return this;
+ };
+
+ var setDocumentProperty = API.__private__.setDocumentProperty = function (key, value) {
+ if (Object.keys(documentProperties).indexOf(key) === -1) {
+ throw new Error('Invalid arguments passed to jsPDF.setDocumentProperty');
+ }
+
+ return documentProperties[key] = value;
+ };
+
+ var objectNumber = 0; // 'n' Current object number
+
+ var offsets = []; // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
+
+ var fonts = {}; // collection of font objects, where key is fontKey - a dynamically created label for a given font.
+
+ var fontmap = {}; // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
+
+ var activeFontKey; // will be string representing the KEY of the font as combination of fontName + fontStyle
+
+ var k; // Scale factor
+
+ var page = 0;
+ var pagesContext = [];
+ var additionalObjects = [];
+ var events = new PubSub(API);
+ var hotfixes = options.hotfixes || [];
+
+ var newObject = API.__private__.newObject = function () {
+ var oid = newObjectDeferred();
+ newObjectDeferredBegin(oid, true);
+ return oid;
+ }; // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
+
+
+ var newObjectDeferred = API.__private__.newObjectDeferred = function () {
+ objectNumber++;
+
+ offsets[objectNumber] = function () {
+ return content_length;
};
- 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;
+ return objectNumber;
+ };
- flags = flags || {};
- sourceEncoding = flags.sourceEncoding || 'Unicode';
- outputEncoding = flags.outputEncoding;
+ var newObjectDeferredBegin = function newObjectDeferredBegin(oid, doOutput) {
+ doOutput = typeof doOutput === 'boolean' ? doOutput : false;
+ offsets[oid] = content_length;
- // 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;
+ if (doOutput) {
+ out(oid + ' 0 obj');
+ }
- // each font has default encoding. Some have it clearly defined.
- if (!outputEncoding && fonts[activeFontKey].encoding) {
- outputEncoding = fonts[activeFontKey].encoding;
- }
+ return oid;
+ }; // Does not output the object until after the pages have been output.
+ // Returns an object containing the objectId and content.
+ // All pages have been added so the object ID can be estimated to start right after.
+ // This does not modify the current objectNumber; It must be updated after the newObjects are output.
- // 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]);
- }
+ var newAdditionalObject = API.__private__.newAdditionalObject = function () {
+ var objId = newObjectDeferred();
+ var obj = {
+ objId: objId,
+ content: ''
+ };
+ additionalObjects.push(obj);
+ return obj;
+ };
- // 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('');
- }
+ var rootDictionaryObjId = newObjectDeferred();
+ var resourceDictionaryObjId = newObjectDeferred(); /////////////////////
+ // Private functions
+ /////////////////////
+
+ var decodeColorString = API.__private__.decodeColorString = function (color) {
+ var colorEncoded = color.split(' ');
+
+ if (colorEncoded.length === 2 && (colorEncoded[1] === 'g' || colorEncoded[1] === 'G')) {
+ // convert grayscale value to rgb so that it can be converted to hex for consistency
+ var floatVal = parseFloat(colorEncoded[0]);
+ colorEncoded = [floatVal, floatVal, floatVal, 'r'];
}
- 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--;
+ var colorAsRGB = '#';
+
+ for (var i = 0; i < 3; i++) {
+ colorAsRGB += ('0' + Math.floor(parseFloat(colorEncoded[i]) * 255).toString(16)).slice(-2);
}
- if (!isUnicode) {
- return text;
+
+ return colorAsRGB;
+ };
+
+ var encodeColorString = API.__private__.encodeColorString = function (options) {
+ var color;
+
+ if (typeof options === "string") {
+ options = {
+ ch1: options
+ };
}
- 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");
+ var ch1 = options.ch1;
+ var ch2 = options.ch2;
+ var ch3 = options.ch3;
+ var ch4 = options.ch4;
+ var precision = options.precision;
+ var letterArray = options.pdfColorType === "draw" ? ['G', 'RG', 'K'] : ['g', 'rg', 'k'];
+
+ if (typeof ch1 === "string" && ch1.charAt(0) !== '#') {
+ var rgbColor = new RGBColor(ch1);
+
+ if (rgbColor.ok) {
+ ch1 = rgbColor.toHex();
+ } else if (!/^\d*\.?\d*$/.test(ch1)) {
+ throw new Error('Invalid color "' + ch1 + '" passed to jsPDF.encodeColorString.');
}
- newtext.push(bch);
- newtext.push(ch - (bch << 8));
+ } //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];
}
- 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.
+
+ if (typeof ch1 === "string" && /^#[0-9A-Fa-f]{6}$/.test(ch1)) {
+ var hex = parseInt(ch1.substr(1), 16);
+ ch1 = hex >> 16 & 255;
+ ch2 = hex >> 8 & 255;
+ ch3 = hex & 255;
+ }
+
+ if (typeof ch2 === "undefined" || typeof ch4 === "undefined" && ch1 === ch2 && ch2 === ch3) {
+ // Gray color space.
+ if (typeof ch1 === "string") {
+ color = ch1 + " " + letterArray[0];
+ } else {
+ switch (options.precision) {
+ case 2:
+ color = f2(ch1 / 255) + " " + letterArray[0];
+ break;
+
+ case 3:
+ default:
+ color = f3(ch1 / 255) + " " + letterArray[0];
+ }
+ }
+ } else if (typeof ch4 === "undefined" || _typeof(ch4) === "object") {
+ // assume RGBA
+ if (ch4 && !isNaN(ch4.a)) {
+ //TODO Implement transparency.
+ //WORKAROUND use white for now, if transparent, otherwise handle as rgb
+ if (ch4.a === 0) {
+ color = ['1.000', '1.000', '1.000', letterArray[1]].join(" ");
+ return color;
+ }
+ } // assume RGB
+
+
+ if (typeof ch1 === "string") {
+ color = [ch1, ch2, ch3, letterArray[1]].join(" ");
+ } else {
+ switch (options.precision) {
+ case 2:
+ color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), letterArray[1]].join(" ");
+ break;
+
+ default:
+ case 3:
+ color = [f3(ch1 / 255), f3(ch2 / 255), f3(ch3 / 255), letterArray[1]].join(" ");
+ }
+ }
+ } else {
+ // assume CMYK
+ if (typeof ch1 === 'string') {
+ color = [ch1, ch2, ch3, ch4, letterArray[2]].join(" ");
+ } else {
+ switch (options.precision) {
+ case 2:
+ color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), f2(ch4 / 255), letterArray[2]].join(" ");
+ break;
+
+ case 3:
+ default:
+ color = [f3(ch1 / 255), f3(ch2 / 255), f3(ch3 / 255), f3(ch4 / 255), letterArray[2]].join(" ");
+ }
+ }
+ }
+
+ return color;
+ };
+
+ var getFilters = API.__private__.getFilters = function () {
+ return filters;
+ };
+
+ var putStream = API.__private__.putStream = function (options) {
+ options = options || {};
+ var data = options.data || '';
+ var filters = options.filters || getFilters();
+ var alreadyAppliedFilters = options.alreadyAppliedFilters || [];
+ var addLength1 = options.addLength1 || false;
+ var valueOfLength1 = data.length;
+ var processedData = {};
+
+ if (filters === true) {
+ filters = ['FlateEncode'];
+ }
+
+ var keyValues = options.additionalKeyValues || [];
+
+ if (typeof jsPDF.API.processDataByFilters !== 'undefined') {
+ processedData = jsPDF.API.processDataByFilters(data, filters);
+ } else {
+ processedData = {
+ data: data,
+ reverseChain: []
+ };
+ }
+
+ var filterAsString = processedData.reverseChain + (Array.isArray(alreadyAppliedFilters) ? alreadyAppliedFilters.join(' ') : alreadyAppliedFilters.toString());
+
+ if (processedData.data.length !== 0) {
+ keyValues.push({
+ key: 'Length',
+ value: processedData.data.length
+ });
+
+ if (addLength1 === true) {
+ keyValues.push({
+ key: 'Length1',
+ value: valueOfLength1
+ });
+ }
+ }
+
+ if (filterAsString.length != 0) {
+ //if (filters.length === 0 && alreadyAppliedFilters.length === 1 && typeof alreadyAppliedFilters !== "undefined") {
+ if (filterAsString.split('/').length - 1 === 1) {
+ keyValues.push({
+ key: 'Filter',
+ value: filterAsString
+ });
+ } else {
+ keyValues.push({
+ key: 'Filter',
+ value: '[' + filterAsString + ']'
+ });
+ }
+ }
+
+ out('<<');
+
+ for (var i = 0; i < keyValues.length; i++) {
+ out('/' + keyValues[i].key + ' ' + keyValues[i].value);
+ }
+
+ out('>>');
+
+ if (processedData.data.length !== 0) {
+ out('stream');
+ out(processedData.data);
+ out('endstream');
+ }
+ };
+
+ var putPage = API.__private__.putPage = function (page) {
+ var mediaBox = page.mediaBox;
+ var pageNumber = page.number;
+ var data = page.data;
+ var pageObjectNumber = page.objId;
+ var pageContentsObjId = page.contentsObjId;
+ newObjectDeferredBegin(pageObjectNumber, true);
+ var wPt = pagesContext[currentPage].mediaBox.topRightX - pagesContext[currentPage].mediaBox.bottomLeftX;
+ var hPt = pagesContext[currentPage].mediaBox.topRightY - pagesContext[currentPage].mediaBox.bottomLeftY;
+ out('<>');
+ out('endobj'); // Page content
+
+ var pageContent = data.join('\n');
+ newObjectDeferredBegin(pageContentsObjId, true);
+ putStream({
+ data: pageContent,
+ filters: getFilters()
+ });
+ out('endobj');
+ return pageObjectNumber;
+ };
+
+ var putPages = API.__private__.putPages = function () {
+ var n,
+ i,
+ pageObjectNumbers = [];
+
+ for (n = 1; n <= page; n++) {
+ pagesContext[n].objId = newObjectDeferred();
+ pagesContext[n].contentsObjId = newObjectDeferred();
+ }
+
+ for (n = 1; n <= page; n++) {
+ pageObjectNumbers.push(putPage({
+ number: n,
+ data: pages[n],
+ objId: pagesContext[n].objId,
+ contentsObjId: pagesContext[n].contentsObjId,
+ mediaBox: pagesContext[n].mediaBox,
+ cropBox: pagesContext[n].cropBox,
+ bleedBox: pagesContext[n].bleedBox,
+ trimBox: pagesContext[n].trimBox,
+ artBox: pagesContext[n].artBox,
+ userUnit: pagesContext[n].userUnit,
+ rootDictionaryObjId: rootDictionaryObjId,
+ resourceDictionaryObjId: resourceDictionaryObjId
+ }));
+ }
+
+ newObjectDeferredBegin(rootDictionaryObjId, true);
+ out('<>');
+ out('endobj');
+ events.publish('postPutPages');
+ };
+
+ var putFont = function putFont(font) {
+ events.publish('putFont', {
+ font: font,
+ out: out,
+ newObject: newObject,
+ putStream: putStream
+ });
+
+ if (font.isAlreadyPutted !== true) {
+ font.objectNumber = newObject();
+ out('<<');
+ out('/Type /Font');
+ out('/BaseFont /' + font.postScriptName);
+ out('/Subtype /Type1');
+
+ if (typeof font.encoding === 'string') {
+ out('/Encoding /' + font.encoding);
+ }
+
+ out('/FirstChar 32');
+ out('/LastChar 255');
+ out('>>');
+ out('endobj');
+ }
+ };
+
+ var putFonts = function putFonts() {
+ for (var fontKey in fonts) {
+ if (fonts.hasOwnProperty(fontKey)) {
+ if (putOnlyUsedFonts === false || putOnlyUsedFonts === true && usedFonts.hasOwnProperty(fontKey)) {
+ putFont(fonts[fontKey]);
+ }
+ }
+ }
+ };
+
+ var putResourceDictionary = function putResourceDictionary() {
+ out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
+ out('/Font <<'); // Do this for each font, the '1' bit is the index of the font
+
+ for (var fontKey in fonts) {
+ if (fonts.hasOwnProperty(fontKey)) {
+ if (putOnlyUsedFonts === false || putOnlyUsedFonts === true && usedFonts.hasOwnProperty(fontKey)) {
+ out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
+ }
+ }
+ }
+
+ out('>>');
+ out('/XObject <<');
+ events.publish('putXobjectDict');
+ out('>>');
+ };
+
+ var putResources = function putResources() {
+ putFonts();
+ events.publish('putResources');
+ newObjectDeferredBegin(resourceDictionaryObjId, true);
+ out('<<');
+ putResourceDictionary();
+ out('>>');
+ out('endobj');
+ events.publish('postPutResources');
+ };
+
+ var putAdditionalObjects = function putAdditionalObjects() {
+ events.publish('putAdditionalObjects');
+
+ for (var i = 0; i < additionalObjects.length; i++) {
+ var obj = additionalObjects[i];
+ newObjectDeferredBegin(obj.objId, true);
+ out(obj.content);
+ out('endobj');
+ }
+
+ events.publish('postPutAdditionalObjects');
+ };
+
+ var addToFontDictionary = function addToFontDictionary(fontKey, fontName, fontStyle) {
+ // this is mapping structure for quick font key lookup.
+ // returns the KEY of the font (ex: "F1") for a given
+ // pair of font name and type (ex: "Arial". "Italic")
+ if (!fontmap.hasOwnProperty(fontName)) {
+ fontmap[fontName] = {};
+ }
+
+ fontmap[fontName][fontStyle] = fontKey;
+ };
+
+ var addFont = function addFont(postScriptName, fontName, fontStyle, encoding, isStandardFont) {
+ isStandardFont = isStandardFont || false;
+ var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10),
+ // This is FontObject
+ font = {
+ 'id': fontKey,
+ 'postScriptName': postScriptName,
+ 'fontName': fontName,
+ 'fontStyle': fontStyle,
+ 'encoding': encoding,
+ 'isStandardFont': isStandardFont,
+ 'metadata': {}
+ };
+ var instance = this;
+ events.publish('addFont', {
+ font: font,
+ instance: instance
+ });
+
+ if (fontKey !== undefined) {
+ fonts[fontKey] = font;
+ addToFontDictionary(fontKey, fontName, fontStyle);
+ }
+
+ return fontKey;
+ };
+
+ var addFonts = function addFonts(arrayOfFonts) {
+ for (var i = 0, l = standardFonts.length; i < l; i++) {
+ var fontKey = addFont(arrayOfFonts[i][0], arrayOfFonts[i][1], arrayOfFonts[i][2], standardFonts[i][3], true);
+ usedFonts[fontKey] = true; // adding aliases for standard fonts, this time matching the capitalization
+
+ var parts = arrayOfFonts[i][0].split('-');
+ addToFontDictionary(fontKey, parts[0], parts[1] || '');
+ }
+
+ events.publish('addFonts', {
+ fonts: fonts,
+ dictionary: fontmap
+ });
+ };
+
+ var SAFE = function __safeCall(fn) {
+ fn.foo = function __safeCallWrapper() {
+ try {
+ return fn.apply(this, arguments);
+ } catch (e) {
+ var stack = e.stack || '';
+ if (~stack.indexOf(' at ')) stack = stack.split(" at ")[1];
+ var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message;
+
+ if (global.console) {
+ global.console.error(m, e);
+ if (global.alert) alert(m);
+ } else {
+ throw new Error(m);
+ }
+ }
+ };
+
+ fn.foo.bar = fn;
+ return fn.foo;
+ };
+
+ var to8bitStream = function to8bitStream(text, flags) {
+ /**
+ * PDF 1.3 spec:
+ * "For text strings encoded in Unicode, the first two bytes must be 254 followed by
+ * 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
+ * with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
+ * to be a meaningful beginning of a word or phrase.) The remainder of the
+ * string consists of Unicode character codes, according to the UTF-16 encoding
+ * specified in the Unicode standard, version 2.0. Commonly used Unicode values
+ * are represented as 2 bytes per character, with the high-order byte appearing first
+ * in the string."
+ *
+ * In other words, if there are chars in a string with char code above 255, we
+ * recode the string to UCS2 BE - string doubles in length and BOM is prepended.
+ *
+ * HOWEVER!
+ * Actual *content* (body) text (as opposed to strings used in document properties etc)
+ * does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
+ *
+ * Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
+ * a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
+ * fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
+ * code page. There, however, all characters in the stream are treated as GIDs,
+ * including BOM, which is the reason we need to skip BOM in content text (i.e. that
+ * that is tied to a font).
+ *
+ * To signal this "special" PDFEscape / to8bitStream handling mode,
+ * API.text() function sets (unless you overwrite it with manual values
+ * given to API.text(.., flags) )
+ * flags.autoencode = true
+ * flags.noBOM = true
+ *
+ * ===================================================================================
+ * `flags` properties relied upon:
+ * .sourceEncoding = string with encoding label.
+ * "Unicode" by default. = encoding of the incoming text.
+ * pass some non-existing encoding name
+ * (ex: 'Do not touch my strings! I know what I am doing.')
+ * to make encoding code skip the encoding step.
+ * .outputEncoding = Either valid PDF encoding name
+ * (must be supported by jsPDF font metrics, otherwise no encoding)
+ * or a JS object, where key = sourceCharCode, value = outputCharCode
+ * missing keys will be treated as: sourceCharCode === outputCharCode
+ * .noBOM
+ * See comment higher above for explanation for why this is important
+ * .autoencode
+ * See comment higher above for explanation for why this is important
*/
- return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
- },
- putInfo = function putInfo() {
- out('/Producer (jsPDF ' + jsPDF.version + ')');
+ var i, l, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch;
+ flags = flags || {};
+ sourceEncoding = flags.sourceEncoding || 'Unicode';
+ outputEncoding = flags.outputEncoding; // This 'encoding' section relies on font metrics format
+ // attached to font objects by, among others,
+ // "Willow Systems' standard_font_metrics plugin"
+ // see jspdf.plugin.standard_font_metrics.js for format
+ // of the font.metadata.encoding Object.
+ // It should be something like
+ // .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
+ // .widths = {0:width, code:width, ..., 'fof':divisor}
+ // .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
+
+ if ((flags.autoencode || outputEncoding) && fonts[activeFontKey].metadata && fonts[activeFontKey].metadata[sourceEncoding] && fonts[activeFontKey].metadata[sourceEncoding].encoding) {
+ encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding; // each font has default encoding. Some have it clearly defined.
+
+ if (!outputEncoding && fonts[activeFontKey].encoding) {
+ outputEncoding = fonts[activeFontKey].encoding;
+ } // Hmmm, the above did not work? Let's try again, in different place.
+
+
+ if (!outputEncoding && encodingBlock.codePages) {
+ outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
+ }
+
+ if (typeof outputEncoding === 'string') {
+ outputEncoding = encodingBlock[outputEncoding];
+ } // we want output encoding to be a JS Object, where
+ // key = sourceEncoding's character code and
+ // value = outputEncoding's character code.
+
+
+ if (outputEncoding) {
+ isUnicode = false;
+ newtext = [];
+
+ for (i = 0, l = text.length; i < l; i++) {
+ ch = outputEncoding[text.charCodeAt(i)];
+
+ if (ch) {
+ newtext.push(String.fromCharCode(ch));
+ } else {
+ newtext.push(text[i]);
+ } // since we are looping over chars anyway, might as well
+ // check for residual unicodeness
+
+
+ if (newtext[i].charCodeAt(0) >> 8) {
+ /* more than 255 */
+ isUnicode = true;
+ }
+ }
+
+ text = newtext.join('');
+ }
+ }
+
+ i = text.length; // isUnicode may be set to false above. Hence the triple-equal to undefined
+
+ while (isUnicode === undefined && i !== 0) {
+ if (text.charCodeAt(i - 1) >> 8) {
+ /* more than 255 */
+ isUnicode = true;
+ }
+
+ i--;
+ }
+
+ if (!isUnicode) {
+ return text;
+ }
+
+ newtext = flags.noBOM ? [] : [254, 255];
+
+ for (i = 0, l = text.length; i < l; i++) {
+ ch = text.charCodeAt(i);
+ bch = ch >> 8; // divide by 256
+
+ if (bch >> 8) {
+ /* something left after dividing by 256 second time */
+ throw new Error("Character at position " + i + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
+ }
+
+ newtext.push(bch);
+ newtext.push(ch - (bch << 8));
+ }
+
+ return String.fromCharCode.apply(undefined, newtext);
+ };
+
+ var pdfEscape = API.__private__.pdfEscape = API.pdfEscape = function (text, flags) {
+ /**
+ * Replace '/', '(', and ')' with pdf-safe versions
+ *
+ * Doing to8bitStream does NOT make this PDF display unicode text. For that
+ * we also need to reference a unicode font and embed it - royal pain in the rear.
+ *
+ * There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
+ * which JavaScript Strings are happy to provide. So, while we still cannot display
+ * 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
+ * 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
+ * is still parseable.
+ * This will allow immediate support for unicode in document properties strings.
+ */
+ return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
+ };
+
+ var beginPage = API.__private__.beginPage = function (width, height) {
+ var tmp; // Dimensions are stored as user units and converted to points on output
+
+ var orientation = typeof height === 'string' && height.toLowerCase();
+
+ if (typeof width === 'string') {
+ if (getPageFormat(width.toLowerCase())) {
+ width = getPageFormat(width.toLowerCase())[0];
+ height = getPageFormat(width.toLowerCase())[1];
+ }
+ }
+
+ if (Array.isArray(width)) {
+ height = width[1];
+ width = width[0];
+ }
+
+ if (isNaN(width) || isNaN(height)) {
+ width = format[0];
+ height = format[1];
+ }
+
+ if (orientation) {
+ switch (orientation.substr(0, 1)) {
+ case 'l':
+ if (height > width) orientation = 's';
+ break;
+
+ case 'p':
+ if (width > height) orientation = 's';
+ break;
+ }
+
+ if (orientation === 's') {
+ tmp = width;
+ width = height;
+ height = tmp;
+ }
+ }
+
+ if (width > 14400 || height > 14400) {
+ console.warn('A page in a PDF can not be wider or taller than 14400 userUnit. jsPDF limits the width/height to 14400');
+ width = Math.min(14400, width);
+ height = Math.min(14400, height);
+ }
+
+ format = [width, height];
+ outToPages = true;
+ pages[++page] = [];
+ pagesContext[page] = {
+ objId: 0,
+ contentsObjId: 0,
+ userUnit: Number(userUnit),
+ artBox: null,
+ bleedBox: null,
+ cropBox: null,
+ trimBox: null,
+ mediaBox: {
+ bottomLeftX: 0,
+ bottomLeftY: 0,
+ topRightX: Number(width),
+ topRightY: Number(height)
+ }
+ };
+
+ _setPage(page);
+ };
+
+ var _addPage = function _addPage() {
+ beginPage.apply(this, arguments); // Set line width
+
+ setLineWidth(lineWidth); // Set draw color
+
+ out(strokeColor); // resurrecting non-default line caps, joins
+
+ if (lineCapID !== 0) {
+ out(lineCapID + ' J');
+ }
+
+ if (lineJoinID !== 0) {
+ out(lineJoinID + ' j');
+ }
+
+ events.publish('addPage', {
+ pageNumber: page
+ });
+ };
+
+ var _deletePage = function _deletePage(n) {
+ if (n > 0 && n <= page) {
+ pages.splice(n, 1);
+ page--;
+
+ if (currentPage > page) {
+ currentPage = page;
+ }
+
+ this.setPage(currentPage);
+ }
+ };
+
+ var _setPage = function _setPage(n) {
+ if (n > 0 && n <= page) {
+ currentPage = n;
+ }
+ };
+
+ var getNumberOfPages = API.__private__.getNumberOfPages = API.getNumberOfPages = function () {
+ return pages.length - 1;
+ };
+ /**
+ * Returns a document-specific font key - a label assigned to a
+ * font name + font type combination at the time the font was added
+ * to the font inventory.
+ *
+ * Font key is used as label for the desired font for a block of text
+ * to be added to the PDF document stream.
+ * @private
+ * @function
+ * @param fontName {string} can be undefined on "falthy" to indicate "use current"
+ * @param fontStyle {string} can be undefined on "falthy" to indicate "use current"
+ * @returns {string} Font key.
+ * @ignore
+ */
+
+
+ var _getFont = function getFont(fontName, fontStyle, options) {
+ var key = undefined,
+ fontNameLowerCase;
+ options = options || {};
+ fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName;
+ fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle;
+ fontNameLowerCase = fontName.toLowerCase();
+
+ if (fontmap[fontNameLowerCase] !== undefined && fontmap[fontNameLowerCase][fontStyle] !== undefined) {
+ key = fontmap[fontNameLowerCase][fontStyle];
+ } else if (fontmap[fontName] !== undefined && fontmap[fontName][fontStyle] !== undefined) {
+ key = fontmap[fontName][fontStyle];
+ } else {
+ if (options.disableWarning === false) {
+ console.warn("Unable to look up font label for font '" + fontName + "', '" + fontStyle + "'. Refer to getFontList() for available fonts.");
+ }
+ }
+
+ if (!key && !options.noFallback) {
+ key = fontmap['times'][fontStyle];
+
+ if (key == null) {
+ key = fontmap['times']['normal'];
+ }
+ }
+
+ return key;
+ };
+
+ var putInfo = API.__private__.putInfo = function () {
+ newObject();
+ out('<<');
+ out('/Producer (jsPDF ' + jsPDF.version + ')');
+
for (var key in documentProperties) {
if (documentProperties.hasOwnProperty(key) && documentProperties[key]) {
out('/' + key.substr(0, 1).toUpperCase() + key.substr(1) + ' (' + pdfEscape(documentProperties[key]) + ')');
}
- }
- out('/CreationDate (' + creationDate + ')');
- },
- 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) + ']');
+ }
+
+ out('/CreationDate (' + creationDate + ')');
+ out('>>');
+ out('endobj');
+ };
+
+ var putCatalog = API.__private__.putCatalog = function (options) {
+ options = options || {};
+ var tmpRootDictionaryObjId = options.rootDictionaryObjId || rootDictionaryObjId;
+ newObject();
+ out('<<');
+ out('/Type /Catalog');
+ out('/Pages ' + tmpRootDictionaryObjId + ' 0 R'); // PDF13ref Section 7.2.1
+
+ if (!zoomMode) zoomMode = 'fullwidth';
+
+ switch (zoomMode) {
+ case 'fullwidth':
+ out('/OpenAction [3 0 R /FitH null]');
+ break;
+
+ case 'fullheight':
+ out('/OpenAction [3 0 R /FitV null]');
+ break;
+
+ case 'fullpage':
+ out('/OpenAction [3 0 R /Fit]');
+ break;
+
+ case 'original':
+ out('/OpenAction [3 0 R /XYZ null null 1]');
+ 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');
+ out('>>');
+ out('endobj');
+ };
+
+ var putTrailer = API.__private__.putTrailer = function () {
+ out('trailer');
+ out('<<');
+ out('/Size ' + (objectNumber + 1));
+ out('/Root ' + objectNumber + ' 0 R');
+ out('/Info ' + (objectNumber - 1) + ' 0 R');
+ out("/ID [ <" + fileId + "> <" + fileId + "> ]");
+ out('>>');
+ };
+
+ var putHeader = API.__private__.putHeader = function () {
+ out('%PDF-' + pdfVersion);
+ out("%\xBA\xDF\xAC\xE0");
+ };
+
+ var putXRef = API.__private__.putXRef = function () {
+ var i = 1;
+ var p = "0000000000";
+ out('xref');
+ out('0 ' + (objectNumber + 1));
+ out('0000000000 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 {
+ if (typeof offsets[i] !== "undefined") {
+ out((p + offsets[i]).slice(-10) + ' 00000 n ');
+ } else {
+ out('0000000000 00000 n ');
+ }
+ }
+ }
+ };
+
+ var buildDocument = API.__private__.buildDocument = function () {
+ outToPages = false; // switches out() to content
+ //reset fields relevant for objectNumber generation and xref.
+
+ objectNumber = 0;
+ content_length = 0;
+ content = [];
+ offsets = [];
+ additionalObjects = [];
+ rootDictionaryObjId = newObjectDeferred();
+ resourceDictionaryObjId = newObjectDeferred();
+ events.publish('buildDocument');
+ putHeader();
+ putPages();
+ putAdditionalObjects();
+ putResources();
+ putInfo();
+ putCatalog();
+ var offsetOfXRef = content_length;
+ putXRef();
+ putTrailer();
+ out('startxref');
+ out('' + offsetOfXRef);
+ out('%%EOF');
+ outToPages = true;
+ return content.join('\n');
+ };
+
+ var getBlob = API.__private__.getBlob = function (data) {
+ return new Blob([getArrayBuffer(data)], {
+ 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. Possible values are 'arraybuffer', 'blob', 'bloburi'/'bloburl', 'datauristring'/'dataurlstring', 'datauri'/'dataurl', 'dataurlnewwindow'.
+ * @param {Object} options An object providing some additional signalling to PDF generator. Possible options are 'filename'.
+ *
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name output
+ */
+
+
+ var output = API.output = API.__private__.output = SAFE(function output(type, options) {
+ options = options || {};
+ var pdfDocument = buildDocument();
+
+ if (typeof options === "string") {
+ options = {
+ filename: options
+ };
+ } else {
+ options.filename = options.filename || 'generated.pdf';
+ }
+
+ switch (type) {
+ case undefined:
+ return pdfDocument;
+
+ case 'save':
+ API.save(options.filename);
+ break;
+
+ case 'arraybuffer':
+ return getArrayBuffer(pdfDocument);
+
+ case 'blob':
+ return getBlob(pdfDocument);
+
+ case 'bloburi':
+ case 'bloburl':
+ // User is responsible of calling revokeObjectURL
+ return global.URL && global.URL.createObjectURL(getBlob(pdfDocument)) || void 0;
+
+ case 'datauristring':
+ case 'dataurlstring':
+ return 'data:application/pdf;filename=' + options.filename + ';base64,' + btoa(pdfDocument);
+
+ case 'dataurlnewwindow':
+ var htmlForNewWindow = '' + '' + '' + '' + '';
+ var nW = global.open();
+
+ if (nW !== null) {
+ nW.document.write(htmlForNewWindow);
+ }
+
+ if (nW || typeof safari === "undefined") return nW;
+
+ /* pass through */
+
+ case 'datauri':
+ case 'dataurl':
+ return global.document.location.href = 'data:application/pdf;filename=' + options.filename + ';base64,' + btoa(pdfDocument);
+
+ default:
+ return null;
+ }
+ });
+ /**
+ * 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}
+ */
+
+ var 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 new Error('Invalid unit: ' + unit);
+ }
+
+ setCreationDate();
+ setFileId(); //---------------------------------------
+ // Public API
+
+ var getPageInfo = API.__private__.getPageInfo = function (pageNumberOneBased) {
+ if (isNaN(pageNumberOneBased) || pageNumberOneBased % 1 !== 0) {
+ throw new Error('Invalid argument passed to jsPDF.getPageInfo');
+ }
+
+ var objId = pagesContext[pageNumberOneBased].objId;
+ return {
+ objId: objId,
+ pageNumber: pageNumberOneBased,
+ pageContext: pagesContext[pageNumberOneBased]
+ };
+ };
+
+ var getPageInfoByObjId = API.__private__.getPageInfoByObjId = function (objId) {
+
+ for (var pageNumber in pagesContext) {
+ if (pagesContext[pageNumber].objId === objId) {
+ break;
+ }
+ }
+
+ if (isNaN(objId) || objId % 1 !== 0) {
+ throw new Error('Invalid argument passed to jsPDF.getPageInfoByObjId');
+ }
+
+ return getPageInfo(pageNumber);
+ };
+
+ var getCurrentPageInfo = API.__private__.getCurrentPageInfo = function () {
+ return {
+ objId: pagesContext[currentPage].objId,
+ pageNumber: currentPage,
+ pageContext: pagesContext[currentPage]
+ };
+ };
+ /**
+ * Adds (and transfers the focus to) new page to the PDF document.
+ * @param format {String/Array} The format of the new page. Can be:
+ * 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
+ * @instance
+ * @returns {jsPDF}
+ *
+ * @memberOf 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
+ * @instance
+ * @returns {jsPDF}
+ *
+ * @memberOf 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;
+ };
+ /**
+ * @name insertPage
+ * @memberOf jsPDF
+ *
+ * @function
+ * @instance
+ * @param {Object} beforePage
+ * @returns {jsPDF}
+ */
+
+
+ API.insertPage = function (beforePage) {
+ this.addPage();
+ this.movePage(currentPage, beforePage);
+ return this;
+ };
+ /**
+ * @name movePage
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ * @param {Object} targetPage
+ * @param {Object} beforePage
+ * @returns {jsPDF}
+ */
+
+
+ API.movePage = function (targetPage, beforePage) {
+ if (targetPage > beforePage) {
+ var tmpPages = pages[targetPage];
+ var tmpPagesContext = pagesContext[targetPage];
+
+ for (var i = targetPage; i > beforePage; i--) {
+ pages[i] = pages[i - 1];
+ pagesContext[i] = pagesContext[i - 1];
+ }
+
+ pages[beforePage] = tmpPages;
+ pagesContext[beforePage] = tmpPagesContext;
+ this.setPage(beforePage);
+ } else if (targetPage < beforePage) {
+ var tmpPages = pages[targetPage];
+ var tmpPagesContext = pagesContext[targetPage];
+
+ for (var i = targetPage; i < beforePage; i++) {
+ pages[i] = pages[i + 1];
+ pagesContext[i] = pagesContext[i + 1];
+ }
+
+ pages[beforePage] = tmpPages;
+ pagesContext[beforePage] = tmpPagesContext;
+ this.setPage(beforePage);
+ }
+
+ return this;
+ };
+ /**
+ * Deletes a page from the PDF.
+ * @name deletePage
+ * @memberOf jsPDF
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ */
+
+
+ API.deletePage = function () {
+ _deletePage.apply(this, arguments);
+
+ return this;
+ };
+ /**
+ * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
+ *
+ * @function
+ * @instance
+ * @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 signaling how the text must be encoded.
+ * @param {string} [options.align=left] - The alignment of the text, possible values: left, center, right, justify.
+ * @param {string} [options.baseline=alphabetic] - Sets text baseline used when drawing the text, possible values: alphabetic, ideographic, bottom, top, middle.
+ * @param {string} [options.angle=0] - Rotate the text counterclockwise. Expects the angle in degree.
+ * @param {string} [options.charSpace=0] - The space between each letter.
+ * @param {string} [options.lineHeightFactor=1.15] - The lineheight of each line.
+ * @param {string} [options.flags] - Flags for to8bitStream.
+ * @param {string} [options.flags.noBOM=true] - Don't add BOM to Unicode-text.
+ * @param {string} [options.flags.autoencode=true] - Autoencode the Text.
+ * @param {string} [options.maxWidth=0] - Split the text by given width, 0 = no split.
+ * @param {string} [options.renderingMode=fill] - Set how the text should be rendered, possible values: fill, stroke, fillThenStroke, invisible, fillAndAddForClipping, strokeAndAddPathForClipping, fillThenStrokeAndAddToPathForClipping, addToPathForClipping.
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name text
+ */
+
+
+ var text = API.__private__.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
+ */
+ //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' && typeof x === 'number' && (typeof y === 'string' || Array.isArray(y))) {
+ tmp = y;
+ y = x;
+ x = text;
+ text = tmp;
+ }
+
+ var flags = arguments[3];
+ var angle = arguments[4];
+ var align = arguments[5];
+
+ if (_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
+ };
+ }
+
+ flags = flags || {};
+ flags.noBOM = flags.noBOM || true;
+ flags.autoencode = flags.autoencode || true;
+
+ if (isNaN(x) || isNaN(y) || typeof text === "undefined" || text === null) {
+ throw new Error('Invalid arguments passed to jsPDF.text');
+ }
+
+ if (text.length === 0) {
+ return scope;
+ }
+
+ var xtra = '';
+ var isHex = false;
+ var lineHeight = typeof options.lineHeightFactor === 'number' ? options.lineHeightFactor : lineHeightFactor;
+ var scope = options.scope || this;
+
+ 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 (Array.isArray(text) && 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 (Array.isArray(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;
+ 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 (Array.isArray(curDa) && curDa[0] === "string") {
+ tmpResult = processingFunction(curDa[0], curDa[1], curDa[2]);
+ da.push([tmpResult[0], tmpResult[1], tmpResult[2]]);
+ }
+ }
+
+ result = da;
+ }
+
+ return result;
+ } //Check if text is of type String
+
+
+ var textIsOfTypeString = false;
+ var tmpTextIsOfTypeString = true;
+
+ if (typeof text === 'string') {
+ textIsOfTypeString = true;
+ } else if (Array.isArray(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" || Array.isArray(curDa) && 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];
+ }
+ } //baseline
+
+
+ var height = activeFontSize / scope.internal.scaleFactor;
+ var descent = height * (lineHeightFactor - 1);
+
+ switch (options.baseline) {
+ case 'bottom':
+ y -= descent;
+ break;
+
+ case 'top':
+ y += height - descent;
+ break;
+
+ case 'hanging':
+ y += height - 2 * descent;
+ break;
+
+ case 'middle':
+ y += height / 2 - descent;
+ break;
+
+ case 'ideographic':
+ case 'alphabetic':
+ default:
+ // do nothing, everything is fine
+ break;
+ } //multiline
+
+
+ var maxWidth = options.maxWidth || 0;
+
+ if (maxWidth > 0) {
+ if (typeof text === 'string') {
+ text = scope.splitTextToSize(text, maxWidth);
+ } else if (Object.prototype.toString.call(text) === '[object Array]') {
+ text = scope.splitTextToSize(text.join(" "), maxWidth);
+ }
+ } //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 = scope.internal.scaleFactor;
+ var transformationMatrix = [];
+
+ if (angle) {
+ angle *= Math.PI / 180;
+ var c = Math.cos(angle),
+ s = Math.sin(angle);
+ transformationMatrix = [f2(c), f2(s), f2(s * -1), f2(c)];
+ } //charSpace
+
+
+ var charSpace = options.charSpace;
+
+ if (typeof charSpace !== 'undefined') {
+ xtra += f3(charSpace * k) + " Tc\n";
+ } //lang
+
+
+ var lang = options.lang;
+ var tmpRenderingMode = -1;
+ var parmRenderingMode = typeof options.renderingMode !== "undefined" ? options.renderingMode : options.stroke;
+ var pageContext = scope.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 = typeof pageContext.usedRenderingMode !== 'undefined' ? 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 pageWidth = scope.internal.pageSize.getWidth();
+ var k = scope.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 scope.getStringUnitWidth(v, {
+ font: activeFont,
+ charSpace: charSpace,
+ fontSize: activeFontSize
+ }) * 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 = getHorizontalCoordinate(x);
+ newY = getVerticalCoordinate(y);
+ } 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 = getHorizontalCoordinate(x);
+ newY = getVerticalCoordinate(y);
+ } 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 ? getVerticalCoordinate(y) : -leading;
+ newX = i === 0 ? getHorizontalCoordinate(x) : 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 ? getVerticalCoordinate(y) : -leading;
+ newX = i === 0 ? getHorizontalCoordinate(x) : 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 (!Array.isArray(da[i])) {
+ posX = getHorizontalCoordinate(x);
+ posY = getVerticalCoordinate(y);
+ content = (isHex ? "<" : "(") + da[i] + (isHex ? ">" : ")");
+ } else {
+ posX = parseFloat(da[i][1]);
+ posY = parseFloat(da[i][2]);
+ content = (isHex ? "<" : "(") + da[i][0] + (isHex ? ">" : ")");
+ variant = 1;
+ }
+
+ if (wordSpacingPerLine !== undefined && wordSpacingPerLine[i] !== undefined) {
+ wordSpacing = wordSpacingPerLine[i] + " Tw\n";
+ }
+
+ if (transformationMatrix.length !== 0 && i === 0) {
+ text.push(wordSpacing + transformationMatrix.join(" ") + " " + posX.toFixed(2) + " " + posY.toFixed(2) + " Tm\n" + content);
+ } else if (variant === 1 || variant === 0 && i === 0) {
+ text.push(wordSpacing + posX.toFixed(2) + " " + posY.toFixed(2) + " 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);
+ usedFonts[activeFontKey] = true;
+ return scope;
+ };
+ /**
+ * Letter spacing method to print text with gaps
+ *
+ * @function
+ * @instance
+ * @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}
+ * @memberOf jsPDF
+ * @name lstext
+ * @deprecated We'll be removing this function. It doesn't take character width into account.
+ */
+
+
+ var lstext = API.__private__.lstext = API.lstext = function (text, x, y, charSpace) {
+ console.warn('jsPDF.lstext is deprecated');
+ return this.text(text, x, y, {
+ charSpace: charSpace
+ });
+ };
+ /**
+ *
+ * @name clip
+ * @function
+ * @instance
+ * @param {string} rule
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @description All .clip() after calling drawing ops with a style argument of null.
+ */
+
+
+ var clip = API.__private__.clip = API.clip = function (rule) {
+ // Call .clip() after calling drawing ops with a style argument of null
+ // W is the PDF clipping op
+ if ('evenodd' === rule) {
+ 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');
+ };
+ /**
+ * 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
+ * @ignore
+ */
+
+
+ var clip_fixed = API.__private__.clip_fixed = API.clip_fixed = function (rule) {
+ console.log("clip_fixed is deprecated");
+ API.clip(rule);
+ };
+
+ var isValidStyle = API.__private__.isValidStyle = function (style) {
+ var validStyleVariants = [undefined, null, 'S', 'F', 'DF', 'FD', 'f', 'f*', 'B', 'B*'];
+ var result = false;
+
+ if (validStyleVariants.indexOf(style) !== -1) {
+ result = true;
+ }
+
+ return result;
+ };
+
+ var getStyle = API.__private__.getStyle = function (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;
+ };
+ /**
+ * Draw a line on the current page.
+ *
+ * @name line
+ * @function
+ * @instance
+ * @param {number} x1
+ * @param {number} y1
+ * @param {number} x2
+ * @param {number} y2
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ */
+
+
+ var line = API.__private__.line = API.line = function (x1, y1, x2, y2) {
+ if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
+ throw new Error('Invalid arguments passed to jsPDF.line');
+ }
+
+ return this.lines([[x2 - x1, y2 - y1]], x1, y1);
+ };
+ /**
+ * 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, [1,1], 'F', false) // 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name lines
+ */
+
+
+ var lines = API.__private__.lines = API.lines = function (lines, x, y, scale, style, closed) {
+ var scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4, tmp; // 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];
+ closed = closed || false;
+
+ if (isNaN(x) || isNaN(y) || !Array.isArray(lines) || !Array.isArray(scale) || !isValidStyle(style) || typeof closed !== 'boolean') {
+ throw new Error('Invalid arguments passed to jsPDF.lines');
+ } // starting point
+
+
+ out(f3(getHorizontalCoordinate(x)) + ' ' + f3(getVerticalCoordinate(y)) + ' 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(getHorizontalCoordinate(x4)) + ' ' + f3(getVerticalCoordinate(y4)) + ' 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(getHorizontalCoordinate(x2)) + ' ' + f3(getVerticalCoordinate(y2)) + ' ' + f3(getHorizontalCoordinate(x3)) + ' ' + f3(getVerticalCoordinate(y3)) + ' ' + f3(getHorizontalCoordinate(x4)) + ' ' + f3(getVerticalCoordinate(y4)) + ' 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name rect
+ */
+
+
+ var rect = API.__private__.rect = API.rect = function (x, y, w, h, style) {
+ if (isNaN(x) || isNaN(y) || isNaN(w) || isNaN(h) || !isValidStyle(style)) {
+ throw new Error('Invalid arguments passed to jsPDF.rect');
+ }
+
+ out([f2(getHorizontalCoordinate(x)), f2(getVerticalCoordinate(y)), 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name triangle
+ */
+
+
+ var triangle = API.__private__.triangle = API.triangle = function (x1, y1, x2, y2, x3, y3, style) {
+ if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2) || isNaN(x3) || isNaN(y3) || !isValidStyle(style)) {
+ throw new Error('Invalid arguments passed to jsPDF.triangle');
+ }
+
+ 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} ry 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name roundedRect
+ */
+
+
+ var roundedRect = API.__private__.roundedRect = API.roundedRect = function (x, y, w, h, rx, ry, style) {
+ if (isNaN(x) || isNaN(y) || isNaN(w) || isNaN(h) || isNaN(rx) || isNaN(ry) || !isValidStyle(style)) {
+ throw new Error('Invalid arguments passed to jsPDF.roundedRect');
+ }
+
+ 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} ry 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name ellipse
+ */
+
+
+ var ellise = API.__private__.ellipse = API.ellipse = function (x, y, rx, ry, style) {
+ if (isNaN(x) || isNaN(y) || isNaN(rx) || isNaN(ry) || !isValidStyle(style)) {
+ throw new Error('Invalid arguments passed to jsPDF.ellipse');
+ }
+
+ var lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
+ ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
+ out([f2(getHorizontalCoordinate(x + rx)), f2(getVerticalCoordinate(y)), 'm', f2(getHorizontalCoordinate(x + rx)), f2(getVerticalCoordinate(y - ly)), f2(getHorizontalCoordinate(x + lx)), f2(getVerticalCoordinate(y - ry)), f2(getHorizontalCoordinate(x)), f2(getVerticalCoordinate(y - ry)), 'c'].join(' '));
+ out([f2(getHorizontalCoordinate(x - lx)), f2(getVerticalCoordinate(y - ry)), f2(getHorizontalCoordinate(x - rx)), f2(getVerticalCoordinate(y - ly)), f2(getHorizontalCoordinate(x - rx)), f2(getVerticalCoordinate(y)), 'c'].join(' '));
+ out([f2(getHorizontalCoordinate(x - rx)), f2(getVerticalCoordinate(y + ly)), f2(getHorizontalCoordinate(x - lx)), f2(getVerticalCoordinate(y + ry)), f2(getHorizontalCoordinate(x)), f2(getVerticalCoordinate(y + ry)), 'c'].join(' '));
+ out([f2(getHorizontalCoordinate(x + lx)), f2(getVerticalCoordinate(y + ry)), f2(getHorizontalCoordinate(x + rx)), f2(getVerticalCoordinate(y + ly)), f2(getHorizontalCoordinate(x + rx)), f2(getVerticalCoordinate(y)), '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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name circle
+ */
+
+
+ var circle = API.__private__.circle = API.circle = function (x, y, r, style) {
+ if (isNaN(x) || isNaN(y) || isNaN(r) || !isValidStyle(style)) {
+ throw new Error('Invalid arguments passed to jsPDF.circle');
+ }
+
+ return this.ellipse(x, y, r, r, style);
+ };
+ /**
+ * 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setFont
+ */
+
+
+ API.setFont = function (fontName, fontStyle) {
+ activeFontKey = _getFont(fontName, fontStyle, {
+ disableWarning: false
+ });
+ 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf 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
+ * @instance
+ * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
+ * @memberOf jsPDF
+ * @name getFontList
+ */
+
+
+ var getFontList = API.__private__.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 to the current instance.
+ *
+ * @property {string} postScriptName PDF specification full name for the font.
+ * @property {string} id PDF-document-instance-specific label assinged to the font.
+ * @property {string} fontStyle Style of the Font.
+ * @property {Object} encoding Encoding_name-to-Font_metrics_object mapping.
+ * @function
+ * @instance
+ * @memberOf jsPDF
+ * @name addFont
+ */
+
+
+ API.addFont = function (postScriptName, fontName, fontStyle, encoding) {
+ encoding = encoding || 'Identity-H';
+ addFont.call(this, postScriptName, fontName, fontStyle, encoding);
+ };
+
+ var lineWidth = options.lineWidth || 0.200025; // 2mm
+
+ /**
+ * Sets line width for upcoming lines.
+ *
+ * @param {number} width Line width (in units declared at inception of PDF document).
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setLineWidth
+ */
+
+ var setLineWidth = API.__private__.setLineWidth = API.setLineWidth = function (width) {
+ out((width * k).toFixed(2) + ' w');
+ return this;
+ };
+ /**
+ * Sets the dash pattern for upcoming lines.
+ *
+ * To reset the settings simply call the method without any parameters.
+ * @param {array} dashArray The pattern of the line, expects numbers.
+ * @param {number} dashPhase The phase at which the dash pattern starts.
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setLineDash
+ */
+
+
+ var setLineDash = API.__private__.setLineDash = jsPDF.API.setLineDash = function (dashArray, dashPhase) {
+ dashArray = dashArray || [];
+ dashPhase = dashPhase || 0;
+
+ if (isNaN(dashPhase) || !Array.isArray(dashArray)) {
+ throw new Error('Invalid arguments passed to jsPDF.setLineDash');
+ }
+
+ dashArray = dashArray.map(function (x) {
+ return (x * k).toFixed(3);
+ }).join(' ');
+ dashPhase = parseFloat((dashPhase * k).toFixed(3));
+ out('[' + dashArray + '] ' + dashPhase + ' d');
+ return this;
+ };
+
+ var lineHeightFactor;
+
+ var getLineHeight = API.__private__.getLineHeight = API.getLineHeight = function () {
+ return activeFontSize * lineHeightFactor;
+ };
+
+ var lineHeightFactor;
+
+ var getLineHeight = API.__private__.getLineHeight = API.getLineHeight = function () {
+ return activeFontSize * lineHeightFactor;
+ };
+ /**
+ * Sets the LineHeightFactor of proportion.
+ *
+ * @param {number} value LineHeightFactor value. Default: 1.15.
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setLineHeightFactor
+ */
+
+
+ var setLineHeightFactor = API.__private__.setLineHeightFactor = API.setLineHeightFactor = function (value) {
+ value = value || 1.15;
+
+ if (typeof value === "number") {
+ lineHeightFactor = value;
+ }
+
+ return this;
+ };
+ /**
+ * Gets the LineHeightFactor, default: 1.15.
+ *
+ * @function
+ * @instance
+ * @returns {number} lineHeightFactor
+ * @memberOf jsPDF
+ * @name getLineHeightFactor
+ */
+
+
+ var getLineHeightFactor = API.__private__.getLineHeightFactor = API.getLineHeightFactor = function () {
+ return lineHeightFactor;
+ };
+
+ setLineHeightFactor(options.lineHeight);
+
+ var getHorizontalCoordinate = API.__private__.getHorizontalCoordinate = function (value) {
+ return value * k;
+ };
+
+ var getVerticalCoordinate = API.__private__.getVerticalCoordinate = function (value) {
+ return pagesContext[currentPage].mediaBox.topRightY - pagesContext[currentPage].mediaBox.bottomLeftY - value * k;
+ };
+
+ var getHorizontalCoordinateString = API.__private__.getHorizontalCoordinateString = function (value) {
+ return f2(value * k);
+ };
+
+ var getVerticalCoordinateString = API.__private__.getVerticalCoordinateString = function (value) {
+ return f2(pagesContext[currentPage].mediaBox.topRightY - pagesContext[currentPage].mediaBox.bottomLeftY - value * k);
+ };
+
+ var strokeColor = options.strokeColor || '0 G';
+ /**
+ * Gets the stroke color for upcoming elements.
+ *
+ * @function
+ * @instance
+ * @returns {string} colorAsHex
+ * @memberOf jsPDF
+ * @name getDrawColor
+ */
+
+ var getStrokeColor = API.__private__.getStrokeColor = API.getDrawColor = function () {
+ return decodeColorString(strokeColor);
+ };
+ /**
+ * 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setDrawColor
+ */
+
+
+ var setStrokeColor = API.__private__.setStrokeColor = API.setDrawColor = function (ch1, ch2, ch3, ch4) {
+ var options = {
+ "ch1": ch1,
+ "ch2": ch2,
+ "ch3": ch3,
+ "ch4": ch4,
+ "pdfColorType": "draw",
+ "precision": 2
+ };
+ strokeColor = encodeColorString(options);
+ out(strokeColor);
+ return this;
+ };
+
+ var fillColor = options.fillColor || '0 g';
+ /**
+ * Gets the fill color for upcoming elements.
+ *
+ * @function
+ * @instance
+ * @returns {string} colorAsHex
+ * @memberOf jsPDF
+ * @name getFillColor
+ */
+
+ var getFillColor = API.__private__.getFillColor = API.getFillColor = function () {
+ return decodeColorString(fillColor);
+ };
+ /**
+ * 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setFillColor
+ */
+
+
+ var setFillColor = API.__private__.setFillColor = API.setFillColor = function (ch1, ch2, ch3, ch4) {
+ var options = {
+ "ch1": ch1,
+ "ch2": ch2,
+ "ch3": ch3,
+ "ch4": ch4,
+ "pdfColorType": "fill",
+ "precision": 2
+ };
+ fillColor = encodeColorString(options);
+ out(fillColor);
+ return this;
+ };
+
+ var textColor = options.textColor || '0 g';
+ /**
+ * Gets the text color for upcoming elements.
+ *
+ * @function
+ * @instance
+ * @returns {string} colorAsHex
+ * @memberOf jsPDF
+ * @name getTextColor
+ */
+
+ var getTextColor = API.__private__.getTextColor = API.getTextColor = function () {
+ return decodeColorString(textColor);
+ };
+ /**
+ * 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setTextColor
+ */
+
+
+ var setTextColor = API.__private__.setTextColor = API.setTextColor = function (ch1, ch2, ch3, ch4) {
+ var options = {
+ "ch1": ch1,
+ "ch2": ch2,
+ "ch3": ch3,
+ "ch4": ch4,
+ "pdfColorType": "text",
+ "precision": 3
+ };
+ textColor = encodeColorString(options);
+ return this;
+ };
+
+ var activeCharSpace = options.charSpace || 0;
+ /**
+ * Get global value of CharSpace.
+ *
+ * @function
+ * @instance
+ * @returns {number} charSpace
+ * @memberOf jsPDF
+ * @name getCharSpace
+ */
+
+ var getCharSpace = API.__private__.getCharSpace = API.getCharSpace = function () {
+ return activeCharSpace;
+ };
+ /**
+ * Set global value of CharSpace.
+ *
+ * @param {number} charSpace
+ * @function
+ * @instance
+ * @returns {jsPDF} jsPDF-instance
+ * @memberOf jsPDF
+ * @name setCharSpace
+ */
+
+
+ var setCharSpace = API.__private__.setCharSpace = API.setCharSpace = function (charSpace) {
+ if (isNaN(charSpace)) {
+ throw new Error('Invalid argument passed to jsPDF.setCharSpace');
+ }
+
+ activeCharSpace = charSpace;
+ return this;
+ };
+
+ var lineCapID = 0;
+ /**
+ * Is an Object providing a mapping from human-readable to
+ * integer flag values designating the varieties of line cap
+ * and join styles.
+ *
+ * @memberOf 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setLineCap
+ */
+
+ var setLineCap = API.__private__.setLineCap = API.setLineCap = function (style) {
+ var id = API.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;
+ };
+
+ var lineJoinID = 0;
+ /**
+ * 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
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setLineJoin
+ */
+
+ var setLineJoin = API.__private__.setLineJoin = API.setLineJoin = function (style) {
+ var id = API.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;
+ };
+
+ var miterLimit;
+ /**
+ * Sets the miterLimit property, which effects the maximum miter length.
+ *
+ * @param {number} length The length of the miter
+ * @function
+ * @instance
+ * @returns {jsPDF}
+ * @memberOf jsPDF
+ * @name setMiterLimit
+ */
+
+ var setMiterLimit = API.__private__.setMiterLimit = API.setMiterLimit = function (length) {
+ length = length || 0;
+
+ if (isNaN(length)) {
+ throw new Error('Invalid argument passed to jsPDF.setMiterLimit');
+ }
+
+ miterLimit = parseFloat(f2(length * k));
+ out(miterLimit + ' M');
+ return this;
+ };
+ /**
+ * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf').
+ * Uses FileSaver.js-method saveAs.
+ *
+ * @memberOf jsPDF
+ * @name save
+ * @function
+ * @instance
+ * @param {string} filename The filename including extension.
+ * @param {Object} options An Object with additional options, possible options: 'returnPromise'.
+ * @returns {jsPDF} jsPDF-instance
+ */
+
+
+ API.save = function (filename, options) {
+ filename = filename || 'generated.pdf';
+ options = options || {};
+ options.returnPromise = options.returnPromise || false;
+
+ if (options.returnPromise === false) {
+ saveAs(getBlob(buildDocument()), filename);
+
+ if (typeof saveAs.unload === 'function') {
+ if (global.setTimeout) {
+ setTimeout(saveAs.unload, 911);
+ }
+ }
+ } else {
+ return new Promise(function (resolve, reject) {
+ try {
+ var result = saveAs(getBlob(buildDocument()), filename);
+
+ if (typeof saveAs.unload === 'function') {
+ if (global.setTimeout) {
+ setTimeout(saveAs.unload, 911);
+ }
+ }
+
+ resolve(result);
+ } catch (e) {
+ reject(e.message);
+ }
+ });
+ }
+ }; // 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];
+ }
+ }
+ }
+ /**
+ * Object exposing internal API to plugins
+ * @public
+ * @ignore
+ */
+
+
+ API.internal = {
+ 'pdfEscape': pdfEscape,
+ 'getStyle': getStyle,
+ 'getFont': function getFont() {
+ return fonts[_getFont.apply(API, arguments)];
+ },
+ 'getFontSize': getFontSize,
+ 'getCharSpace': getCharSpace,
+ 'getTextColor': getTextColor,
+ 'getLineHeight': getLineHeight,
+ 'getLineHeightFactor': getLineHeightFactor,
+ 'write': write,
+ 'getHorizontalCoordinate': getHorizontalCoordinate,
+ 'getVerticalCoordinate': getVerticalCoordinate,
+ 'getCoordinateString': getHorizontalCoordinateString,
+ 'getVerticalCoordinateString': getVerticalCoordinateString,
+ 'collections': {},
+ 'newObject': newObject,
+ 'newAdditionalObject': newAdditionalObject,
+ 'newObjectDeferred': newObjectDeferred,
+ 'newObjectDeferredBegin': newObjectDeferredBegin,
+ 'getFilters': getFilters,
+ '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 (pagesContext[currentPage].mediaBox.topRightX - pagesContext[currentPage].mediaBox.bottomLeftX) / k;
+ },
+ setWidth: function setWidth(value) {
+ pagesContext[currentPage].mediaBox.topRightX = value * k + pagesContext[currentPage].mediaBox.bottomLeftX;
+ },
+ getHeight: function getHeight() {
+ return (pagesContext[currentPage].mediaBox.topRightY - pagesContext[currentPage].mediaBox.bottomLeftY) / k;
+ },
+ setHeight: function setHeight(value) {
+ pagesContext[currentPage].mediaBox.topRightY = value * k + pagesContext[currentPage].mediaBox.bottomLeftY;
+ }
+ },
+ 'output': output,
+ 'getNumberOfPages': getNumberOfPages,
+ 'pages': pages,
+ 'out': out,
+ 'f2': f2,
+ 'f3': f3,
+ 'getPageInfo': getPageInfo,
+ 'getPageInfoByObjId': getPageInfoByObjId,
+ 'getCurrentPageInfo': getCurrentPageInfo,
+ 'getPDFVersion': getPdfVersion,
+ 'hasHotfix': hasHotfix //Expose the hasHotfix check so plugins can also check them.
+
+ };
+ Object.defineProperty(API.internal.pageSize, 'width', {
+ get: function get() {
+ return (pagesContext[currentPage].mediaBox.topRightX - pagesContext[currentPage].mediaBox.bottomLeftX) / k;
+ },
+ set: function set(value) {
+ pagesContext[currentPage].mediaBox.topRightX = value * k + pagesContext[currentPage].mediaBox.bottomLeftX;
+ },
+ enumerable: true,
+ configurable: true
+ });
+ Object.defineProperty(API.internal.pageSize, 'height', {
+ get: function get() {
+ return (pagesContext[currentPage].mediaBox.topRightY - pagesContext[currentPage].mediaBox.bottomLeftY) / k;
+ },
+ set: function set(value) {
+ pagesContext[currentPage].mediaBox.topRightY = value * k + pagesContext[currentPage].mediaBox.bottomLeftY;
+ },
+ enumerable: true,
+ configurable: true
+ }); //////////////////////////////////////////////////////
+ // continuing initialization of jsPDF Document object
+ //////////////////////////////////////////////////////
+ // Add the first page automatically
+
+ addFonts(standardFonts);
+ 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.
+ *
+ * @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: []
+ };
+ /**
+ * The version of jsPDF.
+ * @name version
+ * @type {string}
+ * @memberOf jsPDF
+ */
+
+ jsPDF.version = '1.5.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
+
+ /**
+ * @license
+ * Copyright (c) 2016 Alexander Weidt,
+ * https://github.com/BiggA94
+ *
+ * Licensed under the MIT License. http://opensource.org/licenses/mit-license
+ */
+
+ /**
+ * jsPDF AcroForm Plugin
+ * @module AcroForm
+ */
+ (function (jsPDFAPI, globalObj) {
+
+ var scope;
+ var scaleFactor = 1;
+
+ var pdfEscape = function pdfEscape(value) {
+ return value.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
+ };
+
+ var pdfUnescape = function pdfUnescape(value) {
+ return value.replace(/\\\\/g, '\\').replace(/\\\(/g, '(').replace(/\\\)/g, ')');
+ };
+
+ var f2 = function f2(number) {
+ if (isNaN(number)) {
+ throw new Error('Invalid argument passed to jsPDF.f2');
+ }
+
+ return number.toFixed(2); // Ie, %.2f
+ };
+
+ var f5 = function f5(number) {
+ if (isNaN(number)) {
+ throw new Error('Invalid argument passed to jsPDF.f2');
+ }
+
+ return number.toFixed(5); // Ie, %.2f
+ };
+
+ jsPDFAPI.__acroform__ = {};
+
+ var inherit = function inherit(child, parent) {
+
+ child.prototype = Object.create(parent.prototype);
+ child.prototype.constructor = child;
+ };
+
+ var scale = function scale(x) {
+ return x * scaleFactor;
+ };
+
+ var antiScale = function antiScale(x) {
+ return x / scaleFactor;
+ };
+
+ 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, Number(f2(width)), Number(f2(height))];
+ return xobj;
+ };
+ /**
+ * Bit-Operations
+ */
+
+
+ var setBit = jsPDFAPI.__acroform__.setBit = function (number, bitPosition) {
+ number = number || 0;
+ bitPosition = bitPosition || 0;
+
+ if (isNaN(number) || isNaN(bitPosition)) {
+ throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.setBit');
+ }
+
+ var bitMask = 1 << bitPosition;
+ number |= bitMask;
+ return number;
+ };
+
+ var clearBit = jsPDFAPI.__acroform__.clearBit = function (number, bitPosition) {
+ number = number || 0;
+ bitPosition = bitPosition || 0;
+
+ if (isNaN(number) || isNaN(bitPosition)) {
+ throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.clearBit');
+ }
+
+ var bitMask = 1 << bitPosition;
+ number &= ~bitMask;
+ return number;
+ };
+
+ var getBit = jsPDFAPI.__acroform__.getBit = function (number, bitPosition) {
+ if (isNaN(number) || isNaN(bitPosition)) {
+ throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.getBit');
+ }
+
+ return (number & 1 << bitPosition) === 0 ? 0 : 1;
+ };
+ /*
+ * Ff starts counting the bit position at 1 and not like javascript at 0
+ */
+
+
+ var getBitForPdf = jsPDFAPI.__acroform__.getBitForPdf = function (number, bitPosition) {
+ if (isNaN(number) || isNaN(bitPosition)) {
+ throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.getBitForPdf');
+ }
+
+ return getBit(number, bitPosition - 1);
+ };
+
+ var setBitForPdf = jsPDFAPI.__acroform__.setBitForPdf = function (number, bitPosition) {
+ if (isNaN(number) || isNaN(bitPosition)) {
+ throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.setBitForPdf');
+ }
+
+ return setBit(number, bitPosition - 1);
+ };
+
+ var clearBitForPdf = jsPDFAPI.__acroform__.clearBitForPdf = function (number, bitPosition, value) {
+ if (isNaN(number) || isNaN(bitPosition)) {
+ throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.clearBitForPdf');
+ }
+
+ return clearBit(number, bitPosition - 1);
+ };
+
+ var calculateCoordinates = jsPDFAPI.__acroform__.calculateCoordinates = function (args) {
+ var getHorizontalCoordinate = this.internal.getHorizontalCoordinate;
+ var getVerticalCoordinate = this.internal.getVerticalCoordinate;
+ var x = args[0];
+ var y = args[1];
+ var w = args[2];
+ var h = args[3];
+ var coordinates = {};
+ coordinates.lowerLeft_X = getHorizontalCoordinate(x) || 0;
+ coordinates.lowerLeft_Y = getVerticalCoordinate(y + h) || 0;
+ coordinates.upperRight_X = getHorizontalCoordinate(x + w) || 0;
+ coordinates.upperRight_Y = getVerticalCoordinate(y) || 0;
+ return [Number(f2(coordinates.lowerLeft_X)), Number(f2(coordinates.lowerLeft_Y)), Number(f2(coordinates.upperRight_X)), Number(f2(coordinates.upperRight_Y))];
+ };
+
+ var calculateAppearanceStream = function calculateAppearanceStream(formObject) {
+ if (formObject.appearanceStreamContent) {
+ 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);
+ var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id; //PDF 32000-1:2008, page 444
+
+ stream.push('/Tx BMC');
+ stream.push('q');
+ stream.push('BT'); // Begin Text
+
+ stream.push(scope.__private__.encodeColorString(formObject.color));
+ stream.push('/' + fontKey + ' ' + f2(calcRes.fontSize) + ' Tf');
+ stream.push('1 0 0 1 0 0 Tm'); // Transformation Matrix
+
+ 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) {
+ var maxFontSize = formObject.maxFontSize || 12;
+ var font = formObject.fontName;
+ 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 color = scope.__private__.encodeColorString(formObject.color);
+
+ 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, formObject, fontSize).width;
+ var FieldWidth = width - 2 * borderPadding;
+ return TextWidth <= FieldWidth;
+ } else {
+ return false;
+ }
+ };
+
+ fontSize++;
+
+ FontSize: while (true) {
+ var text = "";
+ fontSize--;
+ var textHeight = calculateFontSpace("3", formObject, fontSize).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, formObject, fontSize).width + ", FieldWidth:" + width + "\n";
+ break;
+ }
+
+ lastLength = calculateFontSpace(textSplit[0] + " ", formObject, fontSize).width;
+ var lastLine = "";
+ var lineCount = 0;
+
+ Line: for (var i in textSplit) {
+ if (textSplit.hasOwnProperty(i)) {
+ 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 + " ", formObject, fontSize).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, formObject, fontSize).width; // Calculate startX
+
+ switch (formObject.textAlign) {
+ case 'right':
+ startX = width - lastLength - borderPadding;
+ break;
+
+ case 'center':
+ startX = (width - lastLength) / 2;
+ break;
+
+ case 'left':
+ default:
+ startX = borderPadding;
+ break;
+ }
+
+ text += f2(startX) + ' ' + f2(lastY) + ' Td\n';
+ text += '(' + pdfEscape(line) + ') Tj\n'; // reset X in PDF
+
+ text += -f2(startX) + ' 0 Td\n'; // After a Line, adjust y position
+
+ lastY = -(fontSize + lineSpacing);
+
+ 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, formObject, fontSize) {
+ var font = scope.internal.getFont(formObject.fontName, formObject.fontStyle);
+ var width = scope.getStringUnitWidth(text, {
+ font: font,
+ fontSize: parseFloat(fontSize),
+ charSpace: 0
+ }) * parseFloat(fontSize);
+ var height = scope.getStringUnitWidth("3", {
+ font: font,
+ fontSize: parseFloat(fontSize),
+ charSpace: 0
+ }) * parseFloat(fontSize) * 1.5;
+ return {
+ height: height,
+ width: width
+ };
+ };
+
+ 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() {
+ //set objId to undefined and force it to get a new objId on buildDocument
+ scope.internal.acroformPlugin.acroFormDictionaryRoot.objId = undefined;
+ var fields = scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields;
+
+ for (var i in fields) {
+ if (fields.hasOwnProperty(i)) {
+ var formObject = fields[i]; //set objId to undefined and force it to get a new objId on buildDocument
+
+ formObject.objId = undefined; // 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
+ };
+
+ var findEntry = function findEntry(entry) {
+ return entry.type === options.type && entry.object === options.object;
+ };
+
+ if (scope.internal.getPageInfo(object.page).pageContext.annotations.find(findEntry) === undefined) {
+ scope.internal.getPageInfo(object.page).pageContext.annotations.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 {
+ throw new Error('putCatalogCallback: 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, true);
+ scope.internal.acroformPlugin.acroFormDictionaryRoot.putStream();
+ }
+
+ var fieldArray = fieldArray || scope.internal.acroformPlugin.acroFormDictionaryRoot.Kids;
+
+ for (var i in fieldArray) {
+ if (fieldArray.hasOwnProperty(i)) {
+ var fieldObject = fieldArray[i];
+ var keyValueList = [];
+ var oldRect = fieldObject.Rect;
+
+ if (fieldObject.Rect) {
+ fieldObject.Rect = calculateCoordinates.call(this, fieldObject.Rect);
+ } // Start Writing the Object
+
+
+ scope.internal.newObjectDeferredBegin(fieldObject.objId, true);
+ fieldObject.DA = AcroFormAppearance.createDefaultAppearanceStream(fieldObject);
+
+ if (_typeof(fieldObject) === "object" && typeof fieldObject.getKeyValueListForStream === "function") {
+ keyValueList = fieldObject.getKeyValueListForStream();
+ }
+
+ fieldObject.Rect = oldRect;
+
+ if (fieldObject.hasAppearanceStream && !fieldObject.appearanceStreamContent) {
+ // Calculate Appearance
+ var appearance = calculateAppearanceStream.call(this, fieldObject);
+ keyValueList.push({
+ key: 'AP',
+ value: "<>"
+ });
+ scope.internal.acroformPlugin.xForms.push(appearance);
+ } // Assume AppearanceStreamContent is a Array with N,R,D (at least
+ // one of them!)
+
+
+ if (fieldObject.appearanceStreamContent) {
+ var appearanceStreamString = ""; // Iterate over N,R and D
+
+ for (var k in fieldObject.appearanceStreamContent) {
+ if (fieldObject.appearanceStreamContent.hasOwnProperty(k)) {
+ var value = fieldObject.appearanceStreamContent[k];
+ appearanceStreamString += "/" + k + " ";
+ appearanceStreamString += "<<";
+
+ if (Object.keys(value).length >= 1 || Array.isArray(value)) {
+ // appearanceStream is an Array or Object!
+ for (var i in value) {
+ if (value.hasOwnProperty(i)) {
+ var obj = value[i];
+
+ if (typeof obj === 'function') {
+ // if Function is referenced, call it in order
+ // to get the FormXObject
+ obj = obj.call(this, fieldObject);
+ }
+
+ appearanceStreamString += "/" + 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, fieldObject);
+ }
+
+ appearanceStreamString += "/" + i + " " + obj;
+ if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0)) scope.internal.acroformPlugin.xForms.push(obj);
+ }
+
+ appearanceStreamString += ">>";
+ }
+ } // appearance stream is a normal Object..
+
+
+ keyValueList.push({
+ key: 'AP',
+ value: "<<\n" + appearanceStreamString + ">>"
+ });
+ }
+
+ scope.internal.putStream({
+ additionalKeyValues: keyValueList
+ });
+ scope.internal.out("endobj");
+ }
+ }
+
+ if (standardFields) {
+ createXFormObjectCallback.call(this, scope.internal.acroformPlugin.xForms);
+ }
+ };
+
+ var createXFormObjectCallback = function createXFormObjectCallback(fieldArray) {
+ for (var i in fieldArray) {
+ if (fieldArray.hasOwnProperty(i)) {
+ var key = i;
+ var fieldObject = fieldArray[i]; // Start Writing the Object
+
+ scope.internal.newObjectDeferredBegin(fieldObject && fieldObject.objId, true);
+
+ if (_typeof(fieldObject) === "object" && typeof fieldObject.putStream === "function") {
+ fieldObject.putStream();
+ }
+
+ 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) {
+ throw new Error("Exception while creating AcroformDictionary");
+ }
+
+ scaleFactor = scope.internal.scaleFactor; // 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;
+ }
+ }; //PDF 32000-1:2008, page 26, 7.3.6
+
+
+ var arrayToPdfArray = jsPDFAPI.__acroform__.arrayToPdfArray = function (array) {
+ if (Array.isArray(array)) {
+ var content = '[';
+
+ for (var i = 0; i < array.length; i++) {
+ if (i !== 0) {
+ content += ' ';
+ }
+
+ switch (_typeof(array[i])) {
+ case 'boolean':
+ case 'number':
+ case 'object':
+ content += array[i].toString();
+ break;
+
+ case 'string':
+ if (array[i].substr(0, 1) !== '/') {
+ content += '(' + pdfEscape(array[i].toString()) + ')';
+ } else {
+ content += array[i].toString();
+ }
+
+ break;
+ }
+ }
+
+ content += ']';
+ return content;
+ }
+
+ throw new Error('Invalid argument passed to jsPDF.__acroform__.arrayToPdfArray');
+ };
+
+ function getMatches(string, regex, index) {
+ index || (index = 1); // default to the first capturing group
+
+ var matches = [];
+ var match;
+
+ while (match = regex.exec(string)) {
+ matches.push(match[index]);
+ }
+
+ return matches;
+ }
+
+ var pdfArrayToStringArray = function pdfArrayToStringArray(array) {
+ var result = [];
+
+ if (typeof array === "string") {
+ result = getMatches(array, /\((.*?)\)/g);
+ }
+
+ return result;
+ };
+
+ var toPdfString = function toPdfString(string) {
+ string = string || "";
+ string.toString();
+ string = '(' + pdfEscape(string) + ')';
+ return string;
+ }; // ##########################
+ // Classes
+ // ##########################
+
+ /**
+ * @class AcroFormPDFObject
+ * @classdesc A AcroFormPDFObject
+ */
+
+
+ var AcroFormPDFObject = function AcroFormPDFObject() {
+ var _objId;
+ /** *
+ * @name AcroFormPDFObject#objId
+ * @type {any}
+ */
+
+
+ Object.defineProperty(this, 'objId', {
+ configurable: true,
+ get: function get() {
+ if (!_objId) {
+ _objId = scope.internal.newObjectDeferred();
+ }
+
+ if (!_objId) {
+ throw new Error("AcroFormPDFObject: Couldn't create Object ID");
+ }
+
+ return _objId;
+ },
+ set: function set(value) {
+ _objId = value;
+ }
+ });
+ };
+ /**
+ * @function AcroFormPDFObject.toString
+ */
+
+
+ AcroFormPDFObject.prototype.toString = function () {
+ return this.objId + " 0 R";
+ };
+
+ AcroFormPDFObject.prototype.putStream = function () {
+ var keyValueList = this.getKeyValueListForStream();
+ scope.internal.putStream({
+ data: this.stream,
+ additionalKeyValues: keyValueList
+ });
+ scope.internal.out("endobj");
+ };
+ /**
+ * Returns an key-value-List of all non-configurable Variables from the Object
+ *
+ * @name getKeyValueListForStream
+ * @returns {string}
+ */
+
+
+ AcroFormPDFObject.prototype.getKeyValueListForStream = function () {
+ var createKeyValueListFromFieldObject = function createKeyValueListFromFieldObject(fieldObject) {
+ var keyValueList = [];
+ var keys = Object.getOwnPropertyNames(fieldObject).filter(function (key) {
+ return key != 'content' && key != 'appearanceStreamContent' && key.substring(0, 1) != "_";
+ });
+
+ for (var i in keys) {
+ if (Object.getOwnPropertyDescriptor(fieldObject, keys[i]).configurable === false) {
+ var key = keys[i];
+ var value = fieldObject[key];
+
+ if (value) {
+ if (Array.isArray(value)) {
+ keyValueList.push({
+ key: key,
+ value: arrayToPdfArray(value)
+ });
+ } else if (value instanceof AcroFormPDFObject) {
+ // In case it is a reference to another PDFObject,
+ // take the reference number
+ keyValueList.push({
+ key: key,
+ value: value.objId + " 0 R"
+ });
+ } else if (typeof value !== "function") {
+ keyValueList.push({
+ key: key,
+ value: value
+ });
+ }
+ }
+ }
+ }
+
+ return keyValueList;
+ };
+
+ return createKeyValueListFromFieldObject(this);
+ };
+
+ var AcroFormXObject = function AcroFormXObject() {
+ AcroFormPDFObject.call(this);
+ Object.defineProperty(this, 'Type', {
+ value: "/XObject",
+ configurable: false,
+ writeable: true
+ });
+ Object.defineProperty(this, 'Subtype', {
+ value: "/Form",
+ configurable: false,
+ writeable: true
+ });
+ Object.defineProperty(this, 'FormType', {
+ value: 1,
+ configurable: false,
+ writeable: true
+ });
+ var _BBox = [];
+ Object.defineProperty(this, 'BBox', {
+ configurable: false,
+ writeable: true,
+ get: function get() {
+ return _BBox;
+ },
+ set: function set(value) {
+ _BBox = value;
+ }
+ });
+ Object.defineProperty(this, 'Resources', {
+ value: "2 0 R",
+ configurable: false,
+ writeable: true
+ });
+
+ var _stream;
+
+ Object.defineProperty(this, 'stream', {
+ enumerable: false,
+ configurable: true,
+ set: function set(value) {
+ _stream = value.trim();
+ },
+ get: function get() {
+ if (_stream) {
+ return _stream;
+ } else {
+ return null;
+ }
+ }
+ });
+ };
+
+ inherit(AcroFormXObject, AcroFormPDFObject);
+
+ var AcroFormDictionary = function AcroFormDictionary() {
+ AcroFormPDFObject.call(this);
+ var _Kids = [];
+ Object.defineProperty(this, 'Kids', {
+ enumerable: false,
+ configurable: true,
+ get: function get() {
+ if (_Kids.length > 0) {
+ return _Kids;
+ } else {
+ return;
+ }
+ }
+ });
+ Object.defineProperty(this, 'Fields', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ return _Kids;
+ }
+ }); // Default Appearance
+
+ var _DA;
+
+ Object.defineProperty(this, 'DA', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ if (!_DA) {
+ return;
+ }
+
+ return '(' + _DA + ')';
+ },
+ set: function set(value) {
+ _DA = value;
+ }
+ });
+ };
+
+ inherit(AcroFormDictionary, AcroFormPDFObject);
+ /**
+ * The Field Object contains the Variables, that every Field needs
+ *
+ * @class AcroFormField
+ * @classdesc An AcroForm FieldObject
+ */
+
+ var AcroFormField = function AcroFormField() {
+ AcroFormPDFObject.call(this); //Annotation-Flag See Table 165
+
+ var _F = 4;
+ Object.defineProperty(this, 'F', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ return _F;
+ },
+ set: function set(value) {
+ if (!isNaN(value)) {
+ _F = value;
+ } else {
+ throw new Error('Invalid value "' + value + '" for attribute F supplied.');
+ }
+ }
+ });
+ /**
+ * (PDF 1.2) If set, print the annotation when the page is printed. If clear, never print the annotation, regardless of wether is is displayed on the screen.
+ * NOTE 2 This can be useful for annotations representing interactive pushbuttons, which would serve no meaningful purpose on the printed page.
+ *
+ * @name AcroFormField#showWhenPrinted
+ * @default true
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'showWhenPrinted', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(_F, 3));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.F = setBitForPdf(_F, 3);
+ } else {
+ this.F = clearBitForPdf(_F, 3);
+ }
+ }
+ });
+ var _Ff = 0;
+ Object.defineProperty(this, 'Ff', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ return _Ff;
+ },
+ set: function set(value) {
+ if (!isNaN(value)) {
+ _Ff = value;
+ } else {
+ throw new Error('Invalid value "' + value + '" for attribute Ff supplied.');
+ }
+ }
+ });
+ var _Rect = [];
+ Object.defineProperty(this, 'Rect', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ if (_Rect.length === 0) {
+ return;
+ }
+
+ return _Rect;
+ },
+ set: function set(value) {
+ if (typeof value !== "undefined") {
+ _Rect = value;
+ } else {
+ _Rect = [];
+ }
+ }
+ });
+ /**
+ * The x-position of the field.
+ *
+ * @name AcroFormField#x
+ * @default null
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'x', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ if (!_Rect || isNaN(_Rect[0])) {
+ return 0;
+ }
+
+ return antiScale(_Rect[0]);
+ },
+ set: function set(value) {
+ _Rect[0] = scale(value);
+ }
+ });
+ /**
+ * The y-position of the field.
+ *
+ * @name AcroFormField#y
+ * @default null
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'y', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ if (!_Rect || isNaN(_Rect[1])) {
+ return 0;
+ }
+
+ return antiScale(_Rect[1]);
+ },
+ set: function set(value) {
+ _Rect[1] = scale(value);
+ }
+ });
+ /**
+ * The width of the field.
+ *
+ * @name AcroFormField#width
+ * @default null
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'width', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ if (!_Rect || isNaN(_Rect[2])) {
+ return 0;
+ }
+
+ return antiScale(_Rect[2]);
+ },
+ set: function set(value) {
+ _Rect[2] = scale(value);
+ }
+ });
+ /**
+ * The height of the field.
+ *
+ * @name AcroFormField#height
+ * @default null
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'height', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ if (!_Rect || isNaN(_Rect[3])) {
+ return 0;
+ }
+
+ return antiScale(_Rect[3]);
+ },
+ set: function set(value) {
+ _Rect[3] = scale(value);
+ }
+ });
+ var _FT = "";
+ Object.defineProperty(this, 'FT', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return _FT;
+ },
+ set: function set(value) {
+ switch (value) {
+ case '/Btn':
+ case '/Tx':
+ case '/Ch':
+ case '/Sig':
+ _FT = value;
+ break;
+
+ default:
+ throw new Error('Invalid value "' + value + '" for attribute FT supplied.');
+ }
+ }
+ });
+ var _T = null;
+ Object.defineProperty(this, 'T', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ if (!_T || _T.length < 1) {
+ // In case of a Child from a Radio´Group, you don't need a FieldName
+ if (this instanceof AcroFormChildClass) {
+ return;
+ }
+
+ _T = "FieldObject" + AcroFormField.FieldNum++;
+ }
+
+ return '(' + pdfEscape(_T) + ')';
+ },
+ set: function set(value) {
+ _T = value.toString();
+ }
+ });
+ /**
+ * (Optional) The partial field name (see 12.7.3.2, “Field Names”).
+ *
+ * @name AcroFormField#fieldName
+ * @default null
+ * @type {string}
+ */
+
+ Object.defineProperty(this, 'fieldName', {
+ configurable: true,
+ enumerable: true,
+ get: function get() {
+ return _T;
+ },
+ set: function set(value) {
+ _T = value;
+ }
+ });
+ var _fontName = 'helvetica';
+ /**
+ * The fontName of the font to be used.
+ *
+ * @name AcroFormField#fontName
+ * @default 'helvetica'
+ * @type {string}
+ */
+
+ Object.defineProperty(this, 'fontName', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _fontName;
+ },
+ set: function set(value) {
+ _fontName = value;
+ }
+ });
+ var _fontStyle = 'normal';
+ /**
+ * The fontStyle of the font to be used.
+ *
+ * @name AcroFormField#fontStyle
+ * @default 'normal'
+ * @type {string}
+ */
+
+ Object.defineProperty(this, 'fontStyle', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _fontStyle;
+ },
+ set: function set(value) {
+ _fontStyle = value;
+ }
+ });
+ var _fontSize = 0;
+ /**
+ * The fontSize of the font to be used.
+ *
+ * @name AcroFormField#fontSize
+ * @default 0 (for auto)
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'fontSize', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return antiScale(_fontSize);
+ },
+ set: function set(value) {
+ _fontSize = scale(value);
+ }
+ });
+ var _maxFontSize = 50;
+ /**
+ * The maximum fontSize of the font to be used.
+ *
+ * @name AcroFormField#maxFontSize
+ * @default 0 (for auto)
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'maxFontSize', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return antiScale(_maxFontSize);
+ },
+ set: function set(value) {
+ _maxFontSize = scale(value);
+ }
+ });
+ var _color = 'black';
+ /**
+ * The color of the text
+ *
+ * @name AcroFormField#color
+ * @default 'black'
+ * @type {string|rgba}
+ */
+
+ Object.defineProperty(this, 'color', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _color;
+ },
+ set: function set(value) {
+ _color = value;
+ }
+ });
+ var _DA = '/F1 0 Tf 0 g'; // Defines the default appearance (Needed for variable Text)
+
+ Object.defineProperty(this, 'DA', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ if (!_DA || this instanceof AcroFormChildClass || this instanceof AcroFormTextField) {
+ return;
+ }
+
+ return toPdfString(_DA);
+ },
+ set: function set(value) {
+ value = value.toString();
+ _DA = value;
+ }
+ });
+ var _DV = null;
+ Object.defineProperty(this, 'DV', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ if (!_DV) {
+ return;
+ }
+
+ if (this instanceof AcroFormButton === false) {
+ return toPdfString(_DV);
+ }
+
+ return _DV;
+ },
+ set: function set(value) {
+ value = value.toString();
+
+ if (this instanceof AcroFormButton === false) {
+ if (value.substr(0, 1) === '(') {
+ _DV = pdfUnescape(value.substr(1, value.length - 2));
+ } else {
+ _DV = pdfUnescape(value);
+ }
+ } else {
+ _DV = value;
+ }
+ }
+ });
+ /**
+ * (Optional; inheritable) The default value to which the field reverts when a reset-form action is executed (see 12.7.5.3, “Reset-Form Action”). The format of this value is the same as that of value.
+ *
+ * @name AcroFormField#defaultValue
+ * @default null
+ * @type {any}
+ */
+
+ Object.defineProperty(this, 'defaultValue', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ if (this instanceof AcroFormButton === true) {
+ return pdfUnescape(_DV.substr(1, _DV.length - 1));
+ } else {
+ return _DV;
+ }
+ },
+ set: function set(value) {
+ value = value.toString();
+
+ if (this instanceof AcroFormButton === true) {
+ _DV = '/' + value;
+ } else {
+ _DV = value;
+ }
+ }
+ });
+ var _V = null;
+ Object.defineProperty(this, 'V', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ if (!_V) {
+ return;
+ }
+
+ if (this instanceof AcroFormButton === false) {
+ return toPdfString(_V);
+ }
+
+ return _V;
+ },
+ set: function set(value) {
+ value = value.toString();
+
+ if (this instanceof AcroFormButton === false) {
+ if (value.substr(0, 1) === '(') {
+ _V = pdfUnescape(value.substr(1, value.length - 2));
+ } else {
+ _V = pdfUnescape(value);
+ }
+ } else {
+ _V = value;
+ }
+ }
+ });
+ /**
+ * (Optional; inheritable) The field’s value, whose format varies depending on the field type. See the descriptions of individual field types for further information.
+ *
+ * @name AcroFormField#value
+ * @default null
+ * @type {any}
+ */
+
+ Object.defineProperty(this, 'value', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ if (this instanceof AcroFormButton === true) {
+ return pdfUnescape(_V.substr(1, _V.length - 1));
+ } else {
+ return _V;
+ }
+ },
+ set: function set(value) {
+ value = value.toString();
+
+ if (this instanceof AcroFormButton === true) {
+ _V = '/' + value;
+ } else {
+ _V = value;
+ }
+ }
+ });
+ /**
+ * Check if field has annotations
+ *
+ * @name AcroFormField#hasAnnotation
+ * @readonly
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'hasAnnotation', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return this.Rect;
+ }
+ });
+ Object.defineProperty(this, 'Type', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return this.hasAnnotation ? "/Annot" : null;
+ }
+ });
+ Object.defineProperty(this, 'Subtype', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return this.hasAnnotation ? "/Widget" : null;
+ }
+ });
+ var _hasAppearanceStream = false;
+ /**
+ * true if field has an appearanceStream
+ *
+ * @name AcroFormField#hasAppearanceStream
+ * @readonly
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'hasAppearanceStream', {
+ enumerable: true,
+ configurable: true,
+ writeable: true,
+ get: function get() {
+ return _hasAppearanceStream;
+ },
+ set: function set(value) {
+ value = Boolean(value);
+ _hasAppearanceStream = value;
+ }
+ });
+ /**
+ * The page on which the AcroFormField is placed
+ *
+ * @name AcroFormField#page
+ * @type {number}
+ */
+
+ var _page;
+
+ Object.defineProperty(this, 'page', {
+ enumerable: true,
+ configurable: true,
+ writeable: true,
+ get: function get() {
+ if (!_page) {
+ return;
+ }
+
+ return _page;
+ },
+ set: function set(value) {
+ _page = value;
+ }
+ });
+ /**
+ * If set, the user may not change the value of the field. Any associated widget annotations will not interact with the user; that is, they will not respond to mouse clicks or change their appearance in response to mouse motions. This flag is useful for fields whose values are computed or imported from a database.
+ *
+ * @name AcroFormField#readOnly
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'readOnly', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 1));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 1);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 1);
+ }
+ }
+ });
+ /**
+ * If set, the field shall have a value at the time it is exported by a submitform action (see 12.7.5.2, “Submit-Form Action”).
+ *
+ * @name AcroFormField#required
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'required', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 2));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 2);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 2);
+ }
+ }
+ });
+ /**
+ * If set, the field shall not be exported by a submit-form action (see 12.7.5.2, “Submit-Form Action”)
+ *
+ * @name AcroFormField#noExport
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'noExport', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 3));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 3);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 3);
+ }
+ }
+ });
+ var _Q = null;
+ Object.defineProperty(this, 'Q', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ if (_Q === null) {
+ return;
+ }
+
+ return _Q;
+ },
+ set: function set(value) {
+ if ([0, 1, 2].indexOf(value) !== -1) {
+ _Q = value;
+ } else {
+ throw new Error('Invalid value "' + value + '" for attribute Q supplied.');
+ }
+ }
+ });
+ /**
+ * (Optional; inheritable) A code specifying the form of quadding (justification) that shall be used in displaying the text:
+ * 'left', 'center', 'right'
+ *
+ * @name AcroFormField#textAlign
+ * @default 'left'
+ * @type {string}
+ */
+
+ Object.defineProperty(this, 'textAlign', {
+ get: function get() {
+ var result = 'left';
+
+ switch (_Q) {
+ case 0:
+ default:
+ result = 'left';
+ break;
+
+ case 1:
+ result = 'center';
+ break;
+
+ case 2:
+ result = 'right';
+ break;
+ }
+
+ return result;
+ },
+ configurable: true,
+ enumerable: true,
+ set: function set(value) {
+ switch (value) {
+ case 'right':
+ case 2:
+ _Q = 2;
+ break;
+
+ case 'center':
+ case 1:
+ _Q = 1;
+ break;
+
+ case 'left':
+ case 0:
+ default:
+ _Q = 0;
+ }
+ }
+ });
+ };
+
+ inherit(AcroFormField, AcroFormPDFObject);
+ /**
+ * @class AcroFormChoiceField
+ * @extends AcroFormField
+ */
+
+ var AcroFormChoiceField = function AcroFormChoiceField() {
+ AcroFormField.call(this); // Field Type = Choice Field
+
+ this.FT = "/Ch"; // options
+
+ this.V = '()';
+ this.fontName = 'zapfdingbats'; // Top Index
+
+ var _TI = 0;
+ Object.defineProperty(this, 'TI', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return _TI;
+ },
+ set: function set(value) {
+ _TI = value;
+ }
+ });
+ /**
+ * (Optional) For scrollable list boxes, the top index (the index in the Opt array of the first option visible in the list). Default value: 0.
+ *
+ * @name AcroFormChoiceField#topIndex
+ * @default 0
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'topIndex', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _TI;
+ },
+ set: function set(value) {
+ _TI = value;
+ }
+ });
+ var _Opt = [];
+ Object.defineProperty(this, 'Opt', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return arrayToPdfArray(_Opt);
+ },
+ set: function set(value) {
+ _Opt = pdfArrayToStringArray(value);
+ }
+ });
+ /**
+ * @memberof AcroFormChoiceField
+ * @name getOptions
+ * @function
+ * @instance
+ * @returns {array} array of Options
+ */
+
+ this.getOptions = function () {
+ return _Opt;
+ };
+ /**
+ * @memberof AcroFormChoiceField
+ * @name setOptions
+ * @function
+ * @instance
+ * @param {array} value
+ */
+
+
+ this.setOptions = function (value) {
+ _Opt = value;
+
+ if (this.sort) {
+ _Opt.sort();
+ }
+ };
+ /**
+ * @memberof AcroFormChoiceField
+ * @name addOption
+ * @function
+ * @instance
+ * @param {string} value
+ */
+
+
+ this.addOption = function (value) {
+ value = value || "";
+ value = value.toString();
+
+ _Opt.push(value);
+
+ if (this.sort) {
+ _Opt.sort();
+ }
+ };
+ /**
+ * @memberof AcroFormChoiceField
+ * @name removeOption
+ * @function
+ * @instance
+ * @param {string} value
+ * @param {boolean} allEntries (default: false)
+ */
+
+
+ this.removeOption = function (value, allEntries) {
+ allEntries = allEntries || false;
+ value = value || "";
+ value = value.toString();
+
+ while (_Opt.indexOf(value) !== -1) {
+ _Opt.splice(_Opt.indexOf(value), 1);
+
+ if (allEntries === false) {
+ break;
+ }
+ }
+ };
+ /**
+ * If set, the field is a combo box; if clear, the field is a list box.
+ *
+ * @name AcroFormChoiceField#combo
+ * @default false
+ * @type {boolean}
+ */
+
+
+ Object.defineProperty(this, 'combo', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 18));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 18);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 18);
+ }
+ }
+ });
+ /**
+ * If set, the combo box shall include an editable text box as well as a drop-down list; if clear, it shall include only a drop-down list. This flag shall be used only if the Combo flag is set.
+ *
+ * @name AcroFormChoiceField#edit
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'edit', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 19));
+ },
+ set: function set(value) {
+ //PDF 32000-1:2008, page 444
+ if (this.combo === true) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 19);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 19);
+ }
+ }
+ }
+ });
+ /**
+ * If set, the field’s option items shall be sorted alphabetically. This flag is intended for use by writers, not by readers. Conforming readers shall display the options in the order in which they occur in the Opt array (see Table 231).
+ *
+ * @name AcroFormChoiceField#sort
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'sort', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 20));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 20);
+
+ _Opt.sort();
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 20);
+ }
+ }
+ });
+ /**
+ * (PDF 1.4) If set, more than one of the field’s option items may be selected simultaneously; if clear, at most one item shall be selected
+ *
+ * @name AcroFormChoiceField#multiSelect
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'multiSelect', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 22));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 22);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 22);
+ }
+ }
+ });
+ /**
+ * (PDF 1.4) If set, text entered in the field shall not be spellchecked. This flag shall not be used unless the Combo and Edit flags are both set.
+ *
+ * @name AcroFormChoiceField#doNotSpellCheck
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'doNotSpellCheck', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 23));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 23);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 23);
+ }
+ }
+ });
+ /**
+ * (PDF 1.5) If set, the new value shall be committed as soon as a selection is made (commonly with the pointing device). In this case, supplying a value for a field involves three actions: selecting the field for fill-in, selecting a choice for the fill-in value, and leaving that field, which finalizes or “commits” the data choice and triggers any actions associated with the entry or changing of this data. If this flag is on, then processing does not wait for leaving the field action to occur, but immediately proceeds to the third step.
+ * This option enables applications to perform an action once a selection is made, without requiring the user to exit the field. If clear, the new value is not committed until the user exits the field.
+ *
+ * @name AcroFormChoiceField#commitOnSelChange
+ * @default false
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'commitOnSelChange', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 27));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 27);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 27);
+ }
+ }
+ });
+ this.hasAppearanceStream = false;
+ };
+
+ inherit(AcroFormChoiceField, AcroFormField);
+ /**
+ * @class AcroFormListBox
+ * @extends AcroFormChoiceField
+ * @extends AcroFormField
+ */
+
+ var AcroFormListBox = function AcroFormListBox() {
+ AcroFormChoiceField.call(this);
+ this.fontName = 'helvetica'; //PDF 32000-1:2008, page 444
+
+ this.combo = false;
+ };
+
+ inherit(AcroFormListBox, AcroFormChoiceField);
+ /**
+ * @class AcroFormComboBox
+ * @extends AcroFormListBox
+ * @extends AcroFormChoiceField
+ * @extends AcroFormField
+ */
+
+ var AcroFormComboBox = function AcroFormComboBox() {
+ AcroFormListBox.call(this);
+ this.combo = true;
+ };
+
+ inherit(AcroFormComboBox, AcroFormListBox);
+ /**
+ * @class AcroFormEditBox
+ * @extends AcroFormComboBox
+ * @extends AcroFormListBox
+ * @extends AcroFormChoiceField
+ * @extends AcroFormField
+ */
+
+ var AcroFormEditBox = function AcroFormEditBox() {
+ AcroFormComboBox.call(this);
+ this.edit = true;
+ };
+
+ inherit(AcroFormEditBox, AcroFormComboBox);
+ /**
+ * @class AcroFormButton
+ * @extends AcroFormField
+ */
+
+ var AcroFormButton = function AcroFormButton() {
+ AcroFormField.call(this);
+ this.FT = "/Btn";
+ /**
+ * (Radio buttons only) If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect. If clear, clicking the selected button deselects it, leaving no button selected.
+ *
+ * @name AcroFormButton#noToggleToOff
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'noToggleToOff', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 15));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 15);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 15);
+ }
+ }
+ });
+ /**
+ * If set, the field is a set of radio buttons; if clear, the field is a checkbox. This flag may be set only if the Pushbutton flag is clear.
+ *
+ * @name AcroFormButton#radio
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'radio', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 16));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 16);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 16);
+ }
+ }
+ });
+ /**
+ * If set, the field is a pushbutton that does not retain a permanent value.
+ *
+ * @name AcroFormButton#pushButton
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'pushButton', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 17));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 17);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 17);
+ }
+ }
+ });
+ /**
+ * (PDF 1.5) If set, a group of radio buttons within a radio button field that use the same value for the on state will turn on and off in unison; that is if one is checked, they are all checked. If clear, the buttons are mutually exclusive (the same behavior as HTML radio buttons).
+ *
+ * @name AcroFormButton#radioIsUnison
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'radioIsUnison', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 26));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 26);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 26);
+ }
+ }
+ });
+ var _MK = {};
+ Object.defineProperty(this, 'MK', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ if (Object.keys(_MK).length !== 0) {
+ var result = [];
+ result.push('<<');
+ var key;
+
+ for (key in _MK) {
+ result.push('/' + key + ' (' + _MK[key] + ')');
+ }
+
+ result.push('>>');
+ return result.join('\n');
+ }
+
+ return;
+ },
+ set: function set(value) {
+ if (_typeof(value) === "object") {
+ _MK = value;
+ }
+ }
+ });
+ /**
+ * From the PDF reference:
+ * (Optional, button fields only) The widget annotation's normal caption which shall be displayed when it is not interacting with the user.
+ * Unlike the remaining entries listed in this Table which apply only to widget annotations associated with pushbutton fields (see Pushbuttons in 12.7.4.2, "Button Fields"), the CA entry may be used with any type of button field, including check boxes (see Check Boxes in 12.7.4.2, "Button Fields") and radio buttons (Radio Buttons in 12.7.4.2, "Button Fields").
+ *
+ * - '8' = Cross,
+ * - 'l' = Circle,
+ * - '' = nothing
+ * @name AcroFormButton#caption
+ * @type {string}
+ */
+
+ Object.defineProperty(this, 'caption', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _MK.CA || '';
+ },
+ set: function set(value) {
+ if (typeof value === "string") {
+ _MK.CA = value;
+ }
+ }
+ });
+
+ var _AS;
+
+ Object.defineProperty(this, 'AS', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ return _AS;
+ },
+ set: function set(value) {
+ _AS = value;
+ }
+ });
+ /**
+ * (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) The annotation's appearance state, which selects the applicable appearance stream from an appearance subdictionary (see Section 12.5.5, "Appearance Streams")
+ *
+ * @name AcroFormButton#appearanceState
+ * @type {any}
+ */
+
+ Object.defineProperty(this, 'appearanceState', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _AS.substr(1, _AS.length - 1);
+ },
+ set: function set(value) {
+ _AS = '/' + value;
+ }
+ });
+ };
+
+ inherit(AcroFormButton, AcroFormField);
+ /**
+ * @class AcroFormPushButton
+ * @extends AcroFormButton
+ * @extends AcroFormField
+ */
+
+ var AcroFormPushButton = function AcroFormPushButton() {
+ AcroFormButton.call(this);
+ this.pushButton = true;
+ };
+
+ inherit(AcroFormPushButton, AcroFormButton);
+ /**
+ * @class AcroFormRadioButton
+ * @extends AcroFormButton
+ * @extends AcroFormField
+ */
+
+ var AcroFormRadioButton = function AcroFormRadioButton() {
+ AcroFormButton.call(this);
+ this.radio = true;
+ this.pushButton = false;
+ var _Kids = [];
+ Object.defineProperty(this, 'Kids', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return _Kids;
+ },
+ set: function set(value) {
+ if (typeof value !== "undefined") {
+ _Kids = value;
+ } else {
+ _Kids = [];
+ }
+ }
+ });
+ };
+
+ inherit(AcroFormRadioButton, AcroFormButton);
+ /**
+ * The Child class of a RadioButton (the radioGroup) -> The single Buttons
+ *
+ * @class AcroFormChildClass
+ * @extends AcroFormField
+ * @ignore
+ */
+
+ var AcroFormChildClass = function AcroFormChildClass() {
+ AcroFormField.call(this);
+
+ var _parent;
+
+ Object.defineProperty(this, 'Parent', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ return _parent;
+ },
+ set: function set(value) {
+ _parent = value;
+ }
+ });
+
+ var _optionName;
+
+ Object.defineProperty(this, 'optionName', {
+ enumerable: false,
+ configurable: true,
+ get: function get() {
+ return _optionName;
+ },
+ set: function set(value) {
+ _optionName = value;
+ }
+ });
+ var _MK = {};
+ Object.defineProperty(this, 'MK', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ var result = [];
+ result.push('<<');
+ var key;
+
+ for (key in _MK) {
+ result.push('/' + key + ' (' + _MK[key] + ')');
+ }
+
+ result.push('>>');
+ return result.join('\n');
+ },
+ set: function set(value) {
+ if (_typeof(value) === "object") {
+ _MK = value;
+ }
+ }
+ });
+ /**
+ * From the PDF reference:
+ * (Optional, button fields only) The widget annotation's normal caption which shall be displayed when it is not interacting with the user.
+ * Unlike the remaining entries listed in this Table which apply only to widget annotations associated with pushbutton fields (see Pushbuttons in 12.7.4.2, "Button Fields"), the CA entry may be used with any type of button field, including check boxes (see Check Boxes in 12.7.4.2, "Button Fields") and radio buttons (Radio Buttons in 12.7.4.2, "Button Fields").
+ *
+ * - '8' = Cross,
+ * - 'l' = Circle,
+ * - '' = nothing
+ * @name AcroFormButton#caption
+ * @type {string}
+ */
+
+ Object.defineProperty(this, 'caption', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _MK.CA || '';
+ },
+ set: function set(value) {
+ if (typeof value === "string") {
+ _MK.CA = value;
+ }
+ }
+ });
+
+ var _AS;
+
+ Object.defineProperty(this, 'AS', {
+ enumerable: false,
+ configurable: false,
+ get: function get() {
+ return _AS;
+ },
+ set: function set(value) {
+ _AS = value;
+ }
+ });
+ /**
+ * (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) The annotation's appearance state, which selects the applicable appearance stream from an appearance subdictionary (see Section 12.5.5, "Appearance Streams")
+ *
+ * @name AcroFormButton#appearanceState
+ * @type {any}
+ */
+
+ Object.defineProperty(this, 'appearanceState', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _AS.substr(1, _AS.length - 1);
+ },
+ set: function set(value) {
+ _AS = '/' + value;
+ }
+ });
+ this.optionName = name;
+ this.caption = 'l';
+ this.appearanceState = 'Off'; // 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);
+ };
+
+ inherit(AcroFormChildClass, AcroFormField);
+
+ AcroFormRadioButton.prototype.setAppearance = function (appearance) {
+ if (!('createAppearanceStream' in appearance && 'getCA' in appearance)) {
+ throw new Error("Couldn't assign Appearance to RadioButton. Appearance was Invalid!");
+ return;
+ }
+
+ for (var objId in this.Kids) {
+ if (this.Kids.hasOwnProperty(objId)) {
+ var child = this.Kids[objId];
+ child.appearanceStreamContent = appearance.createAppearanceStream(child.optionName);
+ child.caption = appearance.getCA();
+ }
+ }
+ };
+
+ AcroFormRadioButton.prototype.createOption = function (name) {
+ var kidCount = this.Kids.length; // Create new Child for RadioGroup
+
+ var child = new AcroFormChildClass();
+ child.Parent = this;
+ child.optionName = name; // Add to Parent
+
+ this.Kids.push(child);
+ addField.call(this, child);
+ return child;
+ };
+ /**
+ * @class AcroFormCheckBox
+ * @extends AcroFormButton
+ * @extends AcroFormField
+ */
+
+
+ var AcroFormCheckBox = function AcroFormCheckBox() {
+ AcroFormButton.call(this);
+ this.fontName = 'zapfdingbats';
+ this.caption = '3';
+ this.appearanceState = 'On';
+ this.value = "On";
+ this.textAlign = 'center';
+ this.appearanceStreamContent = AcroFormAppearance.CheckBox.createAppearanceStream();
+ };
+
+ inherit(AcroFormCheckBox, AcroFormButton);
+ /**
+ * @class AcroFormTextField
+ * @extends AcroFormField
+ */
+
+ var AcroFormTextField = function AcroFormTextField() {
+ AcroFormField.call(this);
+ this.FT = '/Tx';
+ /**
+ * If set, the field may contain multiple lines of text; if clear, the field’s text shall be restricted to a single line.
+ *
+ * @name AcroFormTextField#multiline
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'multiline', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 13));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 13);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 13);
+ }
+ }
+ });
+ /**
+ * (PDF 1.4) If set, the text entered in the field represents the pathname of a file whose contents shall be submitted as the value of the field.
+ *
+ * @name AcroFormTextField#fileSelect
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'fileSelect', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 21));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 21);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 21);
+ }
+ }
+ });
+ /**
+ * (PDF 1.4) If set, text entered in the field shall not be spell-checked.
+ *
+ * @name AcroFormTextField#doNotSpellCheck
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'doNotSpellCheck', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 23));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 23);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 23);
+ }
+ }
+ });
+ /**
+ * (PDF 1.4) If set, the field shall not scroll (horizontally for single-line fields, vertically for multiple-line fields) to accommodate more text than fits within its annotation rectangle. Once the field is full, no further text shall be accepted for interactive form filling; for noninteractive form filling, the filler should take care not to add more character than will visibly fit in the defined area.
+ *
+ * @name AcroFormTextField#doNotScroll
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'doNotScroll', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 24));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 24);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 24);
+ }
+ }
+ });
+ /**
+ * (PDF 1.5) May be set only if the MaxLen entry is present in the text field dictionary (see Table 229) and if the Multiline, Password, and FileSelect flags are clear. If set, the field shall be automatically divided into as many equally spaced positions, or combs, as the value of MaxLen, and the text is laid out into those combs.
+ *
+ * @name AcroFormTextField#comb
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'comb', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 25));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 25);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 25);
+ }
+ }
+ });
+ /**
+ * (PDF 1.5) If set, the value of this field shall be a rich text string (see 12.7.3.4, “Rich Text Strings”). If the field has a value, the RV entry of the field dictionary (Table 222) shall specify the rich text string.
+ *
+ * @name AcroFormTextField#richText
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'richText', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 26));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 26);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 26);
+ }
+ }
+ });
+ var _MaxLen = null;
+ Object.defineProperty(this, 'MaxLen', {
+ enumerable: true,
+ configurable: false,
+ get: function get() {
+ return _MaxLen;
+ },
+ set: function set(value) {
+ _MaxLen = value;
+ }
+ });
+ /**
+ * (Optional; inheritable) The maximum length of the field’s text, in characters.
+ *
+ * @name AcroFormTextField#maxLength
+ * @type {number}
+ */
+
+ Object.defineProperty(this, 'maxLength', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return _MaxLen;
+ },
+ set: function set(value) {
+ if (Number.isInteger(value)) {
+ _MaxLen = value;
+ }
+ }
+ });
+ Object.defineProperty(this, 'hasAppearanceStream', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return this.V || this.DV;
+ }
+ });
+ };
+
+ inherit(AcroFormTextField, AcroFormField);
+ /**
+ * @class AcroFormPasswordField
+ * @extends AcroFormTextField
+ * @extends AcroFormField
+ */
+
+ var AcroFormPasswordField = function AcroFormPasswordField() {
+ AcroFormTextField.call(this);
+ /**
+ * If set, the field is intended for entering a secure password that should not be echoed visibly to the screen. Characters typed from the keyboard shall instead be echoed in some unreadable form, such as asterisks or bullet characters.
+ * NOTE To protect password confidentiality, readers should never store the value of the text field in the PDF file if this flag is set.
+ *
+ * @name AcroFormTextField#password
+ * @type {boolean}
+ */
+
+ Object.defineProperty(this, 'password', {
+ enumerable: true,
+ configurable: true,
+ get: function get() {
+ return Boolean(getBitForPdf(this.Ff, 14));
+ },
+ set: function set(value) {
+ if (Boolean(value) === true) {
+ this.Ff = setBitForPdf(this.Ff, 14);
+ } else {
+ this.Ff = clearBitForPdf(this.Ff, 14);
+ }
+ }
+ });
+ this.password = true;
+ };
+
+ 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;
+ },
+
+ /**
+ * Returns the standard On Appearance for a CheckBox
+ *
+ * @returns {AcroFormXObject}
+ */
+ YesPushDown: function YesPushDown(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
+
+ var encodedColor = scope.__private__.encodeColorString(formObject.color);
+
+ var calcRes = calculateX(formObject, formObject.caption);
+ stream.push("0.749023 g");
+ stream.push("0 0 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " re");
+ stream.push("f");
+ stream.push("BMC");
+ stream.push("q");
+ stream.push("0 0 1 rg");
+ stream.push("/" + fontKey + " " + f2(calcRes.fontSize) + " Tf " + encodedColor);
+ 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 fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
+
+ var encodedColor = scope.__private__.encodeColorString(formObject.color);
+
+ var stream = [];
+ var height = AcroFormAppearance.internal.getHeight(formObject);
+ var width = AcroFormAppearance.internal.getWidth(formObject);
+ var calcRes = calculateX(formObject, formObject.caption);
+ stream.push("1 g");
+ stream.push("0 0 " + f2(width) + " " + f2(height) + " re");
+ stream.push("f");
+ stream.push("q");
+ stream.push("0 0 1 rg");
+ stream.push("0 0 " + f2(width - 1) + " " + f2(height - 1) + " re");
+ stream.push("W");
+ stream.push("n");
+ stream.push("0 g");
+ stream.push("BT");
+ stream.push("/" + fontKey + " " + f2(calcRes.fontSize) + " Tf " + encodedColor);
+ 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 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " 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;
+ },
+ getCA: function getCA() {
+ return '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 = Number((DotRadius * 0.9).toFixed(5));
+ var c = AcroFormAppearance.internal.Bezier_C;
+ var DotRadiusBezier = Number((DotRadius * c).toFixed(5));
+ /*
+ * The Following is a Circle created with Bezier-Curves.
+ */
+
+ stream.push("q");
+ stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 2) + " cm");
+ stream.push(DotRadius + " 0 m");
+ stream.push(DotRadius + " " + DotRadiusBezier + " " + DotRadiusBezier + " " + DotRadius + " 0 " + DotRadius + " c");
+ stream.push("-" + DotRadiusBezier + " " + DotRadius + " -" + DotRadius + " " + DotRadiusBezier + " -" + DotRadius + " 0 c");
+ stream.push("-" + DotRadius + " -" + DotRadiusBezier + " -" + DotRadiusBezier + " -" + DotRadius + " 0 -" + DotRadius + " c");
+ stream.push(DotRadiusBezier + " -" + DotRadius + " " + DotRadius + " -" + DotRadiusBezier + " " + 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...
+
+ var DotRadius = Number((DotRadius * 0.9).toFixed(5)); // Save results for later use; no need to waste
+ // processor ticks on doing math
+
+ var k = Number((DotRadius * 2).toFixed(5));
+ var kc = Number((k * AcroFormAppearance.internal.Bezier_C).toFixed(5));
+ var dc = Number((DotRadius * AcroFormAppearance.internal.Bezier_C).toFixed(5));
+ stream.push("0.749023 g");
+ stream.push("q");
+ stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 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 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 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...
+
+ var DotRadius = Number((DotRadius * 0.9).toFixed(5)); // Save results for later use; no need to waste
+ // processor ticks on doing math
+
+ var k = Number((DotRadius * 2).toFixed(5));
+ var kc = Number((k * AcroFormAppearance.internal.Bezier_C).toFixed(5));
+ stream.push("0.749023 g");
+ stream.push("q");
+ stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 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 {string} name
+ * @returns {Object}
+ * @ignore
+ */
+ 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;
+ },
+ getCA: function getCA() {
+ return '8';
+ },
+ YesNormal: function YesNormal(formObject) {
+ var xobj = createFormXObject(formObject);
+ var stream = [];
+ var cross = AcroFormAppearance.internal.calculateCross(formObject);
+ stream.push("q");
+ stream.push("1 1 " + f2(AcroFormAppearance.internal.getWidth(formObject) - 2) + " " + f2(AcroFormAppearance.internal.getHeight(formObject) - 2) + " re");
+ stream.push("W");
+ stream.push("n");
+ stream.push(f2(cross.x1.x) + " " + f2(cross.x1.y) + " m");
+ stream.push(f2(cross.x2.x) + " " + f2(cross.x2.y) + " l");
+ stream.push(f2(cross.x4.x) + " " + f2(cross.x4.y) + " m");
+ stream.push(f2(cross.x3.x) + " " + f2(cross.x3.y) + " 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 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " re");
+ stream.push("f");
+ stream.push("q");
+ stream.push("1 1 " + f2(AcroFormAppearance.internal.getWidth(formObject) - 2) + " " + f2(AcroFormAppearance.internal.getHeight(formObject) - 2) + " re");
+ stream.push("W");
+ stream.push("n");
+ stream.push(f2(cross.x1.x) + " " + f2(cross.x1.y) + " m");
+ stream.push(f2(cross.x2.x) + " " + f2(cross.x2.y) + " l");
+ stream.push(f2(cross.x4.x) + " " + f2(cross.x4.y) + " m");
+ stream.push(f2(cross.x3.x) + " " + f2(cross.x3.y) + " 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 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " 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
+ var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
+
+ var encodedColor = scope.__private__.encodeColorString(formObject.color);
+
+ var fontSize = formObject.fontSize;
+ var result = '/' + fontKey + ' ' + fontSize + ' Tf ' + encodedColor;
+ return result;
+ }
+ };
+ AcroFormAppearance.internal = {
+ Bezier_C: 0.551915024494,
+ calculateCross: function calculateCross(formObject) {
+ var width = AcroFormAppearance.internal.getWidth(formObject);
+ var height = AcroFormAppearance.internal.getHeight(formObject);
+ var a = Math.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) === "object") {
+ result = scale(formObject.Rect[2]);
+ }
+
+ return result;
+ };
+
+ AcroFormAppearance.internal.getHeight = function (formObject) {
+ var result = 0;
+
+ if (_typeof(formObject) === "object") {
+ result = scale(formObject.Rect[3]);
+ }
+
+ return result;
+ }; // Public:
+
+ /**
+ * Add an AcroForm-Field to the jsPDF-instance
+ *
+ * @name addField
+ * @function
+ * @instance
+ * @param {Object} fieldObject
+ * @returns {jsPDF}
+ */
+
+
+ var addField = jsPDFAPI.addField = function (fieldObject) {
+ initializeAcroForm.call(this);
+
+ if (fieldObject instanceof AcroFormField) {
+ putForm.call(this, fieldObject);
+ } else {
+ throw new Error('Invalid argument passed to jsPDF.addField.');
+ }
+
+ fieldObject.page = scope.internal.getCurrentPageInfo().pageNumber;
+ return this;
+ };
+ /**
+ * @name addButton
+ * @function
+ * @instance
+ * @param {AcroFormButton} options
+ * @returns {jsPDF}
+ * @deprecated
+ */
+
+
+ var addButton = jsPDFAPI.addButton = function (button) {
+ if (button instanceof AcroFormButton === false) {
+ throw new Error('Invalid argument passed to jsPDF.addButton.');
+ }
+
+ return addField.call(this, button);
+ };
+ /**
+ * @name addTextField
+ * @function
+ * @instance
+ * @param {AcroFormTextField} textField
+ * @returns {jsPDF}
+ * @deprecated
+ */
+
+
+ var addTextField = jsPDFAPI.addTextField = function (textField) {
+ if (textField instanceof AcroFormTextField === false) {
+ throw new Error('Invalid argument passed to jsPDF.addTextField.');
+ }
+
+ return addField.call(this, textField);
+ };
+ /**
+ * @name addChoiceField
+ * @function
+ * @instance
+ * @param {AcroFormChoiceField}
+ * @returns {jsPDF}
+ * @deprecated
+ */
+
+
+ var addChoiceField = jsPDFAPI.addChoiceField = function (choiceField) {
+ if (choiceField instanceof AcroFormChoiceField === false) {
+ throw new Error('Invalid argument passed to jsPDF.addChoiceField.');
+ }
+
+ return addField.call(this, choiceField);
+ };
+
+ if (_typeof(globalObj) == "object" && typeof globalObj["ChoiceField"] === "undefined" && typeof globalObj["ListBox"] === "undefined" && typeof globalObj["ComboBox"] === "undefined" && typeof globalObj["EditBox"] === "undefined" && typeof globalObj["Button"] === "undefined" && typeof globalObj["PushButton"] === "undefined" && typeof globalObj["RadioButton"] === "undefined" && typeof globalObj["CheckBox"] === "undefined" && typeof globalObj["TextField"] === "undefined" && typeof globalObj["PasswordField"] === "undefined") {
+ 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
+ };
+ } else {
+ console.warn("AcroForm-Classes are not populated into global-namespace, because the class-Names exist already.");
+ }
+
+ jsPDFAPI.AcroFormChoiceField = AcroFormChoiceField;
+ jsPDFAPI.AcroFormListBox = AcroFormListBox;
+ jsPDFAPI.AcroFormComboBox = AcroFormComboBox;
+ jsPDFAPI.AcroFormEditBox = AcroFormEditBox;
+ jsPDFAPI.AcroFormButton = AcroFormButton;
+ jsPDFAPI.AcroFormPushButton = AcroFormPushButton;
+ jsPDFAPI.AcroFormRadioButton = AcroFormRadioButton;
+ jsPDFAPI.AcroFormCheckBox = AcroFormCheckBox;
+ jsPDFAPI.AcroFormTextField = AcroFormTextField;
+ jsPDFAPI.AcroFormPasswordField = AcroFormPasswordField;
+ jsPDFAPI.AcroFormAppearance = AcroFormAppearance;
+ jsPDFAPI.AcroForm = {
+ ChoiceField: AcroFormChoiceField,
+ ListBox: AcroFormListBox,
+ ComboBox: AcroFormComboBox,
+ EditBox: AcroFormEditBox,
+ Button: AcroFormButton,
+ PushButton: AcroFormPushButton,
+ RadioButton: AcroFormRadioButton,
+ CheckBox: AcroFormCheckBox,
+ TextField: AcroFormTextField,
+ PasswordField: AcroFormPasswordField,
+ Appearance: AcroFormAppearance
+ };
+ })(jsPDF.API, typeof window !== "undefined" && window || typeof global !== "undefined" && global);
+
+ /** @license
+ * 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
+ *
+ *
+ */
+
+ /**
+ * @name addImage
+ * @module
+ */
+ (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
+ ]
+ };
+ /**
+ * Recognize filetype of Image by magic-bytes
+ *
+ * https://en.wikipedia.org/wiki/List_of_file_signatures
+ *
+ * @name getImageFileTypeByImageData
+ * @public
+ * @function
+ * @param {string|arraybuffer} imageData imageData as binary String or arraybuffer
+ * @param {string} format format of file if filetype-recognition fails, e.g. 'JPEG'
+ *
+ * @returns {string} filetype of Image
+ */
+
+ var getImageFileTypeByImageData = jsPDFAPI.getImageFileTypeByImageData = function (imageData, fallbackFormat) {
+ fallbackFormat = fallbackFormat || 'UNKNOWN';
+ var i;
+ var j;
+ var result = 'UNKNOWN';
+ var headerSchemata;
+ var compareResult;
+ var fileType;
+
+ if (jsPDFAPI.isArrayBufferView(imageData)) {
+ imageData = jsPDFAPI.arrayBufferToBinaryString(imageData);
+ }
+
+ 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;
+ }
+ }
+ }
+
+ if (result === 'UNKNOWN' && fallbackFormat !== 'UNKNOWN') {
+ console.warn('FileType of Image not recognized. Processing image as "' + fallbackFormat + '".');
+ result = fallbackFormat;
+ }
+
+ 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,
+ getFilters = this.internal.getFilters;
+ var filters = getFilters();
+
+ while (filters.indexOf('FlateEncode') !== -1) {
+ filters.splice(filters.indexOf('FlateEncode'), 1);
+ }
+
+ img['n'] = objectNumber;
+ var additionalKeyValues = [];
+ additionalKeyValues.push({
+ key: 'Type',
+ value: '/XObject'
+ });
+ additionalKeyValues.push({
+ key: 'Subtype',
+ value: '/Image'
+ });
+ additionalKeyValues.push({
+ key: 'Width',
+ value: img['w']
+ });
+ additionalKeyValues.push({
+ key: 'Height',
+ value: img['h']
+ });
+
+ if (img['cs'] === this.color_spaces.INDEXED) {
+ additionalKeyValues.push({
+ key: 'ColorSpace',
+ value: '[/Indexed /DeviceRGB ' // if an indexed png defines more than one colour with transparency, we've created a smask
+ + (img['pal'].length / 3 - 1) + ' ' + ('smask' in img ? objectNumber + 2 : objectNumber + 1) + ' 0 R]'
+ });
+ } else {
+ additionalKeyValues.push({
+ key: 'ColorSpace',
+ value: '/' + img['cs']
+ });
+
+ if (img['cs'] === this.color_spaces.DEVICE_CMYK) {
+ additionalKeyValues.push({
+ key: 'Decode',
+ value: '[1 0 1 0 1 0 1 0]'
+ });
+ }
+ }
+
+ additionalKeyValues.push({
+ key: 'BitsPerComponent',
+ value: img['bpc']
+ });
+
+ if ('dp' in img) {
+ additionalKeyValues.push({
+ key: 'DecodeParms',
+ value: '<<' + img['dp'] + '>>'
+ });
+ }
+
+ 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] + ' ';
+ }
+
+ additionalKeyValues.push({
+ key: 'Mask',
+ value: '[' + trns + ']'
+ });
+ }
+
+ if ('smask' in img) {
+ additionalKeyValues.push({
+ key: 'SMask',
+ value: objectNumber + 1 + ' 0 R'
+ });
+ }
+
+ var alreadyAppliedFilters = typeof img['f'] !== "undefined" ? ['/' + img['f']] : undefined;
+ putStream({
+ data: img['data'],
+ additionalKeyValues: additionalKeyValues,
+ alreadyAppliedFilters: alreadyAppliedFilters
+ });
+ 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']));
+
+ putStream({
+ data: 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;
+ },
+ generateAliasFromImageData = function generateAliasFromImageData(imageData) {
+ if (typeof imageData === 'string') {
+ return jsPDFAPI.sHashCode(imageData);
+ }
+
+ if (jsPDFAPI.isArrayBufferView(imageData)) {
+ return jsPDFAPI.sHashCode(jsPDFAPI.arrayBufferToBinaryString(imageData));
+ }
+
+ return null;
+ },
+ isImageTypeSupported = function isImageTypeSupported(type) {
+ return typeof jsPDFAPI["process" + type.toUpperCase()] === "function";
+ },
+ isDOMElement = function isDOMElement(object) {
+ return _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'); //is base64 encoded dataUrl, directly process it
+
+ if (src.indexOf('data:image/') === 0) {
+ return unescape(src);
+ } //it is probably an url, try to load it
+
+
+ var tmpImageData = jsPDFAPI.loadFile(src);
+
+ if (tmpImageData !== undefined) {
+ return btoa(tmpImageData);
+ }
+ }
+
+ if (element.nodeName === 'CANVAS') {
+ var canvas = element;
+ return element.toDataURL('image/jpeg', 1.0);
+ } //absolute fallback method
+
+
+ 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'
+ };
+ /**
+ * @name sHashCode
+ * @function
+ * @param {string} str
+ * @returns {string}
+ */
+
+ jsPDFAPI.sHashCode = function (str) {
+ str = str || "";
+ var hash = 0,
+ i,
+ chr;
+ if (str.length === 0) return hash;
+
+ for (i = 0; i < str.length; i++) {
+ chr = str.charCodeAt(i);
+ hash = (hash << 5) - hash + chr;
+ hash |= 0; // Convert to 32bit integer
+ }
+
+ return hash;
+ };
+ /**
+ * @name isString
+ * @function
+ * @param {any} object
+ * @returns {boolean}
+ */
+
+
+ jsPDFAPI.isString = function (object) {
+ return typeof object === 'string';
+ };
+ /**
+ * Validates if given String is a valid Base64-String
+ *
+ * @name validateStringAsBase64
+ * @public
+ * @function
+ * @param {String} possible Base64-String
+ *
+ * @returns {boolean}
+ */
+
+
+ jsPDFAPI.validateStringAsBase64 = function (possibleBase64String) {
+ possibleBase64String = possibleBase64String || '';
+ possibleBase64String.toString().trim();
+ var result = true;
+
+ if (possibleBase64String.length === 0) {
+ result = false;
+ }
+
+ if (possibleBase64String.length % 4 !== 0) {
+ result = false;
+ }
+
+ if (/^[A-Za-z0-9+\/]+$/.test(possibleBase64String.substr(0, possibleBase64String.length - 2)) === false) {
+ result = false;
+ }
+
+ if (/^[A-Za-z0-9\/][A-Za-z0-9+\/]|[A-Za-z0-9+\/]=|==$/.test(possibleBase64String.substr(-2)) === false) {
+ result = false;
+ }
+
+ return result;
+ };
+ /**
+ * Strips out and returns info from a valid base64 data URI
+ *
+ * @name extractInfoFromBase64DataURI
+ * @function
+ * @param {string} dataUrl a valid data URI of format 'data:[
+ * This plugin current supports
+ *
If pageNumber is specified, top and zoom may also be specified
+ * @name link + * @function + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @param {Object} options + */ + + + jsPDFAPI.link = function (x, y, w, h, options) { + var pageInfo = this.internal.getCurrentPageInfo(); + pageInfo.pageContext.annotations.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 + * + * @name textWithLink + * @function + * @param {string} text + * @param {number} x + * @param {number} y + * @param {Object} options + * @returns {number} width 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 + + /** + * @name getTextWidth + * @function + * @param {string} text + * @returns {number} txtWidth + */ + + + jsPDFAPI.getTextWidth = function (text) { + var fontSize = this.internal.getFontSize(); + var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor; + return txtWidth; + }; + + return this; + })(jsPDF.API); + + /** + * @license + * Copyright (c) 2017 Aras Abbasi + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + + /** + * jsPDF arabic parser PlugIn + * + * @name arabic + * @module + */ + (function (jsPDFAPI) { + /** + * Arabic shape substitutions: char code => (isolated, final, initial, medial). + * Arabic Substition A + */ + + var arabicSubstitionA = { + 0x0621: [0xFE80], + // ARABIC LETTER HAMZA + 0x0622: [0xFE81, 0xFE82], + // ARABIC LETTER ALEF WITH MADDA ABOVE + 0x0623: [0xFE83, 0xFE84], + // ARABIC LETTER ALEF WITH HAMZA ABOVE + 0x0624: [0xFE85, 0xFE86], + // ARABIC LETTER WAW WITH HAMZA ABOVE + 0x0625: [0xFE87, 0xFE88], + // ARABIC LETTER ALEF WITH HAMZA BELOW + 0x0626: [0xFE89, 0xFE8A, 0xFE8B, 0xFE8C], + // ARABIC LETTER YEH WITH HAMZA ABOVE + 0x0627: [0xFE8D, 0xFE8E], + // ARABIC LETTER ALEF + 0x0628: [0xFE8F, 0xFE90, 0xFE91, 0xFE92], + // ARABIC LETTER BEH + 0x0629: [0xFE93, 0xFE94], + // ARABIC LETTER TEH MARBUTA + 0x062A: [0xFE95, 0xFE96, 0xFE97, 0xFE98], + // ARABIC LETTER TEH + 0x062B: [0xFE99, 0xFE9A, 0xFE9B, 0xFE9C], + // ARABIC LETTER THEH + 0x062C: [0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0], + // ARABIC LETTER JEEM + 0x062D: [0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4], + // ARABIC LETTER HAH + 0x062E: [0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8], + // ARABIC LETTER KHAH + 0x062F: [0xFEA9, 0xFEAA], + // ARABIC LETTER DAL + 0x0630: [0xFEAB, 0xFEAC], + // ARABIC LETTER THAL + 0x0631: [0xFEAD, 0xFEAE], + // ARABIC LETTER REH + 0x0632: [0xFEAF, 0xFEB0], + // ARABIC LETTER ZAIN + 0x0633: [0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4], + // ARABIC LETTER SEEN + 0x0634: [0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8], + // ARABIC LETTER SHEEN + 0x0635: [0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC], + // ARABIC LETTER SAD + 0x0636: [0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0], + // ARABIC LETTER DAD + 0x0637: [0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4], + // ARABIC LETTER TAH + 0x0638: [0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8], + // ARABIC LETTER ZAH + 0x0639: [0xFEC9, 0xFECA, 0xFECB, 0xFECC], + // ARABIC LETTER AIN + 0x063A: [0xFECD, 0xFECE, 0xFECF, 0xFED0], + // ARABIC LETTER GHAIN + 0x0641: [0xFED1, 0xFED2, 0xFED3, 0xFED4], + // ARABIC LETTER FEH + 0x0642: [0xFED5, 0xFED6, 0xFED7, 0xFED8], + // ARABIC LETTER QAF + 0x0643: [0xFED9, 0xFEDA, 0xFEDB, 0xFEDC], + // ARABIC LETTER KAF + 0x0644: [0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0], + // ARABIC LETTER LAM + 0x0645: [0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4], + // ARABIC LETTER MEEM + 0x0646: [0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8], + // ARABIC LETTER NOON + 0x0647: [0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC], + // ARABIC LETTER HEH + 0x0648: [0xFEED, 0xFEEE], + // ARABIC LETTER WAW + 0x0649: [0xFEEF, 0xFEF0, 64488, 64489], + // ARABIC LETTER ALEF MAKSURA + 0x064A: [0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4], + // ARABIC LETTER YEH + 0x0671: [0xFB50, 0xFB51], + // ARABIC LETTER ALEF WASLA + 0x0677: [0xFBDD], + // ARABIC LETTER U WITH HAMZA ABOVE + 0x0679: [0xFB66, 0xFB67, 0xFB68, 0xFB69], + // ARABIC LETTER TTEH + 0x067A: [0xFB5E, 0xFB5F, 0xFB60, 0xFB61], + // ARABIC LETTER TTEHEH + 0x067B: [0xFB52, 0xFB53, 0xFB54, 0xFB55], + // ARABIC LETTER BEEH + 0x067E: [0xFB56, 0xFB57, 0xFB58, 0xFB59], + // ARABIC LETTER PEH + 0x067F: [0xFB62, 0xFB63, 0xFB64, 0xFB65], + // ARABIC LETTER TEHEH + 0x0680: [0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D], + // ARABIC LETTER BEHEH + 0x0683: [0xFB76, 0xFB77, 0xFB78, 0xFB79], + // ARABIC LETTER NYEH + 0x0684: [0xFB72, 0xFB73, 0xFB74, 0xFB75], + // ARABIC LETTER DYEH + 0x0686: [0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D], + // ARABIC LETTER TCHEH + 0x0687: [0xFB7E, 0xFB7F, 0xFB80, 0xFB81], + // ARABIC LETTER TCHEHEH + 0x0688: [0xFB88, 0xFB89], + // ARABIC LETTER DDAL + 0x068C: [0xFB84, 0xFB85], + // ARABIC LETTER DAHAL + 0x068D: [0xFB82, 0xFB83], + // ARABIC LETTER DDAHAL + 0x068E: [0xFB86, 0xFB87], + // ARABIC LETTER DUL + 0x0691: [0xFB8C, 0xFB8D], + // ARABIC LETTER RREH + 0x0698: [0xFB8A, 0xFB8B], + // ARABIC LETTER JEH + 0x06A4: [0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D], + // ARABIC LETTER VEH + 0x06A6: [0xFB6E, 0xFB6F, 0xFB70, 0xFB71], + // ARABIC LETTER PEHEH + 0x06A9: [0xFB8E, 0xFB8F, 0xFB90, 0xFB91], + // ARABIC LETTER KEHEH + 0x06AD: [0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6], + // ARABIC LETTER NG + 0x06AF: [0xFB92, 0xFB93, 0xFB94, 0xFB95], + // ARABIC LETTER GAF + 0x06B1: [0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D], + // ARABIC LETTER NGOEH + 0x06B3: [0xFB96, 0xFB97, 0xFB98, 0xFB99], + // ARABIC LETTER GUEH + 0x06BA: [0xFB9E, 0xFB9F], + // ARABIC LETTER NOON GHUNNA + 0x06BB: [0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3], + // ARABIC LETTER RNOON + 0x06BE: [0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD], + // ARABIC LETTER HEH DOACHASHMEE + 0x06C0: [0xFBA4, 0xFBA5], + // ARABIC LETTER HEH WITH YEH ABOVE + 0x06C1: [0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9], + // ARABIC LETTER HEH GOAL + 0x06C5: [0xFBE0, 0xFBE1], + // ARABIC LETTER KIRGHIZ OE + 0x06C6: [0xFBD9, 0xFBDA], + // ARABIC LETTER OE + 0x06C7: [0xFBD7, 0xFBD8], + // ARABIC LETTER U + 0x06C8: [0xFBDB, 0xFBDC], + // ARABIC LETTER YU + 0x06C9: [0xFBE2, 0xFBE3], + // ARABIC LETTER KIRGHIZ YU + 0x06CB: [0xFBDE, 0xFBDF], + // ARABIC LETTER VE + 0x06CC: [0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF], + // ARABIC LETTER FARSI YEH + 0x06D0: [0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7], + //ARABIC LETTER E + 0x06D2: [0xFBAE, 0xFBAF], + // ARABIC LETTER YEH BARREE + 0x06D3: [0xFBB0, 0xFBB1] // ARABIC LETTER YEH BARREE WITH HAMZA ABOVE + + }; + var ligatures = { + 0xFEDF: { + 0xFE82: 0xFEF5, + // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM + 0xFE84: 0xFEF7, + // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM + 0xFE88: 0xFEF9, + // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM + 0xFE8E: 0xFEFB // ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM + + }, + 0xFEE0: { + 0xFE82: 0xFEF6, + // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM + 0xFE84: 0xFEF8, + // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM + 0xFE88: 0xFEFA, + // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM + 0xFE8E: 0xFEFC // ARABIC LIGATURE LAM WITH ALEF FINAL FORM + + }, + 0xFE8D: { + 0xFEDF: { + 0xFEE0: { + 0xFEEA: 0xFDF2 + } + } + }, + // ALLAH + 0x0651: { + 0x064C: 0xFC5E, + // Shadda + Dammatan + 0x064D: 0xFC5F, + // Shadda + Kasratan + 0x064E: 0xFC60, + // Shadda + Fatha + 0x064F: 0xFC61, + // Shadda + Damma + 0x0650: 0xFC62 // Shadda + Kasra + + } + }; + 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 noChangeInForm = -1; + var isolatedForm = 0; + var finalForm = 1; + var initialForm = 2; + var medialForm = 3; + jsPDFAPI.__arabicParser__ = {}; //private + + var isInArabicSubstitutionA = jsPDFAPI.__arabicParser__.isInArabicSubstitutionA = function (letter) { + return typeof arabicSubstitionA[letter.charCodeAt(0)] !== "undefined"; + }; + + var isArabicLetter = jsPDFAPI.__arabicParser__.isArabicLetter = function (letter) { + return typeof letter === "string" && /^[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]+$/.test(letter); + }; + + var isArabicEndLetter = jsPDFAPI.__arabicParser__.isArabicEndLetter = function (letter) { + return isArabicLetter(letter) && isInArabicSubstitutionA(letter) && arabicSubstitionA[letter.charCodeAt(0)].length <= 2; + }; + + var isArabicAlfLetter = jsPDFAPI.__arabicParser__.isArabicAlfLetter = function (letter) { + return isArabicLetter(letter) && alfletter.indexOf(letter.charCodeAt(0)) >= 0; + }; + + var arabicLetterHasIsolatedForm = jsPDFAPI.__arabicParser__.arabicLetterHasIsolatedForm = function (letter) { + return isArabicLetter(letter) && isInArabicSubstitutionA(letter) && arabicSubstitionA[letter.charCodeAt(0)].length >= 1; + }; + + var arabicLetterHasFinalForm = jsPDFAPI.__arabicParser__.arabicLetterHasFinalForm = function (letter) { + return isArabicLetter(letter) && isInArabicSubstitutionA(letter) && arabicSubstitionA[letter.charCodeAt(0)].length >= 2; + }; + + var arabicLetterHasInitialForm = jsPDFAPI.__arabicParser__.arabicLetterHasInitialForm = function (letter) { + return isArabicLetter(letter) && isInArabicSubstitutionA(letter) && arabicSubstitionA[letter.charCodeAt(0)].length >= 3; + }; + + var arabicLetterHasMedialForm = jsPDFAPI.__arabicParser__.arabicLetterHasMedialForm = function (letter) { + return isArabicLetter(letter) && isInArabicSubstitutionA(letter) && arabicSubstitionA[letter.charCodeAt(0)].length == 4; + }; + + var resolveLigatures = jsPDFAPI.__arabicParser__.resolveLigatures = function (letters) { + var i = 0; + var tmpLigatures = ligatures; + var position = isolatedForm; + var result = ''; + var effectedLetters = 0; + + for (i = 0; i < letters.length; i += 1) { + if (typeof tmpLigatures[letters.charCodeAt(i)] !== "undefined") { + effectedLetters++; + tmpLigatures = tmpLigatures[letters.charCodeAt(i)]; + + if (typeof tmpLigatures === "number") { + position = getCorrectForm(letters.charAt(i), letters.charAt(i - effectedLetters), letters.charAt(i + 1)); + position = position !== -1 ? position : 0; + result += String.fromCharCode(tmpLigatures); + tmpLigatures = ligatures; + effectedLetters = 0; + } + + if (i === letters.length - 1) { + tmpLigatures = ligatures; + result += letters.charAt(i - (effectedLetters - 1)); + i = i - (effectedLetters - 1); + effectedLetters = 0; + } + } else { + tmpLigatures = ligatures; + result += letters.charAt(i - effectedLetters); + i = i - effectedLetters; + effectedLetters = 0; + } + } + + return result; + }; + + var isArabicDiacritic = jsPDFAPI.__arabicParser__.isArabicDiacritic = function (letter) { + return letter !== undefined && arabic_diacritics[letter.charCodeAt(0)] !== undefined; + }; + + var getCorrectForm = jsPDFAPI.__arabicParser__.getCorrectForm = function (currentChar, beforeChar, nextChar) { + + if (!isArabicLetter(currentChar)) { + return -1; + } + + if (isInArabicSubstitutionA(currentChar) === false) { + return noChangeInForm; + } + + if (!arabicLetterHasFinalForm(currentChar) || !isArabicLetter(beforeChar) && !isArabicLetter(nextChar) || !isArabicLetter(nextChar) && isArabicEndLetter(beforeChar) || isArabicEndLetter(currentChar) && !isArabicLetter(beforeChar) || isArabicEndLetter(currentChar) && isArabicAlfLetter(beforeChar) || isArabicEndLetter(currentChar) && isArabicEndLetter(beforeChar)) { + return isolatedForm; + } + + if (arabicLetterHasMedialForm(currentChar) && isArabicLetter(beforeChar) && !isArabicEndLetter(beforeChar) && isArabicLetter(nextChar) && arabicLetterHasFinalForm(nextChar)) { + return medialForm; + } + + if (isArabicEndLetter(currentChar) || !isArabicLetter(nextChar)) { + return finalForm; + } + + return initialForm; + }; + /** + * @name processArabic + * @function + * @param {string} text + * @param {boolean} reverse + * @returns {string} + */ + + + var processArabic = jsPDFAPI.__arabicParser__.processArabic = jsPDFAPI.processArabic = function (text) { + text = text || ""; + var result = ""; + var i = 0; + var j = 0; + var position = 0; + var currentLetter = ""; + var prevLetter = ""; + var nextLetter = ""; + var words = text.split("\\s+"); + var newWords = []; + + for (i = 0; i < words.length; i += 1) { + newWords.push(''); + + for (j = 0; j < words[i].length; j += 1) { + currentLetter = words[i][j]; + prevLetter = words[i][j - 1]; + nextLetter = words[i][j + 1]; + + if (isArabicLetter(currentLetter)) { + position = getCorrectForm(currentLetter, prevLetter, nextLetter); + + if (position !== -1) { + newWords[i] += String.fromCharCode(arabicSubstitionA[currentLetter.charCodeAt(0)][position]); + } else { + newWords[i] += currentLetter; + } + } else { + newWords[i] += currentLetter; + } + } + + newWords[i] = resolveLigatures(newWords[i]); + } + + result = newWords.join(' '); + return result; + }; + + 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 (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); + } + }; + + jsPDFAPI.events.push(['preProcessText', arabicParserFunction]); + })(jsPDF.API); + + /** @license + * jsPDF Autoprint Plugin + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + + /** + * @name autoprint + * @module + */ + (function (jsPDFAPI) { + /** + * Makes the PDF automatically print. This works in Chrome, Firefox, Acrobat + * Reader. + * + * @name autoPrint + * @function + * @param {Object} options (optional) Set the attribute variant to 'non-conform' (default) or 'javascript' to activate different methods of automatic printing when opening in a PDF-viewer . + * @returns {jsPDF} + * @example + * var doc = new jsPDF(); + * doc.text(10, 10, 'This is a test'); + * doc.autoPrint({variant: 'non-conform'}); + * doc.save('autoprint.pdf'); + */ + + 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); + + /** + * @license + * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + + /** + * jsPDF Canvas PlugIn + * This plugin mimics the HTML5 Canvas + * + * The goal is to provide a way for current canvas users to print directly to a PDF. + * @name canvas + * @module + */ + (function (jsPDFAPI) { + /** + * @class Canvas + * @classdesc A Canvas Wrapper for jsPDF + */ + + var Canvas = function Canvas() { + var jsPdfInstance = undefined; + Object.defineProperty(this, 'pdf', { + get: function get() { + return jsPdfInstance; + }, + set: function set(value) { + jsPdfInstance = value; + } + }); + var _width = 150; + /** + * The height property is a positive integer reflecting the height HTML attribute of the