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