diff --git a/README.md b/README.md index 3bfb6950f7a16046efb6d60e940ac1b7e2250fc3..e2f78c26edc77cf80ade56c9d611c0d631c125fd 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ You should have received a copy of the GNU General Public License along with Moodle. If not, see <http://www.gnu.org/licenses/>. Please note that this plugin also contains files that are under the -- MIT License: namely index.js, jquery-3.2.1, textclipper.js, chart.js +- MIT License: namely index.js, jquery-3.2.1, textclipper.js, chart.js, jsPDF - Apache License, Version 2.0: namely pdf.js. -Google Charts API is used in download function to convert [LaTeX](https://www.latex-project.org/) formulae to PNG: <https://chart.googleapis.com/chart>. +Optionally Google Charts API can be used in download function to convert [LaTeX](https://www.latex-project.org/) formulae to PNG: <https://chart.googleapis.com/chart>. ### Installation: diff --git a/locallib.php b/locallib.php index efd3f29a61cef5947d557cf2fa757fd2a94333d1..e02d956d85b6ae55ee416471f758440c6a15041d 100644 --- a/locallib.php +++ b/locallib.php @@ -55,7 +55,6 @@ function pdfannotator_display_embed($pdfannotator, $cm, $course, $file, $page = // Method to use the language-strings in javascript. $PAGE->requires->strings_for_js(array_keys($strings), 'pdfannotator'); // Load and execute the javascript files. - $PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/jspdf.debug.js?ver=00001")); $PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/pdf.js")); $PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/pdf_viewer.js")); $PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/textclipper.js")); diff --git a/shared/index.js b/shared/index.js index f325ac0055b554d74dce9c08a2d7c4727e001895..cd761a7a0c8485f2aaa41a906c7466da9d9fda2d 100644 --- a/shared/index.js +++ b/shared/index.js @@ -720,7 +720,6 @@ function startIndex(Y,_cm,_documentObject,_userid,_capabilities, _toolbarSetting function openCommentsCallback() { _2.default.getStoreAdapter().getCommentsToPrint(RENDER_OPTIONS.documentId) .then(function(data){ - if(data.status === "success") { // Get annotation type images. @@ -786,7 +785,6 @@ function startIndex(Y,_cm,_documentObject,_userid,_capabilities, _toolbarSetting var timeasked = post['timemodified']; doc.setTextColor(0,84,159); breakLines(author, timeasked, question); - // Add answers to the question in black (extremely dark blue which looks better). doc.setTextColor(0,0,51); var answers = post['answers']; @@ -808,7 +806,6 @@ function startIndex(Y,_cm,_documentObject,_userid,_capabilities, _toolbarSetting * it contains latex formulae images or not and place its text and/or images on the pdf */ function breakLines(author=null, timemodified=null, post, characters = 150) { - if (typeof post === "string") { // Answer contains text only. printTextblock(author, timemodified, post); } diff --git a/shared/jspdf.debug.js b/shared/jspdf.debug.js deleted file mode 100644 index b25b06fbd593b1663d2c2246e245df5b019f3913..0000000000000000000000000000000000000000 --- a/shared/jspdf.debug.js +++ /dev/null @@ -1,22723 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define('jsPDF', factory) : - (global.jsPDF = factory()); -}(this, (function () -{ 'use strict'; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; - - /** @preserve - * jsPDF - PDF Document creation from JavaScript - * Version 1.4.1 Built on 2018-06-06T07:49:28.721Z - * CommitID 3233f44044 - * - * Copyright (c) 2010-2016 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF - * 2010 Aaron Spike, https://github.com/acspike - * 2012 Willow Systems Corporation, willow-systems.com - * 2012 Pablo Hess, https://github.com/pablohess - * 2012 Florian Jenett, https://github.com/fjenett - * 2013 Warren Weckesser, https://github.com/warrenweckesser - * 2013 Youssef Beddad, https://github.com/lifof - * 2013 Lee Driscoll, https://github.com/lsdriscoll - * 2013 Stefan Slonevskiy, https://github.com/stefslon - * 2013 Jeremy Morel, https://github.com/jmorel - * 2013 Christoph Hartmann, https://github.com/chris-rock - * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria - * 2014 James Makes, https://github.com/dollaruw - * 2014 Diego Casorran, https://github.com/diegocr - * 2014 Steven Spungin, https://github.com/Flamenco - * 2014 Kenneth Glassey, https://github.com/Gavvers - * - * Licensed under the MIT License - * - * Contributor(s): - * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, - * kim3er, mfo, alnorth, Flamenco - */ - - /** - * Creates new jsPDF document object instance. - * @name jsPDF - * @class - * @param orientation {String/Object} Orientation of the first page. Possible values are "portrait" or "landscape" (or shortcuts "p" (Default), "l") <br /> - * Can also be an options object. - * @param unit {String} Measurement unit to be used when coordinates are specified.<br /> - * Possible values are "pt" (points), "mm" (Default), "cm", "in" or "px". - * @param format {String/Array} The format of the first page. Can be <ul><li>a0 - a10</li><li>b0 - b10</li><li>c0 - c10</li><li>c0 - c10</li><li>dl</li><li>letter</li><li>government-letter</li><li>legal</li><li>junior-legal</li><li>ledger</li><li>tabloid</li><li>credit-card</li></ul><br /> - * Default is "a4". If you want to use your own format just pass instead of one of the above predefined formats the size as an number-array , e.g. [595.28, 841.89] - * @returns {jsPDF} - * @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('<</Type /Page'); - out('/Parent 1 0 R'); - out('/Resources 2 0 R'); - out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']'); - // Added for annotation plugin - events.publish('putPage', { - pageNumber: n, - page: pages[n] - }); - out('/Contents ' + (objectNumber + 1) + ' 0 R'); - 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('<</Length ' + p.length + ' /Filter [/FlateDecode]>>'); - } else { - out('<</Length ' + p.length + '>>'); - } - putStream(p); - out('endobj'); - } - offsets[1] = content_length; - out('1 0 obj'); - out('<</Type /Pages'); - var kids = '/Kids ['; - for (i = 0; i < page; i++) { - kids += pageObjectNumbers[i] + ' 0 R '; - } - out(kids + ']'); - out('/Count ' + page); - out('>>'); - out('endobj'); - events.publish('postPutPages'); - }, - 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 <ul><li>a0 - a10</li><li>b0 - b10</li><li>c0 - c10</li><li>c0 - c10</li><li>dl</li><li>letter</li><li>government-letter</li><li>legal</li><li>junior-legal</li><li>ledger</li><li>tabloid</li><li>credit-card</li></ul><br /> - * Default is "a4". If you want to use your own format just pass instead of one of the above predefined formats the size as an number-array , e.g. [595.28, 841.89] - * @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); - // if font is not found, the above line blows up and we never go further - return this; - }; - - /** - * Switches font style or variant for upcoming text elements, - * while keeping the font face or family same. - * See output of jsPDF.getFontList() for possible font names, styles. - * - * @param {String} style Font style or variant. Example: "italic" - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setFontStyle - */ - API.setFontStyle = API.setFontType = function (style) { - activeFontKey = _getFont(undefined, style); - // if font is not found, the above line blows up and we never go further - return this; - }; - - /** - * Returns an object - a tree of fontName to fontStyle relationships available to - * active PDF document. - * - * @public - * @function - * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... } - * @methodOf jsPDF# - * @name getFontList - */ - API.getFontList = function () { - // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added. - var list = {}, - fontName, - fontStyle, - tmp; - - for (fontName in fontmap) { - if (fontmap.hasOwnProperty(fontName)) { - list[fontName] = tmp = []; - for (fontStyle in fontmap[fontName]) { - if (fontmap[fontName].hasOwnProperty(fontStyle)) { - tmp.push(fontStyle); - } - } - } - } - - return list; - }; - - /** - * Add a custom font. - * - * @param {String} Postscript name of the Font. Example: "Menlo-Regular" - * @param {String} Name of font-family from @font-face definition. Example: "Menlo Regular" - * @param {String} Font style. Example: "normal" - * @function - * @returns the {fontKey} (same as the internal method) - * @methodOf jsPDF# - * @name addFont - */ - API.addFont = function (postScriptName, fontName, fontStyle, encoding) { - encoding = encoding || 'Identity-H'; - addFont(postScriptName, fontName, fontStyle, encoding); - }; - - /** - * Sets line width for upcoming lines. - * - * @param {Number} width Line width (in units declared at inception of PDF document) - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setLineWidth - */ - API.setLineWidth = function (width) { - out((width * k).toFixed(2) + ' w'); - return this; - }; - - /** - * Sets the stroke color for upcoming elements. - * - * Depending on the number of arguments given, Gray, RGB, or CMYK - * color space is implied. - * - * When only ch1 is given, "Gray" color space is implied and it - * must be a value in the range from 0.00 (solid black) to to 1.00 (white) - * if values are communicated as String types, or in range from 0 (black) - * to 255 (white) if communicated as Number type. - * The RGB-like 0-255 range is provided for backward compatibility. - * - * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each - * value must be in the range from 0.00 (minimum intensity) to to 1.00 - * (max intensity) if values are communicated as String types, or - * from 0 (min intensity) to to 255 (max intensity) if values are communicated - * as Number types. - * The RGB-like 0-255 range is provided for backward compatibility. - * - * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each - * value must be a in the range from 0.00 (0% concentration) to to - * 1.00 (100% concentration) - * - * Because JavaScript treats fixed point numbers badly (rounds to - * floating point nearest to binary representation) it is highly advised to - * communicate the fractional numbers as String types, not JavaScript Number type. - * - * @param {Number|String} ch1 Color channel value or {String} ch1 color value in hexadecimal, example: '#FFFFFF' - * @param {Number|String} ch2 Color channel value - * @param {Number|String} ch3 Color channel value - * @param {Number|String} ch4 Color channel value - * - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setDrawColor - */ - API.setDrawColor = function (ch1, ch2, ch3, ch4) { - var options = { - "ch1": ch1, - "ch2": ch2, - "ch3": ch3, - "ch4": ch4, - "pdfColorType": "draw", - "precision": 2 - }; - - out(generateColorString(options)); - return this; - }; - - /** - * Sets the fill color for upcoming elements. - * - * Depending on the number of arguments given, Gray, RGB, or CMYK - * color space is implied. - * - * When only ch1 is given, "Gray" color space is implied and it - * must be a value in the range from 0.00 (solid black) to to 1.00 (white) - * if values are communicated as String types, or in range from 0 (black) - * to 255 (white) if communicated as Number type. - * The RGB-like 0-255 range is provided for backward compatibility. - * - * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each - * value must be in the range from 0.00 (minimum intensity) to to 1.00 - * (max intensity) if values are communicated as String types, or - * from 0 (min intensity) to to 255 (max intensity) if values are communicated - * as Number types. - * The RGB-like 0-255 range is provided for backward compatibility. - * - * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each - * value must be a in the range from 0.00 (0% concentration) to to - * 1.00 (100% concentration) - * - * Because JavaScript treats fixed point numbers badly (rounds to - * floating point nearest to binary representation) it is highly advised to - * communicate the fractional numbers as String types, not JavaScript Number type. - * - * @param {Number|String} ch1 Color channel value or {String} ch1 color value in hexadecimal, example: '#FFFFFF' - * @param {Number|String} ch2 Color channel value - * @param {Number|String} ch3 Color channel value - * @param {Number|String} ch4 Color channel value - * - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setFillColor - */ - - API.setFillColor = function (ch1, ch2, ch3, ch4) { - var options = { - "ch1": ch1, - "ch2": ch2, - "ch3": ch3, - "ch4": ch4, - "pdfColorType": "fill", - "precision": 2 - }; - - out(generateColorString(options)); - return this; - }; - - /** - * Sets the text color for upcoming elements. - * - * Depending on the number of arguments given, Gray, RGB, or CMYK - * color space is implied. - * - * When only ch1 is given, "Gray" color space is implied and it - * must be a value in the range from 0.00 (solid black) to to 1.00 (white) - * if values are communicated as String types, or in range from 0 (black) - * to 255 (white) if communicated as Number type. - * The RGB-like 0-255 range is provided for backward compatibility. - * - * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each - * value must be in the range from 0.00 (minimum intensity) to to 1.00 - * (max intensity) if values are communicated as String types, or - * from 0 (min intensity) to to 255 (max intensity) if values are communicated - * as Number types. - * The RGB-like 0-255 range is provided for backward compatibility. - * - * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each - * value must be a in the range from 0.00 (0% concentration) to to - * 1.00 (100% concentration) - * - * Because JavaScript treats fixed point numbers badly (rounds to - * floating point nearest to binary representation) it is highly advised to - * communicate the fractional numbers as String types, not JavaScript Number type. - * - * @param {Number|String} ch1 Color channel value or {String} ch1 color value in hexadecimal, example: '#FFFFFF' - * @param {Number|String} ch2 Color channel value - * @param {Number|String} ch3 Color channel value - * @param {Number|String} ch4 Color channel value - * - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setTextColor - */ - API.setTextColor = function (ch1, ch2, ch3, ch4) { - var options = { - "ch1": ch1, - "ch2": ch2, - "ch3": ch3, - "ch4": ch4, - "pdfColorType": "text", - "precision": 3 - }; - textColor = generateColorString(options); - - return this; - }; - - /** - * Initializes the default character set that the user wants to be global.. - * - * @param {Number} charSpace - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setCharSpace - */ - - API.setCharSpace = function (charSpace) { - activeCharSpace = charSpace; - return this; - }; - - /** - * Initializes the default character set that the user wants to be global.. - * - * @param {Boolean} boolean - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setR2L - */ - - API.setR2L = function (boolean) { - R2L = boolean; - return this; - }; - - /** - * Is an Object providing a mapping from human-readable to - * integer flag values designating the varieties of line cap - * and join styles. - * - * @returns {Object} - * @fieldOf jsPDF# - * @name CapJoinStyles - */ - API.CapJoinStyles = { - 0: 0, - 'butt': 0, - 'but': 0, - 'miter': 0, - 1: 1, - 'round': 1, - 'rounded': 1, - 'circle': 1, - 2: 2, - 'projecting': 2, - 'project': 2, - 'square': 2, - 'bevel': 2 - }; - - /** - * Sets the line cap styles - * See {jsPDF.CapJoinStyles} for variants - * - * @param {String|Number} style A string or number identifying the type of line cap - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setLineCap - */ - API.setLineCap = function (style) { - var id = this.CapJoinStyles[style]; - if (id === undefined) { - throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); - } - lineCapID = id; - out(id + ' J'); - - return this; - }; - - /** - * Sets the line join styles - * See {jsPDF.CapJoinStyles} for variants - * - * @param {String|Number} style A string or number identifying the type of line join - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name setLineJoin - */ - API.setLineJoin = function (style) { - var id = this.CapJoinStyles[style]; - if (id === undefined) { - throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); - } - lineJoinID = id; - out(id + ' j'); - - return this; - }; - - // Output is both an internal (for plugins) and external function - API.output = _output; - - /** - * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf') - * @param {String} filename The filename including extension. - * - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name save - */ - API.save = function (filename) { - API.output('save', filename); - }; - - // applying plugins (more methods) ON TOP of built-in API. - // this is intentional as we allow plugins to override - // built-ins - for (var plugin in jsPDF.API) { - if (jsPDF.API.hasOwnProperty(plugin)) { - if (plugin === 'events' && jsPDF.API.events.length) { - (function (events, newEvents) { - - // jsPDF.API.events is a JS Array of Arrays - // where each Array is a pair of event name, handler - // Events were added by plugins to the jsPDF instantiator. - // These are always added to the new instance and some ran - // during instantiation. - var eventname, handler_and_args, i; - - for (i = newEvents.length - 1; i !== -1; i--) { - // subscribe takes 3 args: 'topic', function, runonce_flag - // if undefined, runonce is false. - // users can attach callback directly, - // or they can attach an array with [callback, runonce_flag] - // that's what the "apply" magic is for below. - eventname = newEvents[i][0]; - handler_and_args = newEvents[i][1]; - events.subscribe.apply(events, [eventname].concat(typeof handler_and_args === 'function' ? [handler_and_args] : handler_and_args)); - } - })(events, jsPDF.API.events); - } else { - API[plugin] = jsPDF.API[plugin]; - } - } - } - - ////////////////////////////////////////////////////// - // continuing initialization of jsPDF Document object - ////////////////////////////////////////////////////// - // Add the first page automatically - addFonts(); - activeFontKey = 'F1'; - _addPage(format, orientation); - - events.publish('initialized'); - return API; - } - - /** - * jsPDF.API is a STATIC property of jsPDF class. - * jsPDF.API is an object you can add methods and properties to. - * The methods / properties you add will show up in new jsPDF objects. - * - * One property is prepopulated. It is the 'events' Object. Plugin authors can add topics, - * callbacks to this object. These will be reassigned to all new instances of jsPDF. - * Examples: - * jsPDF.API.events['initialized'] = function(){ 'this' is API object } - * jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object } - * - * @static - * @public - * @memberOf jsPDF - * @name API - * - * @example - * jsPDF.API.mymethod = function(){ - * // 'this' will be ref to internal API object. see jsPDF source - * // , so you can refer to built-in methods like so: - * // this.line(....) - * // this.text(....) - * } - * var pdfdoc = new jsPDF() - * pdfdoc.mymethod() // <- !!!!!! - */ - jsPDF.API = { - events: [] - }; - jsPDF.version = "0.0.0"; - - if (typeof define === 'function' && define.amd) { - define('jsPDF', function () { - return jsPDF; - }); - } else if (typeof module !== 'undefined' && module.exports) { - module.exports = jsPDF; - module.exports.jsPDF = jsPDF; - } else { - global.jsPDF = jsPDF; - } - return jsPDF; - }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || typeof global !== "undefined" && global || Function('return typeof this === "object" && this.content')() || Function('return this')()); - // `self` is undefined in Firefox for Android content script context - // while `this` is nsIContentFrameMessageManager - // with an attribute `content` that corresponds to the window - - - /** - * jsPDF AcroForm Plugin Copyright (c) 2016 Alexander Weidt, - * https://github.com/BiggA94 - * - * Licensed under the MIT License. http://opensource.org/licenses/mit-license - */ - - (function (jsPDFAPI, globalObj) { - - var scope; - var pageHeight; - var scaleFactor = 1; - var inherit = function inherit(child, parent) { - child.prototype = Object.create(parent.prototype); - child.prototype.constructor = child; - }; - var scale = function scale(x) { - return x * (scaleFactor / 1); // 1 = (96 / 72) - }; - - var createFormXObject = function createFormXObject(formObject) { - var xobj = new AcroFormXObject(); - var height = AcroFormAppearance.internal.getHeight(formObject) || 0; - var width = AcroFormAppearance.internal.getWidth(formObject) || 0; - xobj.BBox = [0, 0, width.toFixed(2), height.toFixed(2)]; - return xobj; - }; - - var setBitPosition = function setBitPosition(variable, position, value) { - variable = variable || 0; - value = value || 1; - - var bitMask = 1; - bitMask = bitMask << position - 1; - - if (value == 1) { - // Set the Bit to 1 - var variable = variable | bitMask; - } else { - // Set the Bit to 0 - var variable = variable & ~bitMask; - } - - return variable; - }; - - /** - * Calculating the Ff entry: - * - * The Ff entry contains flags, that have to be set bitwise In the Following - * the number in the Comment is the BitPosition - */ - var calculateFlagsOnOptions = function calculateFlagsOnOptions(flags, opts, PDFVersion) { - var PDFVersion = PDFVersion || 1.3; - var flags = flags || 0; - - // 1, readOnly - if (opts.readOnly == true) { - flags = setBitPosition(flags, 1); - } - - // 2, required - if (opts.required == true) { - flags = setBitPosition(flags, 2); - } - - // 4, noExport - if (opts.noExport == true) { - flags = setBitPosition(flags, 3); - } - - // 13, multiline - if (opts.multiline == true) { - flags = setBitPosition(flags, 13); - } - - // 14, Password - if (opts.password) { - flags = setBitPosition(flags, 14); - } - - // 15, NoToggleToOff (Radio buttons only - if (opts.noToggleToOff) { - flags = setBitPosition(flags, 15); - } - - // 16, Radio - if (opts.radio) { - flags = setBitPosition(flags, 16); - } - - // 17, Pushbutton - if (opts.pushbutton) { - flags = setBitPosition(flags, 17); - } - - // 18, Combo (If not set, the choiceField is a listBox!!) - if (opts.combo) { - flags = setBitPosition(flags, 18); - } - - // 19, Edit - if (opts.edit) { - flags = setBitPosition(flags, 19); - } - - // 20, Sort - if (opts.sort) { - flags = setBitPosition(flags, 20); - } - - // 21, FileSelect, PDF 1.4... - if (opts.fileSelect && PDFVersion >= 1.4) { - flags = setBitPosition(flags, 21); - } - - // 22, MultiSelect (PDF 1.4) - if (opts.multiSelect && PDFVersion >= 1.4) { - flags = setBitPosition(flags, 22); - } - - // 23, DoNotSpellCheck (PDF 1.4) - if (opts.doNotSpellCheck && PDFVersion >= 1.4) { - flags = setBitPosition(flags, 23); - } - - // 24, DoNotScroll (PDF 1.4) - if (opts.doNotScroll == true && PDFVersion >= 1.4) { - flags = setBitPosition(flags, 24); - } - - // 25, RichText (PDF 1.4) - if (opts.richText && PDFVersion >= 1.4) { - flags = setBitPosition(flags, 25); - } - - return flags; - }; - - var calculateCoordinates = function calculateCoordinates(args) { - var x = args[0]; - var y = args[1]; - var w = args[2]; - var h = args[3]; - - var coordinates = {}; - - if (Array.isArray(x)) { - x[0] = scale(x[0]); - x[1] = scale(x[1]); - x[2] = scale(x[2]); - x[3] = scale(x[3]); - } else { - x = scale(x); - y = scale(y); - w = scale(w); - h = scale(h); - } - coordinates.lowerLeft_X = x || 0; - coordinates.lowerLeft_Y = scale(pageHeight) - y - h || 0; - coordinates.upperRight_X = x + w || 0; - coordinates.upperRight_Y = scale(pageHeight) - y || 0; - - return [coordinates.lowerLeft_X.toFixed(2), coordinates.lowerLeft_Y.toFixed(2), coordinates.upperRight_X.toFixed(2), coordinates.upperRight_Y.toFixed(2)]; - }; - - var calculateAppearanceStream = function calculateAppearanceStream(formObject) { - if (formObject.appearanceStreamContent) { - // If appearanceStream is already set, use it - return formObject.appearanceStreamContent; - } - - if (!formObject.V && !formObject.DV) { - return; - } - - // else calculate it - - var stream = []; - var text = formObject.V || formObject.DV; - var calcRes = calculateX(formObject, text); - - stream.push('/Tx BMC'); - stream.push('q'); - stream.push('/F1 ' + calcRes.fontSize.toFixed(2) + ' Tf'); - stream.push('1 0 0 1 0 0 Tm'); // Text Matrix - - stream.push('BT'); // Begin Text - stream.push(calcRes.text); - - stream.push('ET'); // End Text - stream.push('Q'); - stream.push('EMC'); - - var appearanceStreamContent = new createFormXObject(formObject); - appearanceStreamContent.stream = stream.join("\n"); - - return appearanceStreamContent; - }; - - var calculateX = function calculateX(formObject, text, font, maxFontSize) { - var maxFontSize = maxFontSize || 12; - var font = font || "helvetica"; - var returnValue = { - text: "", - fontSize: "" - }; - // Remove Brackets - text = text.substr(0, 1) == '(' ? text.substr(1) : text; - text = text.substr(text.length - 1) == ')' ? text.substr(0, text.length - 1) : text; - // split into array of words - var textSplit = text.split(' '); - var fontSize = maxFontSize; // The Starting fontSize (The Maximum) - var lineSpacing = 2; - var borderPadding = 2; - - var height = AcroFormAppearance.internal.getHeight(formObject) || 0; - height = height < 0 ? -height : height; - var width = AcroFormAppearance.internal.getWidth(formObject) || 0; - width = width < 0 ? -width : width; - - var isSmallerThanWidth = function isSmallerThanWidth(i, lastLine, fontSize) { - if (i + 1 < textSplit.length) { - var tmp = lastLine + " " + textSplit[i + 1]; - var TextWidth = calculateFontSpace(tmp, fontSize + "px", font).width; - var FieldWidth = width - 2 * borderPadding; - return TextWidth <= FieldWidth; - } else { - return false; - } - }; - - fontSize++; - FontSize: while (true) { - var text = ""; - fontSize--; - var textHeight = calculateFontSpace("3", fontSize + "px", font).height; - var startY = formObject.multiline ? height - fontSize : (height - textHeight) / 2; - startY += lineSpacing; - var startX = -borderPadding; - - var lastY = startY; - var firstWordInLine = 0, - lastWordInLine = 0; - var lastLength = 0; - if (fontSize <= 0) { - // In case, the Text doesn't fit at all - fontSize = 12; - text = "(...) Tj\n"; - text += "% Width of Text: " + calculateFontSpace(text, "1px").width + ", FieldWidth:" + width + "\n"; - break; - } - - lastLength = calculateFontSpace(textSplit[0] + " ", fontSize + "px", font).width; - - var lastLine = ""; - var lineCount = 0; - Line: for (var i in textSplit) { - lastLine += textSplit[i] + " "; - // Remove last blank - lastLine = lastLine.substr(lastLine.length - 1) == " " ? lastLine.substr(0, lastLine.length - 1) : lastLine; - var key = parseInt(i); - lastLength = calculateFontSpace(lastLine + " ", fontSize + "px", font).width; - var nextLineIsSmaller = isSmallerThanWidth(key, lastLine, fontSize); - var isLastWord = i >= textSplit.length - 1; - if (nextLineIsSmaller && !isLastWord) { - lastLine += " "; - continue; // Line - } else if (!nextLineIsSmaller && !isLastWord) { - if (!formObject.multiline) { - continue FontSize; - } else { - if ((textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) { - // If the Text is higher than the - // FieldObject - continue FontSize; - } - lastWordInLine = key; - // go on - } - } else if (isLastWord) { - lastWordInLine = key; - } else { - if (formObject.multiline && (textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) { - // If the Text is higher than the FieldObject - continue FontSize; - } - } - - var line = ''; - - for (var x = firstWordInLine; x <= lastWordInLine; x++) { - line += textSplit[x] + ' '; - } - - // Remove last blank - line = line.substr(line.length - 1) == " " ? line.substr(0, line.length - 1) : line; - // lastLength -= blankSpace.width; - lastLength = calculateFontSpace(line, fontSize + "px", font).width; - - // Calculate startX - switch (formObject.Q) { - case 2: - // Right justified - startX = width - lastLength - borderPadding; - break; - case 1: - // Q = 1 := Text-Alignment: Center - startX = (width - lastLength) / 2; - break; - case 0: - default: - startX = borderPadding; - break; - } - text += startX.toFixed(2) + ' ' + lastY.toFixed(2) + ' Td\n'; - text += '(' + line + ') Tj\n'; - // reset X in PDF - text += -startX.toFixed(2) + ' 0 Td\n'; - - // After a Line, adjust y position - lastY = -(fontSize + lineSpacing); - - // Reset for next iteration step - lastLength = 0; - firstWordInLine = lastWordInLine + 1; - lineCount++; - - lastLine = ""; - continue Line; - } - break; - } - - returnValue.text = text; - returnValue.fontSize = fontSize; - - return returnValue; - }; - - /** - * small workaround for calculating the TextMetric approximately - * - * @param text - * @param fontsize - * @returns {TextMetrics} (Has Height and Width) - */ - var calculateFontSpace = function calculateFontSpace(text, fontSize, fontType) { - 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() { - for (var i in scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields) { - var formObject = scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields[i]; - // add Annot Reference! - if (formObject.hasAnnotation) { - // If theres an Annotation Widget in the Form Object, put the - // Reference in the /Annot array - createAnnotationReference.call(scope, formObject); - } - } - }; - - var putForm = function putForm(formObject) { - if (scope.internal.acroformPlugin.printedOut) { - scope.internal.acroformPlugin.printedOut = false; - scope.internal.acroformPlugin.acroFormDictionaryRoot = null; - } - if (!scope.internal.acroformPlugin.acroFormDictionaryRoot) { - initializeAcroForm.call(scope); - } - scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields.push(formObject); - }; - /** - * Create the Reference to the widgetAnnotation, so that it gets referenced - * in the Annot[] int the+ (Requires the Annotation Plugin) - */ - var createAnnotationReference = function createAnnotationReference(object) { - var options = { - type: 'reference', - object: object - }; - scope.annotationPlugin.annotations[scope.internal.getPageInfo(object.page).pageNumber].push(options); - }; - - // Callbacks - - var putCatalogCallback = function putCatalogCallback() { - // Put reference to AcroForm to DocumentCatalog - if (typeof scope.internal.acroformPlugin.acroFormDictionaryRoot != 'undefined') { - // for safety, shouldn't normally be the case - scope.internal.write('/AcroForm ' + scope.internal.acroformPlugin.acroFormDictionaryRoot.objId + ' ' + 0 + ' R'); - } else { - console.log('Root missing...'); - } - }; - - /** - * Adds /Acroform X 0 R to Document Catalog, and creates the AcroForm - * Dictionary - */ - var AcroFormDictionaryCallback = function AcroFormDictionaryCallback() { - // Remove event - scope.internal.events.unsubscribe(scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID); - delete scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID; - scope.internal.acroformPlugin.printedOut = true; - }; - - /** - * Creates the single Fields and writes them into the Document - * - * If fieldArray is set, use the fields that are inside it instead of the - * fields from the AcroRoot (for the FormXObjects...) - */ - var createFieldCallback = function createFieldCallback(fieldArray) { - var standardFields = !fieldArray; - - if (!fieldArray) { - // in case there is no fieldArray specified, we want to print out - // the Fields of the AcroForm - // Print out Root - scope.internal.newObjectDeferredBegin(scope.internal.acroformPlugin.acroFormDictionaryRoot.objId); - scope.internal.out(scope.internal.acroformPlugin.acroFormDictionaryRoot.getString()); - } - - var fieldArray = fieldArray || scope.internal.acroformPlugin.acroFormDictionaryRoot.Kids; - - for (var i in fieldArray) { - var form = fieldArray[i]; - - var oldRect = form.Rect; - - if (form.Rect) { - form.Rect = calculateCoordinates.call(this, form.Rect); - } - - // Start Writing the Object - scope.internal.newObjectDeferredBegin(form.objId); - - var content = form.objId + " 0 obj\n<<\n"; - - if ((typeof form === 'undefined' ? 'undefined' : _typeof(form)) === "object" && typeof form.getContent === "function") { - content += form.getContent(); - } - - form.Rect = oldRect; - - if (form.hasAppearanceStream && !form.appearanceStreamContent) { - // Calculate Appearance - var appearance = calculateAppearanceStream.call(this, form); - content += "/AP << /N " + appearance + " >>\n"; - - scope.internal.acroformPlugin.xForms.push(appearance); - } - - // Assume AppearanceStreamContent is a Array with N,R,D (at least - // one of them!) - if (form.appearanceStreamContent) { - content += "/AP << "; - // Iterate over N,R and D - for (var k in form.appearanceStreamContent) { - var value = form.appearanceStreamContent[k]; - content += "/" + k + " "; - content += "<< "; - if (Object.keys(value).length >= 1 || Array.isArray(value)) { - // appearanceStream is an Array or Object! - for (var i in value) { - var obj = value[i]; - if (typeof obj === 'function') { - // if Function is referenced, call it in order - // to get the FormXObject - obj = obj.call(this, form); - } - content += "/" + i + " " + obj + " "; - - // In case the XForm is already used, e.g. OffState - // of CheckBoxes, don't add it - if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0)) scope.internal.acroformPlugin.xForms.push(obj); - } - } else { - var obj = value; - if (typeof obj === 'function') { - // if Function is referenced, call it in order to - // get the FormXObject - obj = obj.call(this, form); - } - content += "/" + i + " " + obj + " \n"; - if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0)) scope.internal.acroformPlugin.xForms.push(obj); - } - content += " >>\n"; - } - - // appearance stream is a normal Object.. - content += ">>\n"; - } - - content += ">>\nendobj\n"; - - scope.internal.out(content); - } - if (standardFields) { - createXFormObjectCallback.call(this, scope.internal.acroformPlugin.xForms); - } - }; - - var createXFormObjectCallback = function createXFormObjectCallback(fieldArray) { - for (var i in fieldArray) { - var key = i; - var form = fieldArray[i]; - // Start Writing the Object - scope.internal.newObjectDeferredBegin(form && form.objId); - - var content = ""; - if ((typeof form === 'undefined' ? 'undefined' : _typeof(form)) === "object" && typeof form.getString === "function") { - content = form.getString(); - } - scope.internal.out(content); - - delete fieldArray[key]; - } - }; - - var initializeAcroForm = function initializeAcroForm() { - if (this.internal !== undefined && (this.internal.acroformPlugin === undefined || this.internal.acroformPlugin.isInitialized === false)) { - - scope = this; - - AcroFormField.FieldNum = 0; - this.internal.acroformPlugin = JSON.parse(JSON.stringify(acroformPluginTemplate)); - if (this.internal.acroformPlugin.acroFormDictionaryRoot) { - // return; - throw new Error("Exception while creating AcroformDictionary"); - } - scaleFactor = scope.internal.scaleFactor; - pageHeight = scope.internal.pageSize.getHeight(); - - // The Object Number of the AcroForm Dictionary - scope.internal.acroformPlugin.acroFormDictionaryRoot = new AcroFormDictionary(); - - // add Callback for creating the AcroForm Dictionary - scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID = scope.internal.events.subscribe('postPutResources', AcroFormDictionaryCallback); - - scope.internal.events.subscribe('buildDocument', annotReferenceCallback); // buildDocument - - // Register event, that is triggered when the DocumentCatalog is - // written, in order to add /AcroForm - scope.internal.events.subscribe('putCatalog', putCatalogCallback); - - // Register event, that creates all Fields - scope.internal.events.subscribe('postPutPages', createFieldCallback); - - scope.internal.acroformPlugin.isInitialized = true; - } - }; - - var arrayToPdfArray = function arrayToPdfArray(array) { - if (Array.isArray(array)) { - var content = ' ['; - for (var i in array) { - var element = array[i].toString(); - content += element; - content += i < array.length - 1 ? ' ' : ''; - } - content += ']'; - - return content; - } - }; - - var toPdfString = function toPdfString(string) { - string = string || ""; - - // put Bracket at the Beginning of the String - if (string.indexOf('(') !== 0) { - string = '(' + string; - } - - if (string.substring(string.length - 1) != ')') { - string += ')'; - } - return string; - }; - - // ########################## - // Classes - // ########################## - - var AcroFormPDFObject = function AcroFormPDFObject() { - // The Object ID in the PDF Object Model - // todo - var _objId; - Object.defineProperty(this, 'objId', { - get: function get$$1() { - if (!_objId) { - _objId = scope.internal.newObjectDeferred(); - } - if (!_objId) { - console.log("Couldn't create Object ID"); - } - return _objId; - }, - configurable: false - }); - }; - - AcroFormPDFObject.prototype.toString = function () { - return this.objId + " 0 R"; - }; - - AcroFormPDFObject.prototype.getString = function () { - var res = this.objId + " 0 obj\n<<"; - var content = this.getContent(); - - res += content + ">>\n"; - if (this.stream) { - res += "stream\n"; - res += this.stream; - res += "\nendstream\n"; - } - res += "endobj\n"; - return res; - }; - - AcroFormPDFObject.prototype.getContent = function () { - /** - * Prints out all enumerable Variables from the Object - * - * @param fieldObject - * @returns {string} - */ - var createContentFromFieldObject = function createContentFromFieldObject(fieldObject) { - var content = ''; - - var keys = Object.keys(fieldObject).filter(function (key) { - return key != 'content' && key != 'appearanceStreamContent' && key.substring(0, 1) != "_"; - }); - - for (var i in keys) { - var key = keys[i]; - var value = fieldObject[key]; - - /* - * if (key == 'Rect' && value) { value = - * AcroForm.internal.calculateCoordinates.call(jsPDF.API.acroformPlugin.internal, - * value); } - */ - - if (value) { - if (Array.isArray(value)) { - content += '/' + key + ' ' + arrayToPdfArray(value) + "\n"; - } else if (value instanceof AcroFormPDFObject) { - // In case it is a reference to another PDFObject, - // take the referennce number - content += '/' + key + ' ' + value.objId + " 0 R" + "\n"; - } else { - content += '/' + key + ' ' + value + '\n'; - } - } - } - return content; - }; - - var object = ""; - - object += createContentFromFieldObject(this); - return object; - }; - - var AcroFormXObject = function AcroFormXObject() { - AcroFormPDFObject.call(this); - this.Type = "/XObject"; - this.Subtype = "/Form"; - this.FormType = 1; - this.BBox; - this.Matrix; - this.Resources = "2 0 R"; - this.PieceInfo; - var _stream; - Object.defineProperty(this, 'Length', { - enumerable: true, - get: function get$$1() { - return _stream !== undefined ? _stream.length : 0; - } - }); - Object.defineProperty(this, 'stream', { - enumerable: false, - set: function set$$1(val) { - _stream = val.trim(); - }, - get: function get$$1() { - if (_stream) { - return _stream; - } else { - return null; - } - } - }); - }; - - inherit(AcroFormXObject, AcroFormPDFObject); - // ##### The Objects, the User can Create: - - var AcroFormDictionary = function AcroFormDictionary() { - AcroFormPDFObject.call(this); - var _Kids = []; - Object.defineProperty(this, 'Kids', { - enumerable: false, - configurable: true, - get: function get$$1() { - if (_Kids.length > 0) { - return _Kids; - } else { - return; - } - } - }); - Object.defineProperty(this, 'Fields', { - enumerable: true, - configurable: true, - get: function get$$1() { - return _Kids; - } - }); - // Default Appearance - this.DA; - }; - - inherit(AcroFormDictionary, AcroFormPDFObject); - - // The Field Object contains the Variables, that every Field needs - // Rectangle for Appearance: lower_left_X, lower_left_Y, width, height - var AcroFormField = function AcroFormField() { - - AcroFormPDFObject.call(this); - - var _Rect; - Object.defineProperty(this, 'Rect', { - enumerable: true, - configurable: false, - get: function get$$1() { - if (!_Rect) { - return; - } - var tmp = _Rect; - // var calculatedRes = - // AcroForm.internal.calculateCoordinates(_Rect); // do - // later! - return tmp; - }, - set: function set$$1(val) { - _Rect = val; - } - }); - - var _FT = ""; - Object.defineProperty(this, 'FT', { - enumerable: true, - set: function set$$1(val) { - _FT = val; - }, - get: function get$$1() { - return _FT; - } - }); - /** - * The Partial name of the Field Object. It has to be unique. - */ - var _T; - - Object.defineProperty(this, 'T', { - enumerable: true, - configurable: false, - set: function set$$1(val) { - _T = val; - }, - get: function get$$1() { - if (!_T || _T.length < 1) { - if (this instanceof AcroFormChildClass) { - // In case of a Child from a Radio´Group, you don't - // need a FieldName!!! - return; - } - return "(FieldObject" + AcroFormField.FieldNum++ + ")"; - } - if (_T.substring(0, 1) == "(" && _T.substring(_T.length - 1)) { - return _T; - } - return "(" + _T + ")"; - } - }); - - var _DA; - // Defines the default appearance (Needed for variable Text) - Object.defineProperty(this, 'DA', { - enumerable: true, - get: function get$$1() { - if (!_DA) { - return; - } - return '(' + _DA + ')'; - }, - set: function set$$1(val) { - _DA = val; - } - }); - - var _DV; - // Defines the default value - Object.defineProperty(this, 'DV', { - enumerable: true, - configurable: true, - get: function get$$1() { - if (!_DV) { - return; - } - return _DV; - }, - set: function set$$1(val) { - _DV = val; - } - }); - - var _V; - // Defines the default value - Object.defineProperty(this, 'V', { - enumerable: true, - configurable: true, - get: function get$$1() { - if (!_V) { - return; - } - return _V; - }, - set: function set$$1(val) { - _V = val; - } - }); - - // this.Type = "/Annot"; - // this.Subtype = "/Widget"; - Object.defineProperty(this, 'Type', { - enumerable: true, - get: function get$$1() { - return this.hasAnnotation ? "/Annot" : null; - } - }); - - Object.defineProperty(this, 'Subtype', { - enumerable: true, - get: function get$$1() { - return this.hasAnnotation ? "/Widget" : null; - } - }); - - /** - * - * @type {Array} - */ - this.BG; - - Object.defineProperty(this, 'hasAnnotation', { - enumerable: false, - get: function get$$1() { - if (this.Rect || this.BC || this.BG) { - return true; - } - return false; - } - }); - - Object.defineProperty(this, 'hasAppearanceStream', { - enumerable: false, - configurable: true, - writable: true - }); - - Object.defineProperty(this, 'page', { - enumerable: false, - configurable: true, - writable: true - }); - }; - - inherit(AcroFormField, AcroFormPDFObject); - - var AcroFormChoiceField = function AcroFormChoiceField() { - AcroFormField.call(this); - // Field Type = Choice Field - this.FT = "/Ch"; - // options - this.Opt = []; - this.V = '()'; - // Top Index - this.TI = 0; - /** - * Defines, whether the - * - * @type {boolean} - */ - - var _combo = false; - - Object.defineProperty(this, 'combo', { - enumerable: false, - get: function get$$1() { - return _combo; - }, - set: function set$$1(val) { - _combo = val; - } - }); - /** - * Defines, whether the Choice Field is an Edit Field. An Edit Field - * is automatically an Combo Field. - */ - Object.defineProperty(this, 'edit', { - enumerable: true, - set: function set$$1(val) { - if (val == true) { - this._edit = true; - // ComboBox has to be true - this.combo = true; - } else { - this._edit = false; - } - }, - get: function get$$1() { - if (!this._edit) { - return false; - } - return this._edit; - }, - configurable: false - }); - this.hasAppearanceStream = true; - }; - inherit(AcroFormChoiceField, AcroFormField); - - var AcroFormListBox = function AcroFormListBox() { - AcroFormChoiceField.call(this); - this.combo = false; - }; - inherit(AcroFormListBox, AcroFormChoiceField); - - var AcroFormComboBox = function AcroFormComboBox() { - AcroFormListBox.call(this); - this.combo = true; - }; - inherit(AcroFormComboBox, AcroFormListBox); - - var AcroFormEditBox = function AcroFormEditBox() { - AcroFormComboBox.call(this); - this.edit = true; - }; - inherit(AcroFormEditBox, AcroFormComboBox); - - var AcroFormButton = function AcroFormButton() { - AcroFormField.call(this); - this.FT = "/Btn"; - // this.hasAnnotation = true; - }; - inherit(AcroFormButton, AcroFormField); - - var AcroFormPushButton = function AcroFormPushButton() { - AcroFormButton.call(this); - - var _pushbutton = true; - Object.defineProperty(this, 'pushbutton', { - enumerable: false, - get: function get$$1() { - return _pushbutton; - }, - set: function set$$1(val) { - _pushbutton = val; - } - }); - }; - inherit(AcroFormPushButton, AcroFormButton); - - var AcroFormRadioButton = function AcroFormRadioButton() { - AcroFormButton.call(this); - - var _radio = true; - Object.defineProperty(this, 'radio', { - enumerable: false, - get: function get$$1() { - return _radio; - }, - set: function set$$1(val) { - _radio = val; - } - }); - - var _Kids = []; - Object.defineProperty(this, 'Kids', { - enumerable: true, - get: function get$$1() { - if (_Kids.length > 0) { - return _Kids; - } - } - }); - - Object.defineProperty(this, '__Kids', { - get: function get$$1() { - return _Kids; - } - }); - - var _noToggleToOff; - - Object.defineProperty(this, 'noToggleToOff', { - enumerable: false, - get: function get$$1() { - return _noToggleToOff; - }, - set: function set$$1(val) { - _noToggleToOff = val; - } - }); - - // this.hasAnnotation = false; - }; - inherit(AcroFormRadioButton, AcroFormButton); - - /* - * The Child classs of a RadioButton (the radioGroup) -> The single - * Buttons - */ - var AcroFormChildClass = function AcroFormChildClass(parent, name) { - AcroFormField.call(this); - this.Parent = parent; - - // todo: set AppearanceType as variable that can be set from the - // outside... - this._AppearanceType = AcroFormAppearance.RadioButton.Circle; - // The Default appearanceType is the Circle - this.appearanceStreamContent = this._AppearanceType.createAppearanceStream(name); - - // Set Print in the Annot Flag - this.F = setBitPosition(this.F, 3, 1); - - // Set AppearanceCharacteristicsDictionary with default appearance - // if field is not interacting with user - this.MK = this._AppearanceType.createMK(); - // (8) -> Cross, (1)-> Circle, ()-> nothing - - // Default Appearance is Off - this.AS = "/Off"; // + name; - - this._Name = name; - }; - inherit(AcroFormChildClass, AcroFormField); - - AcroFormRadioButton.prototype.setAppearance = function (appearance) { - if (!('createAppearanceStream' in appearance && 'createMK' in appearance)) { - console.log("Couldn't assign Appearance to RadioButton. Appearance was Invalid!"); - return; - } - for (var i in this.__Kids) { - var child = this.__Kids[i]; - - child.appearanceStreamContent = appearance.createAppearanceStream(child._Name); - child.MK = appearance.createMK(); - } - }; - - AcroFormRadioButton.prototype.createOption = function (name) { - var parent = this; - var kidCount = this.__Kids.length; - - // Create new Child for RadioGroup - var child = new AcroFormChildClass(parent, name); - // Add to Parent - this.__Kids.push(child); - - jsPDFAPI.addField(child); - - return child; - }; - - var AcroFormCheckBox = function AcroFormCheckBox() { - AcroFormButton.call(this); - this.appearanceStreamContent = AcroFormAppearance.CheckBox.createAppearanceStream(); - this.MK = AcroFormAppearance.CheckBox.createMK(); - this.AS = "/On"; - this.V = "/On"; - }; - inherit(AcroFormCheckBox, AcroFormButton); - - var AcroFormTextField = function AcroFormTextField() { - AcroFormField.call(this); - this.DA = AcroFormAppearance.createDefaultAppearanceStream(); - this.F = 4; - var _V; - Object.defineProperty(this, 'V', { - get: function get$$1() { - if (_V) { - return toPdfString(_V); - } else { - return _V; - } - }, - enumerable: true, - set: function set$$1(val) { - _V = val; - } - }); - - var _DV; - Object.defineProperty(this, 'DV', { - get: function get$$1() { - if (_DV) { - return toPdfString(_DV); - } else { - return _DV; - } - }, - enumerable: true, - set: function set$$1(val) { - _DV = val; - } - }); - - var _multiline = false; - Object.defineProperty(this, 'multiline', { - enumerable: false, - get: function get$$1() { - return _multiline; - }, - set: function set$$1(val) { - _multiline = val; - } - }); - - /** - * For PDF 1.4 - * - * @type {boolean} - */ - var _fileSelect = false; - Object.defineProperty(this, 'fileSelect', { - enumerable: false, - get: function get$$1() { - return _fileSelect; - }, - set: function set$$1(val) { - _fileSelect = val; - } - }); - /** - * For PDF 1.4 - * - * @type {boolean} - */ - var _doNotSpellCheck = false; - Object.defineProperty(this, 'doNotSpellCheck', { - enumerable: false, - get: function get$$1() { - return _doNotSpellCheck; - }, - set: function set$$1(val) { - _doNotSpellCheck = val; - } - }); - /** - * For PDF 1.4 - * - * @type {boolean} - */ - var _doNotScroll = false; - Object.defineProperty(this, 'doNotScroll', { - enumerable: false, - get: function get$$1() { - return _doNotScroll; - }, - set: function set$$1(val) { - _doNotScroll = val; - } - }); - - var _MaxLen = false; - Object.defineProperty(this, 'MaxLen', { - enumerable: true, - get: function get$$1() { - return _MaxLen; - }, - set: function set$$1(val) { - _MaxLen = val; - } - }); - - Object.defineProperty(this, 'hasAppearanceStream', { - enumerable: false, - get: function get$$1() { - return this.V || this.DV; - } - }); - }; - inherit(AcroFormTextField, AcroFormField); - - var AcroFormPasswordField = function AcroFormPasswordField() { - AcroFormTextField.call(this); - - var _password = true; - Object.defineProperty(this, 'password', { - enumerable: false, - get: function get$$1() { - return _password; - }, - set: function set$$1(val) { - _password = val; - } - }); - }; - inherit(AcroFormPasswordField, AcroFormTextField); - - // Contains Methods for creating standard appearances - var AcroFormAppearance = { - CheckBox: { - createAppearanceStream: function createAppearanceStream() { - var appearance = { - N: { - On: AcroFormAppearance.CheckBox.YesNormal - }, - D: { - On: AcroFormAppearance.CheckBox.YesPushDown, - Off: AcroFormAppearance.CheckBox.OffPushDown - } - }; - - return appearance; - }, - /** - * If any other icons are needed, the number between the - * brackets can be changed - * - * @returns {string} - */ - createMK: function createMK() { - return "<< /CA (3)>>"; - }, - /** - * Returns the standard On Appearance for a CheckBox - * - * @returns {AcroFormXObject} - */ - YesPushDown: function YesPushDown(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - var zapfDingbatsId = scope.internal.getFont("zapfdingbats", "normal").id; - formObject.Q = 1; // set text-alignment as centered - var calcRes = calculateX(formObject, "3", "ZapfDingbats", 50); - stream.push("0.749023 g"); - stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re"); - stream.push("f"); - stream.push("BMC"); - stream.push("q"); - stream.push("0 0 1 rg"); - stream.push("/" + zapfDingbatsId + " " + calcRes.fontSize.toFixed(2) + " Tf 0 g"); - stream.push("BT"); - stream.push(calcRes.text); - stream.push("ET"); - stream.push("Q"); - stream.push("EMC"); - xobj.stream = stream.join("\n"); - return xobj; - }, - - YesNormal: function YesNormal(formObject) { - var xobj = createFormXObject(formObject); - var zapfDingbatsId = scope.internal.getFont("zapfdingbats", "normal").id; - var stream = []; - formObject.Q = 1; // set text-alignment as centered - var height = AcroFormAppearance.internal.getHeight(formObject); - var width = AcroFormAppearance.internal.getWidth(formObject); - var calcRes = calculateX(formObject, "3", "ZapfDingbats", height * 0.9); - stream.push("1 g"); - stream.push("0 0 " + width.toFixed(2) + " " + height.toFixed(2) + " re"); - stream.push("f"); - stream.push("q"); - stream.push("0 0 1 rg"); - stream.push("0 0 " + (width - 1).toFixed(2) + " " + (height - 1).toFixed(2) + " re"); - stream.push("W"); - stream.push("n"); - stream.push("0 g"); - stream.push("BT"); - stream.push("/" + zapfDingbatsId + " " + calcRes.fontSize.toFixed(2) + " Tf 0 g"); - stream.push(calcRes.text); - stream.push("ET"); - stream.push("Q"); - xobj.stream = stream.join("\n"); - return xobj; - }, - - /** - * Returns the standard Off Appearance for a CheckBox - * - * @returns {AcroFormXObject} - */ - OffPushDown: function OffPushDown(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - stream.push("0.749023 g"); - stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re"); - stream.push("f"); - xobj.stream = stream.join("\n"); - return xobj; - } - }, - - RadioButton: { - Circle: { - createAppearanceStream: function createAppearanceStream(name) { - var appearanceStreamContent = { - D: { - 'Off': AcroFormAppearance.RadioButton.Circle.OffPushDown - }, - N: {} - }; - appearanceStreamContent.N[name] = AcroFormAppearance.RadioButton.Circle.YesNormal; - appearanceStreamContent.D[name] = AcroFormAppearance.RadioButton.Circle.YesPushDown; - return appearanceStreamContent; - }, - createMK: function createMK() { - return "<< /CA (l)>>"; - }, - - YesNormal: function YesNormal(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - // Make the Radius of the Circle relative to min(height, - // width) of formObject - var DotRadius = AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4; - // The Borderpadding... - DotRadius *= 0.9; - var c = AcroFormAppearance.internal.Bezier_C; - /* - * The Following is a Circle created with Bezier-Curves. - */ - stream.push("q"); - stream.push("1 0 0 1 " + AcroFormAppearance.internal.getWidth(formObject) / 2 + " " + AcroFormAppearance.internal.getHeight(formObject) / 2 + " cm"); - stream.push(DotRadius + " 0 m"); - stream.push(DotRadius + " " + DotRadius * c + " " + DotRadius * c + " " + DotRadius + " 0 " + DotRadius + " c"); - stream.push("-" + DotRadius * c + " " + DotRadius + " -" + DotRadius + " " + DotRadius * c + " -" + DotRadius + " 0 c"); - stream.push("-" + DotRadius + " -" + DotRadius * c + " -" + DotRadius * c + " -" + DotRadius + " 0 -" + DotRadius + " c"); - stream.push(DotRadius * c + " -" + DotRadius + " " + DotRadius + " -" + DotRadius * c + " " + DotRadius + " 0 c"); - stream.push("f"); - stream.push("Q"); - xobj.stream = stream.join("\n"); - return xobj; - }, - YesPushDown: function YesPushDown(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - var DotRadius = AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4; - // The Borderpadding... - DotRadius *= 0.9; - // Save results for later use; no need to waste - // processor ticks on doing math - var k = DotRadius * 2; - // var c = AcroFormAppearance.internal.Bezier_C; - var kc = k * AcroFormAppearance.internal.Bezier_C; - var dc = DotRadius * AcroFormAppearance.internal.Bezier_C; - - stream.push("0.749023 g"); - stream.push("q"); - stream.push("1 0 0 1 " + (AcroFormAppearance.internal.getWidth(formObject) / 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) / 2).toFixed(2) + " cm"); - stream.push(k + " 0 m"); - stream.push(k + " " + kc + " " + kc + " " + k + " 0 " + k + " c"); - stream.push("-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c"); - stream.push("-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c"); - stream.push(kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c"); - stream.push("f"); - stream.push("Q"); - stream.push("0 g"); - stream.push("q"); - stream.push("1 0 0 1 " + (AcroFormAppearance.internal.getWidth(formObject) / 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) / 2).toFixed(2) + " cm"); - stream.push(DotRadius + " 0 m"); - stream.push("" + DotRadius + " " + dc + " " + dc + " " + DotRadius + " 0 " + DotRadius + " c"); - stream.push("-" + dc + " " + DotRadius + " -" + DotRadius + " " + dc + " -" + DotRadius + " 0 c"); - stream.push("-" + DotRadius + " -" + dc + " -" + dc + " -" + DotRadius + " 0 -" + DotRadius + " c"); - stream.push(dc + " -" + DotRadius + " " + DotRadius + " -" + dc + " " + DotRadius + " 0 c"); - stream.push("f"); - stream.push("Q"); - xobj.stream = stream.join("\n"); - return xobj; - }, - OffPushDown: function OffPushDown(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - var DotRadius = AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4; - // The Borderpadding... - DotRadius *= 0.9; - // Save results for later use; no need to waste - // processor ticks on doing math - var k = DotRadius * 2; - // var c = AcroFormAppearance.internal.Bezier_C; - var kc = k * AcroFormAppearance.internal.Bezier_C; - - stream.push("0.749023 g"); - stream.push("q"); - stream.push("1 0 0 1 " + (AcroFormAppearance.internal.getWidth(formObject) / 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) / 2).toFixed(2) + " cm"); - stream.push(k + " 0 m"); - stream.push(k + " " + kc + " " + kc + " " + k + " 0 " + k + " c"); - stream.push("-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c"); - stream.push("-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c"); - stream.push(kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c"); - stream.push("f"); - stream.push("Q"); - xobj.stream = stream.join("\n"); - return xobj; - } - }, - - Cross: { - /** - * Creates the Actual AppearanceDictionary-References - * - * @param name - * @returns - */ - createAppearanceStream: function createAppearanceStream(name) { - var appearanceStreamContent = { - D: { - 'Off': AcroFormAppearance.RadioButton.Cross.OffPushDown - }, - N: {} - }; - appearanceStreamContent.N[name] = AcroFormAppearance.RadioButton.Cross.YesNormal; - appearanceStreamContent.D[name] = AcroFormAppearance.RadioButton.Cross.YesPushDown; - return appearanceStreamContent; - }, - createMK: function createMK() { - return "<< /CA (8)>>"; - }, - - YesNormal: function YesNormal(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - var cross = AcroFormAppearance.internal.calculateCross(formObject); - stream.push("q"); - stream.push("1 1 " + (AcroFormAppearance.internal.getWidth(formObject) - 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) - 2).toFixed(2) + " re"); - stream.push("W"); - stream.push("n"); - stream.push(cross.x1.x.toFixed(2) + " " + cross.x1.y.toFixed(2) + " m"); - stream.push(cross.x2.x.toFixed(2) + " " + cross.x2.y.toFixed(2) + " l"); - stream.push(cross.x4.x.toFixed(2) + " " + cross.x4.y.toFixed(2) + " m"); - stream.push(cross.x3.x.toFixed(2) + " " + cross.x3.y.toFixed(2) + " l"); - stream.push("s"); - stream.push("Q"); - xobj.stream = stream.join("\n"); - return xobj; - }, - YesPushDown: function YesPushDown(formObject) { - var xobj = createFormXObject(formObject); - var cross = AcroFormAppearance.internal.calculateCross(formObject); - var stream = []; - stream.push("0.749023 g"); - stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re"); - stream.push("f"); - stream.push("q"); - stream.push("1 1 " + (AcroFormAppearance.internal.getWidth(formObject) - 2).toFixed(2) + " " + (AcroFormAppearance.internal.getHeight(formObject) - 2).toFixed(2) + " re"); - stream.push("W"); - stream.push("n"); - stream.push(cross.x1.x.toFixed(2) + " " + cross.x1.y.toFixed(2) + " m"); - stream.push(cross.x2.x.toFixed(2) + " " + cross.x2.y.toFixed(2) + " l"); - stream.push(cross.x4.x.toFixed(2) + " " + cross.x4.y.toFixed(2) + " m"); - stream.push(cross.x3.x.toFixed(2) + " " + cross.x3.y.toFixed(2) + " l"); - stream.push("s"); - stream.push("Q"); - xobj.stream = stream.join("\n"); - return xobj; - }, - OffPushDown: function OffPushDown(formObject) { - var xobj = createFormXObject(formObject); - var stream = []; - stream.push("0.749023 g"); - stream.push("0 0 " + AcroFormAppearance.internal.getWidth(formObject).toFixed(2) + " " + AcroFormAppearance.internal.getHeight(formObject).toFixed(2) + " re"); - stream.push("f"); - xobj.stream = stream.join("\n"); - return xobj; - } - } - }, - - /** - * Returns the standard Appearance - * - * @returns {AcroFormXObject} - */ - createDefaultAppearanceStream: function createDefaultAppearanceStream(formObject) { - // Set Helvetica to Standard Font (size: auto) - // Color: Black - return "/F1 0 Tf 0 g"; - } - }; - - AcroFormAppearance.internal = { - Bezier_C: 0.551915024494, - - calculateCross: function calculateCross(formObject) { - var min = function min(x, y) { - return x > y ? y : x; - }; - - var width = AcroFormAppearance.internal.getWidth(formObject); - var height = AcroFormAppearance.internal.getHeight(formObject); - var a = min(width, height); - - - var cross = { - x1: { // upperLeft - x: (width - a) / 2, - y: (height - a) / 2 + a // height - borderPadding - }, - x2: { // lowerRight - x: (width - a) / 2 + a, - y: (height - a) / 2 // borderPadding - }, - x3: { // lowerLeft - x: (width - a) / 2, - y: (height - a) / 2 // borderPadding - }, - x4: { // upperRight - x: (width - a) / 2 + a, - y: (height - a) / 2 + a // height - borderPadding - } - }; - - return cross; - } - }; - AcroFormAppearance.internal.getWidth = function (formObject) { - var result = 0; - if ((typeof formObject === 'undefined' ? 'undefined' : _typeof(formObject)) === "object") { - result = scale(formObject.Rect[2]); // (formObject.Rect[2] - - // formObject.Rect[0]) || 0; - } - return result; - }; - AcroFormAppearance.internal.getHeight = function (formObject) { - var result = 0; - if ((typeof formObject === 'undefined' ? 'undefined' : _typeof(formObject)) === "object") { - result = scale(formObject.Rect[3]); // (formObject.Rect[1] - - // formObject.Rect[3]) || 0; - } - return result; - }; - - // Public: - - jsPDFAPI.addField = function (fieldObject) { - initializeAcroForm.call(this); - // var opt = parseOptions(fieldObject); - if (fieldObject instanceof AcroFormTextField) { - this.addTextField.call(this, fieldObject); - } else if (fieldObject instanceof AcroFormChoiceField) { - this.addChoiceField.call(this, fieldObject); - } else if (fieldObject instanceof AcroFormButton) { - this.addButton.call(this, fieldObject); - } else if (fieldObject instanceof AcroFormChildClass) { - putForm.call(this, fieldObject); - } else if (fieldObject) { - // try to put.. - putForm.call(this, fieldObject); - } - fieldObject.page = scope.internal.getCurrentPageInfo().pageNumber; - return this; - }; - - /** - * Button FT = Btn - */ - jsPDFAPI.addButton = function (opts) { - initializeAcroForm.call(this); - var options = opts || new AcroFormField(); - - options.FT = '/Btn'; - options.Ff = calculateFlagsOnOptions(options.Ff, opts, scope.internal.getPDFVersion()); - - putForm.call(this, options); - }; - - jsPDFAPI.addTextField = function (opts) { - initializeAcroForm.call(this); - var options = opts || new AcroFormField(); - - options.FT = '/Tx'; - - options.Ff = calculateFlagsOnOptions(options.Ff, opts, scope.internal.getPDFVersion()); - - // Add field - putForm.call(this, options); - }; - - jsPDFAPI.addChoiceField = function (opts) { - initializeAcroForm.call(this); - var options = opts || new AcroFormField(); - - options.FT = '/Ch'; - - options.Ff = calculateFlagsOnOptions(options.Ff, opts, scope.internal.getPDFVersion()); - // options.hasAnnotation = true; - - // Add field - putForm.call(this, options); - }; - - if ((typeof globalObj === 'undefined' ? 'undefined' : _typeof(globalObj)) == "object") { - globalObj["ChoiceField"] = AcroFormChoiceField; - globalObj["ListBox"] = AcroFormListBox; - globalObj["ComboBox"] = AcroFormComboBox; - globalObj["EditBox"] = AcroFormEditBox; - globalObj["Button"] = AcroFormButton; - globalObj["PushButton"] = AcroFormPushButton; - globalObj["RadioButton"] = AcroFormRadioButton; - globalObj["CheckBox"] = AcroFormCheckBox; - globalObj["TextField"] = AcroFormTextField; - globalObj["PasswordField"] = AcroFormPasswordField; - - // backwardsCompatibility - globalObj["AcroForm"] = { Appearance: AcroFormAppearance }; - } - - 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.AcroForm = { - ChoiceField: AcroFormChoiceField, - ListBox: AcroFormListBox, - ComboBox: AcroFormComboBox, - EditBox: AcroFormEditBox, - Button: AcroFormButton, - PushButton: AcroFormPushButton, - RadioButton: AcroFormRadioButton, - CheckBox: AcroFormCheckBox, - TextField: AcroFormTextField, - PasswordField: AcroFormPasswordField - }; - })(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 === 'UNKOWN' && 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('<</Type /XObject'); - out('/Subtype /Image'); - out('/Width ' + img['w']); - out('/Height ' + img['h']); - if (img['cs'] === this.color_spaces.INDEXED) { - out('/ColorSpace [/Indexed /DeviceRGB ' - // if an indexed png defines more than one colour with transparency, we've created a smask - + (img['pal'].length / 3 - 1) + ' ' + ('smask' in img ? objectNumber + 2 : objectNumber + 1) + ' 0 R]'); - } else { - out('/ColorSpace /' + img['cs']); - if (img['cs'] === this.color_spaces.DEVICE_CMYK) { - out('/Decode [1 0 1 0 1 0 1 0]'); - } - } - out('/BitsPerComponent ' + img['bpc']); - if ('f' in img) { - out('/Filter /' + img['f']); - } - if ('dp' in img) { - out('/DecodeParms <<' + img['dp'] + '>>'); - } - if ('trns' in img && img['trns'].constructor == Array) { - var trns = '', - i = 0, - len = img['trns'].length; - for (; i < len; i++) { - trns += img['trns'][i] + ' ' + img['trns'][i] + ' '; - }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:[<MIME-type>][;base64],<data>' - * @returns an Array containing the following - * [0] the complete data URI - * [1] <MIME-type> - * [2] format - the second part of the mime-type i.e 'png' in 'image/png' - * [4] <data> - */ - 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. - * <br /> - * This plugin current supports <br /> - * <li> Goto Page (set pageNumber and top in options) - * <li> Goto Name (set name and top in options) - * <li> Goto URL (set url in options) - * <p> - * 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. - * </p> - * <p> - * Links, Text, Popup, and FreeText are supported. - * </p> - * <p> - * Options In PDF spec Not Implemented Yet - * <li> link border - * <li> named target - * <li> page coordinates - * <li> destination page scaling and layout - * <li> actions other than URL and GotoPage - * <li> background / hover actions - * </p> - */ - - /* - 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 <em>pageNumber</em>. - */ - 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 = '<</Type /Annot /Subtype /' + 'Text' + ' ' + rect + '/Contents (' + anno.contents + ')'; - line += ' /Popup ' + objPopup.objId + " 0 R"; - line += ' /P ' + pageInfo.objId + " 0 R"; - line += ' /T (' + title + ') >>'; - 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 = '<</Type /Annot /Subtype /' + 'Popup' + ' ' + rect + ' /Parent ' + parent; - if (anno.open) { - line += ' /Open true'; - } - 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 = '<</Type /Annot /Subtype /' + 'FreeText' + ' ' + rect + '/Contents (' + anno.contents + ')'; - line += ' /DS(font: Helvetica,sans-serif 12.0pt; text-align:left; color:#' + color + ')'; - line += ' /Border [0 0 0]'; - 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 = '<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /A <</S /URI /URI (' + anno.options.url + ') >>'; - } else if (anno.options.pageNumber) { - // first page is 0 - var info = this.internal.getPageInfo(anno.options.pageNumber); - line = '<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /Dest [' + info.objId + " 0 R"; - anno.options.magFactor = anno.options.magFactor || "XYZ"; - switch (anno.options.magFactor) { - case 'Fit': - line += ' /Fit]'; - break; - case 'FitH': - //anno.options.top = anno.options.top || f2(pageHeight * k); - line += ' /FitH ' + anno.options.top + ']'; - break; - case 'FitV': - anno.options.left = anno.options.left || 0; - line += ' /FitV ' + anno.options.left + ']'; - break; - case 'XYZ': - default: - var top = f2((pageHeight - anno.options.top) * k); // || f2(pageHeight * k); - anno.options.left = anno.options.left || 0; - // 0 or null zoom will not change zoom factor - if (typeof anno.options.zoom === 'undefined') { - anno.options.zoom = 0; - } - line += ' /XYZ ' + anno.options.left + ' ' + top + ' ' + anno.options.zoom + ']'; - break; - } - } - if (line != '') { - 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 - * <li> pageNumber or url [required] - * <p>If pageNumber is specified, top and zoom may also be specified</p> - */ - 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. <br /> - * 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('<</Type /ExtGState'); - obj2.push('/SMask <</S /Alpha /G ' + obj.objId + ' 0 R>>'); // /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('<</Type /ExtGState'); - //objOpac.push(this.ctx.globalAlpha + " CA"); // Stroke - //objOpac.push(this.ctx.globalAlpha + " ca"); // Not Stroke - objOpac.push('/CA ' + alpha); // Stroke - objOpac.push('/ca ' + alpha); // Not Stroke - 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('<</Type /ExtGState'); - obj2.push('/SMask <</S /Alpha /G ' + obj.objId + ' 0 R>>'); // /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 <footer> element - var footer = elem.getElementsByTagName("footer"); - if (footer.length > 0) { - - footer = footer[0]; - - //bad hack to get height of footer - //creat dummy out and check new y after fake rendering - var oldOut = renderer.pdf.internal.write; - var oldY = renderer.y; - renderer.pdf.internal.write = function () {}; - _DrillForContent(footer, renderer, elementHandlers); - var footerHeight = Math.ceil(renderer.y - oldY) + 5; - renderer.y = oldY; - renderer.pdf.internal.write = oldOut; - - //add 20% to prevent overlapping - renderer.pdf.margins_doc.bottom += footerHeight; - - //Create function render header on every page - var renderFooter = function renderFooter(pageInfo) { - var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1; - //set current y position to old margin - var oldPosition = renderer.y; - //render all child nodes of the header element - renderer.y = renderer.pdf.internal.pageSize.getHeight() - renderer.pdf.margins_doc.bottom; - renderer.pdf.margins_doc.bottom -= footerHeight; - - //check if we have to add page numbers - var spans = footer.getElementsByTagName('span'); - for (var i = 0; i < spans.length; ++i) { - //if we find some span element with class pageCounter, set the page - if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) { - spans[i].innerHTML = pageNumber; - } - //if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages - if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) { - spans[i].innerHTML = '###jsPDFVarTotalPages###'; - } - } - - //render footer content - _DrillForContent(footer, renderer, elementHandlers); - //set bottom margin to previous height including the footer height - renderer.pdf.margins_doc.bottom += footerHeight; - //important for other plugins (e.g. table) to start rendering at correct position after header - renderer.y = oldPosition; - }; - - //check if footer contains totalPages which should be replace at the disoposal of the document - var spans = footer.getElementsByTagName('span'); - for (var i = 0; i < spans.length; ++i) { - if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) { - renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true); - } - } - - //register event to render footer on every new page - renderer.pdf.internal.events.subscribe('addPage', renderFooter, false); - //render footer on first page - renderFooter(); - - //prevent footer rendering - SkipNode['FOOTER'] = 1; - } - }; - process = function process(pdf, element, x, y, settings, callback) { - if (!element) return false; - if (typeof element !== "string" && !element.parentNode) element = '' + element.innerHTML; - if (typeof element === "string") { - element = function (element) { - var $frame, $hiddendiv, framename, visuallyhidden; - framename = "jsPDFhtmlText" + Date.now().toString() + (Math.random() * 1000).toFixed(0); - visuallyhidden = "position: absolute !important;" + "clip: rect(1px 1px 1px 1px); /* IE6, IE7 */" + "clip: rect(1px, 1px, 1px, 1px);" + "padding:0 !important;" + "border:0 !important;" + "height: 1px !important;" + "width: 1px !important; " + "top:auto;" + "left:-100px;" + "overflow: hidden;"; - $hiddendiv = document.createElement('div'); - $hiddendiv.style.cssText = visuallyhidden; - $hiddendiv.innerHTML = "<iframe style=\"height:1px;width:1px\" name=\"" + framename + "\" />"; - document.body.appendChild($hiddendiv); - $frame = window.frames[framename]; - $frame.document.open(); - $frame.document.writeln(element); - $frame.document.close(); - return $frame.document.body; - }(element.replace(/<\/?script[^>]*?>/gi, '')); - } - var r = new Renderer(pdf, x, y, settings), - out; - - // 1. load images - // 2. prepare optional footer elements - // 3. render content - loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) { - checkForFooter(element, r, settings.elementHandlers); - _DrillForContent(element, r, settings.elementHandlers); - //send event dispose for final taks (e.g. footer totalpage replacement) - r.pdf.internal.events.publish('htmlRenderingFinished'); - out = r.dispose(); - if (typeof callback === 'function') callback(out);else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!'); - }); - return out || { x: r.x, y: r.y }; - }; - Renderer.prototype.init = function () { - this.paragraph = { - text: [], - style: [] - }; - return this.pdf.internal.write("q"); - }; - Renderer.prototype.dispose = function () { - this.pdf.internal.write("Q"); - return { - x: this.x, - y: this.y, - ready: true - }; - }; - - //Checks if we have to execute some watcher functions - //e.g. to end text floating around an image - Renderer.prototype.executeWatchFunctions = function (el) { - var ret = false; - var narray = []; - if (this.watchFunctions.length > 0) { - for (var i = 0; i < this.watchFunctions.length; ++i) { - if (this.watchFunctions[i](el) === true) { - ret = true; - } else { - narray.push(this.watchFunctions[i]); - } - } - this.watchFunctions = narray; - } - return ret; - }; - - Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) { - var currentLineLength, defaultFontSize, ff, fontMetrics, fontMetricsCache, fragment, fragmentChopped, fragmentLength, fragmentSpecificMetrics, fs, k, line, lines, maxLineLength, style; - defaultFontSize = 12; - k = this.pdf.internal.scaleFactor; - fontMetricsCache = {}; - ff = void 0; - fs = void 0; - fontMetrics = void 0; - fragment = void 0; - style = void 0; - fragmentSpecificMetrics = void 0; - fragmentLength = void 0; - fragmentChopped = void 0; - line = []; - lines = [line]; - currentLineLength = 0; - maxLineLength = this.settings.width; - while (fragments.length) { - fragment = fragments.shift(); - style = styles.shift(); - if (fragment) { - ff = style["font-family"]; - fs = style["font-style"]; - fontMetrics = fontMetricsCache[ff + fs]; - if (!fontMetrics) { - fontMetrics = this.pdf.internal.getFont(ff, fs).metadata.Unicode; - fontMetricsCache[ff + fs] = fontMetrics; - } - fragmentSpecificMetrics = { - widths: fontMetrics.widths, - kerning: fontMetrics.kerning, - fontSize: style["font-size"] * defaultFontSize, - textIndent: currentLineLength - }; - fragmentLength = this.pdf.getStringUnitWidth(fragment, fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; - if (fragment == "\u2028") { - line = []; - lines.push(line); - } else if (currentLineLength + fragmentLength > maxLineLength) { - fragmentChopped = this.pdf.splitTextToSize(fragment, maxLineLength, fragmentSpecificMetrics); - line.push([fragmentChopped.shift(), style]); - while (fragmentChopped.length) { - line = [[fragmentChopped.shift(), style]]; - lines.push(line); - } - currentLineLength = this.pdf.getStringUnitWidth(line[0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; - } else { - line.push([fragment, style]); - currentLineLength += fragmentLength; - } - } - } - - //if text alignment was set, set margin/indent of each line - if (style['text-align'] !== undefined && (style['text-align'] === 'center' || style['text-align'] === 'right' || style['text-align'] === 'justify')) { - for (var i = 0; i < lines.length; ++i) { - var length = this.pdf.getStringUnitWidth(lines[i][0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; - //if there is more than on line we have to clone the style object as all lines hold a reference on this object - if (i > 0) { - lines[i][0][1] = clone(lines[i][0][1]); - } - var space = maxLineLength - length; - - if (style['text-align'] === 'right') { - lines[i][0][1]['margin-left'] = space; - //if alignment is not right, it has to be center so split the space to the left and the right - } else if (style['text-align'] === 'center') { - lines[i][0][1]['margin-left'] = space / 2; - //if justify was set, calculate the word spacing and define in by using the css property - } else if (style['text-align'] === 'justify') { - var countSpaces = lines[i][0][0].split(' ').length - 1; - lines[i][0][1]['word-spacing'] = space / countSpaces; - //ignore the last line in justify mode - if (i === lines.length - 1) { - lines[i][0][1]['word-spacing'] = 0; - } - } - } - } - - return lines; - }; - Renderer.prototype.RenderTextFragment = function (text, style) { - var defaultFontSize, font, maxLineHeight; - - maxLineHeight = 0; - defaultFontSize = 12; - - if (this.pdf.internal.pageSize.getHeight() - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) { - this.pdf.internal.write("ET", "Q"); - this.pdf.addPage(); - this.y = this.pdf.margins_doc.top; - this.pdf.internal.write("q", "BT", this.getPdfColor(style.color), this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); - //move cursor by one line on new page - maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]); - this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td"); - } - - font = this.pdf.internal.getFont(style["font-family"], style["font-style"]); - - // text color - var pdfTextColor = this.getPdfColor(style["color"]); - if (pdfTextColor !== this.lastTextColor) { - this.pdf.internal.write(pdfTextColor); - this.lastTextColor = pdfTextColor; - } - - //set the word spacing for e.g. justify style - if (style['word-spacing'] !== undefined && style['word-spacing'] > 0) { - this.pdf.internal.write(style['word-spacing'].toFixed(2), "Tw"); - } - - this.pdf.internal.write("/" + font.id, (defaultFontSize * style["font-size"]).toFixed(2), "Tf", "(" + this.pdf.internal.pdfEscape(text) + ") Tj"); - - //set the word spacing back to neutral => 0 - if (style['word-spacing'] !== undefined) { - this.pdf.internal.write(0, "Tw"); - } - }; - - // Accepts #FFFFFF, rgb(int,int,int), or CSS Color Name - Renderer.prototype.getPdfColor = function (style) { - var textColor; - var r, g, b; - - var rgbColor = new RGBColor(style); - var rx = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/; - var m = rx.exec(style); - if (m != null) { - r = parseInt(m[1]); - g = parseInt(m[2]); - b = parseInt(m[3]); - } else { - if (style.charAt(0) != '#') { - if (rgbColor.ok) { - style = rgbColor.toHex(); - } else { - style = '#000000'; - } - } - r = style.substring(1, 3); - r = parseInt(r, 16); - g = style.substring(3, 5); - g = parseInt(g, 16); - b = style.substring(5, 7); - b = parseInt(b, 16); - } - - if (typeof r === 'string' && /^#[0-9A-Fa-f]{6}$/.test(r)) { - var hex = parseInt(r.substr(1), 16); - r = hex >> 16 & 255; - g = hex >> 8 & 255; - b = hex & 255; - } - - var f3 = this.f3; - if (r === 0 && g === 0 && b === 0 || typeof g === 'undefined') { - textColor = f3(r / 255) + ' g'; - } else { - textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' '); - } - return textColor; - }; - - Renderer.prototype.f3 = function (number) { - return number.toFixed(3); // Ie, %.3f - }, Renderer.prototype.renderParagraph = function (cb) { - var blockstyle, defaultFontSize, fontToUnitRatio, fragments, i, l, line, lines, maxLineHeight, out, paragraphspacing_after, paragraphspacing_before, priorblockstyle, styles, fontSize; - fragments = PurgeWhiteSpace(this.paragraph.text); - styles = this.paragraph.style; - blockstyle = this.paragraph.blockstyle; - priorblockstyle = this.paragraph.priorblockstyle || {}; - this.paragraph = { - text: [], - style: [], - blockstyle: {}, - priorblockstyle: blockstyle - }; - if (!fragments.join("").trim()) { - return; - } - lines = this.splitFragmentsIntoLines(fragments, styles); - line = void 0; - maxLineHeight = void 0; - defaultFontSize = 12; - fontToUnitRatio = defaultFontSize / this.pdf.internal.scaleFactor; - this.priorMarginBottom = this.priorMarginBottom || 0; - paragraphspacing_before = (Math.max((blockstyle["margin-top"] || 0) - this.priorMarginBottom, 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio; - paragraphspacing_after = ((blockstyle["margin-bottom"] || 0) + (blockstyle["padding-bottom"] || 0)) * fontToUnitRatio; - this.priorMarginBottom = blockstyle["margin-bottom"] || 0; - - if (blockstyle['page-break-before'] === 'always') { - this.pdf.addPage(); - this.y = 0; - paragraphspacing_before = ((blockstyle["margin-top"] || 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio; - } - - out = this.pdf.internal.write; - i = void 0; - l = void 0; - this.y += paragraphspacing_before; - out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); - - //stores the current indent of cursor position - var currentIndent = 0; - - while (lines.length) { - line = lines.shift(); - maxLineHeight = 0; - i = 0; - l = line.length; - while (i !== l) { - if (line[i][0].trim()) { - maxLineHeight = Math.max(maxLineHeight, line[i][1]["line-height"], line[i][1]["font-size"]); - fontSize = line[i][1]["font-size"] * 7; - } - i++; - } - //if we have to move the cursor to adapt the indent - var indentMove = 0; - var wantedIndent = 0; - //if a margin was added (by e.g. a text-alignment), move the cursor - if (line[0][1]["margin-left"] !== undefined && line[0][1]["margin-left"] > 0) { - wantedIndent = this.pdf.internal.getCoordinateString(line[0][1]["margin-left"]); - indentMove = wantedIndent - currentIndent; - currentIndent = wantedIndent; - } - var indentMore = Math.max(blockstyle["margin-left"] || 0, 0) * fontToUnitRatio; - //move the cursor - out(indentMove + indentMore, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td"); - i = 0; - l = line.length; - while (i !== l) { - if (line[i][0]) { - this.RenderTextFragment(line[i][0], line[i][1]); - } - i++; - } - this.y += maxLineHeight * fontToUnitRatio; - - //if some watcher function was executed successful, so e.g. margin and widths were changed, - //reset line drawing and calculate position and lines again - //e.g. to stop text floating around an image - if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) { - var localFragments = []; - var localStyles = []; - //create fragment array of - lines.forEach(function (localLine) { - var i = 0; - var l = localLine.length; - while (i !== l) { - if (localLine[i][0]) { - localFragments.push(localLine[i][0] + ' '); - localStyles.push(localLine[i][1]); - } - ++i; - } - }); - //split lines again due to possible coordinate changes - lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles); - //reposition the current cursor - out("ET", "Q"); - out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); - } - } - if (cb && typeof cb === "function") { - cb.call(this, this.x - 9, this.y - fontSize / 2); - } - out("ET", "Q"); - return this.y += paragraphspacing_after; - }; - Renderer.prototype.setBlockBoundary = function (cb) { - return this.renderParagraph(cb); - }; - Renderer.prototype.setBlockStyle = function (css) { - return this.paragraph.blockstyle = css; - }; - Renderer.prototype.addText = function (text, css) { - this.paragraph.text.push(text); - return this.paragraph.style.push(css); - }; - FontNameDB = { - helvetica: "helvetica", - "sans-serif": "helvetica", - "times new roman": "times", - serif: "times", - times: "times", - monospace: "courier", - courier: "courier" - }; - FontWeightMap = { - 100: "normal", - 200: "normal", - 300: "normal", - 400: "normal", - 500: "bold", - 600: "bold", - 700: "bold", - 800: "bold", - 900: "bold", - normal: "normal", - bold: "bold", - bolder: "bold", - lighter: "normal" - }; - FontStyleMap = { - normal: "normal", - italic: "italic", - oblique: "italic" - }; - TextAlignMap = { - left: "left", - right: "right", - center: "center", - justify: "justify" - }; - FloatMap = { - none: 'none', - right: 'right', - left: 'left' - }; - ClearMap = { - none: 'none', - both: 'both' - }; - UnitedNumberMap = { - normal: 1 - }; - /** - * Converts HTML-formatted text into formatted PDF text. - * - * Notes: - * 2012-07-18 - * Plugin relies on having browser, DOM around. The HTML is pushed into dom and traversed. - * Plugin relies on jQuery for CSS extraction. - * Targeting HTML output from Markdown templating, which is a very simple - * markup - div, span, em, strong, p. No br-based paragraph separation supported explicitly (but still may work.) - * Images, tables are NOT supported. - * - * @public - * @function - * @param HTML {String or DOM Element} HTML-formatted text, or pointer to DOM element that is to be rendered into PDF. - * @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 settings {Object} Additional / optional variables controlling parsing, rendering. - * @returns {Object} jsPDF instance - */ - jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins) { - - this.margins_doc = margins || { - top: 0, - bottom: 0 - }; - if (!settings) settings = {}; - if (!settings.elementHandlers) settings.elementHandlers = {}; - - return process(this, HTML, isNaN(x) ? 4 : x, isNaN(y) ? 4 : y, settings, callback); - }; - })(jsPDF.API); - - /** ==================================================================== - * jsPDF JavaScript plugin - * Copyright (c) 2013 Youssef Beddad, youssef.beddad@gmail.com - * - * - * ==================================================================== - */ - - /*global jsPDF */ - - (function (jsPDFAPI) { - - var jsNamesObj, jsJsObj, text; - jsPDFAPI.addJS = function (txt) { - text = txt; - this.internal.events.subscribe('postPutResources', function (txt) { - jsNamesObj = this.internal.newObject(); - this.internal.out('<<'); - this.internal.out('/Names [(EmbeddedJS) ' + (jsNamesObj + 1) + ' 0 R]'); - this.internal.out('>>'); - this.internal.out('endobj'); - - jsJsObj = this.internal.newObject(); - this.internal.out('<<'); - this.internal.out('/S /JavaScript'); - this.internal.out('/JS (' + text + ')'); - this.internal.out('>>'); - this.internal.out('endobj'); - }); - this.internal.events.subscribe('putCatalog', function () { - if (jsNamesObj !== undefined && jsJsObj !== undefined) { - this.internal.out('/Names <</JavaScript ' + jsNamesObj + ' 0 R>>'); - } - }); - return this; - }; - })(jsPDF.API); - - /** - * jsPDF Outline PlugIn - * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - (function (jsPDFAPI) { - - jsPDFAPI.events.push(['postPutResources', function () { - var pdf = this; - var rx = /^(\d+) 0 obj$/; - - // Write action goto objects for each page - // this.outline.destsGoto = []; - // for (var i = 0; i < totalPages; i++) { - // var id = pdf.internal.newObject(); - // this.outline.destsGoto.push(id); - // pdf.internal.write("<</D[" + (i * 2 + 3) + " 0 R /XYZ null - // null null]/S/GoTo>> endobj"); - // } - // - // for (var i = 0; i < dests.length; i++) { - // pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 - // R"); - // } - // - if (this.outline.root.children.length > 0) { - var lines = pdf.outline.render().split(/\r\n/); - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - var m = rx.exec(line); - if (m != null) { - var oid = m[1]; - pdf.internal.newObjectDeferredBegin(oid); - } - pdf.internal.write(line); - } - } - - // This code will write named destination for each page reference - // (page_1, etc) - if (this.outline.createNamedDestinations) { - var totalPages = this.internal.pages.length; - // WARNING: this assumes jsPDF starts on page 3 and pageIDs - // follow 5, 7, 9, etc - // Write destination objects for each page - var dests = []; - for (var i = 0; i < totalPages; i++) { - var id = pdf.internal.newObject(); - dests.push(id); - var info = pdf.internal.getPageInfo(i + 1); - pdf.internal.write("<< /D[" + info.objId + " 0 R /XYZ null null null]>> endobj"); - } - - // assign a name for each destination - var names2Oid = pdf.internal.newObject(); - pdf.internal.write('<< /Names [ '); - for (var i = 0; i < dests.length; i++) { - pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 R"); - } - pdf.internal.write(' ] >>', 'endobj'); - - // var kids = pdf.internal.newObject(); - // pdf.internal.write('<< /Kids [ ' + names2Oid + ' 0 R'); - // pdf.internal.write(' ] >>', 'endobj'); - - var namesOid = pdf.internal.newObject(); - pdf.internal.write('<< /Dests ' + names2Oid + " 0 R"); - pdf.internal.write('>>', 'endobj'); - } - }]); - - jsPDFAPI.events.push(['putCatalog', function () { - var pdf = this; - if (pdf.outline.root.children.length > 0) { - pdf.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); - if (this.outline.createNamedDestinations) { - pdf.internal.write("/Names " + namesOid + " 0 R"); - } - // Open with Bookmarks showing - // pdf.internal.write("/PageMode /UseOutlines"); - } - }]); - - jsPDFAPI.events.push(['initialized', function () { - var pdf = this; - - pdf.outline = { - createNamedDestinations: false, - root: { - children: [] - } - }; - - /** - * Options: pageNumber - */ - pdf.outline.add = function (parent, title, options) { - var item = { - title: title, - options: options, - children: [] - }; - if (parent == null) { - parent = this.root; - } - parent.children.push(item); - return item; - }; - - pdf.outline.render = function () { - this.ctx = {}; - this.ctx.val = ''; - this.ctx.pdf = pdf; - - this.genIds_r(this.root); - this.renderRoot(this.root); - this.renderItems(this.root); - - return this.ctx.val; - }; - - pdf.outline.genIds_r = function (node) { - node.id = pdf.internal.newObjectDeferred(); - for (var i = 0; i < node.children.length; i++) { - this.genIds_r(node.children[i]); - } - }; - - pdf.outline.renderRoot = function (node) { - this.objStart(node); - this.line('/Type /Outlines'); - if (node.children.length > 0) { - this.line('/First ' + this.makeRef(node.children[0])); - this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); - } - this.line('/Count ' + this.count_r({ - count: 0 - }, node)); - this.objEnd(); - }; - - pdf.outline.renderItems = function (node) { - for (var i = 0; i < node.children.length; i++) { - var item = node.children[i]; - this.objStart(item); - - this.line('/Title ' + this.makeString(item.title)); - - this.line('/Parent ' + this.makeRef(node)); - if (i > 0) { - this.line('/Prev ' + this.makeRef(node.children[i - 1])); - } - if (i < node.children.length - 1) { - this.line('/Next ' + this.makeRef(node.children[i + 1])); - } - if (item.children.length > 0) { - this.line('/First ' + this.makeRef(item.children[0])); - this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); - } - - var count = this.count = this.count_r({ - count: 0 - }, item); - if (count > 0) { - this.line('/Count ' + count); - } - - if (item.options) { - if (item.options.pageNumber) { - // Explicit Destination - //WARNING this assumes page ids are 3,5,7, etc. - var info = pdf.internal.getPageInfo(item.options.pageNumber); - this.line('/Dest ' + '[' + info.objId + ' 0 R /XYZ 0 ' + this.ctx.pdf.internal.pageSize.getHeight() * this.ctx.pdf.internal.scaleFactor + ' 0]'); - // this line does not work on all clients (pageNumber instead of page ref) - //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.getHeight() + ' 0]'); - - // Named Destination - // this.line('/Dest (page_' + (item.options.pageNumber) + ')'); - - // Action Destination - // var id = pdf.internal.newObject(); - // pdf.internal.write('<</D[' + (item.options.pageNumber - 1) + ' /XYZ null null null]/S/GoTo>> endobj'); - // this.line('/A ' + id + ' 0 R' ); - } - } - this.objEnd(); - } - for (var i = 0; i < node.children.length; i++) { - var item = node.children[i]; - this.renderItems(item); - } - }; - - pdf.outline.line = function (text) { - this.ctx.val += text + '\r\n'; - }; - - pdf.outline.makeRef = function (node) { - return node.id + ' 0 R'; - }; - - pdf.outline.makeString = function (val) { - return '(' + pdf.internal.pdfEscape(val) + ')'; - }; - - pdf.outline.objStart = function (node) { - this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; - }; - - pdf.outline.objEnd = function (node) { - this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; - }; - - pdf.outline.count_r = function (ctx, node) { - for (var i = 0; i < node.children.length; i++) { - ctx.count++; - this.count_r(ctx, node.children[i]); - } - return ctx.count; - }; - }]); - - return this; - })(jsPDF.API); - - /**@preserve - * ==================================================================== - * jsPDF PNG PlugIn - * Copyright (c) 2014 James Robb, https://github.com/jamesbrobb - * - * - * ==================================================================== - */ - - (function (jsPDFAPI) { - - /* - * @see http://www.w3.org/TR/PNG-Chunks.html - * - Color Allowed Interpretation - Type Bit Depths - 0 1,2,4,8,16 Each pixel is a grayscale sample. - 2 8,16 Each pixel is an R,G,B triple. - 3 1,2,4,8 Each pixel is a palette index; - a PLTE chunk must appear. - 4 8,16 Each pixel is a grayscale sample, - followed by an alpha sample. - 6 8,16 Each pixel is an R,G,B triple, - followed by an alpha sample. - */ - - /* - * PNG filter method types - * - * @see http://www.w3.org/TR/PNG-Filters.html - * @see http://www.libpng.org/pub/png/book/chapter09.html - * - * This is what the value 'Predictor' in decode params relates to - * - * 15 is "optimal prediction", which means the prediction algorithm can change from line to line. - * In that case, you actually have to read the first byte off each line for the prediction algorthim (which should be 0-4, corresponding to PDF 10-14) and select the appropriate unprediction algorithm based on that byte. - * - 0 None - 1 Sub - 2 Up - 3 Average - 4 Paeth - */ - - var doesNotHavePngJS = function doesNotHavePngJS() { - return typeof PNG !== 'function' || typeof FlateStream !== 'function'; - }, - canCompress = function canCompress(value) { - return value !== jsPDFAPI.image_compression.NONE && hasCompressionJS(); - }, - hasCompressionJS = function hasCompressionJS() { - var inst = typeof Deflater === 'function'; - if (!inst) throw new Error("requires deflate.js for compression"); - return inst; - }, - compressBytes = function compressBytes(bytes, lineLength, colorsPerPixel, compression) { - - var level = 5, - filter_method = filterUp; - - switch (compression) { - - case jsPDFAPI.image_compression.FAST: - - level = 3; - filter_method = filterSub; - break; - - case jsPDFAPI.image_compression.MEDIUM: - - level = 6; - filter_method = filterAverage; - break; - - case jsPDFAPI.image_compression.SLOW: - - level = 9; - filter_method = filterPaeth; //uses to sum to choose best filter for each line - break; - } - - bytes = applyPngFilterMethod(bytes, lineLength, colorsPerPixel, filter_method); - - var header = new Uint8Array(createZlibHeader(level)); - var checksum = adler32(bytes); - - var deflate = new Deflater(level); - var a = deflate.append(bytes); - var cBytes = deflate.flush(); - - var len = header.length + a.length + cBytes.length; - - var cmpd = new Uint8Array(len + 4); - cmpd.set(header); - cmpd.set(a, header.length); - cmpd.set(cBytes, header.length + a.length); - - cmpd[len++] = checksum >>> 24 & 0xff; - cmpd[len++] = checksum >>> 16 & 0xff; - cmpd[len++] = checksum >>> 8 & 0xff; - cmpd[len++] = checksum & 0xff; - - return jsPDFAPI.arrayBufferToBinaryString(cmpd); - }, - createZlibHeader = function createZlibHeader(bytes, level) { - /* - * @see http://www.ietf.org/rfc/rfc1950.txt for zlib header - */ - var cm = 8; - var cinfo = Math.LOG2E * Math.log(0x8000) - 8; - var cmf = cinfo << 4 | cm; - - var hdr = cmf << 8; - var flevel = Math.min(3, (level - 1 & 0xff) >> 1); - - hdr |= flevel << 6; - hdr |= 0; //FDICT - hdr += 31 - hdr % 31; - - return [cmf, hdr & 0xff & 0xff]; - }, - adler32 = function adler32(array, param) { - var adler = 1; - var s1 = adler & 0xffff, - s2 = adler >>> 16 & 0xffff; - var len = array.length; - var tlen; - var i = 0; - - while (len > 0) { - tlen = len > param ? param : len; - len -= tlen; - do { - s1 += array[i++]; - s2 += s1; - } while (--tlen); - - s1 %= 65521; - s2 %= 65521; - } - - return (s2 << 16 | s1) >>> 0; - }, - applyPngFilterMethod = function applyPngFilterMethod(bytes, lineLength, colorsPerPixel, filter_method) { - var lines = bytes.length / lineLength, - result = new Uint8Array(bytes.length + lines), - filter_methods = getFilterMethods(), - i = 0, - line, - prevLine, - offset; - - for (; i < lines; i++) { - offset = i * lineLength; - line = bytes.subarray(offset, offset + lineLength); - - if (filter_method) { - result.set(filter_method(line, colorsPerPixel, prevLine), offset + i); - } else { - - var j = 0, - len = filter_methods.length, - results = []; - - for (; j < len; j++) { - results[j] = filter_methods[j](line, colorsPerPixel, prevLine); - }var ind = getIndexOfSmallestSum(results.concat()); - - result.set(results[ind], offset + i); - } - - prevLine = line; - } - - return result; - }, - filterNone = function filterNone(line, colorsPerPixel, prevLine) { - /*var result = new Uint8Array(line.length + 1); - result[0] = 0; - result.set(line, 1);*/ - - var result = Array.apply([], line); - result.unshift(0); - - return result; - }, - filterSub = function filterSub(line, colorsPerPixel, prevLine) { - var result = [], - i = 0, - len = line.length, - left; - - result[0] = 1; - - for (; i < len; i++) { - left = line[i - colorsPerPixel] || 0; - result[i + 1] = line[i] - left + 0x0100 & 0xff; - } - - return result; - }, - filterUp = function filterUp(line, colorsPerPixel, prevLine) { - var result = [], - i = 0, - len = line.length, - up; - - result[0] = 2; - - for (; i < len; i++) { - up = prevLine && prevLine[i] || 0; - result[i + 1] = line[i] - up + 0x0100 & 0xff; - } - - return result; - }, - filterAverage = function filterAverage(line, colorsPerPixel, prevLine) { - var result = [], - i = 0, - len = line.length, - left, - up; - - result[0] = 3; - - for (; i < len; i++) { - left = line[i - colorsPerPixel] || 0; - up = prevLine && prevLine[i] || 0; - result[i + 1] = line[i] + 0x0100 - (left + up >>> 1) & 0xff; - } - - return result; - }, - filterPaeth = function filterPaeth(line, colorsPerPixel, prevLine) { - var result = [], - i = 0, - len = line.length, - left, - up, - upLeft, - paeth; - - result[0] = 4; - - for (; i < len; i++) { - left = line[i - colorsPerPixel] || 0; - up = prevLine && prevLine[i] || 0; - upLeft = prevLine && prevLine[i - colorsPerPixel] || 0; - paeth = paethPredictor(left, up, upLeft); - result[i + 1] = line[i] - paeth + 0x0100 & 0xff; - } - - return result; - }, - paethPredictor = function paethPredictor(left, up, upLeft) { - - var p = left + up - upLeft, - pLeft = Math.abs(p - left), - pUp = Math.abs(p - up), - pUpLeft = Math.abs(p - upLeft); - - return pLeft <= pUp && pLeft <= pUpLeft ? left : pUp <= pUpLeft ? up : upLeft; - }, - getFilterMethods = function getFilterMethods() { - return [filterNone, filterSub, filterUp, filterAverage, filterPaeth]; - }, - getIndexOfSmallestSum = function getIndexOfSmallestSum(arrays) { - var i = 0, - len = arrays.length, - sum, - min, - ind; - - while (i < len) { - sum = absSum(arrays[i].slice(1)); - - if (sum < min || !min) { - min = sum; - ind = i; - } - - i++; - } - - return ind; - }, - absSum = function absSum(array) { - var i = 0, - len = array.length, - sum = 0; - - while (i < len) { - sum += Math.abs(array[i++]); - }return sum; - }, - getPredictorFromCompression = function getPredictorFromCompression(compression) { - var predictor; - switch (compression) { - case jsPDFAPI.image_compression.FAST: - predictor = 11; - break; - - case jsPDFAPI.image_compression.MEDIUM: - predictor = 13; - break; - - case jsPDFAPI.image_compression.SLOW: - predictor = 14; - break; - - default: - predictor = 12; - break; - } - return predictor; - }; - - jsPDFAPI.processPNG = function (imageData, imageIndex, alias, compression, dataAsBinaryString) { - - var colorSpace = this.color_spaces.DEVICE_RGB, - decode = this.decode.FLATE_DECODE, - bpc = 8, - img, - dp, - trns, - colors, - pal, - smask; - - /* if(this.isString(imageData)) { - }*/ - - if (this.isArrayBuffer(imageData)) imageData = new Uint8Array(imageData); - - if (this.isArrayBufferView(imageData)) { - - if (doesNotHavePngJS()) throw new Error("PNG support requires png.js and zlib.js"); - - img = new PNG(imageData); - imageData = img.imgData; - bpc = img.bits; - colorSpace = img.colorSpace; - colors = img.colors; - - //logImg(img); - - /* - * colorType 6 - Each pixel is an R,G,B triple, followed by an alpha sample. - * - * colorType 4 - Each pixel is a grayscale sample, followed by an alpha sample. - * - * Extract alpha to create two separate images, using the alpha as a sMask - */ - if ([4, 6].indexOf(img.colorType) !== -1) { - - /* - * processes 8 bit RGBA and grayscale + alpha images - */ - if (img.bits === 8) { - - var pixels = img.pixelBitlength == 32 ? new Uint32Array(img.decodePixels().buffer) : img.pixelBitlength == 16 ? new Uint16Array(img.decodePixels().buffer) : new Uint8Array(img.decodePixels().buffer), - len = pixels.length, - imgData = new Uint8Array(len * img.colors), - alphaData = new Uint8Array(len), - pDiff = img.pixelBitlength - img.bits, - i = 0, - n = 0, - pixel, - pbl; - - for (; i < len; i++) { - pixel = pixels[i]; - pbl = 0; - - while (pbl < pDiff) { - - imgData[n++] = pixel >>> pbl & 0xff; - pbl = pbl + img.bits; - } - - alphaData[i] = pixel >>> pbl & 0xff; - } - } - - /* - * processes 16 bit RGBA and grayscale + alpha images - */ - if (img.bits === 16) { - - var pixels = new Uint32Array(img.decodePixels().buffer), - len = pixels.length, - imgData = new Uint8Array(len * (32 / img.pixelBitlength) * img.colors), - alphaData = new Uint8Array(len * (32 / img.pixelBitlength)), - hasColors = img.colors > 1, - i = 0, - n = 0, - a = 0, - pixel; - - while (i < len) { - pixel = pixels[i++]; - - imgData[n++] = pixel >>> 0 & 0xFF; - - if (hasColors) { - imgData[n++] = pixel >>> 16 & 0xFF; - - pixel = pixels[i++]; - imgData[n++] = pixel >>> 0 & 0xFF; - } - - alphaData[a++] = pixel >>> 16 & 0xFF; - } - - bpc = 8; - } - - if (canCompress(compression)) { - - imageData = compressBytes(imgData, img.width * img.colors, img.colors, compression); - smask = compressBytes(alphaData, img.width, 1, compression); - } else { - - imageData = imgData; - smask = alphaData; - decode = null; - } - } - - /* - * Indexed png. Each pixel is a palette index. - */ - if (img.colorType === 3) { - - colorSpace = this.color_spaces.INDEXED; - pal = img.palette; - - if (img.transparency.indexed) { - - var trans = img.transparency.indexed; - - var total = 0, - i = 0, - len = trans.length; - - for (; i < len; ++i) { - total += trans[i]; - }total = total / 255; - - /* - * a single color is specified as 100% transparent (0), - * so we set trns to use a /Mask with that index - */ - if (total === len - 1 && trans.indexOf(0) !== -1) { - trns = [trans.indexOf(0)]; - - /* - * there's more than one colour within the palette that specifies - * a transparency value less than 255, so we unroll the pixels to create an image sMask - */ - } else if (total !== len) { - - var pixels = img.decodePixels(), - alphaData = new Uint8Array(pixels.length), - i = 0, - len = pixels.length; - - for (; i < len; i++) { - alphaData[i] = trans[pixels[i]]; - }smask = compressBytes(alphaData, img.width, 1); - } - } - } - - var predictor = getPredictorFromCompression(compression); - - if (decode === this.decode.FLATE_DECODE) dp = '/Predictor ' + predictor + ' /Colors ' + colors + ' /BitsPerComponent ' + bpc + ' /Columns ' + img.width;else - //remove 'Predictor' as it applies to the type of png filter applied to its IDAT - we only apply with compression - dp = '/Colors ' + colors + ' /BitsPerComponent ' + bpc + ' /Columns ' + img.width; - - if (this.isArrayBuffer(imageData) || this.isArrayBufferView(imageData)) imageData = this.arrayBufferToBinaryString(imageData); - - if (smask && this.isArrayBuffer(smask) || this.isArrayBufferView(smask)) smask = this.arrayBufferToBinaryString(smask); - - return this.createImageInfo(imageData, img.width, img.height, colorSpace, bpc, decode, imageIndex, alias, dp, trns, pal, smask, predictor); - } - - throw new Error("Unsupported PNG image data, try using JPEG instead."); - }; - })(jsPDF.API); - - /** - * jsPDF gif Support PlugIn - * Copyright (c) 2017 Aras Abbasi - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - - (function (jsPDFAPI) { - - jsPDFAPI.processGIF89A = function (imageData, imageIndex, alias, compression, dataAsBinaryString) { - var reader = new GifReader(imageData); - var width = reader.width, - height = reader.height; - var qu = 100; - var pixels = []; - - reader.decodeAndBlitFrameRGBA(0, pixels); - var rawImageData = { - data: pixels, - width: width, - height: height - }; - - var encoder = new JPEGEncoder(qu); - var data = encoder.encode(rawImageData, qu); - return jsPDFAPI.processJPEG.call(this, data, imageIndex, alias, compression); - }; - - jsPDFAPI.processGIF87A = jsPDFAPI.processGIF89A; - })(jsPDF.API); - - /** - * jsPDF bmp Support PlugIn - * Copyright (c) 2018 Aras Abbasi - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - - (function (jsPDFAPI) { - - jsPDFAPI.processBMP = function (imageData, imageIndex, alias, compression, dataAsBinaryString) { - var reader = new BmpDecoder(imageData, false); - var width = reader.width, - height = reader.height; - var qu = 100; - var pixels = reader.getData(); - - var rawImageData = { - data: pixels, - width: width, - height: height - }; - - var encoder = new JPEGEncoder(qu); - var data = encoder.encode(rawImageData, qu); - return jsPDFAPI.processJPEG.call(this, data, imageIndex, alias, compression); - }; - })(jsPDF.API); - - /** - * jsPDF setLanguage Plugin - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - - (function (jsPDFAPI) { - - /** - * Add Language Tag to PDF - * - * @returns {jsPDF} - * @name setLanguage - * @example - * var doc = new jsPDF() - * doc.text(10, 10, 'This is a test') - * doc.setLanguage("en-US") - * doc.save('english.pdf') - */ - - jsPDFAPI.setLanguage = function (langCode) { - - var langCodes = { - "af": "Afrikaans", - "sq": "Albanian", - "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)", - "an": "Aragonese", - "hy": "Armenian", - "as": "Assamese", - "ast": "Asturian", - "az": "Azerbaijani", - "eu": "Basque", - "be": "Belarusian", - "bn": "Bengali", - "bs": "Bosnian", - "br": "Breton", - "bg": "Bulgarian", - "my": "Burmese", - "ca": "Catalan", - "ch": "Chamorro", - "ce": "Chechen", - "zh": "Chinese", - "zh-HK": "Chinese (Hong Kong)", - "zh-CN": "Chinese (PRC)", - "zh-SG": "Chinese (Singapore)", - "zh-TW": "Chinese (Taiwan)", - "cv": "Chuvash", - "co": "Corsican", - "cr": "Cree", - "hr": "Croatian", - "cs": "Czech", - "da": "Danish", - "nl": "Dutch (Standard)", - "nl-BE": "Dutch (Belgian)", - "en": "English", - "en-AU": "English (Australia)", - "en-BZ": "English (Belize)", - "en-CA": "English (Canada)", - "en-IE": "English (Ireland)", - "en-JM": "English (Jamaica)", - "en-NZ": "English (New Zealand)", - "en-PH": "English (Philippines)", - "en-ZA": "English (South Africa)", - "en-TT": "English (Trinidad & Tobago)", - "en-GB": "English (United Kingdom)", - "en-US": "English (United States)", - "en-ZW": "English (Zimbabwe)", - "eo": "Esperanto", - "et": "Estonian", - "fo": "Faeroese", - "fj": "Fijian", - "fi": "Finnish", - "fr": "French (Standard)", - "fr-BE": "French (Belgium)", - "fr-CA": "French (Canada)", - "fr-FR": "French (France)", - "fr-LU": "French (Luxembourg)", - "fr-MC": "French (Monaco)", - "fr-CH": "French (Switzerland)", - "fy": "Frisian", - "fur": "Friulian", - "gd": "Gaelic (Scots)", - "gd-IE": "Gaelic (Irish)", - "gl": "Galacian", - "ka": "Georgian", - "de": "German (Standard)", - "de-AT": "German (Austria)", - "de-DE": "German (Germany)", - "de-LI": "German (Liechtenstein)", - "de-LU": "German (Luxembourg)", - "de-CH": "German (Switzerland)", - "el": "Greek", - "gu": "Gujurati", - "ht": "Haitian", - "he": "Hebrew", - "hi": "Hindi", - "hu": "Hungarian", - "is": "Icelandic", - "id": "Indonesian", - "iu": "Inuktitut", - "ga": "Irish", - "it": "Italian (Standard)", - "it-CH": "Italian (Switzerland)", - "ja": "Japanese", - "kn": "Kannada", - "ks": "Kashmiri", - "kk": "Kazakh", - "km": "Khmer", - "ky": "Kirghiz", - "tlh": "Klingon", - "ko": "Korean", - "ko-KP": "Korean (North Korea)", - "ko-KR": "Korean (South Korea)", - "la": "Latin", - "lv": "Latvian", - "lt": "Lithuanian", - "lb": "Luxembourgish", - "mk": "FYRO Macedonian", - "ms": "Malay", - "ml": "Malayalam", - "mt": "Maltese", - "mi": "Maori", - "mr": "Marathi", - "mo": "Moldavian", - "nv": "Navajo", - "ng": "Ndonga", - "ne": "Nepali", - "no": "Norwegian", - "nb": "Norwegian (Bokmal)", - "nn": "Norwegian (Nynorsk)", - "oc": "Occitan", - "or": "Oriya", - "om": "Oromo", - "fa": "Persian", - "fa-IR": "Persian/Iran", - "pl": "Polish", - "pt": "Portuguese", - "pt-BR": "Portuguese (Brazil)", - "pa": "Punjabi", - "pa-IN": "Punjabi (India)", - "pa-PK": "Punjabi (Pakistan)", - "qu": "Quechua", - "rm": "Rhaeto-Romanic", - "ro": "Romanian", - "ro-MO": "Romanian (Moldavia)", - "ru": "Russian", - "ru-MO": "Russian (Moldavia)", - "sz": "Sami (Lappish)", - "sg": "Sango", - "sa": "Sanskrit", - "sc": "Sardinian", - "sd": "Sindhi", - "si": "Singhalese", - "sr": "Serbian", - "sk": "Slovak", - "sl": "Slovenian", - "so": "Somani", - "sb": "Sorbian", - "es": "Spanish", - "es-AR": "Spanish (Argentina)", - "es-BO": "Spanish (Bolivia)", - "es-CL": "Spanish (Chile)", - "es-CO": "Spanish (Colombia)", - "es-CR": "Spanish (Costa Rica)", - "es-DO": "Spanish (Dominican Republic)", - "es-EC": "Spanish (Ecuador)", - "es-SV": "Spanish (El Salvador)", - "es-GT": "Spanish (Guatemala)", - "es-HN": "Spanish (Honduras)", - "es-MX": "Spanish (Mexico)", - "es-NI": "Spanish (Nicaragua)", - "es-PA": "Spanish (Panama)", - "es-PY": "Spanish (Paraguay)", - "es-PE": "Spanish (Peru)", - "es-PR": "Spanish (Puerto Rico)", - "es-ES": "Spanish (Spain)", - "es-UY": "Spanish (Uruguay)", - "es-VE": "Spanish (Venezuela)", - "sx": "Sutu", - "sw": "Swahili", - "sv": "Swedish", - "sv-FI": "Swedish (Finland)", - "sv-SV": "Swedish (Sweden)", - "ta": "Tamil", - "tt": "Tatar", - "te": "Teluga", - "th": "Thai", - "tig": "Tigre", - "ts": "Tsonga", - "tn": "Tswana", - "tr": "Turkish", - "tk": "Turkmen", - "uk": "Ukrainian", - "hsb": "Upper Sorbian", - "ur": "Urdu", - "ve": "Venda", - "vi": "Vietnamese", - "vo": "Volapuk", - "wa": "Walloon", - "cy": "Welsh", - "xh": "Xhosa", - "ji": "Yiddish", - "zu": "Zulu" - }; - - if (this.internal.languageSettings === undefined) { - this.internal.languageSettings = {}; - this.internal.languageSettings.isSubscribed = false; - } - - if (langCodes[langCode] !== undefined) { - this.internal.languageSettings.languageCode = langCode; - if (this.internal.languageSettings.isSubscribed === false) { - this.internal.events.subscribe("putCatalog", function () { - this.internal.write("/Lang (" + this.internal.languageSettings.languageCode + ")"); - }); - this.internal.languageSettings.isSubscribed = true; - } - } - return this; - }; - })(jsPDF.API); - - /** @preserve - * jsPDF split_text_to_size plugin - MIT license. - * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com - * 2014 Diego Casorran, https://github.com/diegocr - */ - /** - * - * ==================================================================== - */ - - (function (API) { - /** - * Returns an array of length matching length of the 'word' string, with each - * cell occupied by the width of the char in that position. - * - * @function - * @param word {String} - * @param widths {Object} - * @param kerning {Object} - * @returns {Array} - */ - - var getCharWidthsArray = API.getCharWidthsArray = function (text, options) { - options = options || {}; - - var activeFont = options.font || this.internal.getFont(); - var fontSize = options.fontSize || this.internal.getFontSize(); - var charSpace = options.charSpace || this.internal.getCharSpace(); - - var widths = options.widths ? options.widths : activeFont.metadata.Unicode.widths; - var widthsFractionOf = widths.fof ? widths.fof : 1; - var kerning = options.kerning ? options.kerning : activeFont.metadata.Unicode.kerning; - var kerningFractionOf = kerning.fof ? kerning.fof : 1; - - var i; - var l; - var char_code; - var prior_char_code = 0; //for kerning - var default_char_width = widths[0] || widthsFractionOf; - var output = []; - - for (i = 0, l = text.length; i < l; i++) { - char_code = text.charCodeAt(i); - - if (typeof activeFont.metadata.widthOfString === "function") { - output.push((activeFont.metadata.widthOfGlyph(activeFont.metadata.characterToGlyph(char_code)) + charSpace * (1000 / fontSize) || 0) / 1000); - } else { - output.push((widths[char_code] || default_char_width) / widthsFractionOf + (kerning[char_code] && kerning[char_code][prior_char_code] || 0) / kerningFractionOf); - } - prior_char_code = char_code; - } - - return output; - }; - - /** - * Calculate the sum of a number-array - * - * @name getArraySum - * @public - * @function - * @param {array} array of numbers - * @returns {Number} - */ - var getArraySum = API.getArraySum = function (array) { - var i = array.length, - output = 0; - while (i) { - i--; - output += array[i]; - } - return output; - }; - /** - Returns a widths of string in a given font, if the font size is set as 1 point. - In other words, this is "proportional" value. For 1 unit of font size, the length - of the string will be that much. - Multiply by font size to get actual width in *points* - Then divide by 72 to get inches or divide by (72/25.6) to get 'mm' etc. - @public - @function - @param - @returns {Type} - */ - var getStringUnitWidth = API.getStringUnitWidth = function (text, options) { - options = options || {}; - - var fontSize = options.fontSize || this.internal.getFontSize(); - var font = options.font || this.internal.getFont(); - var charSpace = options.charSpace || this.internal.getCharSpace(); - var result = 0; - if (typeof font.metadata.widthOfString === "function") { - result = font.metadata.widthOfString(text, fontSize, charSpace) / fontSize; - } else { - result = getArraySum(getCharWidthsArray.apply(this, arguments)); - } - return result; - }; - - /** - returns array of lines - */ - var splitLongWord = function splitLongWord(word, widths_array, firstLineMaxLen, maxLen) { - var answer = []; - - // 1st, chop off the piece that can fit on the hanging line. - var i = 0, - l = word.length, - workingLen = 0; - while (i !== l && workingLen + widths_array[i] < firstLineMaxLen) { - workingLen += widths_array[i]; - i++; - } - // this is first line. - answer.push(word.slice(0, i)); - - // 2nd. Split the rest into maxLen pieces. - var startOfLine = i; - workingLen = 0; - while (i !== l) { - if (workingLen + widths_array[i] > maxLen) { - answer.push(word.slice(startOfLine, i)); - workingLen = 0; - startOfLine = i; - } - workingLen += widths_array[i]; - i++; - } - if (startOfLine !== i) { - answer.push(word.slice(startOfLine, i)); - } - - return answer; - }; - - // Note, all sizing inputs for this function must be in "font measurement units" - // By default, for PDF, it's "point". - var splitParagraphIntoLines = function splitParagraphIntoLines(text, maxlen, options) { - // at this time works only on Western scripts, ones with space char - // separating the words. Feel free to expand. - - if (!options) { - options = {}; - } - - var line = [], - lines = [line], - line_length = options.textIndent || 0, - separator_length = 0, - current_word_length = 0, - word, - widths_array, - words = text.split(' '), - spaceCharWidth = getCharWidthsArray.apply(this, [' ', options])[0], - i, - l, - tmp, - lineIndent; - - if (options.lineIndent === -1) { - lineIndent = words[0].length + 2; - } else { - lineIndent = options.lineIndent || 0; - } - if (lineIndent) { - var pad = Array(lineIndent).join(" "), - wrds = []; - words.map(function (wrd) { - wrd = wrd.split(/\s*\n/); - if (wrd.length > 1) { - wrds = wrds.concat(wrd.map(function (wrd, idx) { - return (idx && wrd.length ? "\n" : "") + wrd; - })); - } else { - wrds.push(wrd[0]); - } - }); - words = wrds; - lineIndent = getStringUnitWidth.apply(this, [pad, options]); - } - - for (i = 0, l = words.length; i < l; i++) { - var force = 0; - - word = words[i]; - if (lineIndent && word[0] == "\n") { - word = word.substr(1); - force = 1; - } - widths_array = getCharWidthsArray.apply(this, [word, options]); - current_word_length = getArraySum(widths_array); - - if (line_length + separator_length + current_word_length > maxlen || force) { - if (current_word_length > maxlen) { - // this happens when you have space-less long URLs for example. - // we just chop these to size. We do NOT insert hiphens - tmp = splitLongWord.apply(this, [word, widths_array, maxlen - (line_length + separator_length), maxlen]); - // first line we add to existing line object - line.push(tmp.shift()); // it's ok to have extra space indicator there - // last line we make into new line object - line = [tmp.pop()]; - // lines in the middle we apped to lines object as whole lines - while (tmp.length) { - lines.push([tmp.shift()]); // single fragment occupies whole line - } - current_word_length = getArraySum(widths_array.slice(word.length - (line[0] ? line[0].length : 0))); - } else { - // just put it on a new line - line = [word]; - } - - // now we attach new line to lines - lines.push(line); - line_length = current_word_length + lineIndent; - separator_length = spaceCharWidth; - } else { - line.push(word); - - line_length += separator_length + current_word_length; - separator_length = spaceCharWidth; - } - } - - if (lineIndent) { - var postProcess = function postProcess(ln, idx) { - return (idx ? pad : '') + ln.join(" "); - }; - } else { - var postProcess = function postProcess(ln) { - return ln.join(" "); - }; - } - - return lines.map(postProcess); - }; - - /** - Splits a given string into an array of strings. Uses 'size' value - (in measurement units declared as default for the jsPDF instance) - and the font's "widths" and "Kerning" tables, where available, to - determine display length of a given string for a given font. - We use character's 100% of unit size (height) as width when Width - table or other default width is not available. - @public - @function - @param text {String} Unencoded, regular JavaScript (Unicode, UTF-16 / UCS-2) string. - @param size {Number} Nominal number, measured in units default to this instance of jsPDF. - @param options {Object} Optional flags needed for chopper to do the right thing. - @returns {Array} with strings chopped to size. - */ - API.splitTextToSize = function (text, maxlen, options) { - - options = options || {}; - - var fsize = options.fontSize || this.internal.getFontSize(), - newOptions = function (options) { - var widths = { - 0: 1 - }, - kerning = {}; - - if (!options.widths || !options.kerning) { - var f = this.internal.getFont(options.fontName, options.fontStyle), - encoding = 'Unicode'; - // NOT UTF8, NOT UTF16BE/LE, NOT UCS2BE/LE - // Actual JavaScript-native String's 16bit char codes used. - // no multi-byte logic here - - if (f.metadata[encoding]) { - return { - widths: f.metadata[encoding].widths || widths, - kerning: f.metadata[encoding].kerning || kerning - }; - } else { - return { - font: f.metadata, - fontSize: this.internal.getFontSize(), - charSpace: this.internal.getCharSpace() - }; - } - } else { - return { - widths: options.widths, - kerning: options.kerning - }; - } - - // then use default values - return { - widths: widths, - kerning: kerning - }; - }.call(this, options); - - // first we split on end-of-line chars - var paragraphs; - if (Array.isArray(text)) { - paragraphs = text; - } else { - paragraphs = text.split(/\r?\n/); - } - - // now we convert size (max length of line) into "font size units" - // at present time, the "font size unit" is always 'point' - // 'proportional' means, "in proportion to font size" - var fontUnit_maxLen = 1.0 * this.internal.scaleFactor * maxlen / fsize; - // at this time, fsize is always in "points" regardless of the default measurement unit of the doc. - // this may change in the future? - // until then, proportional_maxlen is likely to be in 'points' - - // If first line is to be indented (shorter or longer) than maxLen - // we indicate that by using CSS-style "text-indent" option. - // here it's in font units too (which is likely 'points') - // it can be negative (which makes the first line longer than maxLen) - newOptions.textIndent = options.textIndent ? options.textIndent * 1.0 * this.internal.scaleFactor / fsize : 0; - newOptions.lineIndent = options.lineIndent; - - var i, - l, - output = []; - for (i = 0, l = paragraphs.length; i < l; i++) { - output = output.concat(splitParagraphIntoLines.apply(this, [paragraphs[i], fontUnit_maxLen, newOptions])); - } - - return output; - }; - })(jsPDF.API); - - /** @preserve - jsPDF standard_fonts_metrics plugin - Copyright (c) 2012 Willow Systems Corporation, willow-systems.com - MIT license. - */ - (function (API) { - - /* - # reference (Python) versions of 'compress' and 'uncompress' - # only 'uncompress' function is featured lower as JavaScript - # if you want to unit test "roundtrip", just transcribe the reference - # 'compress' function from Python into JavaScript - - def compress(data): - - keys = '0123456789abcdef' - values = 'klmnopqrstuvwxyz' - mapping = dict(zip(keys, values)) - vals = [] - for key in data.keys(): - value = data[key] - try: - keystring = hex(key)[2:] - keystring = keystring[:-1] + mapping[keystring[-1:]] - except: - keystring = key.join(["'","'"]) - #print('Keystring is %s' % keystring) - - try: - if value < 0: - valuestring = hex(value)[3:] - numberprefix = '-' - else: - valuestring = hex(value)[2:] - numberprefix = '' - valuestring = numberprefix + valuestring[:-1] + mapping[valuestring[-1:]] - except: - if type(value) == dict: - valuestring = compress(value) - else: - raise Exception("Don't know what to do with value type %s" % type(value)) - - vals.append(keystring+valuestring) - - return '{' + ''.join(vals) + '}' - - def uncompress(data): - - decoded = '0123456789abcdef' - encoded = 'klmnopqrstuvwxyz' - mapping = dict(zip(encoded, decoded)) - - sign = +1 - stringmode = False - stringparts = [] - - output = {} - - activeobject = output - parentchain = [] - - keyparts = '' - valueparts = '' - - key = None - - ending = set(encoded) - - i = 1 - l = len(data) - 1 # stripping starting, ending {} - while i != l: # stripping {} - # -, {, }, ' are special. - - ch = data[i] - i += 1 - - if ch == "'": - if stringmode: - # end of string mode - stringmode = False - key = ''.join(stringparts) - else: - # start of string mode - stringmode = True - stringparts = [] - elif stringmode == True: - #print("Adding %s to stringpart" % ch) - stringparts.append(ch) - - elif ch == '{': - # start of object - parentchain.append( [activeobject, key] ) - activeobject = {} - key = None - #DEBUG = True - elif ch == '}': - # end of object - parent, key = parentchain.pop() - parent[key] = activeobject - key = None - activeobject = parent - #DEBUG = False - - elif ch == '-': - sign = -1 - else: - # must be number - if key == None: - #debug("In Key. It is '%s', ch is '%s'" % (keyparts, ch)) - if ch in ending: - #debug("End of key") - keyparts += mapping[ch] - key = int(keyparts, 16) * sign - sign = +1 - keyparts = '' - else: - keyparts += ch - else: - #debug("In value. It is '%s', ch is '%s'" % (valueparts, ch)) - if ch in ending: - #debug("End of value") - valueparts += mapping[ch] - activeobject[key] = int(valueparts, 16) * sign - sign = +1 - key = None - valueparts = '' - else: - valueparts += ch - - #debug(activeobject) - - return output - - */ - - /** - Uncompresses data compressed into custom, base16-like format. - @public - @function - @param - @returns {Type} - */ - - var uncompress = function uncompress(data) { - - var decoded = '0123456789abcdef', - encoded = 'klmnopqrstuvwxyz', - mapping = {}; - - for (var i = 0; i < encoded.length; i++) { - mapping[encoded[i]] = decoded[i]; - } - - var undef, - output = {}, - sign = 1, - stringparts // undef. will be [] in string mode - - , - activeobject = output, - parentchain = [], - parent_key_pair, - keyparts = '', - valueparts = '', - key // undef. will be Truthy when Key is resolved. - , - datalen = data.length - 1 // stripping ending } - , - ch; - - i = 1; // stripping starting { - - while (i != datalen) { - // - { } ' are special. - - ch = data[i]; - i += 1; - - if (ch == "'") { - if (stringparts) { - // end of string mode - key = stringparts.join(''); - stringparts = undef; - } else { - // start of string mode - stringparts = []; - } - } else if (stringparts) { - stringparts.push(ch); - } else if (ch == '{') { - // start of object - parentchain.push([activeobject, key]); - activeobject = {}; - key = undef; - } else if (ch == '}') { - // end of object - parent_key_pair = parentchain.pop(); - parent_key_pair[0][parent_key_pair[1]] = activeobject; - key = undef; - activeobject = parent_key_pair[0]; - } else if (ch == '-') { - sign = -1; - } else { - // must be number - if (key === undef) { - if (mapping.hasOwnProperty(ch)) { - keyparts += mapping[ch]; - key = parseInt(keyparts, 16) * sign; - sign = +1; - keyparts = ''; - } else { - keyparts += ch; - } - } else { - if (mapping.hasOwnProperty(ch)) { - valueparts += mapping[ch]; - activeobject[key] = parseInt(valueparts, 16) * sign; - sign = +1; - key = undef; - valueparts = ''; - } else { - valueparts += ch; - } - } - } - } // end while - - return output; - }; - - // encoding = 'Unicode' - // NOT UTF8, NOT UTF16BE/LE, NOT UCS2BE/LE. NO clever BOM behavior - // Actual 16bit char codes used. - // no multi-byte logic here - - // Unicode characters to WinAnsiEncoding: - // {402: 131, 8211: 150, 8212: 151, 8216: 145, 8217: 146, 8218: 130, 8220: 147, 8221: 148, 8222: 132, 8224: 134, 8225: 135, 8226: 149, 8230: 133, 8364: 128, 8240:137, 8249: 139, 8250: 155, 710: 136, 8482: 153, 338: 140, 339: 156, 732: 152, 352: 138, 353: 154, 376: 159, 381: 142, 382: 158} - // as you can see, all Unicode chars are outside of 0-255 range. No char code conflicts. - // this means that you can give Win cp1252 encoded strings to jsPDF for rendering directly - // as well as give strings with some (supported by these fonts) Unicode characters and - // these will be mapped to win cp1252 - // for example, you can send char code (cp1252) 0x80 or (unicode) 0x20AC, getting "Euro" glyph displayed in both cases. - - var encodingBlock = { - 'codePages': ['WinAnsiEncoding'], - 'WinAnsiEncoding': uncompress("{19m8n201n9q201o9r201s9l201t9m201u8m201w9n201x9o201y8o202k8q202l8r202m9p202q8p20aw8k203k8t203t8v203u9v2cq8s212m9t15m8w15n9w2dw9s16k8u16l9u17s9z17x8y17y9y}") - }, - encodings = { 'Unicode': { - 'Courier': encodingBlock, - 'Courier-Bold': encodingBlock, - 'Courier-BoldOblique': encodingBlock, - 'Courier-Oblique': encodingBlock, - 'Helvetica': encodingBlock, - 'Helvetica-Bold': encodingBlock, - 'Helvetica-BoldOblique': encodingBlock, - 'Helvetica-Oblique': encodingBlock, - 'Times-Roman': encodingBlock, - 'Times-Bold': encodingBlock, - 'Times-BoldItalic': encodingBlock, - 'Times-Italic': encodingBlock - // , 'Symbol' - // , 'ZapfDingbats' - } - /** - Resources: - Font metrics data is reprocessed derivative of contents of - "Font Metrics for PDF Core 14 Fonts" package, which exhibits the following copyright and license: - - Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. - - This file and the 14 PostScript(R) AFM files it accompanies may be used, - copied, and distributed for any purpose and without charge, with or without - modification, provided that all copyright notices are retained; that the AFM - files are not distributed without this file; that all modifications to this - file or any of the AFM files are prominently noted in the modified file(s); - and that this paragraph is not modified. Adobe Systems has no responsibility - or obligation to support the use of the AFM files. - - */ - }, - fontMetrics = { 'Unicode': { - // all sizing numbers are n/fontMetricsFractionOf = one font size unit - // this means that if fontMetricsFractionOf = 1000, and letter A's width is 476, it's - // width is 476/1000 or 47.6% of its height (regardless of font size) - // At this time this value applies to "widths" and "kerning" numbers. - - // char code 0 represents "default" (average) width - use it for chars missing in this table. - // key 'fof' represents the "fontMetricsFractionOf" value - - 'Courier-Oblique': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), - 'Times-BoldItalic': uncompress("{'widths'{k3o2q4ycx2r201n3m201o6o201s2l201t2l201u2l201w3m201x3m201y3m2k1t2l2r202m2n2n3m2o3m2p5n202q6o2r1w2s2l2t2l2u3m2v3t2w1t2x2l2y1t2z1w3k3m3l3m3m3m3n3m3o3m3p3m3q3m3r3m3s3m203t2l203u2l3v2l3w3t3x3t3y3t3z3m4k5n4l4m4m4m4n4m4o4s4p4m4q4m4r4s4s4y4t2r4u3m4v4m4w3x4x5t4y4s4z4s5k3x5l4s5m4m5n3r5o3x5p4s5q4m5r5t5s4m5t3x5u3x5v2l5w1w5x2l5y3t5z3m6k2l6l3m6m3m6n2w6o3m6p2w6q2l6r3m6s3r6t1w6u1w6v3m6w1w6x4y6y3r6z3m7k3m7l3m7m2r7n2r7o1w7p3r7q2w7r4m7s3m7t2w7u2r7v2n7w1q7x2n7y3t202l3mcl4mal2ram3man3mao3map3mar3mas2lat4uau1uav3maw3way4uaz2lbk2sbl3t'fof'6obo2lbp3tbq3mbr1tbs2lbu1ybv3mbz3mck4m202k3mcm4mcn4mco4mcp4mcq5ycr4mcs4mct4mcu4mcv4mcw2r2m3rcy2rcz2rdl4sdm4sdn4sdo4sdp4sdq4sds4sdt4sdu4sdv4sdw4sdz3mek3mel3mem3men3meo3mep3meq4ser2wes2wet2weu2wev2wew1wex1wey1wez1wfl3rfm3mfn3mfo3mfp3mfq3mfr3tfs3mft3rfu3rfv3rfw3rfz2w203k6o212m6o2dw2l2cq2l3t3m3u2l17s3x19m3m}'kerning'{cl{4qu5kt5qt5rs17ss5ts}201s{201ss}201t{cks4lscmscnscoscpscls2wu2yu201ts}201x{2wu2yu}2k{201ts}2w{4qx5kx5ou5qx5rs17su5tu}2x{17su5tu5ou}2y{4qx5kx5ou5qx5rs17ss5ts}'fof'-6ofn{17sw5tw5ou5qw5rs}7t{cksclscmscnscoscps4ls}3u{17su5tu5os5qs}3v{17su5tu5os5qs}7p{17su5tu}ck{4qu5kt5qt5rs17ss5ts}4l{4qu5kt5qt5rs17ss5ts}cm{4qu5kt5qt5rs17ss5ts}cn{4qu5kt5qt5rs17ss5ts}co{4qu5kt5qt5rs17ss5ts}cp{4qu5kt5qt5rs17ss5ts}6l{4qu5ou5qw5rt17su5tu}5q{ckuclucmucnucoucpu4lu}5r{ckuclucmucnucoucpu4lu}7q{cksclscmscnscoscps4ls}6p{4qu5ou5qw5rt17sw5tw}ek{4qu5ou5qw5rt17su5tu}el{4qu5ou5qw5rt17su5tu}em{4qu5ou5qw5rt17su5tu}en{4qu5ou5qw5rt17su5tu}eo{4qu5ou5qw5rt17su5tu}ep{4qu5ou5qw5rt17su5tu}es{17ss5ts5qs4qu}et{4qu5ou5qw5rt17sw5tw}eu{4qu5ou5qw5rt17ss5ts}ev{17ss5ts5qs4qu}6z{17sw5tw5ou5qw5rs}fm{17sw5tw5ou5qw5rs}7n{201ts}fo{17sw5tw5ou5qw5rs}fp{17sw5tw5ou5qw5rs}fq{17sw5tw5ou5qw5rs}7r{cksclscmscnscoscps4ls}fs{17sw5tw5ou5qw5rs}ft{17su5tu}fu{17su5tu}fv{17su5tu}fw{17su5tu}fz{cksclscmscnscoscps4ls}}}"), - 'Helvetica-Bold': uncompress("{'widths'{k3s2q4scx1w201n3r201o6o201s1w201t1w201u1w201w3m201x3m201y3m2k1w2l2l202m2n2n3r2o3r2p5t202q6o2r1s2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v2l3w3u3x3u3y3u3z3x4k6l4l4s4m4s4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3r4v4s4w3x4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v2l5w1w5x2l5y3u5z3r6k2l6l3r6m3x6n3r6o3x6p3r6q2l6r3x6s3x6t1w6u1w6v3r6w1w6x5t6y3x6z3x7k3x7l3x7m2r7n3r7o2l7p3x7q3r7r4y7s3r7t3r7u3m7v2r7w1w7x2r7y3u202l3rcl4sal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3xbq3rbr1wbs2lbu2obv3rbz3xck4s202k3rcm4scn4sco4scp4scq6ocr4scs4mct4mcu4mcv4mcw1w2m2zcy1wcz1wdl4sdm4ydn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3res3ret3reu3rev3rew1wex1wey1wez1wfl3xfm3xfn3xfo3xfp3xfq3xfr3ufs3xft3xfu3xfv3xfw3xfz3r203k6o212m6o2dw2l2cq2l3t3r3u2l17s4m19m3r}'kerning'{cl{4qs5ku5ot5qs17sv5tv}201t{2ww4wy2yw}201w{2ks}201x{2ww4wy2yw}2k{201ts201xs}2w{7qs4qu5kw5os5qw5rs17su5tu7tsfzs}2x{5ow5qs}2y{7qs4qu5kw5os5qw5rs17su5tu7tsfzs}'fof'-6o7p{17su5tu5ot}ck{4qs5ku5ot5qs17sv5tv}4l{4qs5ku5ot5qs17sv5tv}cm{4qs5ku5ot5qs17sv5tv}cn{4qs5ku5ot5qs17sv5tv}co{4qs5ku5ot5qs17sv5tv}cp{4qs5ku5ot5qs17sv5tv}6l{17st5tt5os}17s{2kwclvcmvcnvcovcpv4lv4wwckv}5o{2kucltcmtcntcotcpt4lt4wtckt}5q{2ksclscmscnscoscps4ls4wvcks}5r{2ks4ws}5t{2kwclvcmvcnvcovcpv4lv4wwckv}eo{17st5tt5os}fu{17su5tu5ot}6p{17ss5ts}ek{17st5tt5os}el{17st5tt5os}em{17st5tt5os}en{17st5tt5os}6o{201ts}ep{17st5tt5os}es{17ss5ts}et{17ss5ts}eu{17ss5ts}ev{17ss5ts}6z{17su5tu5os5qt}fm{17su5tu5os5qt}fn{17su5tu5os5qt}fo{17su5tu5os5qt}fp{17su5tu5os5qt}fq{17su5tu5os5qt}fs{17su5tu5os5qt}ft{17su5tu5ot}7m{5os}fv{17su5tu5ot}fw{17su5tu5ot}}}"), - 'Courier': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), - 'Courier-BoldOblique': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), - 'Times-Bold': uncompress("{'widths'{k3q2q5ncx2r201n3m201o6o201s2l201t2l201u2l201w3m201x3m201y3m2k1t2l2l202m2n2n3m2o3m2p6o202q6o2r1w2s2l2t2l2u3m2v3t2w1t2x2l2y1t2z1w3k3m3l3m3m3m3n3m3o3m3p3m3q3m3r3m3s3m203t2l203u2l3v2l3w3t3x3t3y3t3z3m4k5x4l4s4m4m4n4s4o4s4p4m4q3x4r4y4s4y4t2r4u3m4v4y4w4m4x5y4y4s4z4y5k3x5l4y5m4s5n3r5o4m5p4s5q4s5r6o5s4s5t4s5u4m5v2l5w1w5x2l5y3u5z3m6k2l6l3m6m3r6n2w6o3r6p2w6q2l6r3m6s3r6t1w6u2l6v3r6w1w6x5n6y3r6z3m7k3r7l3r7m2w7n2r7o2l7p3r7q3m7r4s7s3m7t3m7u2w7v2r7w1q7x2r7y3o202l3mcl4sal2lam3man3mao3map3mar3mas2lat4uau1yav3maw3tay4uaz2lbk2sbl3t'fof'6obo2lbp3rbr1tbs2lbu2lbv3mbz3mck4s202k3mcm4scn4sco4scp4scq6ocr4scs4mct4mcu4mcv4mcw2r2m3rcy2rcz2rdl4sdm4ydn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3rek3mel3mem3men3meo3mep3meq4ser2wes2wet2weu2wev2wew1wex1wey1wez1wfl3rfm3mfn3mfo3mfp3mfq3mfr3tfs3mft3rfu3rfv3rfw3rfz3m203k6o212m6o2dw2l2cq2l3t3m3u2l17s4s19m3m}'kerning'{cl{4qt5ks5ot5qy5rw17sv5tv}201t{cks4lscmscnscoscpscls4wv}2k{201ts}2w{4qu5ku7mu5os5qx5ru17su5tu}2x{17su5tu5ou5qs}2y{4qv5kv7mu5ot5qz5ru17su5tu}'fof'-6o7t{cksclscmscnscoscps4ls}3u{17su5tu5os5qu}3v{17su5tu5os5qu}fu{17su5tu5ou5qu}7p{17su5tu5ou5qu}ck{4qt5ks5ot5qy5rw17sv5tv}4l{4qt5ks5ot5qy5rw17sv5tv}cm{4qt5ks5ot5qy5rw17sv5tv}cn{4qt5ks5ot5qy5rw17sv5tv}co{4qt5ks5ot5qy5rw17sv5tv}cp{4qt5ks5ot5qy5rw17sv5tv}6l{17st5tt5ou5qu}17s{ckuclucmucnucoucpu4lu4wu}5o{ckuclucmucnucoucpu4lu4wu}5q{ckzclzcmzcnzcozcpz4lz4wu}5r{ckxclxcmxcnxcoxcpx4lx4wu}5t{ckuclucmucnucoucpu4lu4wu}7q{ckuclucmucnucoucpu4lu}6p{17sw5tw5ou5qu}ek{17st5tt5qu}el{17st5tt5ou5qu}em{17st5tt5qu}en{17st5tt5qu}eo{17st5tt5qu}ep{17st5tt5ou5qu}es{17ss5ts5qu}et{17sw5tw5ou5qu}eu{17sw5tw5ou5qu}ev{17ss5ts5qu}6z{17sw5tw5ou5qu5rs}fm{17sw5tw5ou5qu5rs}fn{17sw5tw5ou5qu5rs}fo{17sw5tw5ou5qu5rs}fp{17sw5tw5ou5qu5rs}fq{17sw5tw5ou5qu5rs}7r{cktcltcmtcntcotcpt4lt5os}fs{17sw5tw5ou5qu5rs}ft{17su5tu5ou5qu}7m{5os}fv{17su5tu5ou5qu}fw{17su5tu5ou5qu}fz{cksclscmscnscoscps4ls}}}"), - 'Symbol': uncompress("{'widths'{k3uaw4r19m3m2k1t2l2l202m2y2n3m2p5n202q6o3k3m2s2l2t2l2v3r2w1t3m3m2y1t2z1wbk2sbl3r'fof'6o3n3m3o3m3p3m3q3m3r3m3s3m3t3m3u1w3v1w3w3r3x3r3y3r3z2wbp3t3l3m5v2l5x2l5z3m2q4yfr3r7v3k7w1o7x3k}'kerning'{'fof'-6o}}"), - 'Helvetica': uncompress("{'widths'{k3p2q4mcx1w201n3r201o6o201s1q201t1q201u1q201w2l201x2l201y2l2k1w2l1w202m2n2n3r2o3r2p5t202q6o2r1n2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v1w3w3u3x3u3y3u3z3r4k6p4l4m4m4m4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3m4v4m4w3r4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v1w5w1w5x1w5y2z5z3r6k2l6l3r6m3r6n3m6o3r6p3r6q1w6r3r6s3r6t1q6u1q6v3m6w1q6x5n6y3r6z3r7k3r7l3r7m2l7n3m7o1w7p3r7q3m7r4s7s3m7t3m7u3m7v2l7w1u7x2l7y3u202l3rcl4mal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3rbr1wbs2lbu2obv3rbz3xck4m202k3rcm4mcn4mco4mcp4mcq6ocr4scs4mct4mcu4mcv4mcw1w2m2ncy1wcz1wdl4sdm4ydn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3mes3ret3reu3rev3rew1wex1wey1wez1wfl3rfm3rfn3rfo3rfp3rfq3rfr3ufs3xft3rfu3rfv3rfw3rfz3m203k6o212m6o2dw2l2cq2l3t3r3u1w17s4m19m3r}'kerning'{5q{4wv}cl{4qs5kw5ow5qs17sv5tv}201t{2wu4w1k2yu}201x{2wu4wy2yu}17s{2ktclucmucnu4otcpu4lu4wycoucku}2w{7qs4qz5k1m17sy5ow5qx5rsfsu5ty7tufzu}2x{17sy5ty5oy5qs}2y{7qs4qz5k1m17sy5ow5qx5rsfsu5ty7tufzu}'fof'-6o7p{17sv5tv5ow}ck{4qs5kw5ow5qs17sv5tv}4l{4qs5kw5ow5qs17sv5tv}cm{4qs5kw5ow5qs17sv5tv}cn{4qs5kw5ow5qs17sv5tv}co{4qs5kw5ow5qs17sv5tv}cp{4qs5kw5ow5qs17sv5tv}6l{17sy5ty5ow}do{17st5tt}4z{17st5tt}7s{fst}dm{17st5tt}dn{17st5tt}5o{ckwclwcmwcnwcowcpw4lw4wv}dp{17st5tt}dq{17st5tt}7t{5ow}ds{17st5tt}5t{2ktclucmucnu4otcpu4lu4wycoucku}fu{17sv5tv5ow}6p{17sy5ty5ow5qs}ek{17sy5ty5ow}el{17sy5ty5ow}em{17sy5ty5ow}en{5ty}eo{17sy5ty5ow}ep{17sy5ty5ow}es{17sy5ty5qs}et{17sy5ty5ow5qs}eu{17sy5ty5ow5qs}ev{17sy5ty5ow5qs}6z{17sy5ty5ow5qs}fm{17sy5ty5ow5qs}fn{17sy5ty5ow5qs}fo{17sy5ty5ow5qs}fp{17sy5ty5qs}fq{17sy5ty5ow5qs}7r{5ow}fs{17sy5ty5ow5qs}ft{17sv5tv5ow}7m{5ow}fv{17sv5tv5ow}fw{17sv5tv5ow}}}"), - 'Helvetica-BoldOblique': uncompress("{'widths'{k3s2q4scx1w201n3r201o6o201s1w201t1w201u1w201w3m201x3m201y3m2k1w2l2l202m2n2n3r2o3r2p5t202q6o2r1s2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v2l3w3u3x3u3y3u3z3x4k6l4l4s4m4s4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3r4v4s4w3x4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v2l5w1w5x2l5y3u5z3r6k2l6l3r6m3x6n3r6o3x6p3r6q2l6r3x6s3x6t1w6u1w6v3r6w1w6x5t6y3x6z3x7k3x7l3x7m2r7n3r7o2l7p3x7q3r7r4y7s3r7t3r7u3m7v2r7w1w7x2r7y3u202l3rcl4sal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3xbq3rbr1wbs2lbu2obv3rbz3xck4s202k3rcm4scn4sco4scp4scq6ocr4scs4mct4mcu4mcv4mcw1w2m2zcy1wcz1wdl4sdm4ydn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3res3ret3reu3rev3rew1wex1wey1wez1wfl3xfm3xfn3xfo3xfp3xfq3xfr3ufs3xft3xfu3xfv3xfw3xfz3r203k6o212m6o2dw2l2cq2l3t3r3u2l17s4m19m3r}'kerning'{cl{4qs5ku5ot5qs17sv5tv}201t{2ww4wy2yw}201w{2ks}201x{2ww4wy2yw}2k{201ts201xs}2w{7qs4qu5kw5os5qw5rs17su5tu7tsfzs}2x{5ow5qs}2y{7qs4qu5kw5os5qw5rs17su5tu7tsfzs}'fof'-6o7p{17su5tu5ot}ck{4qs5ku5ot5qs17sv5tv}4l{4qs5ku5ot5qs17sv5tv}cm{4qs5ku5ot5qs17sv5tv}cn{4qs5ku5ot5qs17sv5tv}co{4qs5ku5ot5qs17sv5tv}cp{4qs5ku5ot5qs17sv5tv}6l{17st5tt5os}17s{2kwclvcmvcnvcovcpv4lv4wwckv}5o{2kucltcmtcntcotcpt4lt4wtckt}5q{2ksclscmscnscoscps4ls4wvcks}5r{2ks4ws}5t{2kwclvcmvcnvcovcpv4lv4wwckv}eo{17st5tt5os}fu{17su5tu5ot}6p{17ss5ts}ek{17st5tt5os}el{17st5tt5os}em{17st5tt5os}en{17st5tt5os}6o{201ts}ep{17st5tt5os}es{17ss5ts}et{17ss5ts}eu{17ss5ts}ev{17ss5ts}6z{17su5tu5os5qt}fm{17su5tu5os5qt}fn{17su5tu5os5qt}fo{17su5tu5os5qt}fp{17su5tu5os5qt}fq{17su5tu5os5qt}fs{17su5tu5os5qt}ft{17su5tu5ot}7m{5os}fv{17su5tu5ot}fw{17su5tu5ot}}}"), - 'ZapfDingbats': uncompress("{'widths'{k4u2k1w'fof'6o}'kerning'{'fof'-6o}}"), - 'Courier-Bold': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), - 'Times-Italic': uncompress("{'widths'{k3n2q4ycx2l201n3m201o5t201s2l201t2l201u2l201w3r201x3r201y3r2k1t2l2l202m2n2n3m2o3m2p5n202q5t2r1p2s2l2t2l2u3m2v4n2w1t2x2l2y1t2z1w3k3m3l3m3m3m3n3m3o3m3p3m3q3m3r3m3s3m203t2l203u2l3v2l3w4n3x4n3y4n3z3m4k5w4l3x4m3x4n4m4o4s4p3x4q3x4r4s4s4s4t2l4u2w4v4m4w3r4x5n4y4m4z4s5k3x5l4s5m3x5n3m5o3r5p4s5q3x5r5n5s3x5t3r5u3r5v2r5w1w5x2r5y2u5z3m6k2l6l3m6m3m6n2w6o3m6p2w6q1w6r3m6s3m6t1w6u1w6v2w6w1w6x4s6y3m6z3m7k3m7l3m7m2r7n2r7o1w7p3m7q2w7r4m7s2w7t2w7u2r7v2s7w1v7x2s7y3q202l3mcl3xal2ram3man3mao3map3mar3mas2lat4wau1vav3maw4nay4waz2lbk2sbl4n'fof'6obo2lbp3mbq3obr1tbs2lbu1zbv3mbz3mck3x202k3mcm3xcn3xco3xcp3xcq5tcr4mcs3xct3xcu3xcv3xcw2l2m2ucy2lcz2ldl4mdm4sdn4sdo4sdp4sdq4sds4sdt4sdu4sdv4sdw4sdz3mek3mel3mem3men3meo3mep3meq4mer2wes2wet2weu2wev2wew1wex1wey1wez1wfl3mfm3mfn3mfo3mfp3mfq3mfr4nfs3mft3mfu3mfv3mfw3mfz2w203k6o212m6m2dw2l2cq2l3t3m3u2l17s3r19m3m}'kerning'{cl{5kt4qw}201s{201sw}201t{201tw2wy2yy6q-t}201x{2wy2yy}2k{201tw}2w{7qs4qy7rs5ky7mw5os5qx5ru17su5tu}2x{17ss5ts5os}2y{7qs4qy7rs5ky7mw5os5qx5ru17su5tu}'fof'-6o6t{17ss5ts5qs}7t{5os}3v{5qs}7p{17su5tu5qs}ck{5kt4qw}4l{5kt4qw}cm{5kt4qw}cn{5kt4qw}co{5kt4qw}cp{5kt4qw}6l{4qs5ks5ou5qw5ru17su5tu}17s{2ks}5q{ckvclvcmvcnvcovcpv4lv}5r{ckuclucmucnucoucpu4lu}5t{2ks}6p{4qs5ks5ou5qw5ru17su5tu}ek{4qs5ks5ou5qw5ru17su5tu}el{4qs5ks5ou5qw5ru17su5tu}em{4qs5ks5ou5qw5ru17su5tu}en{4qs5ks5ou5qw5ru17su5tu}eo{4qs5ks5ou5qw5ru17su5tu}ep{4qs5ks5ou5qw5ru17su5tu}es{5ks5qs4qs}et{4qs5ks5ou5qw5ru17su5tu}eu{4qs5ks5qw5ru17su5tu}ev{5ks5qs4qs}ex{17ss5ts5qs}6z{4qv5ks5ou5qw5ru17su5tu}fm{4qv5ks5ou5qw5ru17su5tu}fn{4qv5ks5ou5qw5ru17su5tu}fo{4qv5ks5ou5qw5ru17su5tu}fp{4qv5ks5ou5qw5ru17su5tu}fq{4qv5ks5ou5qw5ru17su5tu}7r{5os}fs{4qv5ks5ou5qw5ru17su5tu}ft{17su5tu5qs}fu{17su5tu5qs}fv{17su5tu5qs}fw{17su5tu5qs}}}"), - 'Times-Roman': uncompress("{'widths'{k3n2q4ycx2l201n3m201o6o201s2l201t2l201u2l201w2w201x2w201y2w2k1t2l2l202m2n2n3m2o3m2p5n202q6o2r1m2s2l2t2l2u3m2v3s2w1t2x2l2y1t2z1w3k3m3l3m3m3m3n3m3o3m3p3m3q3m3r3m3s3m203t2l203u2l3v1w3w3s3x3s3y3s3z2w4k5w4l4s4m4m4n4m4o4s4p3x4q3r4r4s4s4s4t2l4u2r4v4s4w3x4x5t4y4s4z4s5k3r5l4s5m4m5n3r5o3x5p4s5q4s5r5y5s4s5t4s5u3x5v2l5w1w5x2l5y2z5z3m6k2l6l2w6m3m6n2w6o3m6p2w6q2l6r3m6s3m6t1w6u1w6v3m6w1w6x4y6y3m6z3m7k3m7l3m7m2l7n2r7o1w7p3m7q3m7r4s7s3m7t3m7u2w7v3k7w1o7x3k7y3q202l3mcl4sal2lam3man3mao3map3mar3mas2lat4wau1vav3maw3say4waz2lbk2sbl3s'fof'6obo2lbp3mbq2xbr1tbs2lbu1zbv3mbz2wck4s202k3mcm4scn4sco4scp4scq5tcr4mcs3xct3xcu3xcv3xcw2l2m2tcy2lcz2ldl4sdm4sdn4sdo4sdp4sdq4sds4sdt4sdu4sdv4sdw4sdz3mek2wel2wem2wen2weo2wep2weq4mer2wes2wet2weu2wev2wew1wex1wey1wez1wfl3mfm3mfn3mfo3mfp3mfq3mfr3sfs3mft3mfu3mfv3mfw3mfz3m203k6o212m6m2dw2l2cq2l3t3m3u1w17s4s19m3m}'kerning'{cl{4qs5ku17sw5ou5qy5rw201ss5tw201ws}201s{201ss}201t{ckw4lwcmwcnwcowcpwclw4wu201ts}2k{201ts}2w{4qs5kw5os5qx5ru17sx5tx}2x{17sw5tw5ou5qu}2y{4qs5kw5os5qx5ru17sx5tx}'fof'-6o7t{ckuclucmucnucoucpu4lu5os5rs}3u{17su5tu5qs}3v{17su5tu5qs}7p{17sw5tw5qs}ck{4qs5ku17sw5ou5qy5rw201ss5tw201ws}4l{4qs5ku17sw5ou5qy5rw201ss5tw201ws}cm{4qs5ku17sw5ou5qy5rw201ss5tw201ws}cn{4qs5ku17sw5ou5qy5rw201ss5tw201ws}co{4qs5ku17sw5ou5qy5rw201ss5tw201ws}cp{4qs5ku17sw5ou5qy5rw201ss5tw201ws}6l{17su5tu5os5qw5rs}17s{2ktclvcmvcnvcovcpv4lv4wuckv}5o{ckwclwcmwcnwcowcpw4lw4wu}5q{ckyclycmycnycoycpy4ly4wu5ms}5r{cktcltcmtcntcotcpt4lt4ws}5t{2ktclvcmvcnvcovcpv4lv4wuckv}7q{cksclscmscnscoscps4ls}6p{17su5tu5qw5rs}ek{5qs5rs}el{17su5tu5os5qw5rs}em{17su5tu5os5qs5rs}en{17su5qs5rs}eo{5qs5rs}ep{17su5tu5os5qw5rs}es{5qs}et{17su5tu5qw5rs}eu{17su5tu5qs5rs}ev{5qs}6z{17sv5tv5os5qx5rs}fm{5os5qt5rs}fn{17sv5tv5os5qx5rs}fo{17sv5tv5os5qx5rs}fp{5os5qt5rs}fq{5os5qt5rs}7r{ckuclucmucnucoucpu4lu5os}fs{17sv5tv5os5qx5rs}ft{17ss5ts5qs}fu{17sw5tw5qs}fv{17sw5tw5qs}fw{17ss5ts5qs}fz{ckuclucmucnucoucpu4lu5os5rs}}}"), - 'Helvetica-Oblique': uncompress("{'widths'{k3p2q4mcx1w201n3r201o6o201s1q201t1q201u1q201w2l201x2l201y2l2k1w2l1w202m2n2n3r2o3r2p5t202q6o2r1n2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v1w3w3u3x3u3y3u3z3r4k6p4l4m4m4m4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3m4v4m4w3r4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v1w5w1w5x1w5y2z5z3r6k2l6l3r6m3r6n3m6o3r6p3r6q1w6r3r6s3r6t1q6u1q6v3m6w1q6x5n6y3r6z3r7k3r7l3r7m2l7n3m7o1w7p3r7q3m7r4s7s3m7t3m7u3m7v2l7w1u7x2l7y3u202l3rcl4mal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3rbr1wbs2lbu2obv3rbz3xck4m202k3rcm4mcn4mco4mcp4mcq6ocr4scs4mct4mcu4mcv4mcw1w2m2ncy1wcz1wdl4sdm4ydn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3mes3ret3reu3rev3rew1wex1wey1wez1wfl3rfm3rfn3rfo3rfp3rfq3rfr3ufs3xft3rfu3rfv3rfw3rfz3m203k6o212m6o2dw2l2cq2l3t3r3u1w17s4m19m3r}'kerning'{5q{4wv}cl{4qs5kw5ow5qs17sv5tv}201t{2wu4w1k2yu}201x{2wu4wy2yu}17s{2ktclucmucnu4otcpu4lu4wycoucku}2w{7qs4qz5k1m17sy5ow5qx5rsfsu5ty7tufzu}2x{17sy5ty5oy5qs}2y{7qs4qz5k1m17sy5ow5qx5rsfsu5ty7tufzu}'fof'-6o7p{17sv5tv5ow}ck{4qs5kw5ow5qs17sv5tv}4l{4qs5kw5ow5qs17sv5tv}cm{4qs5kw5ow5qs17sv5tv}cn{4qs5kw5ow5qs17sv5tv}co{4qs5kw5ow5qs17sv5tv}cp{4qs5kw5ow5qs17sv5tv}6l{17sy5ty5ow}do{17st5tt}4z{17st5tt}7s{fst}dm{17st5tt}dn{17st5tt}5o{ckwclwcmwcnwcowcpw4lw4wv}dp{17st5tt}dq{17st5tt}7t{5ow}ds{17st5tt}5t{2ktclucmucnu4otcpu4lu4wycoucku}fu{17sv5tv5ow}6p{17sy5ty5ow5qs}ek{17sy5ty5ow}el{17sy5ty5ow}em{17sy5ty5ow}en{5ty}eo{17sy5ty5ow}ep{17sy5ty5ow}es{17sy5ty5qs}et{17sy5ty5ow5qs}eu{17sy5ty5ow5qs}ev{17sy5ty5ow5qs}6z{17sy5ty5ow5qs}fm{17sy5ty5ow5qs}fn{17sy5ty5ow5qs}fo{17sy5ty5ow5qs}fp{17sy5ty5qs}fq{17sy5ty5ow5qs}7r{5ow}fs{17sy5ty5ow5qs}ft{17sv5tv5ow}7m{5ow}fv{17sv5tv5ow}fw{17sv5tv5ow}}}") - } }; - - /* - This event handler is fired when a new jsPDF object is initialized - This event handler appends metrics data to standard fonts within - that jsPDF instance. The metrics are mapped over Unicode character - codes, NOT CIDs or other codes matching the StandardEncoding table of the - standard PDF fonts. - Future: - Also included is the encoding maping table, converting Unicode (UCS-2, UTF-16) - char codes to StandardEncoding character codes. The encoding table is to be used - somewhere around "pdfEscape" call. - */ - - API.events.push(['addFont', function (font) { - var metrics, - unicode_section, - encoding = 'Unicode', - encodingBlock; - - metrics = fontMetrics[encoding][font.postScriptName]; - if (metrics) { - if (font.metadata[encoding]) { - unicode_section = font.metadata[encoding]; - } else { - unicode_section = font.metadata[encoding] = {}; - } - - unicode_section.widths = metrics.widths; - unicode_section.kerning = metrics.kerning; - } - - encodingBlock = encodings[encoding][font.postScriptName]; - if (encodingBlock) { - if (font.metadata[encoding]) { - unicode_section = font.metadata[encoding]; - } else { - unicode_section = font.metadata[encoding] = {}; - } - - unicode_section.encoding = encodingBlock; - if (encodingBlock.codePages && encodingBlock.codePages.length) { - font.encoding = encodingBlock.codePages[0]; - } - } - }]); // end of adding event handler - })(jsPDF.API); - - /** - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - (function (jsPDF, global) { - - jsPDF.API.events.push(['addFont', function (font) { - if (jsPDF.API.existsFileInVFS(font.postScriptName)) { - font.metadata = jsPDF.API.TTFFont.open(font.postScriptName, font.fontName, jsPDF.API.getFileFromVFS(font.postScriptName), font.encoding); - font.metadata.Unicode = font.metadata.Unicode || { encoding: {}, kerning: {}, widths: [] }; - } else if (font.id.slice(1) > 14) { - console.error("Font does not exist in FileInVFS, import fonts or remove declaration doc.addFont('" + font.postScriptName + "')."); - } - }]); // end of adding event handler - })(jsPDF, typeof self !== "undefined" && self || typeof global !== "undefined" && global || typeof window !== "undefined" && window || Function("return this")()); - - /** @preserve - jsPDF SVG plugin - Copyright (c) 2012 Willow Systems Corporation, willow-systems.com - */ - (function (jsPDFAPI) { - - /** - * Parses SVG XML and converts only some of the SVG elements into - * PDF elements. - * - * Supports: - * paths - * - * @name addSvg - * @public - * @function - * @param {String} SVG-Data as Text - * @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 SVG (in units declared at inception of PDF document) - * @param {Number} height of SVG (in units declared at inception of PDF document) - * @returns {Object} jsPDF-instance - */ - - jsPDFAPI.addSvg = function (svgtext, x, y, w, h) { - // 'this' is _jsPDF object returned when jsPDF is inited (new jsPDF()) - - var undef; - - if (x === undef || y === undef) { - throw new Error("addSVG needs values for 'x' and 'y'"); - } - - function InjectCSS(cssbody, document) { - var styletag = document.createElement('style'); - styletag.type = 'text/css'; - if (styletag.styleSheet) { - // ie - styletag.styleSheet.cssText = cssbody; - } else { - // others - styletag.appendChild(document.createTextNode(cssbody)); - } - document.getElementsByTagName("head")[0].appendChild(styletag); - } - - function createWorkerNode(document) { - - var frameID = 'childframe' // Date.now().toString() + '_' + (Math.random() * 100).toString() - , - frame = document.createElement('iframe'); - - InjectCSS('.jsPDF_sillysvg_iframe {display:none;position:absolute;}', document); - - frame.name = frameID; - frame.setAttribute("width", 0); - frame.setAttribute("height", 0); - frame.setAttribute("frameborder", "0"); - frame.setAttribute("scrolling", "no"); - frame.setAttribute("seamless", "seamless"); - frame.setAttribute("class", "jsPDF_sillysvg_iframe"); - - document.body.appendChild(frame); - - return frame; - } - - function attachSVGToWorkerNode(svgtext, frame) { - var framedoc = (frame.contentWindow || frame.contentDocument).document; - framedoc.write(svgtext); - framedoc.close(); - return framedoc.getElementsByTagName('svg')[0]; - } - - function convertPathToPDFLinesArgs(path) { - // we will use 'lines' method call. it needs: - // - starting coordinate pair - // - array of arrays of vector shifts (2-len for line, 6 len for bezier) - // - scale array [horizontal, vertical] ratios - // - style (stroke, fill, both) - - var x = parseFloat(path[1]), - y = parseFloat(path[2]), - vectors = [], - position = 3, - len = path.length; - - while (position < len) { - if (path[position] === 'c') { - vectors.push([parseFloat(path[position + 1]), parseFloat(path[position + 2]), parseFloat(path[position + 3]), parseFloat(path[position + 4]), parseFloat(path[position + 5]), parseFloat(path[position + 6])]); - position += 7; - } else if (path[position] === 'l') { - vectors.push([parseFloat(path[position + 1]), parseFloat(path[position + 2])]); - position += 3; - } else { - position += 1; - } - } - return [x, y, vectors]; - } - - var workernode = createWorkerNode(document), - svgnode = attachSVGToWorkerNode(svgtext, workernode), - scale = [1, 1], - svgw = parseFloat(svgnode.getAttribute('width')), - svgh = parseFloat(svgnode.getAttribute('height')); - - if (svgw && svgh) { - // setting both w and h makes image stretch to size. - // this may distort the image, but fits your demanded size - if (w && h) { - scale = [w / svgw, h / svgh]; - } - // if only one is set, that value is set as max and SVG - // is scaled proportionately. - else if (w) { - scale = [w / svgw, w / svgw]; - } else if (h) { - scale = [h / svgh, h / svgh]; - } - } - - var i, - l, - tmp, - linesargs, - items = svgnode.childNodes; - for (i = 0, l = items.length; i < l; i++) { - tmp = items[i]; - if (tmp.tagName && tmp.tagName.toUpperCase() === 'PATH') { - linesargs = convertPathToPDFLinesArgs(tmp.getAttribute("d").split(' ')); - // path start x coordinate - linesargs[0] = linesargs[0] * scale[0] + x; // where x is upper left X of image - // path start y coordinate - linesargs[1] = linesargs[1] * scale[1] + y; // where y is upper left Y of image - // the rest of lines are vectors. these will adjust with scale value auto. - this.lines.call(this, linesargs[2] // lines - , linesargs[0] // starting x - , linesargs[1] // starting y - , scale); - } - } - - // clean up - // workernode.parentNode.removeChild(workernode) - - return this; - }; - - //fallback - jsPDFAPI.addSVG = jsPDFAPI.addSvg; - - /** - * Parses SVG XML and saves it as image into the PDF. - * - * Depends on canvas-element and canvg - * - * @name addSvgAsImage - * @public - * @function - * @param {String} SVG-Data as Text - * @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 SVG-Image (in units declared at inception of PDF document) - * @param {Number} height of SVG-Image (in units declared at inception of PDF document) - * @param {String} alias of SVG-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.addSvgAsImage = function (svg, x, y, w, h, alias, compression, rotation) { - - if (isNaN(x) || isNaN(y)) { - console.error('jsPDF.addSvgAsImage: Invalid coordinates', arguments); - throw new Error('Invalid coordinates passed to jsPDF.addSvgAsImage'); - } - - if (isNaN(w) || isNaN(h)) { - console.error('jsPDF.addSvgAsImage: Invalid measurements', arguments); - throw new Error('Invalid measurements (width and/or height) passed to jsPDF.addSvgAsImage'); - } - - var canvas = document.createElement('canvas'); - canvas.width = w; - canvas.height = h; - var ctx = canvas.getContext('2d'); - ctx.fillStyle = '#fff'; /// set white fill style - ctx.fillRect(0, 0, canvas.width, canvas.height); - - //load a svg snippet in the canvas with id = 'drawingArea' - canvg(canvas, svg, { - ignoreMouse: true, - ignoreAnimation: true, - ignoreDimensions: true, - ignoreClear: true - }); - - this.addImage(canvas.toDataURL("image/jpeg", 1.0), x, y, w, h, compression, rotation); - return this; - }; - })(jsPDF.API); - - /** ==================================================================== - * jsPDF total_pages plugin - * Copyright (c) 2013 Eduardo Menezes de Morais, eduardo.morais@usp.br - * - * - * ==================================================================== - */ - - (function (jsPDFAPI) { - - jsPDFAPI.putTotalPages = function (pageExpression) { - - var replaceExpression = new RegExp(pageExpression, 'g'); - for (var n = 1; n <= this.internal.getNumberOfPages(); n++) { - for (var i = 0; i < this.internal.pages[n].length; i++) { - this.internal.pages[n][i] = this.internal.pages[n][i].replace(replaceExpression, this.internal.getNumberOfPages()); - } - } - return this; - }; - })(jsPDF.API); - - /** - * jsPDF viewerPreferences Plugin - * @author Aras Abbasi (github.com/arasabbasi) - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - - /** - * Adds the ability to set ViewerPreferences and by thus - * controlling the way the document is to be presented on the - * screen or in print. - */ - - (function (jsPDFAPI) { - /** - * Set the ViewerPreferences of the generated PDF - * - * @param {Object} options Array with the ViewerPreferences<br /> - * Example: doc.viewerPreferences({"FitWindow":true});<br /> - * <br /> - * You can set following preferences:<br /> - * <br/> - * <b>HideToolbar</b> <i>(boolean)</i><br /> - * Default value: false<br /> - * <br /> - * <b>HideMenubar</b> <i>(boolean)</i><br /> - * Default value: false.<br /> - * <br /> - * <b>HideWindowUI</b> <i>(boolean)</i><br /> - * Default value: false.<br /> - * <br /> - * <b>FitWindow</b> <i>(boolean)</i><br /> - * Default value: false.<br /> - * <br /> - * <b>CenterWindow</b> <i>(boolean)</i><br /> - * Default value: false<br /> - * <br /> - * <b>DisplayDocTitle</b> <i>(boolean)</i><br /> - * Default value: false.<br /> - * <br /> - * <b>NonFullScreenPageMode</b> <i>(String)</i><br /> - * Possible values: UseNone, UseOutlines, UseThumbs, UseOC<br /> - * Default value: UseNone<br/> - * <br /> - * <b>Direction</b> <i>(String)</i><br /> - * Possible values: L2R, R2L<br /> - * Default value: L2R.<br /> - * <br /> - * <b>ViewArea</b> <i>(String)</i><br /> - * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br /> - * Default value: CropBox.<br /> - * <br /> - * <b>ViewClip</b> <i>(String)</i><br /> - * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br /> - * Default value: CropBox<br /> - * <br /> - * <b>PrintArea</b> <i>(String)</i><br /> - * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br /> - * Default value: CropBox<br /> - * <br /> - * <b>PrintClip</b> <i>(String)</i><br /> - * Possible values: MediaBox, CropBox, TrimBox, BleedBox, ArtBox<br /> - * Default value: CropBox.<br /> - * <br /> - * <b>PrintScaling</b> <i>(String)</i><br /> - * Possible values: AppDefault, None<br /> - * Default value: AppDefault.<br /> - * <br /> - * <b>Duplex</b> <i>(String)</i><br /> - * Possible values: Simplex, DuplexFlipLongEdge, DuplexFlipShortEdge - * Default value: none<br /> - * <br /> - * <b>PickTrayByPDFSize</b> <i>(boolean)</i><br /> - * Default value: false<br /> - * <br /> - * <b>PrintPageRange</b> <i>(Array)</i><br /> - * Example: [[1,5], [7,9]]<br /> - * Default value: as defined by PDF viewer application<br /> - * <br /> - * <b>NumCopies</b> <i>(Number)</i><br /> - * Possible values: 1, 2, 3, 4, 5<br /> - * Default value: 1<br /> - * <br /> - * For more information see the PDF Reference, sixth edition on Page 577 - * @param {boolean} doReset True to reset the settings - * @function - * @returns jsPDF - * @methodOf jsPDF# - * @example - * var doc = new jsPDF() - * doc.text('This is a test', 10, 10) - * doc.viewerPreferences({'FitWindow': true}, true) - * doc.save("viewerPreferences.pdf") - * - * // Example printing 10 copies, using cropbox, and hiding UI. - * doc.viewerPreferences({ - * 'HideWindowUI': true, - * 'PrintArea': 'CropBox', - * 'NumCopies': 10 - * }) - * @name viewerPreferences - */ - - jsPDFAPI.viewerPreferences = function (options, doReset) { - options = options || {}; - doReset = doReset || false; - - var configuration; - var configurationTemplate = { - "HideToolbar": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.3 }, - "HideMenubar": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.3 }, - "HideWindowUI": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.3 }, - "FitWindow": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.3 }, - "CenterWindow": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.3 }, - "DisplayDocTitle": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.4 }, - "NonFullScreenPageMode": { defaultValue: "UseNone", value: "UseNone", type: "name", explicitSet: false, valueSet: ["UseNone", "UseOutlines", "UseThumbs", "UseOC"], pdfVersion: 1.3 }, - "Direction": { defaultValue: "L2R", value: "L2R", type: "name", explicitSet: false, valueSet: ["L2R", "R2L"], pdfVersion: 1.3 }, - "ViewArea": { defaultValue: "CropBox", value: "CropBox", type: "name", explicitSet: false, valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"], pdfVersion: 1.4 }, - "ViewClip": { defaultValue: "CropBox", value: "CropBox", type: "name", explicitSet: false, valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"], pdfVersion: 1.4 }, - "PrintArea": { defaultValue: "CropBox", value: "CropBox", type: "name", explicitSet: false, valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"], pdfVersion: 1.4 }, - "PrintClip": { defaultValue: "CropBox", value: "CropBox", type: "name", explicitSet: false, valueSet: ["MediaBox", "CropBox", "TrimBox", "BleedBox", "ArtBox"], pdfVersion: 1.4 }, - "PrintScaling": { defaultValue: "AppDefault", value: "AppDefault", type: "name", explicitSet: false, valueSet: ["AppDefault", "None"], pdfVersion: 1.6 }, - "Duplex": { defaultValue: "", value: "none", type: "name", explicitSet: false, valueSet: ["Simplex", "DuplexFlipShortEdge", "DuplexFlipLongEdge", "none"], pdfVersion: 1.7 }, - "PickTrayByPDFSize": { defaultValue: false, value: false, type: "boolean", explicitSet: false, valueSet: [true, false], pdfVersion: 1.7 }, - "PrintPageRange": { defaultValue: "", value: "", type: "array", explicitSet: false, valueSet: null, pdfVersion: 1.7 }, - "NumCopies": { defaultValue: 1, value: 1, type: "integer", explicitSet: false, valueSet: null, pdfVersion: 1.7 } - }; - - var configurationKeys = Object.keys(configurationTemplate); - - var rangeArray = []; - var i = 0; - var j = 0; - var k = 0; - var isValid = true; - - var method; - var value; - - function arrayContainsElement(array, element) { - var iterator; - var result = false; - - for (iterator = 0; iterator < array.length; iterator += 1) { - if (array[iterator] === element) { - result = true; - } - } - return result; - } - - if (this.internal.viewerpreferences === undefined) { - this.internal.viewerpreferences = {}; - this.internal.viewerpreferences.configuration = JSON.parse(JSON.stringify(configurationTemplate)); - this.internal.viewerpreferences.isSubscribed = false; - } - configuration = this.internal.viewerpreferences.configuration; - - if (options === "reset" || doReset === true) { - var len = configurationKeys.length; - - for (k = 0; k < len; k += 1) { - configuration[configurationKeys[k]].value = configuration[configurationKeys[k]].defaultValue; - configuration[configurationKeys[k]].explicitSet = false; - } - } - - if ((typeof options === "undefined" ? "undefined" : _typeof(options)) === "object") { - for (method in options) { - value = options[method]; - if (arrayContainsElement(configurationKeys, method) && value !== undefined) { - - if (configuration[method].type === "boolean" && typeof value === "boolean") { - configuration[method].value = value; - } else if (configuration[method].type === "name" && arrayContainsElement(configuration[method].valueSet, value)) { - configuration[method].value = value; - } else if (configuration[method].type === "integer" && Number.isInteger(value)) { - configuration[method].value = value; - } else if (configuration[method].type === "array") { - - for (i = 0; i < value.length; i += 1) { - isValid = true; - if (value[i].length === 1 && typeof value[i][0] === "number") { - rangeArray.push(String(value[i])); - } else if (value[i].length > 1) { - for (j = 0; j < value[i].length; j += 1) { - if (typeof value[i][j] !== "number") { - isValid = false; - } - } - if (isValid === true) { - rangeArray.push(String(value[i].join("-"))); - } - } - } - configuration[method].value = String(rangeArray); - } else { - configuration[method].value = configuration[method].defaultValue; - } - - configuration[method].explicitSet = true; - } - } - } - - if (this.internal.viewerpreferences.isSubscribed === false) { - this.internal.events.subscribe("putCatalog", function () { - var pdfDict = []; - var vPref; - for (vPref in configuration) { - if (configuration[vPref].explicitSet === true) { - if (configuration[vPref].type === "name") { - pdfDict.push("/" + vPref + " /" + configuration[vPref].value); - } else { - pdfDict.push("/" + vPref + " " + configuration[vPref].value); - } - } - } - if (pdfDict.length !== 0) { - this.internal.write("/ViewerPreferences\n<<\n" + pdfDict.join("\n") + "\n>>"); - } - }); - this.internal.viewerpreferences.isSubscribed = true; - } - - this.internal.viewerpreferences.configuration = configuration; - return this; - }; - })(jsPDF.API); - - /** ==================================================================== - * jsPDF XMP metadata plugin - * Copyright (c) 2016 Jussi Utunen, u-jussi@suomi24.fi - * - * - * ==================================================================== - */ - - /*global jsPDF */ - - /** - * Adds XMP formatted metadata to PDF - * - * @param {String} metadata The actual metadata to be added. The metadata shall be stored as XMP simple value. Note that if the metadata string contains XML markup characters "<", ">" or "&", those characters should be written using XML entities. - * @param {String} namespaceuri Sets the namespace URI for the metadata. Last character should be slash or hash. - * @function - * @returns {jsPDF} - * @methodOf jsPDF# - * @name addMetadata - */ - - (function (jsPDFAPI) { - - var xmpmetadata = ""; - var xmpnamespaceuri = ""; - var metadata_object_number = ""; - - jsPDFAPI.addMetadata = function (metadata, namespaceuri) { - xmpnamespaceuri = namespaceuri || "http://jspdf.default.namespaceuri/"; //The namespace URI for an XMP name shall not be empty - xmpmetadata = metadata; - this.internal.events.subscribe('postPutResources', function () { - if (!xmpmetadata) { - metadata_object_number = ""; - } else { - var xmpmeta_beginning = '<x:xmpmeta xmlns:x="adobe:ns:meta/">'; - var rdf_beginning = '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:jspdf="' + xmpnamespaceuri + '"><jspdf:metadata>'; - var rdf_ending = '</jspdf:metadata></rdf:Description></rdf:RDF>'; - var xmpmeta_ending = '</x:xmpmeta>'; - var utf8_xmpmeta_beginning = unescape(encodeURIComponent(xmpmeta_beginning)); - var utf8_rdf_beginning = unescape(encodeURIComponent(rdf_beginning)); - var utf8_metadata = unescape(encodeURIComponent(xmpmetadata)); - var utf8_rdf_ending = unescape(encodeURIComponent(rdf_ending)); - var utf8_xmpmeta_ending = unescape(encodeURIComponent(xmpmeta_ending)); - - var total_len = utf8_rdf_beginning.length + utf8_metadata.length + utf8_rdf_ending.length + utf8_xmpmeta_beginning.length + utf8_xmpmeta_ending.length; - - metadata_object_number = this.internal.newObject(); - this.internal.write('<< /Type /Metadata /Subtype /XML /Length ' + total_len + ' >>'); - this.internal.write('stream'); - this.internal.write(utf8_xmpmeta_beginning + utf8_rdf_beginning + utf8_metadata + utf8_rdf_ending + utf8_xmpmeta_ending); - this.internal.write('endstream'); - this.internal.write('endobj'); - } - }); - this.internal.events.subscribe('putCatalog', function () { - if (metadata_object_number) { - this.internal.write('/Metadata ' + metadata_object_number + ' 0 R'); - } - }); - return this; - }; - })(jsPDF.API); - - (function (jsPDF, global) { - - var jsPDFAPI = jsPDF.API; - - var glyID = [0]; - /**************************************************/ - /* function : toHex */ - /* comment : Replace str with a hex string. */ - /**************************************************/ - function toHex(str) { - var hex = ''; - for (var i = 0; i < str.length; i++) { - hex += '' + str.charCodeAt(i).toString(16); - } - return hex; - } - - /***************************************************************************************************/ - /* function : pdfEscape16 */ - /* comment : The character id of a 2-byte string is converted to a hexadecimal number by obtaining */ - /* the corresponding glyph id and width, and then adding padding to the string. */ - /***************************************************************************************************/ - var pdfEscape16 = function pdfEscape16(text, font) { - var widths = font.metadata.Unicode.widths; var padz = ["", "0", "00", "000", "0000"]; - var ar = [""]; - for (var i = 0, l = text.length, t; i < l; ++i) { - t = font.metadata.characterToGlyph(text.charCodeAt(i)); - glyID.push(t); - if (widths.indexOf(t) == -1) { - widths.push(t); - widths.push([parseInt(font.metadata.widthOfGlyph(t), 10)]); - } - if (t == '0') { - //Spaces are not allowed in cmap. - return ar.join(""); - } else { - t = t.toString(16); - ar.push(padz[4 - t.length], t); - } - } - return ar.join(""); - }; - - var identityHFunction = function identityHFunction(font, out, newObject) { - - if (font.metadata instanceof jsPDF.API.TTFFont && font.encoding === 'Identity-H') { - //Tag with Identity-H - var widths = font.metadata.Unicode.widths; - var data = font.metadata.subset.encode(glyID); - var pdfOutput = data; - var pdfOutput2 = ""; - for (var i = 0; i < pdfOutput.length; i++) { - pdfOutput2 += String.fromCharCode(pdfOutput[i]); - } - var fontTable = newObject(); - out('<<'); - out('/Length ' + pdfOutput2.length); - out('/Length1 ' + pdfOutput2.length); - out('>>'); - - out('stream'); - out(pdfOutput2); - out('endstream'); - out('endobj'); - - var fontDescriptor = newObject(); - out('<<'); - out('/Type /FontDescriptor'); - out('/FontName /' + font.fontName); - out('/FontFile2 ' + fontTable + ' 0 R'); - out('/FontBBox ' + jsPDF.API.PDFObject.convert(font.metadata.bbox)); - out('/Flags ' + font.metadata.flags); - out('/StemV ' + font.metadata.stemV); - out('/ItalicAngle ' + font.metadata.italicAngle); - out('/Ascent ' + font.metadata.ascender); - out('/Descent ' + font.metadata.decender); - out('/CapHeight ' + font.metadata.capHeight); - out('>>'); - out('endobj'); - - var DescendantFont = newObject(); - out('<<'); - out('/Type /Font'); - out('/BaseFont /' + font.fontName); - out('/FontDescriptor ' + fontDescriptor + ' 0 R'); - out('/W ' + jsPDF.API.PDFObject.convert(widths)); - out('/CIDToGIDMap /Identity'); - out('/DW 1000'); - out('/Subtype /CIDFontType2'); - out('/CIDSystemInfo'); - out('<<'); - out('/Supplement 0'); - out('/Registry (Adobe)'); - out('/Ordering (' + font.encoding + ')'); - out('>>'); - out('>>'); - out('endobj'); - - font.objectNumber = newObject(); - out('<<'); - out('/Type /Font'); - out('/Subtype /Type0'); - out('/BaseFont /' + font.fontName); - out('/Encoding /' + font.encoding); - out('/DescendantFonts [' + DescendantFont + ' 0 R]'); - out('>>'); - out('endobj'); - - font.isAlreadyPutted = true; - } - }; - - jsPDFAPI.events.push(['putFont', function (args) { - identityHFunction(args.font, args.out, args.newObject); - }]); - - var winAnsiEncodingFunction = function winAnsiEncodingFunction(font, out, newObject) { - - if (font.metadata instanceof jsPDF.API.TTFFont && font.encoding === 'WinAnsiEncoding') { - //Tag with WinAnsi encoding - var widths = font.metadata.Unicode.widths; - var data = font.metadata.rawData; - var pdfOutput = data; - var pdfOutput2 = ""; - for (var i = 0; i < pdfOutput.length; i++) { - pdfOutput2 += String.fromCharCode(pdfOutput[i]); - } - var fontTable = newObject(); - out('<<'); - out('/Length ' + pdfOutput2.length); - out('/Length1 ' + pdfOutput2.length); - out('>>'); - out('stream'); - out(pdfOutput2); - out('endstream'); - out('endobj'); - var fontDescriptor = newObject(); - out('<<'); - out('/Descent ' + font.metadata.decender); - out('/CapHeight ' + font.metadata.capHeight); - out('/StemV ' + font.metadata.stemV); - out('/Type /FontDescriptor'); - out('/FontFile2 ' + fontTable + ' 0 R'); - out('/Flags 96'); - out('/FontBBox ' + jsPDF.API.PDFObject.convert(font.metadata.bbox)); - out('/FontName /' + font.fontName); - out('/ItalicAngle ' + font.metadata.italicAngle); - out('/Ascent ' + font.metadata.ascender); - out('>>'); - out('endobj'); - font.objectNumber = newObject(); - for (var i = 0; i < font.metadata.hmtx.widths.length; i++) { - font.metadata.hmtx.widths[i] = parseInt(font.metadata.hmtx.widths[i] * (1000 / font.metadata.head.unitsPerEm)); //Change the width of Em units to Point units. - } - out('<</Subtype/TrueType/Type/Font/BaseFont/' + font.fontName + '/FontDescriptor ' + fontDescriptor + ' 0 R' + '/Encoding/' + font.encoding + ' /FirstChar 29 /LastChar 255 /Widths ' + jsPDF.API.PDFObject.convert(font.metadata.hmtx.widths) + '>>'); - out('endobj'); - font.isAlreadyPutted = true; - } - }; - - jsPDFAPI.events.push(['putFont', function (args) { - winAnsiEncodingFunction(args.font, args.out, args.newObject); - }]); - - var utf8TextFunction = function utf8TextFunction(args) { - var text = args.text || ''; - var x = args.x; - var y = args.y; - var options = args.options || {}; - var mutex = args.mutex || {}; - - var pdfEscape = mutex.pdfEscape; - var activeFontKey = mutex.activeFontKey; - var fonts = mutex.fonts; - var key, - fontSize = mutex.activeFontSize; - - var str = '', - s = 0, - cmapConfirm; - var strText = ''; - var attr; - var key = activeFontKey; - var encoding = fonts[key].encoding; - - if (fonts[key].encoding !== 'Identity-H') { - return { - text: text, - x: x, - y: y, - options: options, - mutex: mutex - }; - } - strText = text; - - key = attr ? getFont(attr.font, attr.fontStyle) : activeFontKey; - if (Object.prototype.toString.call(text) === '[object Array]') { - strText = text[0]; - } - for (s = 0; s < strText.length; s += 1) { - if (fonts[key].metadata.hasOwnProperty('cmap')) { - cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)]; - /* - if (Object.prototype.toString.call(text) === '[object Array]') { - var i = 0; - // for (i = 0; i < text.length; i += 1) { - if (Object.prototype.toString.call(text[s]) === '[object Array]') { - cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s][0].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id - } else { - - } - //} - - } else { - cmapConfirm = fonts[key].metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)]; //Make sure the cmap has the corresponding glyph id - }*/ - } - if (!cmapConfirm) { - if (strText[s].charCodeAt(0) < 256 && fonts[key].metadata.hasOwnProperty('Unicode')) { - str += strText[s]; - } else { - str += ''; - } - } else { - str += strText[s]; - } - } - var result = ''; - if (parseInt(key.slice(1)) < 14 || encoding === 'WinAnsiEncoding') { - //For the default 13 font - result = toHex(pdfEscape(str, key)); - } else if (encoding === 'Identity-H') { - result = pdfEscape16(str, fonts[key]); - } - mutex.isHex = true; - - return { - text: result, - x: x, - y: y, - options: options, - mutex: mutex - }; - }; - - var utf8EscapeFunction = function utf8EscapeFunction(parms) { - var text = parms.text || '', - x = parms.x, - y = parms.y, - options = parms.options, - mutex = parms.mutex; - var lang = options.lang; - var tmpText = []; - var args = { - text: text, - x: x, - y: y, - options: options, - mutex: mutex - }; - - if (Object.prototype.toString.call(text) === '[object Array]') { - var i = 0; - for (i = 0; i < text.length; i += 1) { - if (Object.prototype.toString.call(text[i]) === '[object Array]') { - if (text[i].length === 3) { - tmpText.push([utf8TextFunction(Object.assign({}, args, { text: text[i][0] })).text, text[i][1], text[i][2]]); - } else { - tmpText.push(utf8TextFunction(Object.assign({}, args, { text: text[i] })).text); - } - } else { - tmpText.push(utf8TextFunction(Object.assign({}, args, { text: text[i] })).text); - } - } - parms.text = tmpText; - } else { - parms.text = utf8TextFunction(Object.assign({}, args, { text: text })).text; - } - }; - - jsPDFAPI.events.push(['postProcessText', utf8EscapeFunction]); - })(jsPDF, typeof self !== "undefined" && self || typeof global !== "undefined" && global || typeof window !== "undefined" && window || Function("return this")()); - - /** - * jsPDF virtual FileSystem functionality - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - - /** - * Use the vFS to handle files - */ - - (function (jsPDFAPI) { - - var vFS = {}; - - /* Check if the file exists in the vFS - * @returns {boolean} - * @name existsFileInVFS - * @example - * doc.existsFileInVFS("someFile.txt"); - */ - jsPDFAPI.existsFileInVFS = function (filename) { - return vFS.hasOwnProperty(filename); - }; - - /* Add a file to the vFS - * @returns {jsPDF} - * @name addFileToVFS - * @example - * doc.addFileToVFS("someFile.txt", "BADFACE1"); - */ - jsPDFAPI.addFileToVFS = function (filename, filecontent) { - vFS[filename] = filecontent; - return this; - }; - - /* Get the file from the vFS - * @returns {string} - * @name addFileToVFS - * @example - * doc.getFileFromVFS("someFile.txt"); - */ - jsPDFAPI.getFileFromVFS = function (filename) { - if (vFS.hasOwnProperty(filename)) { - return vFS[filename]; - } - return null; - }; - })(jsPDF.API); - - /* Blob.js - * A Blob implementation. - * 2014-07-24 - * - * By Eli Grey, http://eligrey.com - * By Devin Samarin, https://github.com/dsamarin - * License: X11/MIT - * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md - */ - - /*global self, unescape */ - /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, - plusplus: true */ - - /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ - - (function (view) { - - view.URL = view.URL || view.webkitURL; - - if (view.Blob && view.URL) { - try { - new Blob; - return; - } catch (e) {} - } - - // Internally we use a BlobBuilder implementation to base Blob off of - // in order to support older browsers that only have BlobBuilder - var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { - var - get_class = function(object) { - return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; - } - , FakeBlobBuilder = function BlobBuilder() { - this.data = []; - } - , FakeBlob = function Blob(data, type, encoding) { - this.data = data; - this.size = data.length; - this.type = type; - this.encoding = encoding; - } - , FBB_proto = FakeBlobBuilder.prototype - , FB_proto = FakeBlob.prototype - , FileReaderSync = view.FileReaderSync - , FileException = function(type) { - this.code = this[this.name = type]; - } - , file_ex_codes = ( - "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " - + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" - ).split(" ") - , file_ex_code = file_ex_codes.length - , real_URL = view.URL || view.webkitURL || view - , real_create_object_URL = real_URL.createObjectURL - , real_revoke_object_URL = real_URL.revokeObjectURL - , URL = real_URL - , btoa = view.btoa - , atob = view.atob - - , ArrayBuffer = view.ArrayBuffer - , Uint8Array = view.Uint8Array - - , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/ - ; - FakeBlob.fake = FB_proto.fake = true; - while (file_ex_code--) { - FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; - } - // Polyfill URL - if (!real_URL.createObjectURL) { - URL = view.URL = function(uri) { - var - uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") - , uri_origin - ; - uri_info.href = uri; - if (!("origin" in uri_info)) { - if (uri_info.protocol.toLowerCase() === "data:") { - uri_info.origin = null; - } else { - uri_origin = uri.match(origin); - uri_info.origin = uri_origin && uri_origin[1]; - } - } - return uri_info; - }; - } - URL.createObjectURL = function(blob) { - var - type = blob.type - , data_URI_header - ; - if (type === null) { - type = "application/octet-stream"; - } - if (blob instanceof FakeBlob) { - data_URI_header = "data:" + type; - if (blob.encoding === "base64") { - return data_URI_header + ";base64," + blob.data; - } else if (blob.encoding === "URI") { - return data_URI_header + "," + decodeURIComponent(blob.data); - } if (btoa) { - return data_URI_header + ";base64," + btoa(blob.data); - } else { - return data_URI_header + "," + encodeURIComponent(blob.data); - } - } else if (real_create_object_URL) { - return real_create_object_URL.call(real_URL, blob); - } - }; - URL.revokeObjectURL = function(object_URL) { - if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { - real_revoke_object_URL.call(real_URL, object_URL); - } - }; - FBB_proto.append = function(data/*, endings*/) { - var bb = this.data; - // decode data to a binary string - if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { - var - str = "" - , buf = new Uint8Array(data) - , i = 0 - , buf_len = buf.length - ; - for (; i < buf_len; i++) { - str += String.fromCharCode(buf[i]); - } - bb.push(str); - } else if (get_class(data) === "Blob" || get_class(data) === "File") { - if (FileReaderSync) { - var fr = new FileReaderSync; - bb.push(fr.readAsBinaryString(data)); - } else { - // async FileReader won't work as BlobBuilder is sync - throw new FileException("NOT_READABLE_ERR"); - } - } else if (data instanceof FakeBlob) { - if (data.encoding === "base64" && atob) { - bb.push(atob(data.data)); - } else if (data.encoding === "URI") { - bb.push(decodeURIComponent(data.data)); - } else if (data.encoding === "raw") { - bb.push(data.data); - } - } else { - if (typeof data !== "string") { - data += ""; // convert unsupported types to strings - } - // decode UTF-16 to binary string - bb.push(unescape(encodeURIComponent(data))); - } - }; - FBB_proto.getBlob = function(type) { - if (!arguments.length) { - type = null; - } - return new FakeBlob(this.data.join(""), type, "raw"); - }; - FBB_proto.toString = function() { - return "[object BlobBuilder]"; - }; - FB_proto.slice = function(start, end, type) { - var args = arguments.length; - if (args < 3) { - type = null; - } - return new FakeBlob( - this.data.slice(start, args > 1 ? end : this.data.length) - , type - , this.encoding - ); - }; - FB_proto.toString = function() { - return "[object Blob]"; - }; - FB_proto.close = function() { - this.size = 0; - delete this.data; - }; - return FakeBlobBuilder; - }(view)); - - view.Blob = function(blobParts, options) { - var type = options ? (options.type || "") : ""; - var builder = new BlobBuilder(); - if (blobParts) { - for (var i = 0, len = blobParts.length; i < len; i++) { - if (Uint8Array && blobParts[i] instanceof Uint8Array) { - builder.append(blobParts[i].buffer); - } - else { - builder.append(blobParts[i]); - } - } - } - var blob = builder.getBlob(type); - if (!blob.slice && blob.webkitSlice) { - blob.slice = blob.webkitSlice; - } - return blob; - }; - - var getPrototypeOf = Object.getPrototypeOf || function(object) { - return object.__proto__; - }; - view.Blob.prototype = getPrototypeOf(new view.Blob()); - }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || window.content || window)); - - /* FileSaver.js - * A saveAs() FileSaver implementation. - * 1.3.2 - * 2016-06-16 18:25:19 - * - * By Eli Grey, http://eligrey.com - * License: MIT - * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md - */ - - /*global self */ - /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ - - /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ - - var saveAs = saveAs || (function(view) { - // IE <10 is explicitly unsupported - if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { - return; - } - var - doc = view.document - // only get URL when necessary in case Blob.js hasn't overridden it yet - , get_URL = function() { - return view.URL || view.webkitURL || view; - } - , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") - , can_use_save_link = "download" in save_link - , click = function(node) { - var event = new MouseEvent("click"); - node.dispatchEvent(event); - } - , is_safari = /constructor/i.test(view.HTMLElement) || view.safari - , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) - , throw_outside = function(ex) { - (view.setImmediate || view.setTimeout)(function() { - throw ex; - }, 0); - } - , force_saveable_type = "application/octet-stream" - // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to - , arbitrary_revoke_timeout = 1000 * 40 // in ms - , revoke = function(file) { - var revoker = function() { - if (typeof file === "string") { // file is an object URL - get_URL().revokeObjectURL(file); - } else { // file is a File - file.remove(); - } - }; - setTimeout(revoker, arbitrary_revoke_timeout); - } - , dispatch = function(filesaver, event_types, event) { - event_types = [].concat(event_types); - var i = event_types.length; - while (i--) { - var listener = filesaver["on" + event_types[i]]; - if (typeof listener === "function") { - try { - listener.call(filesaver, event || filesaver); - } catch (ex) { - throw_outside(ex); - } - } - } - } - , auto_bom = function(blob) { - // prepend BOM for UTF-8 XML and text/* types (including HTML) - // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF - if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { - return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); - } - return blob; - } - , FileSaver = function(blob, name, no_auto_bom) { - if (!no_auto_bom) { - blob = auto_bom(blob); - } - // First try a.download, then web filesystem, then object URLs - var - filesaver = this - , type = blob.type - , force = type === force_saveable_type - , object_url - , dispatch_all = function() { - dispatch(filesaver, "writestart progress write writeend".split(" ")); - } - // on any filesys errors revert to saving with object URLs - , fs_error = function() { - if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { - // Safari doesn't allow downloading of blob urls - var reader = new FileReader(); - reader.onloadend = function() { - var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); - var popup = view.open(url, '_blank'); - if(!popup) view.location.href = url; - url=undefined; // release reference before dispatching - filesaver.readyState = filesaver.DONE; - dispatch_all(); - }; - reader.readAsDataURL(blob); - filesaver.readyState = filesaver.INIT; - return; - } - // don't create more object URLs than needed - if (!object_url) { - object_url = get_URL().createObjectURL(blob); - } - if (force) { - view.location.href = object_url; - } else { - var opened = view.open(object_url, "_blank"); - if (!opened) { - // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html - view.location.href = object_url; - } - } - filesaver.readyState = filesaver.DONE; - dispatch_all(); - revoke(object_url); - } - ; - filesaver.readyState = filesaver.INIT; - - if (can_use_save_link) { - object_url = get_URL().createObjectURL(blob); - setTimeout(function() { - save_link.href = object_url; - save_link.download = name; - click(save_link); - dispatch_all(); - revoke(object_url); - filesaver.readyState = filesaver.DONE; - }); - return; - } - - fs_error(); - } - , FS_proto = FileSaver.prototype - , saveAs = function(blob, name, no_auto_bom) { - return new FileSaver(blob, name || blob.name || "download", no_auto_bom); - } - ; - // IE 10+ (native saveAs) - if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { - return function(blob, name, no_auto_bom) { - name = name || blob.name || "download"; - - if (!no_auto_bom) { - blob = auto_bom(blob); - } - return navigator.msSaveOrOpenBlob(blob, name); - }; - } - - FS_proto.abort = function(){}; - FS_proto.readyState = FS_proto.INIT = 0; - FS_proto.WRITING = 1; - FS_proto.DONE = 2; - - FS_proto.error = - FS_proto.onwritestart = - FS_proto.onprogress = - FS_proto.onwrite = - FS_proto.onabort = - FS_proto.onerror = - FS_proto.onwriteend = - null; - - return saveAs; - }( - typeof self !== "undefined" && self - || typeof window !== "undefined" && window - || window.content - )); - // `self` is undefined in Firefox for Android content script context - // while `this` is nsIContentFrameMessageManager - // with an attribute `content` that corresponds to the window - - if (typeof module !== "undefined" && module.exports) { - module.exports.saveAs = saveAs; - } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { - define("FileSaver.js", function() { - return saveAs; - }); - } - - /* - * Copyright (c) 2012 chick307 <chick307@gmail.com> - * - * Licensed under the MIT License. - * http://opensource.org/licenses/mit-license - */ - - (function(jsPDF, callback) { - jsPDF.API.adler32cs = callback(); - })(jsPDF, function() { - var _hasArrayBuffer = typeof ArrayBuffer === 'function' && - typeof Uint8Array === 'function'; - - var _Buffer = null, _isBuffer = (function() { - if (!_hasArrayBuffer) - return function _isBuffer() { return false }; - - try { - var buffer = {}; - if (typeof buffer.Buffer === 'function') - _Buffer = buffer.Buffer; - } catch (error) {} - - return function _isBuffer(value) { - return value instanceof ArrayBuffer || - _Buffer !== null && value instanceof _Buffer; - }; - }()); - - var _utf8ToBinary = (function() { - if (_Buffer !== null) { - return function _utf8ToBinary(utf8String) { - return new _Buffer(utf8String, 'utf8').toString('binary'); - }; - } else { - return function _utf8ToBinary(utf8String) { - return unescape(encodeURIComponent(utf8String)); - }; - } - }()); - - var MOD = 65521; - - var _update = function _update(checksum, binaryString) { - var a = checksum & 0xFFFF, b = checksum >>> 16; - for (var i = 0, length = binaryString.length; i < length; i++) { - a = (a + (binaryString.charCodeAt(i) & 0xFF)) % MOD; - b = (b + a) % MOD; - } - return (b << 16 | a) >>> 0; - }; - - var _updateUint8Array = function _updateUint8Array(checksum, uint8Array) { - var a = checksum & 0xFFFF, b = checksum >>> 16; - for (var i = 0, length = uint8Array.length; i < length; i++) { - a = (a + uint8Array[i]) % MOD; - b = (b + a) % MOD; - } - return (b << 16 | a) >>> 0 - }; - - var exports = {}; - - var Adler32 = exports.Adler32 = (function() { - var ctor = function Adler32(checksum) { - if (!(this instanceof ctor)) { - throw new TypeError( - 'Constructor cannot called be as a function.'); - } - if (!isFinite(checksum = checksum == null ? 1 : +checksum)) { - throw new Error( - 'First arguments needs to be a finite number.'); - } - this.checksum = checksum >>> 0; - }; - - var proto = ctor.prototype = {}; - proto.constructor = ctor; - - ctor.from = function(from) { - from.prototype = proto; - return from; - }(function from(binaryString) { - if (!(this instanceof ctor)) { - throw new TypeError( - 'Constructor cannot called be as a function.'); - } - if (binaryString == null) - throw new Error('First argument needs to be a string.'); - this.checksum = _update(1, binaryString.toString()); - }); - - ctor.fromUtf8 = function(fromUtf8) { - fromUtf8.prototype = proto; - return fromUtf8; - }(function fromUtf8(utf8String) { - if (!(this instanceof ctor)) { - throw new TypeError( - 'Constructor cannot called be as a function.'); - } - if (utf8String == null) - throw new Error('First argument needs to be a string.'); - var binaryString = _utf8ToBinary(utf8String.toString()); - this.checksum = _update(1, binaryString); - }); - - if (_hasArrayBuffer) { - ctor.fromBuffer = function(fromBuffer) { - fromBuffer.prototype = proto; - return fromBuffer; - }(function fromBuffer(buffer) { - if (!(this instanceof ctor)) { - throw new TypeError( - 'Constructor cannot called be as a function.'); - } - if (!_isBuffer(buffer)) - throw new Error('First argument needs to be ArrayBuffer.'); - var array = new Uint8Array(buffer); - return this.checksum = _updateUint8Array(1, array); - }); - } - - proto.update = function update(binaryString) { - if (binaryString == null) - throw new Error('First argument needs to be a string.'); - binaryString = binaryString.toString(); - return this.checksum = _update(this.checksum, binaryString); - }; - - proto.updateUtf8 = function updateUtf8(utf8String) { - if (utf8String == null) - throw new Error('First argument needs to be a string.'); - var binaryString = _utf8ToBinary(utf8String.toString()); - return this.checksum = _update(this.checksum, binaryString); - }; - - if (_hasArrayBuffer) { - proto.updateBuffer = function updateBuffer(buffer) { - if (!_isBuffer(buffer)) - throw new Error('First argument needs to be ArrayBuffer.'); - var array = new Uint8Array(buffer); - return this.checksum = _updateUint8Array(this.checksum, array); - }; - } - - proto.clone = function clone() { - return new Adler32(this.checksum); - }; - - return ctor; - }()); - - exports.from = function from(binaryString) { - if (binaryString == null) - throw new Error('First argument needs to be a string.'); - return _update(1, binaryString.toString()); - }; - - exports.fromUtf8 = function fromUtf8(utf8String) { - if (utf8String == null) - throw new Error('First argument needs to be a string.'); - var binaryString = _utf8ToBinary(utf8String.toString()); - return _update(1, binaryString); - }; - - if (_hasArrayBuffer) { - exports.fromBuffer = function fromBuffer(buffer) { - if (!_isBuffer(buffer)) - throw new Error('First argument need to be ArrayBuffer.'); - var array = new Uint8Array(buffer); - return _updateUint8Array(1, array); - }; - } - - return exports; - }); - - // (c) Dean McNamee <dean@gmail.com>, 2013. - // - // https://github.com/deanm/omggif - // - // - // - // omggif is a JavaScript implementation of a GIF 89a encoder and decoder, - // including animation and compression. It does not rely on any specific - // underlying system, so should run in the browser, Node, or Plask. - - function GifWriter(buf, width, height, gopts) { - var p = 0; - - var gopts = gopts === undefined ? { } : gopts; - var loop_count = gopts.loop === undefined ? null : gopts.loop; - var global_palette = gopts.palette === undefined ? null : gopts.palette; - - if (width <= 0 || height <= 0 || width > 65535 || height > 65535) - throw "Width/Height invalid." - - function check_palette_and_num_colors(palette) { - var num_colors = palette.length; - if (num_colors < 2 || num_colors > 256 || num_colors & (num_colors-1)) - throw "Invalid code/color length, must be power of 2 and 2 .. 256."; - return num_colors; - } - - // - Header. - buf[p++] = 0x47; buf[p++] = 0x49; buf[p++] = 0x46; // GIF - buf[p++] = 0x38; buf[p++] = 0x39; buf[p++] = 0x61; // 89a - - // Handling of Global Color Table (palette) and background index. - var gp_num_colors_pow2 = 0; - var background = 0; - if (global_palette !== null) { - var gp_num_colors = check_palette_and_num_colors(global_palette); - while (gp_num_colors >>= 1) ++gp_num_colors_pow2; - gp_num_colors = 1 << gp_num_colors_pow2; - --gp_num_colors_pow2; - if (gopts.background !== undefined) { - background = gopts.background; - if (background >= gp_num_colors) throw "Background index out of range."; - // The GIF spec states that a background index of 0 should be ignored, so - // this is probably a mistake and you really want to set it to another - // slot in the palette. But actually in the end most browsers, etc end - // up ignoring this almost completely (including for dispose background). - if (background === 0) - throw "Background index explicitly passed as 0."; - } - } - - // - Logical Screen Descriptor. - // NOTE(deanm): w/h apparently ignored by implementations, but set anyway. - buf[p++] = width & 0xff; buf[p++] = width >> 8 & 0xff; - buf[p++] = height & 0xff; buf[p++] = height >> 8 & 0xff; - // NOTE: Indicates 0-bpp original color resolution (unused?). - buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag. - gp_num_colors_pow2; // NOTE: No sort flag (unused?). - buf[p++] = background; // Background Color Index. - buf[p++] = 0; // Pixel aspect ratio (unused?). - - // - Global Color Table - if (global_palette !== null) { - for (var i = 0, il = global_palette.length; i < il; ++i) { - var rgb = global_palette[i]; - buf[p++] = rgb >> 16 & 0xff; - buf[p++] = rgb >> 8 & 0xff; - buf[p++] = rgb & 0xff; - } - } - - if (loop_count !== null) { // Netscape block for looping. - if (loop_count < 0 || loop_count > 65535) - throw "Loop count invalid." - // Extension code, label, and length. - buf[p++] = 0x21; buf[p++] = 0xff; buf[p++] = 0x0b; - // NETSCAPE2.0 - buf[p++] = 0x4e; buf[p++] = 0x45; buf[p++] = 0x54; buf[p++] = 0x53; - buf[p++] = 0x43; buf[p++] = 0x41; buf[p++] = 0x50; buf[p++] = 0x45; - buf[p++] = 0x32; buf[p++] = 0x2e; buf[p++] = 0x30; - // Sub-block - buf[p++] = 0x03; buf[p++] = 0x01; - buf[p++] = loop_count & 0xff; buf[p++] = loop_count >> 8 & 0xff; - buf[p++] = 0x00; // Terminator. - } - - - var ended = false; - - this.addFrame = function(x, y, w, h, indexed_pixels, opts) { - if (ended === true) { --p; ended = false; } // Un-end. - - opts = opts === undefined ? { } : opts; - - // TODO(deanm): Bounds check x, y. Do they need to be within the virtual - // canvas width/height, I imagine? - if (x < 0 || y < 0 || x > 65535 || y > 65535) - throw "x/y invalid." - - if (w <= 0 || h <= 0 || w > 65535 || h > 65535) - throw "Width/Height invalid." - - if (indexed_pixels.length < w * h) - throw "Not enough pixels for the frame size."; - - var using_local_palette = true; - var palette = opts.palette; - if (palette === undefined || palette === null) { - using_local_palette = false; - palette = global_palette; - } - - if (palette === undefined || palette === null) - throw "Must supply either a local or global palette."; - - var num_colors = check_palette_and_num_colors(palette); - - // Compute the min_code_size (power of 2), destroying num_colors. - var min_code_size = 0; - while (num_colors >>= 1) ++min_code_size; - num_colors = 1 << min_code_size; // Now we can easily get it back. - - var delay = opts.delay === undefined ? 0 : opts.delay; - - // From the spec: - // 0 - No disposal specified. The decoder is - // not required to take any action. - // 1 - Do not dispose. The graphic is to be left - // in place. - // 2 - Restore to background color. The area used by the - // graphic must be restored to the background color. - // 3 - Restore to previous. The decoder is required to - // restore the area overwritten by the graphic with - // what was there prior to rendering the graphic. - // 4-7 - To be defined. - // NOTE(deanm): Dispose background doesn't really work, apparently most - // browsers ignore the background palette index and clear to transparency. - var disposal = opts.disposal === undefined ? 0 : opts.disposal; - if (disposal < 0 || disposal > 3) // 4-7 is reserved. - throw "Disposal out of range."; - - var use_transparency = false; - var transparent_index = 0; - if (opts.transparent !== undefined && opts.transparent !== null) { - use_transparency = true; - transparent_index = opts.transparent; - if (transparent_index < 0 || transparent_index >= num_colors) - throw "Transparent color index."; - } - - if (disposal !== 0 || use_transparency || delay !== 0) { - // - Graphics Control Extension - buf[p++] = 0x21; buf[p++] = 0xf9; // Extension / Label. - buf[p++] = 4; // Byte size. - - buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0); - buf[p++] = delay & 0xff; buf[p++] = delay >> 8 & 0xff; - buf[p++] = transparent_index; // Transparent color index. - buf[p++] = 0; // Block Terminator. - } - - // - Image Descriptor - buf[p++] = 0x2c; // Image Seperator. - buf[p++] = x & 0xff; buf[p++] = x >> 8 & 0xff; // Left. - buf[p++] = y & 0xff; buf[p++] = y >> 8 & 0xff; // Top. - buf[p++] = w & 0xff; buf[p++] = w >> 8 & 0xff; - buf[p++] = h & 0xff; buf[p++] = h >> 8 & 0xff; - // NOTE: No sort flag (unused?). - // TODO(deanm): Support interlace. - buf[p++] = using_local_palette === true ? (0x80 | (min_code_size-1)) : 0; - - // - Local Color Table - if (using_local_palette === true) { - for (var i = 0, il = palette.length; i < il; ++i) { - var rgb = palette[i]; - buf[p++] = rgb >> 16 & 0xff; - buf[p++] = rgb >> 8 & 0xff; - buf[p++] = rgb & 0xff; - } - } - - p = GifWriterOutputLZWCodeStream( - buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels); - }; - - this.end = function() { - if (ended === false) { - buf[p++] = 0x3b; // Trailer. - ended = true; - } - return p; - }; - } - - // Main compression routine, palette indexes -> LZW code stream. - // |index_stream| must have at least one entry. - function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) { - buf[p++] = min_code_size; - var cur_subblock = p++; // Pointing at the length field. - - var clear_code = 1 << min_code_size; - var code_mask = clear_code - 1; - var eoi_code = clear_code + 1; - var next_code = eoi_code + 1; - - var cur_code_size = min_code_size + 1; // Number of bits per code. - var cur_shift = 0; - // We have at most 12-bit codes, so we should have to hold a max of 19 - // bits here (and then we would write out). - var cur = 0; - - function emit_bytes_to_buffer(bit_block_size) { - while (cur_shift >= bit_block_size) { - buf[p++] = cur & 0xff; - cur >>= 8; cur_shift -= 8; - if (p === cur_subblock + 256) { // Finished a subblock. - buf[cur_subblock] = 255; - cur_subblock = p++; - } - } - } - - function emit_code(c) { - cur |= c << cur_shift; - cur_shift += cur_code_size; - emit_bytes_to_buffer(8); - } - - // I am not an expert on the topic, and I don't want to write a thesis. - // However, it is good to outline here the basic algorithm and the few data - // structures and optimizations here that make this implementation fast. - // The basic idea behind LZW is to build a table of previously seen runs - // addressed by a short id (herein called output code). All data is - // referenced by a code, which represents one or more values from the - // original input stream. All input bytes can be referenced as the same - // value as an output code. So if you didn't want any compression, you - // could more or less just output the original bytes as codes (there are - // some details to this, but it is the idea). In order to achieve - // compression, values greater then the input range (codes can be up to - // 12-bit while input only 8-bit) represent a sequence of previously seen - // inputs. The decompressor is able to build the same mapping while - // decoding, so there is always a shared common knowledge between the - // encoding and decoder, which is also important for "timing" aspects like - // how to handle variable bit width code encoding. - // - // One obvious but very important consequence of the table system is there - // is always a unique id (at most 12-bits) to map the runs. 'A' might be - // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship - // can be used for an effecient lookup strategy for the code mapping. We - // need to know if a run has been seen before, and be able to map that run - // to the output code. Since we start with known unique ids (input bytes), - // and then from those build more unique ids (table entries), we can - // continue this chain (almost like a linked list) to always have small - // integer values that represent the current byte chains in the encoder. - // This means instead of tracking the input bytes (AAAABCD) to know our - // current state, we can track the table entry for AAAABC (it is guaranteed - // to exist by the nature of the algorithm) and the next character D. - // Therefor the tuple of (table_entry, byte) is guaranteed to also be - // unique. This allows us to create a simple lookup key for mapping input - // sequences to codes (table indices) without having to store or search - // any of the code sequences. So if 'AAAA' has a table entry of 12, the - // tuple of ('AAAA', K) for any input byte K will be unique, and can be our - // key. This leads to a integer value at most 20-bits, which can always - // fit in an SMI value and be used as a fast sparse array / object key. - - // Output code for the current contents of the index buffer. - var ib_code = index_stream[0] & code_mask; // Load first input index. - var code_table = { }; // Key'd on our 20-bit "tuple". - - emit_code(clear_code); // Spec says first code should be a clear code. - - // First index already loaded, process the rest of the stream. - for (var i = 1, il = index_stream.length; i < il; ++i) { - var k = index_stream[i] & code_mask; - var cur_key = ib_code << 8 | k; // (prev, k) unique tuple. - var cur_code = code_table[cur_key]; // buffer + k. - - // Check if we have to create a new code table entry. - if (cur_code === undefined) { // We don't have buffer + k. - // Emit index buffer (without k). - // This is an inline version of emit_code, because this is the core - // writing routine of the compressor (and V8 cannot inline emit_code - // because it is a closure here in a different context). Additionally - // we can call emit_byte_to_buffer less often, because we can have - // 30-bits (from our 31-bit signed SMI), and we know our codes will only - // be 12-bits, so can safely have 18-bits there without overflow. - // emit_code(ib_code); - cur |= ib_code << cur_shift; - cur_shift += cur_code_size; - while (cur_shift >= 8) { - buf[p++] = cur & 0xff; - cur >>= 8; cur_shift -= 8; - if (p === cur_subblock + 256) { // Finished a subblock. - buf[cur_subblock] = 255; - cur_subblock = p++; - } - } - - if (next_code === 4096) { // Table full, need a clear. - emit_code(clear_code); - next_code = eoi_code + 1; - cur_code_size = min_code_size + 1; - code_table = { }; - } else { // Table not full, insert a new entry. - // Increase our variable bit code sizes if necessary. This is a bit - // tricky as it is based on "timing" between the encoding and - // decoder. From the encoders perspective this should happen after - // we've already emitted the index buffer and are about to create the - // first table entry that would overflow our current code bit size. - if (next_code >= (1 << cur_code_size)) ++cur_code_size; - code_table[cur_key] = next_code++; // Insert into code table. - } - - ib_code = k; // Index buffer to single input k. - } else { - ib_code = cur_code; // Index buffer to sequence in code table. - } - } - - emit_code(ib_code); // There will still be something in the index buffer. - emit_code(eoi_code); // End Of Information. - - // Flush / finalize the sub-blocks stream to the buffer. - emit_bytes_to_buffer(1); - - // Finish the sub-blocks, writing out any unfinished lengths and - // terminating with a sub-block of length 0. If we have already started - // but not yet used a sub-block it can just become the terminator. - if (cur_subblock + 1 === p) { // Started but unused. - buf[cur_subblock] = 0; - } else { // Started and used, write length and additional terminator block. - buf[cur_subblock] = p - cur_subblock - 1; - buf[p++] = 0; - } - return p; - } - - function GifReader(buf) { - var p = 0; - - // - Header (GIF87a or GIF89a). - if (buf[p++] !== 0x47 || buf[p++] !== 0x49 || buf[p++] !== 0x46 || - buf[p++] !== 0x38 || (buf[p++]+1 & 0xfd) !== 0x38 || buf[p++] !== 0x61) { - throw "Invalid GIF 87a/89a header."; - } - - // - Logical Screen Descriptor. - var width = buf[p++] | buf[p++] << 8; - var height = buf[p++] | buf[p++] << 8; - var pf0 = buf[p++]; // <Packed Fields>. - var global_palette_flag = pf0 >> 7; - var num_global_colors_pow2 = pf0 & 0x7; - var num_global_colors = 1 << (num_global_colors_pow2 + 1); - var background = buf[p++]; - buf[p++]; // Pixel aspect ratio (unused?). - - var global_palette_offset = null; - - if (global_palette_flag) { - global_palette_offset = p; - p += num_global_colors * 3; // Seek past palette. - } - - var no_eof = true; - - var frames = [ ]; - - var delay = 0; - var transparent_index = null; - var disposal = 0; // 0 - No disposal specified. - var loop_count = null; - - this.width = width; - this.height = height; - - while (no_eof && p < buf.length) { - switch (buf[p++]) { - case 0x21: // Graphics Control Extension Block - switch (buf[p++]) { - case 0xff: // Application specific block - // Try if it's a Netscape block (with animation loop counter). - if (buf[p ] !== 0x0b || // 21 FF already read, check block size. - // NETSCAPE2.0 - buf[p+1 ] == 0x4e && buf[p+2 ] == 0x45 && buf[p+3 ] == 0x54 && - buf[p+4 ] == 0x53 && buf[p+5 ] == 0x43 && buf[p+6 ] == 0x41 && - buf[p+7 ] == 0x50 && buf[p+8 ] == 0x45 && buf[p+9 ] == 0x32 && - buf[p+10] == 0x2e && buf[p+11] == 0x30 && - // Sub-block - buf[p+12] == 0x03 && buf[p+13] == 0x01 && buf[p+16] == 0) { - p += 14; - loop_count = buf[p++] | buf[p++] << 8; - p++; // Skip terminator. - } else { // We don't know what it is, just try to get past it. - p += 12; - while (true) { // Seek through subblocks. - var block_size = buf[p++]; - if (block_size === 0) break; - p += block_size; - } - } - break; - - case 0xf9: // Graphics Control Extension - if (buf[p++] !== 0x4 || buf[p+4] !== 0) - throw "Invalid graphics extension block."; - var pf1 = buf[p++]; - delay = buf[p++] | buf[p++] << 8; - transparent_index = buf[p++]; - if ((pf1 & 1) === 0) transparent_index = null; - disposal = pf1 >> 2 & 0x7; - p++; // Skip terminator. - break; - - case 0xfe: // Comment Extension. - while (true) { // Seek through subblocks. - var block_size = buf[p++]; - if (block_size === 0) break; - // console.log(buf.slice(p, p+block_size).toString('ascii')); - p += block_size; - } - break; - - default: - throw "Unknown graphic control label: 0x" + buf[p-1].toString(16); - } - break; - - case 0x2c: // Image Descriptor. - var x = buf[p++] | buf[p++] << 8; - var y = buf[p++] | buf[p++] << 8; - var w = buf[p++] | buf[p++] << 8; - var h = buf[p++] | buf[p++] << 8; - var pf2 = buf[p++]; - var local_palette_flag = pf2 >> 7; - var interlace_flag = pf2 >> 6 & 1; - var num_local_colors_pow2 = pf2 & 0x7; - var num_local_colors = 1 << (num_local_colors_pow2 + 1); - var palette_offset = global_palette_offset; - var has_local_palette = false; - if (local_palette_flag) { - var has_local_palette = true; - palette_offset = p; // Override with local palette. - p += num_local_colors * 3; // Seek past palette. - } - - var data_offset = p; - - p++; // codesize - while (true) { - var block_size = buf[p++]; - if (block_size === 0) break; - p += block_size; - } - - frames.push({x: x, y: y, width: w, height: h, - has_local_palette: has_local_palette, - palette_offset: palette_offset, - data_offset: data_offset, - data_length: p - data_offset, - transparent_index: transparent_index, - interlaced: !!interlace_flag, - delay: delay, - disposal: disposal}); - break; - - case 0x3b: // Trailer Marker (end of file). - no_eof = false; - break; - - default: - throw "Unknown gif block: 0x" + buf[p-1].toString(16); - break; - } - } - - this.numFrames = function() { - return frames.length; - }; - - this.loopCount = function() { - return loop_count; - }; - - this.frameInfo = function(frame_num) { - if (frame_num < 0 || frame_num >= frames.length) - throw "Frame index out of range."; - return frames[frame_num]; - }; - - this.decodeAndBlitFrameBGRA = function(frame_num, pixels) { - var frame = this.frameInfo(frame_num); - var num_pixels = frame.width * frame.height; - var index_stream = new Uint8Array(num_pixels); // At most 8-bit indices. - GifReaderLZWOutputIndexStream( - buf, frame.data_offset, index_stream, num_pixels); - var palette_offset = frame.palette_offset; - - // NOTE(deanm): It seems to be much faster to compare index to 256 than - // to === null. Not sure why, but CompareStub_EQ_STRICT shows up high in - // the profile, not sure if it's related to using a Uint8Array. - var trans = frame.transparent_index; - if (trans === null) trans = 256; - - // We are possibly just blitting to a portion of the entire frame. - // That is a subrect within the framerect, so the additional pixels - // must be skipped over after we finished a scanline. - var framewidth = frame.width; - var framestride = width - framewidth; - var xleft = framewidth; // Number of subrect pixels left in scanline. - - // Output indicies of the top left and bottom right corners of the subrect. - var opbeg = ((frame.y * width) + frame.x) * 4; - var opend = ((frame.y + frame.height) * width + frame.x) * 4; - var op = opbeg; - - var scanstride = framestride * 4; - - // Use scanstride to skip past the rows when interlacing. This is skipping - // 7 rows for the first two passes, then 3 then 1. - if (frame.interlaced === true) { - scanstride += (framewidth + framestride) * 4 * 7; // Pass 1. - } - - var interlaceskip = 8; // Tracking the row interval in the current pass. - - for (var i = 0, il = index_stream.length; i < il; ++i) { - var index = index_stream[i]; - - if (xleft === 0) { // Beginning of new scan line - op += scanstride; - xleft = framewidth; - if (op >= opend) { // Catch the wrap to switch passes when interlacing. - scanstride = - framestride + (framewidth + framestride) * 4 * (interlaceskip-1); - // interlaceskip / 2 * 4 is interlaceskip << 1. - op = opbeg + (framewidth + framestride) * (interlaceskip << 1); - interlaceskip >>= 1; - } - } - - if (index === trans) { - op += 4; - } else { - var r = buf[palette_offset + index * 3]; - var g = buf[palette_offset + index * 3 + 1]; - var b = buf[palette_offset + index * 3 + 2]; - pixels[op++] = b; - pixels[op++] = g; - pixels[op++] = r; - pixels[op++] = 255; - } - --xleft; - } - }; - - // I will go to copy and paste hell one day... - this.decodeAndBlitFrameRGBA = function(frame_num, pixels) { - var frame = this.frameInfo(frame_num); - var num_pixels = frame.width * frame.height; - var index_stream = new Uint8Array(num_pixels); // At most 8-bit indices. - GifReaderLZWOutputIndexStream( - buf, frame.data_offset, index_stream, num_pixels); - var palette_offset = frame.palette_offset; - - // NOTE(deanm): It seems to be much faster to compare index to 256 than - // to === null. Not sure why, but CompareStub_EQ_STRICT shows up high in - // the profile, not sure if it's related to using a Uint8Array. - var trans = frame.transparent_index; - if (trans === null) trans = 256; - - // We are possibly just blitting to a portion of the entire frame. - // That is a subrect within the framerect, so the additional pixels - // must be skipped over after we finished a scanline. - var framewidth = frame.width; - var framestride = width - framewidth; - var xleft = framewidth; // Number of subrect pixels left in scanline. - - // Output indicies of the top left and bottom right corners of the subrect. - var opbeg = ((frame.y * width) + frame.x) * 4; - var opend = ((frame.y + frame.height) * width + frame.x) * 4; - var op = opbeg; - - var scanstride = framestride * 4; - - // Use scanstride to skip past the rows when interlacing. This is skipping - // 7 rows for the first two passes, then 3 then 1. - if (frame.interlaced === true) { - scanstride += (framewidth + framestride) * 4 * 7; // Pass 1. - } - - var interlaceskip = 8; // Tracking the row interval in the current pass. - - for (var i = 0, il = index_stream.length; i < il; ++i) { - var index = index_stream[i]; - - if (xleft === 0) { // Beginning of new scan line - op += scanstride; - xleft = framewidth; - if (op >= opend) { // Catch the wrap to switch passes when interlacing. - scanstride = - framestride + (framewidth + framestride) * 4 * (interlaceskip-1); - // interlaceskip / 2 * 4 is interlaceskip << 1. - op = opbeg + (framewidth + framestride) * (interlaceskip << 1); - interlaceskip >>= 1; - } - } - - if (index === trans) { - op += 4; - } else { - var r = buf[palette_offset + index * 3]; - var g = buf[palette_offset + index * 3 + 1]; - var b = buf[palette_offset + index * 3 + 2]; - pixels[op++] = r; - pixels[op++] = g; - pixels[op++] = b; - pixels[op++] = 255; - } - --xleft; - } - }; - } - - function GifReaderLZWOutputIndexStream(code_stream, p, output, output_length) { - var min_code_size = code_stream[p++]; - - var clear_code = 1 << min_code_size; - var eoi_code = clear_code + 1; - var next_code = eoi_code + 1; - - var cur_code_size = min_code_size + 1; // Number of bits per code. - // NOTE: This shares the same name as the encoder, but has a different - // meaning here. Here this masks each code coming from the code stream. - var code_mask = (1 << cur_code_size) - 1; - var cur_shift = 0; - var cur = 0; - - var op = 0; // Output pointer. - - var subblock_size = code_stream[p++]; - - // TODO(deanm): Would using a TypedArray be any faster? At least it would - // solve the fast mode / backing store uncertainty. - // var code_table = Array(4096); - var code_table = new Int32Array(4096); // Can be signed, we only use 20 bits. - - var prev_code = null; // Track code-1. - - while (true) { - // Read up to two bytes, making sure we always 12-bits for max sized code. - while (cur_shift < 16) { - if (subblock_size === 0) break; // No more data to be read. - - cur |= code_stream[p++] << cur_shift; - cur_shift += 8; - - if (subblock_size === 1) { // Never let it get to 0 to hold logic above. - subblock_size = code_stream[p++]; // Next subblock. - } else { - --subblock_size; - } - } - - // TODO(deanm): We should never really get here, we should have received - // and EOI. - if (cur_shift < cur_code_size) - break; - - var code = cur & code_mask; - cur >>= cur_code_size; - cur_shift -= cur_code_size; - - // TODO(deanm): Maybe should check that the first code was a clear code, - // at least this is what you're supposed to do. But actually our encoder - // now doesn't emit a clear code first anyway. - if (code === clear_code) { - // We don't actually have to clear the table. This could be a good idea - // for greater error checking, but we don't really do any anyway. We - // will just track it with next_code and overwrite old entries. - - next_code = eoi_code + 1; - cur_code_size = min_code_size + 1; - code_mask = (1 << cur_code_size) - 1; - - // Don't update prev_code ? - prev_code = null; - continue; - } else if (code === eoi_code) { - break; - } - - // We have a similar situation as the decoder, where we want to store - // variable length entries (code table entries), but we want to do in a - // faster manner than an array of arrays. The code below stores sort of a - // linked list within the code table, and then "chases" through it to - // construct the dictionary entries. When a new entry is created, just the - // last byte is stored, and the rest (prefix) of the entry is only - // referenced by its table entry. Then the code chases through the - // prefixes until it reaches a single byte code. We have to chase twice, - // first to compute the length, and then to actually copy the data to the - // output (backwards, since we know the length). The alternative would be - // storing something in an intermediate stack, but that doesn't make any - // more sense. I implemented an approach where it also stored the length - // in the code table, although it's a bit tricky because you run out of - // bits (12 + 12 + 8), but I didn't measure much improvements (the table - // entries are generally not the long). Even when I created benchmarks for - // very long table entries the complexity did not seem worth it. - // The code table stores the prefix entry in 12 bits and then the suffix - // byte in 8 bits, so each entry is 20 bits. - - var chase_code = code < next_code ? code : prev_code; - - // Chase what we will output, either {CODE} or {CODE-1}. - var chase_length = 0; - var chase = chase_code; - while (chase > clear_code) { - chase = code_table[chase] >> 8; - ++chase_length; - } - - var k = chase; - - var op_end = op + chase_length + (chase_code !== code ? 1 : 0); - if (op_end > output_length) { - console.log("Warning, gif stream longer than expected."); - return; - } - - // Already have the first byte from the chase, might as well write it fast. - output[op++] = k; - - op += chase_length; - var b = op; // Track pointer, writing backwards. - - if (chase_code !== code) // The case of emitting {CODE-1} + k. - output[op++] = k; - - chase = chase_code; - while (chase_length--) { - chase = code_table[chase]; - output[--b] = chase & 0xff; // Write backwards. - chase >>= 8; // Pull down to the prefix code. - } - - if (prev_code !== null && next_code < 4096) { - code_table[next_code++] = prev_code << 8 | k; - // TODO(deanm): Figure out this clearing vs code growth logic better. I - // have an feeling that it should just happen somewhere else, for now it - // is awkward between when we grow past the max and then hit a clear code. - // For now just check if we hit the max 12-bits (then a clear code should - // follow, also of course encoded in 12-bits). - if (next_code >= code_mask+1 && cur_code_size < 12) { - ++cur_code_size; - code_mask = code_mask << 1 | 1; - } - } - - prev_code = code; - } - - if (op !== output_length) { - console.log("Warning, gif stream shorter than expected."); - } - - return output; - } - - try { exports.GifWriter = GifWriter; exports.GifReader = GifReader; } catch(e) { } // CommonJS. - - - /* - Copyright (c) 2008, Adobe Systems Incorporated - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Adobe Systems Incorporated nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - /* - JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009 - - Basic GUI blocking jpeg encoder - */ - - function JPEGEncoder(quality) { - var ffloor = Math.floor; - var YTable = new Array(64); - var UVTable = new Array(64); - var fdtbl_Y = new Array(64); - var fdtbl_UV = new Array(64); - var YDC_HT; - var UVDC_HT; - var YAC_HT; - var UVAC_HT; - - var bitcode = new Array(65535); - var category = new Array(65535); - var outputfDCTQuant = new Array(64); - var DU = new Array(64); - var byteout = []; - var bytenew = 0; - var bytepos = 7; - - var YDU = new Array(64); - var UDU = new Array(64); - var VDU = new Array(64); - var clt = new Array(256); - var RGB_YUV_TABLE = new Array(2048); - var currentQuality; - - var ZigZag = [ - 0, 1, 5, 6,14,15,27,28, - 2, 4, 7,13,16,26,29,42, - 3, 8,12,17,25,30,41,43, - 9,11,18,24,31,40,44,53, - 10,19,23,32,39,45,52,54, - 20,22,33,38,46,51,55,60, - 21,34,37,47,50,56,59,61, - 35,36,48,49,57,58,62,63 - ]; - - var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; - var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; - var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; - var std_ac_luminance_values = [ - 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, - 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, - 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, - 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, - 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, - 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, - 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, - 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, - 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, - 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, - 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, - 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, - 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, - 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, - 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, - 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, - 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, - 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, - 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, - 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, - 0xf9,0xfa - ]; - - var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; - var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; - var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; - var std_ac_chrominance_values = [ - 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, - 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, - 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, - 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, - 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, - 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, - 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, - 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, - 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, - 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, - 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, - 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, - 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, - 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, - 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, - 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, - 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, - 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, - 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, - 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, - 0xf9,0xfa - ]; - - function initQuantTables(sf){ - var YQT = [ - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68,109,103, 77, - 24, 35, 55, 64, 81,104,113, 92, - 49, 64, 78, 87,103,121,120,101, - 72, 92, 95, 98,112,100,103, 99 - ]; - - for (var i = 0; i < 64; i++) { - var t = ffloor((YQT[i]*sf+50)/100); - if (t < 1) { - t = 1; - } else if (t > 255) { - t = 255; - } - YTable[ZigZag[i]] = t; - } - var UVQT = [ - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 - ]; - for (var j = 0; j < 64; j++) { - var u = ffloor((UVQT[j]*sf+50)/100); - if (u < 1) { - u = 1; - } else if (u > 255) { - u = 255; - } - UVTable[ZigZag[j]] = u; - } - var aasf = [ - 1.0, 1.387039845, 1.306562965, 1.175875602, - 1.0, 0.785694958, 0.541196100, 0.275899379 - ]; - var k = 0; - for (var row = 0; row < 8; row++) - { - for (var col = 0; col < 8; col++) - { - fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); - fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); - k++; - } - } - } - - function computeHuffmanTbl(nrcodes, std_table){ - var codevalue = 0; - var pos_in_table = 0; - var HT = new Array(); - for (var k = 1; k <= 16; k++) { - for (var j = 1; j <= nrcodes[k]; j++) { - HT[std_table[pos_in_table]] = []; - HT[std_table[pos_in_table]][0] = codevalue; - HT[std_table[pos_in_table]][1] = k; - pos_in_table++; - codevalue++; - } - codevalue*=2; - } - return HT; - } - - function initHuffmanTbl() - { - YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); - UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); - YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); - UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); - } - - function initCategoryNumber() - { - var nrlower = 1; - var nrupper = 2; - for (var cat = 1; cat <= 15; cat++) { - //Positive numbers - for (var nr = nrlower; nr<nrupper; nr++) { - category[32767+nr] = cat; - bitcode[32767+nr] = []; - bitcode[32767+nr][1] = cat; - bitcode[32767+nr][0] = nr; - } - //Negative numbers - for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) { - category[32767+nrneg] = cat; - bitcode[32767+nrneg] = []; - bitcode[32767+nrneg][1] = cat; - bitcode[32767+nrneg][0] = nrupper-1+nrneg; - } - nrlower <<= 1; - nrupper <<= 1; - } - } - - function initRGBYUVTable() { - for(var i = 0; i < 256;i++) { - RGB_YUV_TABLE[i] = 19595 * i; - RGB_YUV_TABLE[(i+ 256)>>0] = 38470 * i; - RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000; - RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i; - RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i; - RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF; - RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i; - RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i; - } - } - - // IO functions - function writeBits(bs) - { - var value = bs[0]; - var posval = bs[1]-1; - while ( posval >= 0 ) { - if (value & (1 << posval) ) { - bytenew |= (1 << bytepos); - } - posval--; - bytepos--; - if (bytepos < 0) { - if (bytenew == 0xFF) { - writeByte(0xFF); - writeByte(0); - } - else { - writeByte(bytenew); - } - bytepos=7; - bytenew=0; - } - } - } - - function writeByte(value) - { - //byteout.push(clt[value]); // write char directly instead of converting later - byteout.push(value); - } - - function writeWord(value) - { - writeByte((value>>8)&0xFF); - writeByte((value )&0xFF); - } - - // DCT & quantization core - function fDCTQuant(data, fdtbl) - { - var d0, d1, d2, d3, d4, d5, d6, d7; - /* Pass 1: process rows. */ - var dataOff=0; - var i; - var I8 = 8; - var I64 = 64; - for (i=0; i<I8; ++i) - { - d0 = data[dataOff]; - d1 = data[dataOff+1]; - d2 = data[dataOff+2]; - d3 = data[dataOff+3]; - d4 = data[dataOff+4]; - d5 = data[dataOff+5]; - d6 = data[dataOff+6]; - d7 = data[dataOff+7]; - - var tmp0 = d0 + d7; - var tmp7 = d0 - d7; - var tmp1 = d1 + d6; - var tmp6 = d1 - d6; - var tmp2 = d2 + d5; - var tmp5 = d2 - d5; - var tmp3 = d3 + d4; - var tmp4 = d3 - d4; - - /* Even part */ - var tmp10 = tmp0 + tmp3; /* phase 2 */ - var tmp13 = tmp0 - tmp3; - var tmp11 = tmp1 + tmp2; - var tmp12 = tmp1 - tmp2; - - data[dataOff] = tmp10 + tmp11; /* phase 3 */ - data[dataOff+4] = tmp10 - tmp11; - - var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */ - data[dataOff+2] = tmp13 + z1; /* phase 5 */ - data[dataOff+6] = tmp13 - z1; - - /* Odd part */ - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */ - var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */ - var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */ - var z3 = tmp11 * 0.707106781; /* c4 */ - - var z11 = tmp7 + z3; /* phase 5 */ - var z13 = tmp7 - z3; - - data[dataOff+5] = z13 + z2; /* phase 6 */ - data[dataOff+3] = z13 - z2; - data[dataOff+1] = z11 + z4; - data[dataOff+7] = z11 - z4; - - dataOff += 8; /* advance pointer to next row */ - } - - /* Pass 2: process columns. */ - dataOff = 0; - for (i=0; i<I8; ++i) - { - d0 = data[dataOff]; - d1 = data[dataOff + 8]; - d2 = data[dataOff + 16]; - d3 = data[dataOff + 24]; - d4 = data[dataOff + 32]; - d5 = data[dataOff + 40]; - d6 = data[dataOff + 48]; - d7 = data[dataOff + 56]; - - var tmp0p2 = d0 + d7; - var tmp7p2 = d0 - d7; - var tmp1p2 = d1 + d6; - var tmp6p2 = d1 - d6; - var tmp2p2 = d2 + d5; - var tmp5p2 = d2 - d5; - var tmp3p2 = d3 + d4; - var tmp4p2 = d3 - d4; - - /* Even part */ - var tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */ - var tmp13p2 = tmp0p2 - tmp3p2; - var tmp11p2 = tmp1p2 + tmp2p2; - var tmp12p2 = tmp1p2 - tmp2p2; - - data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */ - data[dataOff+32] = tmp10p2 - tmp11p2; - - var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */ - data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */ - data[dataOff+48] = tmp13p2 - z1p2; - - /* Odd part */ - tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */ - tmp11p2 = tmp5p2 + tmp6p2; - tmp12p2 = tmp6p2 + tmp7p2; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */ - var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */ - var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */ - var z3p2 = tmp11p2 * 0.707106781; /* c4 */ - - var z11p2 = tmp7p2 + z3p2; /* phase 5 */ - var z13p2 = tmp7p2 - z3p2; - - data[dataOff+40] = z13p2 + z2p2; /* phase 6 */ - data[dataOff+24] = z13p2 - z2p2; - data[dataOff+ 8] = z11p2 + z4p2; - data[dataOff+56] = z11p2 - z4p2; - - dataOff++; /* advance pointer to next column */ - } - - // Quantize/descale the coefficients - var fDCTQuant; - for (i=0; i<I64; ++i) - { - // Apply the quantization and scaling factor & Round to nearest integer - fDCTQuant = data[i]*fdtbl[i]; - outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0); - //outputfDCTQuant[i] = fround(fDCTQuant); - - } - return outputfDCTQuant; - } - - function writeAPP0() - { - writeWord(0xFFE0); // marker - writeWord(16); // length - writeByte(0x4A); // J - writeByte(0x46); // F - writeByte(0x49); // I - writeByte(0x46); // F - writeByte(0); // = "JFIF",'\0' - writeByte(1); // versionhi - writeByte(1); // versionlo - writeByte(0); // xyunits - writeWord(1); // xdensity - writeWord(1); // ydensity - writeByte(0); // thumbnwidth - writeByte(0); // thumbnheight - } - - function writeSOF0(width, height) - { - writeWord(0xFFC0); // marker - writeWord(17); // length, truecolor YUV JPG - writeByte(8); // precision - writeWord(height); - writeWord(width); - writeByte(3); // nrofcomponents - writeByte(1); // IdY - writeByte(0x11); // HVY - writeByte(0); // QTY - writeByte(2); // IdU - writeByte(0x11); // HVU - writeByte(1); // QTU - writeByte(3); // IdV - writeByte(0x11); // HVV - writeByte(1); // QTV - } - - function writeDQT() - { - writeWord(0xFFDB); // marker - writeWord(132); // length - writeByte(0); - for (var i=0; i<64; i++) { - writeByte(YTable[i]); - } - writeByte(1); - for (var j=0; j<64; j++) { - writeByte(UVTable[j]); - } - } - - function writeDHT() - { - writeWord(0xFFC4); // marker - writeWord(0x01A2); // length - - writeByte(0); // HTYDCinfo - for (var i=0; i<16; i++) { - writeByte(std_dc_luminance_nrcodes[i+1]); - } - for (var j=0; j<=11; j++) { - writeByte(std_dc_luminance_values[j]); - } - - writeByte(0x10); // HTYACinfo - for (var k=0; k<16; k++) { - writeByte(std_ac_luminance_nrcodes[k+1]); - } - for (var l=0; l<=161; l++) { - writeByte(std_ac_luminance_values[l]); - } - - writeByte(1); // HTUDCinfo - for (var m=0; m<16; m++) { - writeByte(std_dc_chrominance_nrcodes[m+1]); - } - for (var n=0; n<=11; n++) { - writeByte(std_dc_chrominance_values[n]); - } - - writeByte(0x11); // HTUACinfo - for (var o=0; o<16; o++) { - writeByte(std_ac_chrominance_nrcodes[o+1]); - } - for (var p=0; p<=161; p++) { - writeByte(std_ac_chrominance_values[p]); - } - } - - function writeSOS() - { - writeWord(0xFFDA); // marker - writeWord(12); // length - writeByte(3); // nrofcomponents - writeByte(1); // IdY - writeByte(0); // HTY - writeByte(2); // IdU - writeByte(0x11); // HTU - writeByte(3); // IdV - writeByte(0x11); // HTV - writeByte(0); // Ss - writeByte(0x3f); // Se - writeByte(0); // Bf - } - - function processDU(CDU, fdtbl, DC, HTDC, HTAC){ - var EOB = HTAC[0x00]; - var M16zeroes = HTAC[0xF0]; - var pos; - var I16 = 16; - var I63 = 63; - var I64 = 64; - var DU_DCT = fDCTQuant(CDU, fdtbl); - //ZigZag reorder - for (var j=0;j<I64;++j) { - DU[ZigZag[j]]=DU_DCT[j]; - } - var Diff = DU[0] - DC; DC = DU[0]; - //Encode DC - if (Diff==0) { - writeBits(HTDC[0]); // Diff might be 0 - } else { - pos = 32767+Diff; - writeBits(HTDC[category[pos]]); - writeBits(bitcode[pos]); - } - //Encode ACs - var end0pos = 63; // was const... which is crazy - for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {} //end0pos = first element in reverse order !=0 - if ( end0pos == 0) { - writeBits(EOB); - return DC; - } - var i = 1; - var lng; - while ( i <= end0pos ) { - var startpos = i; - for (; (DU[i]==0) && (i<=end0pos); ++i) {} - var nrzeroes = i-startpos; - if ( nrzeroes >= I16 ) { - lng = nrzeroes>>4; - for (var nrmarker=1; nrmarker <= lng; ++nrmarker) - writeBits(M16zeroes); - nrzeroes = nrzeroes&0xF; - } - pos = 32767+DU[i]; - writeBits(HTAC[(nrzeroes<<4)+category[pos]]); - writeBits(bitcode[pos]); - i++; - } - if ( end0pos != I63 ) { - writeBits(EOB); - } - return DC; - } - - function initCharLookupTable(){ - var sfcc = String.fromCharCode; - for(var i=0; i < 256; i++){ ///// ACHTUNG // 255 - clt[i] = sfcc(i); - } - } - - this.encode = function(image,quality) // image data object - { - var time_start = new Date().getTime(); - - if(quality) setQuality(quality); - - // Initialize bit writer - byteout = new Array(); - bytenew=0; - bytepos=7; - - // Add JPEG headers - writeWord(0xFFD8); // SOI - writeAPP0(); - writeDQT(); - writeSOF0(image.width,image.height); - writeDHT(); - writeSOS(); - - - // Encode 8x8 macroblocks - var DCY=0; - var DCU=0; - var DCV=0; - - bytenew=0; - bytepos=7; - - - this.encode.displayName = "_encode_"; - - var imageData = image.data; - var width = image.width; - var height = image.height; - - var quadWidth = width*4; - - var x, y = 0; - var r, g, b; - var start,p, col,row,pos; - while(y < height){ - x = 0; - while(x < quadWidth){ - start = quadWidth * y + x; - p = start; - col = -1; - row = 0; - - for(pos=0; pos < 64; pos++){ - row = pos >> 3;// /8 - col = ( pos & 7 ) * 4; // %8 - p = start + ( row * quadWidth ) + col; - - if(y+row >= height){ // padding bottom - p-= (quadWidth*(y+1+row-height)); - } - - if(x+col >= quadWidth){ // padding right - p-= ((x+col) - quadWidth +4); - } - - r = imageData[ p++ ]; - g = imageData[ p++ ]; - b = imageData[ p++ ]; - - - /* // calculate YUV values dynamically - YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80 - UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b)); - VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b)); - */ - - // use lookup table (slightly faster) - YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128; - UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128; - VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128; - - } - - DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); - DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); - x+=32; - } - y+=8; - } - - - //////////////////////////////////////////////////////////////// - - // Do the bit alignment of the EOI marker - if ( bytepos >= 0 ) { - var fillbits = []; - fillbits[1] = bytepos+1; - fillbits[0] = (1<<(bytepos+1))-1; - writeBits(fillbits); - } - - writeWord(0xFFD9); //EOI - - return new Uint8Array(byteout); - //return new Buffer(byteout); - - var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join('')); - - byteout = []; - - // benchmarking - var duration = new Date().getTime() - time_start; - //console.log('Encoding time: '+ duration + 'ms'); - // - - return jpegDataUri - }; - - function setQuality(quality){ - if (quality <= 0) { - quality = 1; - } - if (quality > 100) { - quality = 100; - } - - if(currentQuality == quality) return // don't recalc if unchanged - - var sf = 0; - if (quality < 50) { - sf = Math.floor(5000 / quality); - } else { - sf = Math.floor(200 - quality*2); - } - - initQuantTables(sf); - currentQuality = quality; - //console.log('Quality set to: '+quality +'%'); - } - - function init(){ - var time_start = new Date().getTime(); - if(!quality) quality = 50; - // Create tables - initCharLookupTable(); - initHuffmanTbl(); - initCategoryNumber(); - initRGBYUVTable(); - - setQuality(quality); - var duration = new Date().getTime() - time_start; - //console.log('Initialization '+ duration + 'ms'); - } - - init(); - - } - - - try { module.exports = JPEGEncoder; } catch(e) { } // CommonJS. - - /** - * @author shaozilee - * - * Bmp format decoder,support 1bit 4bit 8bit 24bit bmp - * - */ - - function BmpDecoder(buffer,is_with_alpha) { - this.pos = 0; - this.buffer = buffer; - this.datav = new DataView(buffer.buffer); - this.is_with_alpha = !!is_with_alpha; - this.bottom_up = true; - this.flag = String.fromCharCode(this.buffer[0]) + String.fromCharCode(this.buffer[1]); - this.pos += 2; - if (["BM","BA", "CI", "CP", "IC", "PT"].indexOf(this.flag) === -1) throw new Error("Invalid BMP File"); - this.parseHeader(); - this.parseBGR(); - } - - BmpDecoder.prototype.parseHeader = function() { - this.fileSize = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.reserved = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.offset = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.headerSize = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.width = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.height = this.datav.getInt32(this.pos, true); this.pos += 4; - this.planes = this.datav.getUint16(this.pos, true); - this.pos += 2; - this.bitPP = this.datav.getUint16(this.pos, true); - this.pos += 2; - this.compress = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.rawSize = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.hr = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.vr = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.colors = this.datav.getUint32(this.pos, true); - this.pos += 4; - this.importantColors = this.datav.getUint32(this.pos, true); - this.pos += 4; - - if(this.bitPP === 16 && this.is_with_alpha){ - this.bitPP = 15; - } - if (this.bitPP < 15) { - var len = this.colors === 0 ? 1 << this.bitPP : this.colors; - this.palette = new Array(len); - for (var i = 0; i < len; i++) { - var blue = this.datav.getUint8(this.pos++, true); var green = this.datav.getUint8(this.pos++, true); var red = this.datav.getUint8(this.pos++, true); var quad = this.datav.getUint8(this.pos++, true); this.palette[i] = { - red: red, - green: green, - blue: blue, - quad: quad - }; - } - } - if(this.height < 0) { - this.height *= -1; - this.bottom_up = false; - } - - }; - - BmpDecoder.prototype.parseBGR = function() { - this.pos = this.offset; - try { - var bitn = "bit" + this.bitPP; - var len = this.width * this.height * 4; - this.data = new Uint8Array(len); - - this[bitn](); - } catch (e) { - console.log("bit decode error:" + e); - } - - }; - - BmpDecoder.prototype.bit1 = function() { - var xlen = Math.ceil(this.width / 8); - var mode = xlen%4; - var y = this.height >= 0 ? this.height - 1 : -this.height; - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < xlen; x++) { - var b = this.datav.getUint8(this.pos++, true); var location = line * this.width * 4 + x*8*4; - for (var i = 0; i < 8; i++) { - if(x*8+i<this.width){ - var rgb = this.palette[((b>>(7-i))&0x1)]; - this.data[location+i*4] = rgb.blue; - this.data[location+i*4 + 1] = rgb.green; - this.data[location+i*4 + 2] = rgb.red; - this.data[location+i*4 + 3] = 0xFF; - }else{ - break; - } - } - } - - if (mode != 0){ - this.pos+=(4 - mode); - } - } - }; - - BmpDecoder.prototype.bit4 = function() { - var xlen = Math.ceil(this.width/2); - var mode = xlen%4; - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < xlen; x++) { - var b = this.datav.getUint8(this.pos++, true); var location = line * this.width * 4 + x*2*4; - - var before = b>>4; - var after = b&0x0F; - - var rgb = this.palette[before]; - this.data[location] = rgb.blue; - this.data[location + 1] = rgb.green; - this.data[location + 2] = rgb.red; - this.data[location + 3] = 0xFF; - - if(x*2+1>=this.width)break; - - rgb = this.palette[after]; - this.data[location+4] = rgb.blue; - this.data[location+4 + 1] = rgb.green; - this.data[location+4 + 2] = rgb.red; - this.data[location+4 + 3] = 0xFF; - } - - if (mode != 0){ - this.pos+=(4 - mode); - } - } - - }; - - BmpDecoder.prototype.bit8 = function() { - var mode = this.width%4; - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < this.width; x++) { - var b = this.datav.getUint8(this.pos++, true); var location = line * this.width * 4 + x*4; - if(b < this.palette.length) { - var rgb = this.palette[b]; - this.data[location] = rgb.red; - this.data[location + 1] = rgb.green; - this.data[location + 2] = rgb.blue; - this.data[location + 3] = 0xFF; - } else { - this.data[location] = 0xFF; - this.data[location + 1] = 0xFF; - this.data[location + 2] = 0xFF; - this.data[location + 3] = 0xFF; - } - } - if (mode != 0){ - this.pos+=(4 - mode); - } - } - }; - - BmpDecoder.prototype.bit15 = function() { - var dif_w =this.width % 3; - var _11111 = parseInt("11111", 2),_1_5 = _11111; - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < this.width; x++) { - - var B = this.datav.getUint16(this.pos, true); - this.pos+=2; - var blue = (B & _1_5) / _1_5 * 255 | 0; - var green = (B >> 5 & _1_5 ) / _1_5 * 255 | 0; - var red = (B >> 10 & _1_5) / _1_5 * 255 | 0; - var alpha = (B>>15)?0xFF:0x00; - - var location = line * this.width * 4 + x * 4; - this.data[location] = red; - this.data[location + 1] = green; - this.data[location + 2] = blue; - this.data[location + 3] = alpha; - } - //skip extra bytes - this.pos += dif_w; - } - }; - - BmpDecoder.prototype.bit16 = function() { - var dif_w =this.width % 3; - var _11111 = parseInt("11111", 2),_1_5 = _11111; - var _111111 = parseInt("111111", 2),_1_6 = _111111; - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < this.width; x++) { - - var B = this.datav.getUint16(this.pos, true); - this.pos+=2; - var alpha = 0xFF; - var blue = (B & _1_5) / _1_5 * 255 | 0; - var green = (B >> 5 & _1_6 ) / _1_6 * 255 | 0; - var red = (B >> 11) / _1_5 * 255 | 0; - - var location = line * this.width * 4 + x * 4; - this.data[location] = red; - this.data[location + 1] = green; - this.data[location + 2] = blue; - this.data[location + 3] = alpha; - } - //skip extra bytes - this.pos += dif_w; - } - }; - - BmpDecoder.prototype.bit24 = function() { - //when height > 0 - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < this.width; x++) { - var blue = this.datav.getUint8(this.pos++, true); var green = this.datav.getUint8(this.pos++, true); var red = this.datav.getUint8(this.pos++, true); var location = line * this.width * 4 + x * 4; - this.data[location] = red; - this.data[location + 1] = green; - this.data[location + 2] = blue; - this.data[location + 3] = 0xFF; - } - //skip extra bytes - this.pos += (this.width % 4); - } - - }; - - /** - * add 32bit decode func - * @author soubok - */ - BmpDecoder.prototype.bit32 = function() { - //when height > 0 - for (var y = this.height - 1; y >= 0; y--) { - var line = this.bottom_up ? y : this.height - 1 - y; - for (var x = 0; x < this.width; x++) { - var blue = this.datav.getUint8(this.pos++, true); var green = this.datav.getUint8(this.pos++, true); var red = this.datav.getUint8(this.pos++, true); var alpha = this.datav.getUint8(this.pos++, true); var location = line * this.width * 4 + x * 4; - this.data[location] = red; - this.data[location + 1] = green; - this.data[location + 2] = blue; - this.data[location + 3] = alpha; - } - //skip extra bytes - //this.pos += (this.width % 4); - } - - }; - - BmpDecoder.prototype.getData = function() { - return this.data; - }; - - try { - module.exports = function(bmpData) { - var decoder = new BmpDecoder(bmpData); - return { - data: decoder.getData(), - width: decoder.width, - height: decoder.height - }; - }; - } catch(e) { } // CommonJS. - - - /* - Copyright (c) 2013 Gildas Lormeau. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, - INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - /* - * This program is based on JZlib 1.0.2 ymnk, JCraft,Inc. - * JZlib is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - - (function(global) { - - // Global - - var MAX_BITS = 15; - var D_CODES = 30; - var BL_CODES = 19; - - var LENGTH_CODES = 29; - var LITERALS = 256; - var L_CODES = (LITERALS + 1 + LENGTH_CODES); - var HEAP_SIZE = (2 * L_CODES + 1); - - var END_BLOCK = 256; - - // Bit length codes must not exceed MAX_BL_BITS bits - var MAX_BL_BITS = 7; - - // repeat previous bit length 3-6 times (2 bits of repeat count) - var REP_3_6 = 16; - - // repeat a zero length 3-10 times (3 bits of repeat count) - var REPZ_3_10 = 17; - - // repeat a zero length 11-138 times (7 bits of repeat count) - var REPZ_11_138 = 18; - - // The lengths of the bit length codes are sent in order of decreasing - // probability, to avoid transmitting the lengths for unused bit - // length codes. - - var Buf_size = 8 * 2; - - // JZlib version : "1.0.2" - var Z_DEFAULT_COMPRESSION = -1; - - // compression strategy - var Z_FILTERED = 1; - var Z_HUFFMAN_ONLY = 2; - var Z_DEFAULT_STRATEGY = 0; - - var Z_NO_FLUSH = 0; - var Z_PARTIAL_FLUSH = 1; - var Z_FULL_FLUSH = 3; - var Z_FINISH = 4; - - var Z_OK = 0; - var Z_STREAM_END = 1; - var Z_NEED_DICT = 2; - var Z_STREAM_ERROR = -2; - var Z_DATA_ERROR = -3; - var Z_BUF_ERROR = -5; - - // Tree - - // see definition of array dist_code below - var _dist_code = [ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, - 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 ]; - - function Tree() { - var that = this; - - // dyn_tree; // the dynamic tree - // max_code; // largest code with non zero frequency - // stat_desc; // the corresponding static tree - - // Compute the optimal bit lengths for a tree and update the total bit - // length - // for the current block. - // IN assertion: the fields freq and dad are set, heap[heap_max] and - // above are the tree nodes sorted by increasing frequency. - // OUT assertions: the field len is set to the optimal bit length, the - // array bl_count contains the frequencies for each bit length. - // The length opt_len is updated; static_len is also updated if stree is - // not null. - function gen_bitlen(s) { - var tree = that.dyn_tree; - var stree = that.stat_desc.static_tree; - var extra = that.stat_desc.extra_bits; - var base = that.stat_desc.extra_base; - var max_length = that.stat_desc.max_length; - var h; // heap index - var n, m; // iterate over the tree elements - var bits; // bit length - var xbits; // extra bits - var f; // frequency - var overflow = 0; // number of elements with bit length too large - - for (bits = 0; bits <= MAX_BITS; bits++) - s.bl_count[bits] = 0; - - // In a first pass, compute the optimal bit lengths (which may - // overflow in the case of the bit length tree). - tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap - - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; - if (bits > max_length) { - bits = max_length; - overflow++; - } - tree[n * 2 + 1] = bits; - // We overwrite tree[n*2+1] which is no longer needed - - if (n > that.max_code) - continue; // not a leaf node - - s.bl_count[bits]++; - xbits = 0; - if (n >= base) - xbits = extra[n - base]; - f = tree[n * 2]; - s.opt_len += f * (bits + xbits); - if (stree) - s.static_len += f * (stree[n * 2 + 1] + xbits); - } - if (overflow === 0) - return; - - // This happens for example on obj2 and pic of the Calgary corpus - // Find the first bit length which could increase: - do { - bits = max_length - 1; - while (s.bl_count[bits] === 0) - bits--; - s.bl_count[bits]--; // move one leaf down the tree - s.bl_count[bits + 1] += 2; // move one overflow item as its brother - s.bl_count[max_length]--; - // The brother of the overflow item also moves one step up, - // but this does not affect bl_count[max_length] - overflow -= 2; - } while (overflow > 0); - - for (bits = max_length; bits !== 0; bits--) { - n = s.bl_count[bits]; - while (n !== 0) { - m = s.heap[--h]; - if (m > that.max_code) - continue; - if (tree[m * 2 + 1] != bits) { - s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2]; - tree[m * 2 + 1] = bits; - } - n--; - } - } - } - - // Reverse the first len bits of a code, using straightforward code (a - // faster - // method would use a table) - // IN assertion: 1 <= len <= 15 - function bi_reverse(code, // the value to invert - len // its bit length - ) { - var res = 0; - do { - res |= code & 1; - code >>>= 1; - res <<= 1; - } while (--len > 0); - return res >>> 1; - } - - // Generate the codes for a given tree and bit counts (which need not be - // optimal). - // IN assertion: the array bl_count contains the bit length statistics for - // the given tree and the field len is set for all tree elements. - // OUT assertion: the field code is set for all tree elements of non - // zero code length. - function gen_codes(tree, // the tree to decorate - max_code, // largest code with non zero frequency - bl_count // number of codes at each bit length - ) { - var next_code = []; // next code value for each - // bit length - var code = 0; // running code value - var bits; // bit index - var n; // code index - var len; - - // The distribution counts are first used to generate the code values - // without bit reversal. - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = ((code + bl_count[bits - 1]) << 1); - } - - // Check that the bit counts in bl_count are consistent. The last code - // must be all ones. - // Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, - // "inconsistent bit counts"); - // Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); - - for (n = 0; n <= max_code; n++) { - len = tree[n * 2 + 1]; - if (len === 0) - continue; - // Now reverse the bits - tree[n * 2] = bi_reverse(next_code[len]++, len); - } - } - - // Construct one Huffman tree and assigns the code bit strings and lengths. - // Update the total bit length for the current block. - // IN assertion: the field freq is set for all tree elements. - // OUT assertions: the fields len and code are set to the optimal bit length - // and corresponding code. The length opt_len is updated; static_len is - // also updated if stree is not null. The field max_code is set. - that.build_tree = function(s) { - var tree = that.dyn_tree; - var stree = that.stat_desc.static_tree; - var elems = that.stat_desc.elems; - var n, m; // iterate over heap elements - var max_code = -1; // largest code with non zero frequency - var node; // new node being created - - // Construct the initial heap, with least frequent element in - // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - // heap[0] is not used. - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n * 2] !== 0) { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - } else { - tree[n * 2 + 1] = 0; - } - } - - // The pkzip format requires that at least one distance code exists, - // and that at least one bit should be sent even if there is only one - // possible code. So to avoid special checks later on we force at least - // two codes of non zero frequency. - while (s.heap_len < 2) { - node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0; - tree[node * 2] = 1; - s.depth[node] = 0; - s.opt_len--; - if (stree) - s.static_len -= stree[node * 2 + 1]; - // node is 0 or 1 so it does not have extra bits - } - that.max_code = max_code; - - // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - // establish sub-heaps of increasing lengths: - - for (n = Math.floor(s.heap_len / 2); n >= 1; n--) - s.pqdownheap(tree, n); - - // Construct the Huffman tree by repeatedly combining the least two - // frequent nodes. - - node = elems; // next internal node of the tree - do { - // n = node of least frequency - n = s.heap[1]; - s.heap[1] = s.heap[s.heap_len--]; - s.pqdownheap(tree, 1); - m = s.heap[1]; // m = node of next least frequency - - s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency - s.heap[--s.heap_max] = m; - - // Create a new node father of n and m - tree[node * 2] = (tree[n * 2] + tree[m * 2]); - s.depth[node] = Math.max(s.depth[n], s.depth[m]) + 1; - tree[n * 2 + 1] = tree[m * 2 + 1] = node; - - // and insert the new node in the heap - s.heap[1] = node++; - s.pqdownheap(tree, 1); - } while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1]; - - // At this point, the fields freq and dad are set. We can now - // generate the bit lengths. - - gen_bitlen(s); - - // The field len is now set, we can generate the bit codes - gen_codes(tree, that.max_code, s.bl_count); - }; - - } - - Tree._length_code = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, - 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 ]; - - Tree.base_length = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 ]; - - Tree.base_dist = [ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, - 24576 ]; - - // Mapping from a distance to a distance code. dist is the distance - 1 and - // must not have side effects. _dist_code[256] and _dist_code[257] are never - // used. - Tree.d_code = function(dist) { - return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >>> 7)]); - }; - - // extra bits for each length code - Tree.extra_lbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 ]; - - // extra bits for each distance code - Tree.extra_dbits = [ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ]; - - // extra bits for each bit length code - Tree.extra_blbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 ]; - - Tree.bl_order = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; - - // StaticTree - - function StaticTree(static_tree, extra_bits, extra_base, elems, max_length) { - var that = this; - that.static_tree = static_tree; - that.extra_bits = extra_bits; - that.extra_base = extra_base; - that.elems = elems; - that.max_length = max_length; - } - - StaticTree.static_ltree = [ 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, 2, 8, - 130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, 202, 8, 42, - 8, 170, 8, 106, 8, 234, 8, 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8, - 22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, - 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113, - 8, 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8, 5, 8, 133, 8, - 69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, 77, 8, 205, 8, 45, 8, - 173, 8, 109, 8, 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9, - 51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, 171, 9, - 427, 9, 107, 9, 363, 9, 235, 9, 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, - 9, 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9, 23, - 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, 271, 9, 143, 9, - 399, 9, 79, 9, 335, 9, 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, - 223, 9, 479, 9, 63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, 72, 7, - 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, - 99, 8, 227, 8 ]; - - StaticTree.static_dtree = [ 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, 1, 5, 17, 5, 9, 5, - 25, 5, 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 ]; - - StaticTree.static_l_desc = new StaticTree(StaticTree.static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); - - StaticTree.static_d_desc = new StaticTree(StaticTree.static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS); - - StaticTree.static_bl_desc = new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS); - - // Deflate - - var MAX_MEM_LEVEL = 9; - var DEF_MEM_LEVEL = 8; - - function Config(good_length, max_lazy, nice_length, max_chain, func) { - var that = this; - that.good_length = good_length; - that.max_lazy = max_lazy; - that.nice_length = nice_length; - that.max_chain = max_chain; - that.func = func; - } - - var STORED = 0; - var FAST = 1; - var SLOW = 2; - var config_table = [ new Config(0, 0, 0, 0, STORED), new Config(4, 4, 8, 4, FAST), new Config(4, 5, 16, 8, FAST), new Config(4, 6, 32, 32, FAST), - new Config(4, 4, 16, 16, SLOW), new Config(8, 16, 32, 32, SLOW), new Config(8, 16, 128, 128, SLOW), new Config(8, 32, 128, 256, SLOW), - new Config(32, 128, 258, 1024, SLOW), new Config(32, 258, 258, 4096, SLOW) ]; - - var z_errmsg = [ "need dictionary", // Z_NEED_DICT - // 2 - "stream end", // Z_STREAM_END 1 - "", // Z_OK 0 - "", // Z_ERRNO (-1) - "stream error", // Z_STREAM_ERROR (-2) - "data error", // Z_DATA_ERROR (-3) - "", // Z_MEM_ERROR (-4) - "buffer error", // Z_BUF_ERROR (-5) - "",// Z_VERSION_ERROR (-6) - "" ]; - - // block not completed, need more input or more output - var NeedMore = 0; - - // block flush performed - var BlockDone = 1; - - // finish started, need only more output at next deflate - var FinishStarted = 2; - - // finish done, accept no more input or output - var FinishDone = 3; - - // preset dictionary flag in zlib header - var PRESET_DICT = 0x20; - - var INIT_STATE = 42; - var BUSY_STATE = 113; - var FINISH_STATE = 666; - - // The deflate compression method - var Z_DEFLATED = 8; - - var STORED_BLOCK = 0; - var STATIC_TREES = 1; - var DYN_TREES = 2; - - var MIN_MATCH = 3; - var MAX_MATCH = 258; - var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - - function smaller(tree, n, m, depth) { - var tn2 = tree[n * 2]; - var tm2 = tree[m * 2]; - return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); - } - - function Deflate() { - - var that = this; - var strm; // pointer back to this zlib stream - var status; // as the name implies - // pending_buf; // output still pending - var pending_buf_size; // size of pending_buf - var last_flush; // value of flush param for previous deflate call - - var w_size; // LZ77 window size (32K by default) - var w_bits; // log2(w_size) (8..16) - var w_mask; // w_size - 1 - - var window; - // Sliding window. Input bytes are read into the second half of the window, - // and move to the first half later to keep a dictionary of at least wSize - // bytes. With this organization, matches are limited to a distance of - // wSize-MAX_MATCH bytes, but this ensures that IO is always - // performed with a length multiple of the block size. Also, it limits - // the window size to 64K, which is quite useful on MSDOS. - // To do: use the user input buffer as sliding window. - - var window_size; - // Actual size of window: 2*wSize, except when the user input buffer - // is directly used as sliding window. - - var prev; - // Link to older string with same hash index. To limit the size of this - // array to 64K, this link is maintained only for the last 32K strings. - // An index in this array is thus a window index modulo 32K. - - var head; // Heads of the hash chains or NIL. - - var ins_h; // hash index of string to be inserted - var hash_size; // number of elements in hash table - var hash_bits; // log2(hash_size) - var hash_mask; // hash_size-1 - - // Number of bits by which ins_h must be shifted at each input - // step. It must be such that after MIN_MATCH steps, the oldest - // byte no longer takes part in the hash key, that is: - // hash_shift * MIN_MATCH >= hash_bits - var hash_shift; - - // Window position at the beginning of the current output block. Gets - // negative when the window is moved backwards. - - var block_start; - - var match_length; // length of best match - var prev_match; // previous match - var match_available; // set if previous match exists - var strstart; // start of string to insert - var match_start; // start of matching string - var lookahead; // number of valid bytes ahead in window - - // Length of the best match at previous step. Matches not greater than this - // are discarded. This is used in the lazy match evaluation. - var prev_length; - - // To speed up deflation, hash chains are never searched beyond this - // length. A higher limit improves compression ratio but degrades the speed. - var max_chain_length; - - // Attempt to find a better match only when the current match is strictly - // smaller than this value. This mechanism is used only for compression - // levels >= 4. - var max_lazy_match; - - // Insert new strings in the hash table only if the match length is not - // greater than this length. This saves time but degrades compression. - // max_insert_length is used only for compression levels <= 3. - - var level; // compression level (1..9) - var strategy; // favor or force Huffman coding - - // Use a faster search when the previous match is longer than this - var good_match; - - // Stop searching when current match exceeds this - var nice_match; - - var dyn_ltree; // literal and length tree - var dyn_dtree; // distance tree - var bl_tree; // Huffman tree for bit lengths - - var l_desc = new Tree(); // desc for literal tree - var d_desc = new Tree(); // desc for distance tree - var bl_desc = new Tree(); // desc for bit length tree - - // that.heap_len; // number of elements in the heap - // that.heap_max; // element of largest frequency - // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - // The same heap array is used to build all trees. - - // Depth of each subtree used as tie breaker for trees of equal frequency - that.depth = []; - - var l_buf; // index for literals or lengths */ - - // Size of match buffer for literals/lengths. There are 4 reasons for - // limiting lit_bufsize to 64K: - // - frequencies can be kept in 16 bit counters - // - if compression is not successful for the first block, all input - // data is still in the window so we can still emit a stored block even - // when input comes from standard input. (This can also be done for - // all blocks if lit_bufsize is not greater than 32K.) - // - if compression is not successful for a file smaller than 64K, we can - // even emit a stored file instead of a stored block (saving 5 bytes). - // This is applicable only for zip (not gzip or zlib). - // - creating new Huffman trees less frequently may not provide fast - // adaptation to changes in the input data statistics. (Take for - // example a binary file with poorly compressible code followed by - // a highly compressible string table.) Smaller buffer sizes give - // fast adaptation but have of course the overhead of transmitting - // trees more frequently. - // - I can't count above 4 - var lit_bufsize; - - var last_lit; // running index in l_buf - - // Buffer for distances. To simplify the code, d_buf and l_buf have - // the same number of elements. To use different lengths, an extra flag - // array would be necessary. - - var d_buf; // index of pendig_buf - - // that.opt_len; // bit length of current block with optimal trees - // that.static_len; // bit length of current block with static trees - var matches; // number of string matches in current block - var last_eob_len; // bit length of EOB code for last block - - // Output buffer. bits are inserted starting at the bottom (least - // significant bits). - var bi_buf; - - // Number of valid bits in bi_buf. All bits above the last valid bit - // are always zero. - var bi_valid; - - // number of codes at each bit length for an optimal tree - that.bl_count = []; - - // heap used to build the Huffman trees - that.heap = []; - - dyn_ltree = []; - dyn_dtree = []; - bl_tree = []; - - function lm_init() { - var i; - window_size = 2 * w_size; - - head[hash_size - 1] = 0; - for (i = 0; i < hash_size - 1; i++) { - head[i] = 0; - } - - // Set the default configuration parameters: - max_lazy_match = config_table[level].max_lazy; - good_match = config_table[level].good_length; - nice_match = config_table[level].nice_length; - max_chain_length = config_table[level].max_chain; - - strstart = 0; - block_start = 0; - lookahead = 0; - match_length = prev_length = MIN_MATCH - 1; - match_available = 0; - ins_h = 0; - } - - function init_block() { - var i; - // Initialize the trees. - for (i = 0; i < L_CODES; i++) - dyn_ltree[i * 2] = 0; - for (i = 0; i < D_CODES; i++) - dyn_dtree[i * 2] = 0; - for (i = 0; i < BL_CODES; i++) - bl_tree[i * 2] = 0; - - dyn_ltree[END_BLOCK * 2] = 1; - that.opt_len = that.static_len = 0; - last_lit = matches = 0; - } - - // Initialize the tree data structures for a new zlib stream. - function tr_init() { - - l_desc.dyn_tree = dyn_ltree; - l_desc.stat_desc = StaticTree.static_l_desc; - - d_desc.dyn_tree = dyn_dtree; - d_desc.stat_desc = StaticTree.static_d_desc; - - bl_desc.dyn_tree = bl_tree; - bl_desc.stat_desc = StaticTree.static_bl_desc; - - bi_buf = 0; - bi_valid = 0; - last_eob_len = 8; // enough lookahead for inflate - - // Initialize the first block of the first file: - init_block(); - } - - // Restore the heap property by moving down the tree starting at node k, - // exchanging a node with the smallest of its two sons if necessary, - // stopping - // when the heap property is re-established (each father smaller than its - // two sons). - that.pqdownheap = function(tree, // the tree to restore - k // node to move down - ) { - var heap = that.heap; - var v = heap[k]; - var j = k << 1; // left son of k - while (j <= that.heap_len) { - // Set j to the smallest of the two sons: - if (j < that.heap_len && smaller(tree, heap[j + 1], heap[j], that.depth)) { - j++; - } - // Exit if v is smaller than both sons - if (smaller(tree, v, heap[j], that.depth)) - break; - - // Exchange v with the smallest son - heap[k] = heap[j]; - k = j; - // And continue down the tree, setting j to the left son of k - j <<= 1; - } - heap[k] = v; - }; - - // Scan a literal or distance tree to determine the frequencies of the codes - // in the bit length tree. - function scan_tree(tree,// the tree to be scanned - max_code // and its largest code of non zero frequency - ) { - var n; // iterates over all tree elements - var prevlen = -1; // last emitted length - var curlen; // length of current code - var nextlen = tree[0 * 2 + 1]; // length of next code - var count = 0; // repeat count of the current code - var max_count = 7; // max repeat count - var min_count = 4; // min repeat count - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - tree[(max_code + 1) * 2 + 1] = 0xffff; // guard - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - bl_tree[curlen * 2] += count; - } else if (curlen !== 0) { - if (curlen != prevlen) - bl_tree[curlen * 2]++; - bl_tree[REP_3_6 * 2]++; - } else if (count <= 10) { - bl_tree[REPZ_3_10 * 2]++; - } else { - bl_tree[REPZ_11_138 * 2]++; - } - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } else if (curlen == nextlen) { - max_count = 6; - min_count = 3; - } else { - max_count = 7; - min_count = 4; - } - } - } - - // Construct the Huffman tree for the bit lengths and return the index in - // bl_order of the last bit length code to send. - function build_bl_tree() { - var max_blindex; // index of last bit length code of non zero freq - - // Determine the bit length frequencies for literal and distance trees - scan_tree(dyn_ltree, l_desc.max_code); - scan_tree(dyn_dtree, d_desc.max_code); - - // Build the bit length tree: - bl_desc.build_tree(that); - // opt_len now includes the length of the tree representations, except - // the lengths of the bit lengths codes and the 5+5+4 bits for the - // counts. - - // Determine the number of bit length codes to send. The pkzip format - // requires that at least 4 bit length codes be sent. (appnote.txt says - // 3 but the actual value used is 4.) - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { - if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] !== 0) - break; - } - // Update opt_len to include the bit length tree and counts - that.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - - return max_blindex; - } - - // Output a byte on the stream. - // IN assertion: there is enough room in pending_buf. - function put_byte(p) { - that.pending_buf[that.pending++] = p; - } - - function put_short(w) { - put_byte(w & 0xff); - put_byte((w >>> 8) & 0xff); - } - - function putShortMSB(b) { - put_byte((b >> 8) & 0xff); - put_byte((b & 0xff) & 0xff); - } - - function send_bits(value, length) { - var val, len = length; - if (bi_valid > Buf_size - len) { - val = value; - // bi_buf |= (val << bi_valid); - bi_buf |= ((val << bi_valid) & 0xffff); - put_short(bi_buf); - bi_buf = val >>> (Buf_size - bi_valid); - bi_valid += len - Buf_size; - } else { - // bi_buf |= (value) << bi_valid; - bi_buf |= (((value) << bi_valid) & 0xffff); - bi_valid += len; - } - } - - function send_code(c, tree) { - var c2 = c * 2; - send_bits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff); - } - - // Send a literal or distance tree in compressed form, using the codes in - // bl_tree. - function send_tree(tree,// the tree to be sent - max_code // and its largest code of non zero frequency - ) { - var n; // iterates over all tree elements - var prevlen = -1; // last emitted length - var curlen; // length of current code - var nextlen = tree[0 * 2 + 1]; // length of next code - var count = 0; // repeat count of the current code - var max_count = 7; // max repeat count - var min_count = 4; // min repeat count - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - do { - send_code(curlen, bl_tree); - } while (--count !== 0); - } else if (curlen !== 0) { - if (curlen != prevlen) { - send_code(curlen, bl_tree); - count--; - } - send_code(REP_3_6, bl_tree); - send_bits(count - 3, 2); - } else if (count <= 10) { - send_code(REPZ_3_10, bl_tree); - send_bits(count - 3, 3); - } else { - send_code(REPZ_11_138, bl_tree); - send_bits(count - 11, 7); - } - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } else if (curlen == nextlen) { - max_count = 6; - min_count = 3; - } else { - max_count = 7; - min_count = 4; - } - } - } - - // Send the header for a block using dynamic Huffman trees: the counts, the - // lengths of the bit length codes, the literal tree and the distance tree. - // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - function send_all_trees(lcodes, dcodes, blcodes) { - var rank; // index in bl_order - - send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt - send_bits(dcodes - 1, 5); - send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt - for (rank = 0; rank < blcodes; rank++) { - send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); - } - send_tree(dyn_ltree, lcodes - 1); // literal tree - send_tree(dyn_dtree, dcodes - 1); // distance tree - } - - // Flush the bit buffer, keeping at most 7 bits in it. - function bi_flush() { - if (bi_valid == 16) { - put_short(bi_buf); - bi_buf = 0; - bi_valid = 0; - } else if (bi_valid >= 8) { - put_byte(bi_buf & 0xff); - bi_buf >>>= 8; - bi_valid -= 8; - } - } - - // Send one empty static block to give enough lookahead for inflate. - // This takes 10 bits, of which 7 may remain in the bit buffer. - // The current inflate code requires 9 bits of lookahead. If the - // last two codes for the previous block (real code plus EOB) were coded - // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - // the last real code. In this case we send two empty static blocks instead - // of one. (There are no problems if the previous block is stored or fixed.) - // To simplify the code, we assume the worst case of last real code encoded - // on one bit only. - function _tr_align() { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - - bi_flush(); - - // Of the 10 bits for the empty block, we have already sent - // (10 - bi_valid) bits. The lookahead for the last real code (before - // the EOB of the previous block) was thus at least one plus the length - // of the EOB plus what we have just sent of the empty static block. - if (1 + last_eob_len + 10 - bi_valid < 9) { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - bi_flush(); - } - last_eob_len = 7; - } - - // Save the match info and tally the frequency counts. Return true if - // the current block must be flushed. - function _tr_tally(dist, // distance of matched string - lc // match length-MIN_MATCH or unmatched char (if dist==0) - ) { - var out_length, in_length, dcode; - that.pending_buf[d_buf + last_lit * 2] = (dist >>> 8) & 0xff; - that.pending_buf[d_buf + last_lit * 2 + 1] = dist & 0xff; - - that.pending_buf[l_buf + last_lit] = lc & 0xff; - last_lit++; - - if (dist === 0) { - // lc is the unmatched char - dyn_ltree[lc * 2]++; - } else { - matches++; - // Here, lc is the match length - MIN_MATCH - dist--; // dist = match distance - 1 - dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++; - dyn_dtree[Tree.d_code(dist) * 2]++; - } - - if ((last_lit & 0x1fff) === 0 && level > 2) { - // Compute an upper bound for the compressed length - out_length = last_lit * 8; - in_length = strstart - block_start; - for (dcode = 0; dcode < D_CODES; dcode++) { - out_length += dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]); - } - out_length >>>= 3; - if ((matches < Math.floor(last_lit / 2)) && out_length < Math.floor(in_length / 2)) - return true; - } - - return (last_lit == lit_bufsize - 1); - // We avoid equality with lit_bufsize because of wraparound at 64K - // on 16 bit machines and because stored blocks are restricted to - // 64K-1 bytes. - } - - // Send the block data compressed using the given Huffman trees - function compress_block(ltree, dtree) { - var dist; // distance of matched string - var lc; // match length or unmatched char (if dist === 0) - var lx = 0; // running index in l_buf - var code; // the code to send - var extra; // number of extra bits to send - - if (last_lit !== 0) { - do { - dist = ((that.pending_buf[d_buf + lx * 2] << 8) & 0xff00) | (that.pending_buf[d_buf + lx * 2 + 1] & 0xff); - lc = (that.pending_buf[l_buf + lx]) & 0xff; - lx++; - - if (dist === 0) { - send_code(lc, ltree); // send a literal byte - } else { - // Here, lc is the match length - MIN_MATCH - code = Tree._length_code[lc]; - - send_code(code + LITERALS + 1, ltree); // send the length - // code - extra = Tree.extra_lbits[code]; - if (extra !== 0) { - lc -= Tree.base_length[code]; - send_bits(lc, extra); // send the extra length bits - } - dist--; // dist is now the match distance - 1 - code = Tree.d_code(dist); - - send_code(code, dtree); // send the distance code - extra = Tree.extra_dbits[code]; - if (extra !== 0) { - dist -= Tree.base_dist[code]; - send_bits(dist, extra); // send the extra distance bits - } - } // literal or match pair ? - - // Check that the overlay between pending_buf and d_buf+l_buf is - // ok: - } while (lx < last_lit); - } - - send_code(END_BLOCK, ltree); - last_eob_len = ltree[END_BLOCK * 2 + 1]; - } - - // Flush the bit buffer and align the output on a byte boundary - function bi_windup() { - if (bi_valid > 8) { - put_short(bi_buf); - } else if (bi_valid > 0) { - put_byte(bi_buf & 0xff); - } - bi_buf = 0; - bi_valid = 0; - } - - // Copy a stored block, storing first the length and its - // one's complement if requested. - function copy_block(buf, // the input data - len, // its length - header // true if block header must be written - ) { - bi_windup(); // align on byte boundary - last_eob_len = 8; // enough lookahead for inflate - - if (header) { - put_short(len); - put_short(~len); - } - - that.pending_buf.set(window.subarray(buf, buf + len), that.pending); - that.pending += len; - } - - // Send a stored block - function _tr_stored_block(buf, // input block - stored_len, // length of input block - eof // true if this is the last block for a file - ) { - send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type - copy_block(buf, stored_len, true); // with header - } - - // Determine the best encoding for the current block: dynamic trees, static - // trees or store, and output the encoded block to the zip file. - function _tr_flush_block(buf, // input block, or NULL if too old - stored_len, // length of input block - eof // true if this is the last block for a file - ) { - var opt_lenb, static_lenb;// opt_len and static_len in bytes - var max_blindex = 0; // index of last bit length code of non zero freq - - // Build the Huffman trees unless a stored block is forced - if (level > 0) { - // Construct the literal and distance trees - l_desc.build_tree(that); - - d_desc.build_tree(that); - - // At this point, opt_len and static_len are the total bit lengths - // of - // the compressed block data, excluding the tree representations. - - // Build the bit length tree for the above two trees, and get the - // index - // in bl_order of the last bit length code to send. - max_blindex = build_bl_tree(); - - // Determine the best encoding. Compute first the block length in - // bytes - opt_lenb = (that.opt_len + 3 + 7) >>> 3; - static_lenb = (that.static_len + 3 + 7) >>> 3; - - if (static_lenb <= opt_lenb) - opt_lenb = static_lenb; - } else { - opt_lenb = static_lenb = stored_len + 5; // force a stored block - } - - if ((stored_len + 4 <= opt_lenb) && buf != -1) { - // 4: two words for the lengths - // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - // Otherwise we can't have processed more than WSIZE input bytes - // since - // the last block flush, because compression would have been - // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - // transform a block into a stored block. - _tr_stored_block(buf, stored_len, eof); - } else if (static_lenb == opt_lenb) { - send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); - compress_block(StaticTree.static_ltree, StaticTree.static_dtree); - } else { - send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); - send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1); - compress_block(dyn_ltree, dyn_dtree); - } - - // The above check is made mod 2^32, for files larger than 512 MB - // and uLong implemented on 32 bits. - - init_block(); - - if (eof) { - bi_windup(); - } - } - - function flush_block_only(eof) { - _tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof); - block_start = strstart; - strm.flush_pending(); - } - - // Fill the window when the lookahead becomes insufficient. - // Updates strstart and lookahead. - // - // IN assertion: lookahead < MIN_LOOKAHEAD - // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - // At least one byte has been read, or avail_in === 0; reads are - // performed for at least two bytes (required for the zip translate_eol - // option -- not supported here). - function fill_window() { - var n, m; - var p; - var more; // Amount of free space at the end of the window. - - do { - more = (window_size - lookahead - strstart); - - // Deal with !@#$% 64K limit: - if (more === 0 && strstart === 0 && lookahead === 0) { - more = w_size; - } else if (more == -1) { - // Very unlikely, but possible on 16 bit machine if strstart == - // 0 - // and lookahead == 1 (input done one byte at time) - more--; - - // If the window is almost full and there is insufficient - // lookahead, - // move the upper half to the lower one to make room in the - // upper half. - } else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) { - window.set(window.subarray(w_size, w_size + w_size), 0); - - match_start -= w_size; - strstart -= w_size; // we now have strstart >= MAX_DIST - block_start -= w_size; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). We slide even when level == - // 0 - // to keep the hash table consistent if we switch back to level - // > 0 - // later. (Using level 0 permanently is not an optimal usage of - // zlib, so we don't care about this pathological case.) - - n = hash_size; - p = n; - do { - m = (head[--p] & 0xffff); - head[p] = (m >= w_size ? m - w_size : 0); - } while (--n !== 0); - - n = w_size; - p = n; - do { - m = (prev[--p] & 0xffff); - prev[p] = (m >= w_size ? m - w_size : 0); - // If n is not on any hash chain, prev[n] is garbage but - // its value will never be used. - } while (--n !== 0); - more += w_size; - } - - if (strm.avail_in === 0) - return; - - // If there was no sliding: - // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - // more == window_size - lookahead - strstart - // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - // => more >= window_size - 2*WSIZE + 2 - // In the BIG_MEM or MMAP case (not yet supported), - // window_size == input_size + MIN_LOOKAHEAD && - // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - // Otherwise, window_size == 2*WSIZE so more >= 2. - // If there was sliding, more >= WSIZE. So in all cases, more >= 2. - - n = strm.read_buf(window, strstart + lookahead, more); - lookahead += n; - - // Initialize the hash value now that we have some input: - if (lookahead >= MIN_MATCH) { - ins_h = window[strstart] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; - } - // If the whole input has less than MIN_MATCH bytes, ins_h is - // garbage, - // but this is not important since only literal bytes will be - // emitted. - } while (lookahead < MIN_LOOKAHEAD && strm.avail_in !== 0); - } - - // Copy without compression as much as possible from the input stream, - // return - // the current block state. - // This function does not insert new strings in the dictionary since - // uncompressible data is probably not useful. This function is used - // only for the level=0 compression option. - // NOTE: this function should be optimized to avoid extra copying from - // window to pending_buf. - function deflate_stored(flush) { - // Stored blocks are limited to 0xffff bytes, pending_buf is limited - // to pending_buf_size, and each stored block has a 5 byte header: - - var max_block_size = 0xffff; - var max_start; - - if (max_block_size > pending_buf_size - 5) { - max_block_size = pending_buf_size - 5; - } - - // Copy as much as possible from input to output: - while (true) { - // Fill the window as much as possible: - if (lookahead <= 1) { - fill_window(); - if (lookahead === 0 && flush == Z_NO_FLUSH) - return NeedMore; - if (lookahead === 0) - break; // flush the current block - } - - strstart += lookahead; - lookahead = 0; - - // Emit a stored block if pending_buf will be full: - max_start = block_start + max_block_size; - if (strstart === 0 || strstart >= max_start) { - // strstart === 0 is possible when wraparound on 16-bit machine - lookahead = (strstart - max_start); - strstart = max_start; - - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - - } - - // Flush if we may have to slide, otherwise block_start may become - // negative and the data will be gone: - if (strstart - block_start >= w_size - MIN_LOOKAHEAD) { - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH); - if (strm.avail_out === 0) - return (flush == Z_FINISH) ? FinishStarted : NeedMore; - - return flush == Z_FINISH ? FinishDone : BlockDone; - } - - function longest_match(cur_match) { - var chain_length = max_chain_length; // max hash chain length - var scan = strstart; // current string - var match; // matched string - var len; // length of current match - var best_len = prev_length; // best match length so far - var limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; - var _nice_match = nice_match; - - // Stop when cur_match becomes <= limit. To simplify the code, - // we prevent matches with the string of window index 0. - - var wmask = w_mask; - - var strend = strstart + MAX_MATCH; - var scan_end1 = window[scan + best_len - 1]; - var scan_end = window[scan + best_len]; - - // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of - // 16. - // It is easy to get rid of this optimization if necessary. - - // Do not waste too much time if we already have a good match: - if (prev_length >= good_match) { - chain_length >>= 2; - } - - // Do not look for matches beyond the end of the input. This is - // necessary - // to make deflate deterministic. - if (_nice_match > lookahead) - _nice_match = lookahead; - - do { - match = cur_match; - - // Skip to next match if the match length cannot increase - // or if the match length is less than 2: - if (window[match + best_len] != scan_end || window[match + best_len - 1] != scan_end1 || window[match] != window[scan] - || window[++match] != window[scan + 1]) - continue; - - // The check at best_len-1 can be removed because it will be made - // again later. (This heuristic is not always a win.) - // It is not necessary to compare scan[2] and match[2] since they - // are always equal when the other bytes match, given that - // the hash keys are equal and that HASH_BITS >= 8. - scan += 2; - match++; - - // We check for insufficient lookahead only every 8th comparison; - // the 256th check will be made at strstart+258. - do { - } while (window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] - && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] - && window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend); - - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) { - match_start = cur_match; - best_len = len; - if (len >= _nice_match) - break; - scan_end1 = window[scan + best_len - 1]; - scan_end = window[scan + best_len]; - } - - } while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length !== 0); - - if (best_len <= lookahead) - return best_len; - return lookahead; - } - - // Compress as much as possible from the input stream, return the current - // block state. - // This function does not perform lazy evaluation of matches and inserts - // new strings in the dictionary only for unmatched strings or for short - // matches. It is used only for the fast compression options. - function deflate_fast(flush) { - // short hash_head = 0; // head of the hash chain - var hash_head = 0; // head of the hash chain - var bflush; // set if current block must be flushed - - while (true) { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - if (lookahead < MIN_LOOKAHEAD) { - fill_window(); - if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return NeedMore; - } - if (lookahead === 0) - break; // flush the current block - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - if (lookahead >= MIN_MATCH) { - ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - } - - // Find the longest match, discarding those <= prev_length. - // At this point we have always match_length < MIN_MATCH - - if (hash_head !== 0 && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - if (strategy != Z_HUFFMAN_ONLY) { - match_length = longest_match(hash_head); - } - // longest_match() sets match_start - } - if (match_length >= MIN_MATCH) { - // check_match(strstart, match_start, match_length); - - bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); - - lookahead -= match_length; - - // Insert new strings in the hash table only if the match length - // is not too large. This saves time but degrades compression. - if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) { - match_length--; // string at strstart already in hash table - do { - strstart++; - - ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - - // strstart never exceeds WSIZE-MAX_MATCH, so there are - // always MIN_MATCH bytes ahead. - } while (--match_length !== 0); - strstart++; - } else { - strstart += match_length; - match_length = 0; - ins_h = window[strstart] & 0xff; - - ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; - // If lookahead < MIN_MATCH, ins_h is garbage, but it does - // not - // matter since it will be recomputed at next deflate call. - } - } else { - // No match, output a literal byte - - bflush = _tr_tally(0, window[strstart] & 0xff); - lookahead--; - strstart++; - } - if (bflush) { - - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH); - if (strm.avail_out === 0) { - if (flush == Z_FINISH) - return FinishStarted; - else - return NeedMore; - } - return flush == Z_FINISH ? FinishDone : BlockDone; - } - - // Same as above, but achieves better compression. We use a lazy - // evaluation for matches: a match is finally adopted only if there is - // no better match at the next window position. - function deflate_slow(flush) { - // short hash_head = 0; // head of hash chain - var hash_head = 0; // head of hash chain - var bflush; // set if current block must be flushed - var max_insert; - - // Process the input block. - while (true) { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - - if (lookahead < MIN_LOOKAHEAD) { - fill_window(); - if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return NeedMore; - } - if (lookahead === 0) - break; // flush the current block - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - - if (lookahead >= MIN_MATCH) { - ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - } - - // Find the longest match, discarding those <= prev_length. - prev_length = match_length; - prev_match = match_start; - match_length = MIN_MATCH - 1; - - if (hash_head !== 0 && prev_length < max_lazy_match && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - - if (strategy != Z_HUFFMAN_ONLY) { - match_length = longest_match(hash_head); - } - // longest_match() sets match_start - - if (match_length <= 5 && (strategy == Z_FILTERED || (match_length == MIN_MATCH && strstart - match_start > 4096))) { - - // If prev_match is also MIN_MATCH, match_start is garbage - // but we will ignore the current match anyway. - match_length = MIN_MATCH - 1; - } - } - - // If there was a match at the previous step and the current - // match is not better, output the previous match: - if (prev_length >= MIN_MATCH && match_length <= prev_length) { - max_insert = strstart + lookahead - MIN_MATCH; - // Do not insert strings in hash table beyond this. - - // check_match(strstart-1, prev_match, prev_length); - - bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); - - // Insert in hash table all strings up to the end of the match. - // strstart-1 and strstart are already inserted. If there is not - // enough lookahead, the last two strings are not inserted in - // the hash table. - lookahead -= prev_length - 1; - prev_length -= 2; - do { - if (++strstart <= max_insert) { - ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = strstart; - } - } while (--prev_length !== 0); - match_available = 0; - match_length = MIN_MATCH - 1; - strstart++; - - if (bflush) { - flush_block_only(false); - if (strm.avail_out === 0) - return NeedMore; - } - } else if (match_available !== 0) { - - // If there was no match at the previous position, output a - // single literal. If there was a match but the current match - // is longer, truncate the previous match to a single literal. - - bflush = _tr_tally(0, window[strstart - 1] & 0xff); - - if (bflush) { - flush_block_only(false); - } - strstart++; - lookahead--; - if (strm.avail_out === 0) - return NeedMore; - } else { - // There is no previous match to compare with, wait for - // the next step to decide. - - match_available = 1; - strstart++; - lookahead--; - } - } - - if (match_available !== 0) { - bflush = _tr_tally(0, window[strstart - 1] & 0xff); - match_available = 0; - } - flush_block_only(flush == Z_FINISH); - - if (strm.avail_out === 0) { - if (flush == Z_FINISH) - return FinishStarted; - else - return NeedMore; - } - - return flush == Z_FINISH ? FinishDone : BlockDone; - } - - function deflateReset(strm) { - strm.total_in = strm.total_out = 0; - strm.msg = null; // - - that.pending = 0; - that.pending_out = 0; - - status = BUSY_STATE; - - last_flush = Z_NO_FLUSH; - - tr_init(); - lm_init(); - return Z_OK; - } - - that.deflateInit = function(strm, _level, bits, _method, memLevel, _strategy) { - if (!_method) - _method = Z_DEFLATED; - if (!memLevel) - memLevel = DEF_MEM_LEVEL; - if (!_strategy) - _strategy = Z_DEFAULT_STRATEGY; - - // byte[] my_version=ZLIB_VERSION; - - // - // if (!version || version[0] != my_version[0] - // || stream_size != sizeof(z_stream)) { - // return Z_VERSION_ERROR; - // } - - strm.msg = null; - - if (_level == Z_DEFAULT_COMPRESSION) - _level = 6; - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || _method != Z_DEFLATED || bits < 9 || bits > 15 || _level < 0 || _level > 9 || _strategy < 0 - || _strategy > Z_HUFFMAN_ONLY) { - return Z_STREAM_ERROR; - } - - strm.dstate = that; - - w_bits = bits; - w_size = 1 << w_bits; - w_mask = w_size - 1; - - hash_bits = memLevel + 7; - hash_size = 1 << hash_bits; - hash_mask = hash_size - 1; - hash_shift = Math.floor((hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - window = new Uint8Array(w_size * 2); - prev = []; - head = []; - - lit_bufsize = 1 << (memLevel + 6); // 16K elements by default - - // We overlay pending_buf and d_buf+l_buf. This works since the average - // output size for (length,distance) codes is <= 24 bits. - that.pending_buf = new Uint8Array(lit_bufsize * 4); - pending_buf_size = lit_bufsize * 4; - - d_buf = Math.floor(lit_bufsize / 2); - l_buf = (1 + 2) * lit_bufsize; - - level = _level; - - strategy = _strategy; - - return deflateReset(strm); - }; - - that.deflateEnd = function() { - if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) { - return Z_STREAM_ERROR; - } - // Deallocate in reverse order of allocations: - that.pending_buf = null; - head = null; - prev = null; - window = null; - // free - that.dstate = null; - return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; - }; - - that.deflateParams = function(strm, _level, _strategy) { - var err = Z_OK; - - if (_level == Z_DEFAULT_COMPRESSION) { - _level = 6; - } - if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { - return Z_STREAM_ERROR; - } - - if (config_table[level].func != config_table[_level].func && strm.total_in !== 0) { - // Flush the last buffer: - err = strm.deflate(Z_PARTIAL_FLUSH); - } - - if (level != _level) { - level = _level; - max_lazy_match = config_table[level].max_lazy; - good_match = config_table[level].good_length; - nice_match = config_table[level].nice_length; - max_chain_length = config_table[level].max_chain; - } - strategy = _strategy; - return err; - }; - - that.deflateSetDictionary = function(strm, dictionary, dictLength) { - var length = dictLength; - var n, index = 0; - - if (!dictionary || status != INIT_STATE) - return Z_STREAM_ERROR; - - if (length < MIN_MATCH) - return Z_OK; - if (length > w_size - MIN_LOOKAHEAD) { - length = w_size - MIN_LOOKAHEAD; - index = dictLength - length; // use the tail of the dictionary - } - window.set(dictionary.subarray(index, index + length), 0); - - strstart = length; - block_start = length; - - // Insert all strings in the hash table (except for the last two bytes). - // s->lookahead stays null, so s->ins_h will be recomputed at the next - // call of fill_window. - - ins_h = window[0] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; - - for (n = 0; n <= length - MIN_MATCH; n++) { - ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - prev[n & w_mask] = head[ins_h]; - head[ins_h] = n; - } - return Z_OK; - }; - - that.deflate = function(_strm, flush) { - var i, header, level_flags, old_flush, bstate; - - if (flush > Z_FINISH || flush < 0) { - return Z_STREAM_ERROR; - } - - if (!_strm.next_out || (!_strm.next_in && _strm.avail_in !== 0) || (status == FINISH_STATE && flush != Z_FINISH)) { - _strm.msg = z_errmsg[Z_NEED_DICT - (Z_STREAM_ERROR)]; - return Z_STREAM_ERROR; - } - if (_strm.avail_out === 0) { - _strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - strm = _strm; // just in case - old_flush = last_flush; - last_flush = flush; - - // Write the zlib header - if (status == INIT_STATE) { - header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; - level_flags = ((level - 1) & 0xff) >> 1; - - if (level_flags > 3) - level_flags = 3; - header |= (level_flags << 6); - if (strstart !== 0) - header |= PRESET_DICT; - header += 31 - (header % 31); - - status = BUSY_STATE; - putShortMSB(header); - } - - // Flush as much pending output as possible - if (that.pending !== 0) { - strm.flush_pending(); - if (strm.avail_out === 0) { - // console.log(" avail_out==0"); - // Since avail_out is 0, deflate will be called again with - // more output space, but possibly with both pending and - // avail_in equal to zero. There won't be anything to do, - // but this is not an error situation so make sure we - // return OK instead of BUF_ERROR at next call of deflate: - last_flush = -1; - return Z_OK; - } - - // Make sure there is something to do and avoid duplicate - // consecutive - // flushes. For repeated and useless calls with Z_FINISH, we keep - // returning Z_STREAM_END instead of Z_BUFF_ERROR. - } else if (strm.avail_in === 0 && flush <= old_flush && flush != Z_FINISH) { - strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - // User must not provide more input after the first FINISH: - if (status == FINISH_STATE && strm.avail_in !== 0) { - _strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - // Start a new block or continue the current one. - if (strm.avail_in !== 0 || lookahead !== 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) { - bstate = -1; - switch (config_table[level].func) { - case STORED: - bstate = deflate_stored(flush); - break; - case FAST: - bstate = deflate_fast(flush); - break; - case SLOW: - bstate = deflate_slow(flush); - break; - default: - } - - if (bstate == FinishStarted || bstate == FinishDone) { - status = FINISH_STATE; - } - if (bstate == NeedMore || bstate == FinishStarted) { - if (strm.avail_out === 0) { - last_flush = -1; // avoid BUF_ERROR next call, see above - } - return Z_OK; - // If flush != Z_NO_FLUSH && avail_out === 0, the next call - // of deflate should use the same flush parameter to make sure - // that the flush is complete. So we don't have to output an - // empty block here, this will be done at next call. This also - // ensures that for a very small output buffer, we emit at most - // one empty block. - } - - if (bstate == BlockDone) { - if (flush == Z_PARTIAL_FLUSH) { - _tr_align(); - } else { // FULL_FLUSH or SYNC_FLUSH - _tr_stored_block(0, 0, false); - // For a full flush, this empty block will be recognized - // as a special marker by inflate_sync(). - if (flush == Z_FULL_FLUSH) { - // state.head[s.hash_size-1]=0; - for (i = 0; i < hash_size/*-1*/; i++) - // forget history - head[i] = 0; - } - } - strm.flush_pending(); - if (strm.avail_out === 0) { - last_flush = -1; // avoid BUF_ERROR at next call, see above - return Z_OK; - } - } - } - - if (flush != Z_FINISH) - return Z_OK; - return Z_STREAM_END; - }; - } - - // ZStream - - function ZStream() { - var that = this; - that.next_in_index = 0; - that.next_out_index = 0; - // that.next_in; // next input byte - that.avail_in = 0; // number of bytes available at next_in - that.total_in = 0; // total nb of input bytes read so far - // that.next_out; // next output byte should be put there - that.avail_out = 0; // remaining free space at next_out - that.total_out = 0; // total nb of bytes output so far - // that.msg; - // that.dstate; - } - - ZStream.prototype = { - deflateInit : function(level, bits) { - var that = this; - that.dstate = new Deflate(); - if (!bits) - bits = MAX_BITS; - return that.dstate.deflateInit(that, level, bits); - }, - - deflate : function(flush) { - var that = this; - if (!that.dstate) { - return Z_STREAM_ERROR; - } - return that.dstate.deflate(that, flush); - }, - - deflateEnd : function() { - var that = this; - if (!that.dstate) - return Z_STREAM_ERROR; - var ret = that.dstate.deflateEnd(); - that.dstate = null; - return ret; - }, - - deflateParams : function(level, strategy) { - var that = this; - if (!that.dstate) - return Z_STREAM_ERROR; - return that.dstate.deflateParams(that, level, strategy); - }, - - deflateSetDictionary : function(dictionary, dictLength) { - var that = this; - if (!that.dstate) - return Z_STREAM_ERROR; - return that.dstate.deflateSetDictionary(that, dictionary, dictLength); - }, - - // Read a new buffer from the current input stream, update the - // total number of bytes read. All deflate() input goes through - // this function so some applications may wish to modify it to avoid - // allocating a large strm->next_in buffer and copying from it. - // (See also flush_pending()). - read_buf : function(buf, start, size) { - var that = this; - var len = that.avail_in; - if (len > size) - len = size; - if (len === 0) - return 0; - that.avail_in -= len; - buf.set(that.next_in.subarray(that.next_in_index, that.next_in_index + len), start); - that.next_in_index += len; - that.total_in += len; - return len; - }, - - // Flush as much pending output as possible. All deflate() output goes - // through this function so some applications may wish to modify it - // to avoid allocating a large strm->next_out buffer and copying into it. - // (See also read_buf()). - flush_pending : function() { - var that = this; - var len = that.dstate.pending; - - if (len > that.avail_out) - len = that.avail_out; - if (len === 0) - return; - - // if (that.dstate.pending_buf.length <= that.dstate.pending_out || that.next_out.length <= that.next_out_index - // || that.dstate.pending_buf.length < (that.dstate.pending_out + len) || that.next_out.length < (that.next_out_index + - // len)) { - // console.log(that.dstate.pending_buf.length + ", " + that.dstate.pending_out + ", " + that.next_out.length + ", " + - // that.next_out_index + ", " + len); - // console.log("avail_out=" + that.avail_out); - // } - - that.next_out.set(that.dstate.pending_buf.subarray(that.dstate.pending_out, that.dstate.pending_out + len), that.next_out_index); - - that.next_out_index += len; - that.dstate.pending_out += len; - that.total_out += len; - that.avail_out -= len; - that.dstate.pending -= len; - if (that.dstate.pending === 0) { - that.dstate.pending_out = 0; - } - } - }; - - // Deflater - - function Deflater(options) { - var that = this; - var z = new ZStream(); - var bufsize = 512; - var flush = Z_NO_FLUSH; - var buf = new Uint8Array(bufsize); - var level = options ? options.level : Z_DEFAULT_COMPRESSION; - if (typeof level == "undefined") - level = Z_DEFAULT_COMPRESSION; - z.deflateInit(level); - z.next_out = buf; - - that.append = function(data, onprogress) { - var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array; - if (!data.length) - return; - z.next_in_index = 0; - z.next_in = data; - z.avail_in = data.length; - do { - z.next_out_index = 0; - z.avail_out = bufsize; - err = z.deflate(flush); - if (err != Z_OK) - throw new Error("deflating: " + z.msg); - if (z.next_out_index) - if (z.next_out_index == bufsize) - buffers.push(new Uint8Array(buf)); - else - buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index))); - bufferSize += z.next_out_index; - if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) { - onprogress(z.next_in_index); - lastIndex = z.next_in_index; - } - } while (z.avail_in > 0 || z.avail_out === 0); - array = new Uint8Array(bufferSize); - buffers.forEach(function(chunk) { - array.set(chunk, bufferIndex); - bufferIndex += chunk.length; - }); - return array; - }; - that.flush = function() { - var err, buffers = [], bufferIndex = 0, bufferSize = 0, array; - do { - z.next_out_index = 0; - z.avail_out = bufsize; - err = z.deflate(Z_FINISH); - if (err != Z_STREAM_END && err != Z_OK) - throw new Error("deflating: " + z.msg); - if (bufsize - z.avail_out > 0) - buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index))); - bufferSize += z.next_out_index; - } while (z.avail_in > 0 || z.avail_out === 0); - z.deflateEnd(); - array = new Uint8Array(bufferSize); - buffers.forEach(function(chunk) { - array.set(chunk, bufferIndex); - bufferIndex += chunk.length; - }); - return array; - }; - } - - // 'zip' may not be defined in z-worker and some tests - var env = global.zip || global; - env.Deflater = env._jzlib_Deflater = Deflater; - }(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 - - /** - * A class to parse color values - * @author Stoyan Stefanov <sstoo@gmail.com> - * @link http://www.phpied.com/rgb-color-parser-in-javascript/ - * @license Use it if you like it - */ - - (function ( global ) { - - function RGBColor(color_string) - { - this.ok = false; - - // strip any leading # - if (color_string.charAt(0) == '#') { // remove # if any - color_string = color_string.substr(1,6); - } - - color_string = color_string.replace(/ /g,''); - color_string = color_string.toLowerCase(); - - var channels; - - // before getting into regexps, try simple matches - // and overwrite the input - var simple_colors = { - aliceblue: 'f0f8ff', - antiquewhite: 'faebd7', - aqua: '00ffff', - aquamarine: '7fffd4', - azure: 'f0ffff', - beige: 'f5f5dc', - bisque: 'ffe4c4', - black: '000000', - blanchedalmond: 'ffebcd', - blue: '0000ff', - blueviolet: '8a2be2', - brown: 'a52a2a', - burlywood: 'deb887', - cadetblue: '5f9ea0', - chartreuse: '7fff00', - chocolate: 'd2691e', - coral: 'ff7f50', - cornflowerblue: '6495ed', - cornsilk: 'fff8dc', - crimson: 'dc143c', - cyan: '00ffff', - darkblue: '00008b', - darkcyan: '008b8b', - darkgoldenrod: 'b8860b', - darkgray: 'a9a9a9', - darkgreen: '006400', - darkkhaki: 'bdb76b', - darkmagenta: '8b008b', - darkolivegreen: '556b2f', - darkorange: 'ff8c00', - darkorchid: '9932cc', - darkred: '8b0000', - darksalmon: 'e9967a', - darkseagreen: '8fbc8f', - darkslateblue: '483d8b', - darkslategray: '2f4f4f', - darkturquoise: '00ced1', - darkviolet: '9400d3', - deeppink: 'ff1493', - deepskyblue: '00bfff', - dimgray: '696969', - dodgerblue: '1e90ff', - feldspar: 'd19275', - firebrick: 'b22222', - floralwhite: 'fffaf0', - forestgreen: '228b22', - fuchsia: 'ff00ff', - gainsboro: 'dcdcdc', - ghostwhite: 'f8f8ff', - gold: 'ffd700', - goldenrod: 'daa520', - gray: '808080', - green: '008000', - greenyellow: 'adff2f', - honeydew: 'f0fff0', - hotpink: 'ff69b4', - indianred : 'cd5c5c', - indigo : '4b0082', - ivory: 'fffff0', - khaki: 'f0e68c', - lavender: 'e6e6fa', - lavenderblush: 'fff0f5', - lawngreen: '7cfc00', - lemonchiffon: 'fffacd', - lightblue: 'add8e6', - lightcoral: 'f08080', - lightcyan: 'e0ffff', - lightgoldenrodyellow: 'fafad2', - lightgrey: 'd3d3d3', - lightgreen: '90ee90', - lightpink: 'ffb6c1', - lightsalmon: 'ffa07a', - lightseagreen: '20b2aa', - lightskyblue: '87cefa', - lightslateblue: '8470ff', - lightslategray: '778899', - lightsteelblue: 'b0c4de', - lightyellow: 'ffffe0', - lime: '00ff00', - limegreen: '32cd32', - linen: 'faf0e6', - magenta: 'ff00ff', - maroon: '800000', - mediumaquamarine: '66cdaa', - mediumblue: '0000cd', - mediumorchid: 'ba55d3', - mediumpurple: '9370d8', - mediumseagreen: '3cb371', - mediumslateblue: '7b68ee', - mediumspringgreen: '00fa9a', - mediumturquoise: '48d1cc', - mediumvioletred: 'c71585', - midnightblue: '191970', - mintcream: 'f5fffa', - mistyrose: 'ffe4e1', - moccasin: 'ffe4b5', - navajowhite: 'ffdead', - navy: '000080', - oldlace: 'fdf5e6', - olive: '808000', - olivedrab: '6b8e23', - orange: 'ffa500', - orangered: 'ff4500', - orchid: 'da70d6', - palegoldenrod: 'eee8aa', - palegreen: '98fb98', - paleturquoise: 'afeeee', - palevioletred: 'd87093', - papayawhip: 'ffefd5', - peachpuff: 'ffdab9', - peru: 'cd853f', - pink: 'ffc0cb', - plum: 'dda0dd', - powderblue: 'b0e0e6', - purple: '800080', - red: 'ff0000', - rosybrown: 'bc8f8f', - royalblue: '4169e1', - saddlebrown: '8b4513', - salmon: 'fa8072', - sandybrown: 'f4a460', - seagreen: '2e8b57', - seashell: 'fff5ee', - sienna: 'a0522d', - silver: 'c0c0c0', - skyblue: '87ceeb', - slateblue: '6a5acd', - slategray: '708090', - snow: 'fffafa', - springgreen: '00ff7f', - steelblue: '4682b4', - tan: 'd2b48c', - teal: '008080', - thistle: 'd8bfd8', - tomato: 'ff6347', - turquoise: '40e0d0', - violet: 'ee82ee', - violetred: 'd02090', - wheat: 'f5deb3', - white: 'ffffff', - whitesmoke: 'f5f5f5', - yellow: 'ffff00', - yellowgreen: '9acd32' - }; - for (var key in simple_colors) { - if (color_string == key) { - color_string = simple_colors[key]; - } - } - // emd of simple type-in colors - - // array of color definition objects - var color_defs = [ - { - re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, - example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], - process: function (bits){ - return [ - parseInt(bits[1]), - parseInt(bits[2]), - parseInt(bits[3]) - ]; - } - }, - { - re: /^(\w{2})(\w{2})(\w{2})$/, - example: ['#00ff00', '336699'], - process: function (bits){ - return [ - parseInt(bits[1], 16), - parseInt(bits[2], 16), - parseInt(bits[3], 16) - ]; - } - }, - { - re: /^(\w{1})(\w{1})(\w{1})$/, - example: ['#fb0', 'f0f'], - process: function (bits){ - return [ - parseInt(bits[1] + bits[1], 16), - parseInt(bits[2] + bits[2], 16), - parseInt(bits[3] + bits[3], 16) - ]; - } - } - ]; - - // search through the definitions to find a match - for (var i = 0; i < color_defs.length; i++) { - var re = color_defs[i].re; - var processor = color_defs[i].process; - var bits = re.exec(color_string); - if (bits) { - channels = processor(bits); - this.r = channels[0]; - this.g = channels[1]; - this.b = channels[2]; - this.ok = true; - } - - } - - // validate/cleanup values - this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r); - this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g); - this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b); - - // some getters - this.toRGB = function () { - return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')'; - }; - this.toHex = function () { - var r = this.r.toString(16); - var g = this.g.toString(16); - var b = this.b.toString(16); - if (r.length == 1) r = '0' + r; - if (g.length == 1) g = '0' + g; - if (b.length == 1) b = '0' + b; - return '#' + r + g + b; - }; - - // help - this.getHelpXML = function () { - - var examples = new Array(); - // add regexps - for (var i = 0; i < color_defs.length; i++) { - var example = color_defs[i].example; - for (var j = 0; j < example.length; j++) { - examples[examples.length] = example[j]; - } - } - // add type-in colors - for (var sc in simple_colors) { - examples[examples.length] = sc; - } - - var xml = document.createElement('ul'); - xml.setAttribute('id', 'rgbcolor-examples'); - for (var i = 0; i < examples.length; i++) { - try { - var list_item = document.createElement('li'); - var list_color = new RGBColor(examples[i]); - var example_div = document.createElement('div'); - example_div.style.cssText = - 'margin: 3px; ' - + 'border: 1px solid black; ' - + 'background:' + list_color.toHex() + '; ' - + 'color:' + list_color.toHex() - ; - example_div.appendChild(document.createTextNode('test')); - var list_item_value = document.createTextNode( - ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex() - ); - list_item.appendChild(example_div); - list_item.appendChild(list_item_value); - xml.appendChild(list_item); - - } catch(e){} - } - return xml; - - }; - - } - - // export as AMD... - if ( typeof define !== 'undefined' && define.amd ) { - define('RGBColor', function () { return RGBColor; }); - } - - // ...or as browserify - else if (typeof module !== 'undefined' && module.exports ) { - module.exports = RGBColor; - } - - global.RGBColor = RGBColor; - - }(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 - - /* - html2canvas 0.5.0-beta3 <http://html2canvas.hertzen.com> - Copyright (c) 2016 Niklas von Hertzen - - Released under License - */ - - !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.html2canvas=e();}}(function(){var define;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r);}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ - (function (global){ - (function(root) { - - /** Detect free variables */ - var freeExports = typeof exports == 'object' && exports; - var freeModule = typeof module == 'object' && module && - module.exports == freeExports && module; - var freeGlobal = typeof global == 'object' && global; - if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { - root = freeGlobal; - } - - /** - * The `punycode` object. - * @name punycode - * @type Object - */ - var punycode, - - /** Highest positive signed 32-bit float value */ - maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 - - /** Bootstring parameters */ - base = 36, - tMin = 1, - tMax = 26, - skew = 38, - damp = 700, - initialBias = 72, - initialN = 128, // 0x80 - delimiter = '-', // '\x2D' - - /** Regular expressions */ - regexPunycode = /^xn--/, - regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars - regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators - - /** Error messages */ - errors = { - 'overflow': 'Overflow: input needs wider integers to process', - 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', - 'invalid-input': 'Invalid input' - }, - - /** Convenience shortcuts */ - baseMinusTMin = base - tMin, - floor = Math.floor, - stringFromCharCode = String.fromCharCode, - - /** Temporary variable */ - key; - - /*--------------------------------------------------------------------------*/ - - /** - * A generic error utility function. - * @private - * @param {String} type The error type. - * @returns {Error} Throws a `RangeError` with the applicable error message. - */ - function error(type) { - throw RangeError(errors[type]); - } - - /** - * A generic `Array#map` utility function. - * @private - * @param {Array} array The array to iterate over. - * @param {Function} callback The function that gets called for every array - * item. - * @returns {Array} A new array of values returned by the callback function. - */ - function map(array, fn) { - var length = array.length; - while (length--) { - array[length] = fn(array[length]); - } - return array; - } - - /** - * A simple `Array#map`-like wrapper to work with domain name strings. - * @private - * @param {String} domain The domain name. - * @param {Function} callback The function that gets called for every - * character. - * @returns {Array} A new string of characters returned by the callback - * function. - */ - function mapDomain(string, fn) { - return map(string.split(regexSeparators), fn).join('.'); - } - - /** - * Creates an array containing the numeric code points of each Unicode - * character in the string. While JavaScript uses UCS-2 internally, - * this function will convert a pair of surrogate halves (each of which - * UCS-2 exposes as separate characters) into a single code point, - * matching UTF-16. - * @see `punycode.ucs2.encode` - * @see <http://mathiasbynens.be/notes/javascript-encoding> - * @memberOf punycode.ucs2 - * @name decode - * @param {String} string The Unicode input string (UCS-2). - * @returns {Array} The new array of code points. - */ - function ucs2decode(string) { - var output = [], - counter = 0, - length = string.length, - value, - extra; - while (counter < length) { - value = string.charCodeAt(counter++); - if (value >= 0xD800 && value <= 0xDBFF && counter < length) { - // high surrogate, and there is a next character - extra = string.charCodeAt(counter++); - if ((extra & 0xFC00) == 0xDC00) { // low surrogate - output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); - } else { - // unmatched surrogate; only append this code unit, in case the next - // code unit is the high surrogate of a surrogate pair - output.push(value); - counter--; - } - } else { - output.push(value); - } - } - return output; - } - - /** - * Creates a string based on an array of numeric code points. - * @see `punycode.ucs2.decode` - * @memberOf punycode.ucs2 - * @name encode - * @param {Array} codePoints The array of numeric code points. - * @returns {String} The new Unicode string (UCS-2). - */ - function ucs2encode(array) { - return map(array, function(value) { - var output = ''; - if (value > 0xFFFF) { - value -= 0x10000; - output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); - value = 0xDC00 | value & 0x3FF; - } - output += stringFromCharCode(value); - return output; - }).join(''); - } - - /** - * Converts a basic code point into a digit/integer. - * @see `digitToBasic()` - * @private - * @param {Number} codePoint The basic numeric code point value. - * @returns {Number} The numeric value of a basic code point (for use in - * representing integers) in the range `0` to `base - 1`, or `base` if - * the code point does not represent a value. - */ - function basicToDigit(codePoint) { - if (codePoint - 48 < 10) { - return codePoint - 22; - } - if (codePoint - 65 < 26) { - return codePoint - 65; - } - if (codePoint - 97 < 26) { - return codePoint - 97; - } - return base; - } - - /** - * Converts a digit/integer into a basic code point. - * @see `basicToDigit()` - * @private - * @param {Number} digit The numeric value of a basic code point. - * @returns {Number} The basic code point whose value (when used for - * representing integers) is `digit`, which needs to be in the range - * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is - * used; else, the lowercase form is used. The behavior is undefined - * if `flag` is non-zero and `digit` has no uppercase form. - */ - function digitToBasic(digit, flag) { - // 0..25 map to ASCII a..z or A..Z - // 26..35 map to ASCII 0..9 - return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); - } - - /** - * Bias adaptation function as per section 3.4 of RFC 3492. - * http://tools.ietf.org/html/rfc3492#section-3.4 - * @private - */ - function adapt(delta, numPoints, firstTime) { - var k = 0; - delta = firstTime ? floor(delta / damp) : delta >> 1; - delta += floor(delta / numPoints); - for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { - delta = floor(delta / baseMinusTMin); - } - return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); - } - - /** - * Converts a Punycode string of ASCII-only symbols to a string of Unicode - * symbols. - * @memberOf punycode - * @param {String} input The Punycode string of ASCII-only symbols. - * @returns {String} The resulting string of Unicode symbols. - */ - function decode(input) { - // Don't use UCS-2 - var output = [], - inputLength = input.length, - out, - i = 0, - n = initialN, - bias = initialBias, - basic, - j, - index, - oldi, - w, - k, - digit, - t, - /** Cached calculation results */ - baseMinusT; - - // Handle the basic code points: let `basic` be the number of input code - // points before the last delimiter, or `0` if there is none, then copy - // the first basic code points to the output. - - basic = input.lastIndexOf(delimiter); - if (basic < 0) { - basic = 0; - } - - for (j = 0; j < basic; ++j) { - // if it's not a basic code point - if (input.charCodeAt(j) >= 0x80) { - error('not-basic'); - } - output.push(input.charCodeAt(j)); - } - - // Main decoding loop: start just after the last delimiter if any basic code - // points were copied; start at the beginning otherwise. - - for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { - - // `index` is the index of the next character to be consumed. - // Decode a generalized variable-length integer into `delta`, - // which gets added to `i`. The overflow checking is easier - // if we increase `i` as we go, then subtract off its starting - // value at the end to obtain `delta`. - for (oldi = i, w = 1, k = base; /* no condition */; k += base) { - - if (index >= inputLength) { - error('invalid-input'); - } - - digit = basicToDigit(input.charCodeAt(index++)); - - if (digit >= base || digit > floor((maxInt - i) / w)) { - error('overflow'); - } - - i += digit * w; - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); - - if (digit < t) { - break; - } - - baseMinusT = base - t; - if (w > floor(maxInt / baseMinusT)) { - error('overflow'); - } - - w *= baseMinusT; - - } - - out = output.length + 1; - bias = adapt(i - oldi, out, oldi == 0); - - // `i` was supposed to wrap around from `out` to `0`, - // incrementing `n` each time, so we'll fix that now: - if (floor(i / out) > maxInt - n) { - error('overflow'); - } - - n += floor(i / out); - i %= out; - - // Insert `n` at position `i` of the output - output.splice(i++, 0, n); - - } - - return ucs2encode(output); - } - - /** - * Converts a string of Unicode symbols to a Punycode string of ASCII-only - * symbols. - * @memberOf punycode - * @param {String} input The string of Unicode symbols. - * @returns {String} The resulting Punycode string of ASCII-only symbols. - */ - function encode(input) { - var n, - delta, - handledCPCount, - basicLength, - bias, - j, - m, - q, - k, - t, - currentValue, - output = [], - /** `inputLength` will hold the number of code points in `input`. */ - inputLength, - /** Cached calculation results */ - handledCPCountPlusOne, - baseMinusT, - qMinusT; - - // Convert the input in UCS-2 to Unicode - input = ucs2decode(input); - - // Cache the length - inputLength = input.length; - - // Initialize the state - n = initialN; - delta = 0; - bias = initialBias; - - // Handle the basic code points - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue < 0x80) { - output.push(stringFromCharCode(currentValue)); - } - } - - handledCPCount = basicLength = output.length; - - // `handledCPCount` is the number of code points that have been handled; - // `basicLength` is the number of basic code points. - - // Finish the basic string - if it is not empty - with a delimiter - if (basicLength) { - output.push(delimiter); - } - - // Main encoding loop: - while (handledCPCount < inputLength) { - - // All non-basic code points < n have been handled already. Find the next - // larger one: - for (m = maxInt, j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue >= n && currentValue < m) { - m = currentValue; - } - } - - // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, - // but guard against overflow - handledCPCountPlusOne = handledCPCount + 1; - if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { - error('overflow'); - } - - delta += (m - n) * handledCPCountPlusOne; - n = m; - - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; - - if (currentValue < n && ++delta > maxInt) { - error('overflow'); - } - - if (currentValue == n) { - // Represent delta as a generalized variable-length integer - for (q = delta, k = base; /* no condition */; k += base) { - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); - if (q < t) { - break; - } - qMinusT = q - t; - baseMinusT = base - t; - output.push( - stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) - ); - q = floor(qMinusT / baseMinusT); - } - - output.push(stringFromCharCode(digitToBasic(q, 0))); - bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); - delta = 0; - ++handledCPCount; - } - } - - ++delta; - ++n; - - } - return output.join(''); - } - - /** - * Converts a Punycode string representing a domain name to Unicode. Only the - * Punycoded parts of the domain name will be converted, i.e. it doesn't - * matter if you call it on a string that has already been converted to - * Unicode. - * @memberOf punycode - * @param {String} domain The Punycode domain name to convert to Unicode. - * @returns {String} The Unicode representation of the given Punycode - * string. - */ - function toUnicode(domain) { - return mapDomain(domain, function(string) { - return regexPunycode.test(string) - ? decode(string.slice(4).toLowerCase()) - : string; - }); - } - - /** - * Converts a Unicode string representing a domain name to Punycode. Only the - * non-ASCII parts of the domain name will be converted, i.e. it doesn't - * matter if you call it with a domain that's already in ASCII. - * @memberOf punycode - * @param {String} domain The domain name to convert, as a Unicode string. - * @returns {String} The Punycode representation of the given domain name. - */ - function toASCII(domain) { - return mapDomain(domain, function(string) { - return regexNonASCII.test(string) - ? 'xn--' + encode(string) - : string; - }); - } - - /*--------------------------------------------------------------------------*/ - - /** Define the public API */ - punycode = { - /** - * A string representing the current Punycode.js version number. - * @memberOf punycode - * @type String - */ - 'version': '1.2.4', - /** - * An object of methods to convert from JavaScript's internal character - * representation (UCS-2) to Unicode code points, and back. - * @see <http://mathiasbynens.be/notes/javascript-encoding> - * @memberOf punycode - * @type Object - */ - 'ucs2': { - 'decode': ucs2decode, - 'encode': ucs2encode - }, - 'decode': decode, - 'encode': encode, - 'toASCII': toASCII, - 'toUnicode': toUnicode - }; - - /** Expose `punycode` */ - // Some AMD build optimizers, like r.js, check for specific condition patterns - // like the following: - if ( - typeof define == 'function' && - typeof define.amd == 'object' && define.amd && false - ) { - define('punycode', function() { - return punycode; - }); - } else if (freeExports && !freeExports.nodeType) { - if (freeModule) { // in Node.js or RingoJS v0.8.0+ - freeModule.exports = punycode; - } else { // in Narwhal or RingoJS v0.7.0- - for (key in punycode) { - punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); - } - } - } else { // in Rhino or a web browser - root.punycode = punycode; - } - - }(this)); - - }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}); - },{}],2:[function(_dereq_,module,exports){ - var log = _dereq_('./log'); - - function restoreOwnerScroll(ownerDocument, x, y) { - if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) { - ownerDocument.defaultView.scrollTo(x, y); - } - } - - function cloneCanvasContents(canvas, clonedCanvas) { - try { - if (clonedCanvas) { - clonedCanvas.width = canvas.width; - clonedCanvas.height = canvas.height; - clonedCanvas.getContext("2d").putImageData(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height), 0, 0); - } - } catch(e) { - log("Unable to copy canvas content from", canvas, e); - } - } - - function cloneNode(node, javascriptEnabled) { - var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false); - - var child = node.firstChild; - while(child) { - if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') { - clone.appendChild(cloneNode(child, javascriptEnabled)); - } - child = child.nextSibling; - } - - if (node.nodeType === 1) { - clone._scrollTop = node.scrollTop; - clone._scrollLeft = node.scrollLeft; - if (node.nodeName === "CANVAS") { - cloneCanvasContents(node, clone); - } else if (node.nodeName === "TEXTAREA" || node.nodeName === "SELECT") { - clone.value = node.value; - } - } - - return clone; - } - - function initNode(node) { - if (node.nodeType === 1) { - node.scrollTop = node._scrollTop; - node.scrollLeft = node._scrollLeft; - - var child = node.firstChild; - while(child) { - initNode(child); - child = child.nextSibling; - } - } - } - - module.exports = function(ownerDocument, containerDocument, width, height, options, x ,y) { - var documentElement = cloneNode(ownerDocument.documentElement, options.javascriptEnabled); - var container = containerDocument.createElement("iframe"); - - container.className = "html2canvas-container"; - container.style.visibility = "hidden"; - container.style.position = "fixed"; - container.style.left = "-10000px"; - container.style.top = "0px"; - container.style.border = "0"; - container.width = width; - container.height = height; - container.scrolling = "no"; // ios won't scroll without it - containerDocument.body.appendChild(container); - - return new Promise(function(resolve) { - var documentClone = container.contentWindow.document; - - /* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle - if window url is about:blank, we can assign the url to current by writing onto the document - */ - container.contentWindow.onload = container.onload = function() { - var interval = setInterval(function() { - if (documentClone.body.childNodes.length > 0) { - initNode(documentClone.documentElement); - clearInterval(interval); - if (options.type === "view") { - container.contentWindow.scrollTo(x, y); - if ((/(iPad|iPhone|iPod)/g).test(navigator.userAgent) && (container.contentWindow.scrollY !== y || container.contentWindow.scrollX !== x)) { - documentClone.documentElement.style.top = (-y) + "px"; - documentClone.documentElement.style.left = (-x) + "px"; - documentClone.documentElement.style.position = 'absolute'; - } - } - resolve(container); - } - }, 50); - }; - - documentClone.open(); - documentClone.write("<!DOCTYPE html><html></html>"); - // Chrome scrolls the parent document for some reason after the write to the cloned window??? - restoreOwnerScroll(ownerDocument, x, y); - documentClone.replaceChild(documentClone.adoptNode(documentElement), documentClone.documentElement); - documentClone.close(); - }); - }; - - },{"./log":13}],3:[function(_dereq_,module,exports){ - // http://dev.w3.org/csswg/css-color/ - - function Color(value) { - this.r = 0; - this.g = 0; - this.b = 0; - this.a = null; - var result = this.fromArray(value) || - this.namedColor(value) || - this.rgb(value) || - this.rgba(value) || - this.hex6(value) || - this.hex3(value); - } - - Color.prototype.darken = function(amount) { - var a = 1 - amount; - return new Color([ - Math.round(this.r * a), - Math.round(this.g * a), - Math.round(this.b * a), - this.a - ]); - }; - - Color.prototype.isTransparent = function() { - return this.a === 0; - }; - - Color.prototype.isBlack = function() { - return this.r === 0 && this.g === 0 && this.b === 0; - }; - - Color.prototype.fromArray = function(array) { - if (Array.isArray(array)) { - this.r = Math.min(array[0], 255); - this.g = Math.min(array[1], 255); - this.b = Math.min(array[2], 255); - if (array.length > 3) { - this.a = array[3]; - } - } - - return (Array.isArray(array)); - }; - - var _hex3 = /^#([a-f0-9]{3})$/i; - - Color.prototype.hex3 = function(value) { - var match = null; - if ((match = value.match(_hex3)) !== null) { - this.r = parseInt(match[1][0] + match[1][0], 16); - this.g = parseInt(match[1][1] + match[1][1], 16); - this.b = parseInt(match[1][2] + match[1][2], 16); - } - return match !== null; - }; - - var _hex6 = /^#([a-f0-9]{6})$/i; - - Color.prototype.hex6 = function(value) { - var match = null; - if ((match = value.match(_hex6)) !== null) { - this.r = parseInt(match[1].substring(0, 2), 16); - this.g = parseInt(match[1].substring(2, 4), 16); - this.b = parseInt(match[1].substring(4, 6), 16); - } - return match !== null; - }; - - - var _rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/; - - Color.prototype.rgb = function(value) { - var match = null; - if ((match = value.match(_rgb)) !== null) { - this.r = Number(match[1]); - this.g = Number(match[2]); - this.b = Number(match[3]); - } - return match !== null; - }; - - var _rgba = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?\.?\d+)\s*\)$/; - - Color.prototype.rgba = function(value) { - var match = null; - if ((match = value.match(_rgba)) !== null) { - this.r = Number(match[1]); - this.g = Number(match[2]); - this.b = Number(match[3]); - this.a = Number(match[4]); - } - return match !== null; - }; - - Color.prototype.toString = function() { - return this.a !== null && this.a !== 1 ? - "rgba(" + [this.r, this.g, this.b, this.a].join(",") + ")" : - "rgb(" + [this.r, this.g, this.b].join(",") + ")"; - }; - - Color.prototype.namedColor = function(value) { - value = value.toLowerCase(); - var color = colors[value]; - if (color) { - this.r = color[0]; - this.g = color[1]; - this.b = color[2]; - } else if (value === "transparent") { - this.r = this.g = this.b = this.a = 0; - return true; - } - - return !!color; - }; - - Color.prototype.isColor = true; - - // JSON.stringify([].slice.call($$('.named-color-table tr'), 1).map(function(row) { return [row.childNodes[3].textContent, row.childNodes[5].textContent.trim().split(",").map(Number)] }).reduce(function(data, row) {data[row[0]] = row[1]; return data}, {})) - var colors = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] - }; - - module.exports = Color; - - },{}],4:[function(_dereq_,module,exports){ - var Support = _dereq_('./support'); - var CanvasRenderer = _dereq_('./renderers/canvas'); - var ImageLoader = _dereq_('./imageloader'); - var NodeParser = _dereq_('./nodeparser'); - var NodeContainer = _dereq_('./nodecontainer'); - var log = _dereq_('./log'); - var utils = _dereq_('./utils'); - var createWindowClone = _dereq_('./clone'); - var loadUrlDocument = _dereq_('./proxy').loadUrlDocument; - var getBounds = utils.getBounds; - - var html2canvasNodeAttribute = "data-html2canvas-node"; - var html2canvasCloneIndex = 0; - - function html2canvas(nodeList, options) { - var index = html2canvasCloneIndex++; - options = options || {}; - if (options.logging) { - log.options.logging = true; - log.options.start = Date.now(); - } - - options.async = typeof(options.async) === "undefined" ? true : options.async; - options.allowTaint = typeof(options.allowTaint) === "undefined" ? false : options.allowTaint; - options.removeContainer = typeof(options.removeContainer) === "undefined" ? true : options.removeContainer; - options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled; - options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout; - options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer; - options.strict = !!options.strict; - - if (typeof(nodeList) === "string") { - if (typeof(options.proxy) !== "string") { - return Promise.reject("Proxy must be used when rendering url"); - } - var width = options.width != null ? options.width : window.innerWidth; - var height = options.height != null ? options.height : window.innerHeight; - return loadUrlDocument(absoluteUrl(nodeList), options.proxy, document, width, height, options).then(function(container) { - return renderWindow(container.contentWindow.document.documentElement, container, options, width, height); - }); - } - - var node = ((nodeList === undefined) ? [document.documentElement] : ((nodeList.length) ? nodeList : [nodeList]))[0]; - node.setAttribute(html2canvasNodeAttribute + index, index); - return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) { - if (typeof(options.onrendered) === "function") { - log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"); - options.onrendered(canvas); - } - return canvas; - }); - } - - html2canvas.CanvasRenderer = CanvasRenderer; - html2canvas.NodeContainer = NodeContainer; - html2canvas.log = log; - html2canvas.utils = utils; - - var html2canvasExport = (typeof(document) === "undefined" || typeof(Object.create) !== "function" || typeof(document.createElement("canvas").getContext) !== "function") ? function() { - return Promise.reject("No canvas support"); - } : html2canvas; - - module.exports = html2canvasExport; - - if (typeof(define) === 'function' && define.amd && false) { - define('html2canvas', [], function() { - return html2canvasExport; - }); - } - - function renderDocument(document, options, windowWidth, windowHeight, html2canvasIndex) { - return createWindowClone(document, document, windowWidth, windowHeight, options, document.defaultView.pageXOffset, document.defaultView.pageYOffset).then(function(container) { - log("Document cloned"); - var attributeName = html2canvasNodeAttribute + html2canvasIndex; - var selector = "[" + attributeName + "='" + html2canvasIndex + "']"; - document.querySelector(selector).removeAttribute(attributeName); - var clonedWindow = container.contentWindow; - var node = clonedWindow.document.querySelector(selector); - var oncloneHandler = (typeof(options.onclone) === "function") ? Promise.resolve(options.onclone(clonedWindow.document)) : Promise.resolve(true); - return oncloneHandler.then(function() { - return renderWindow(node, container, options, windowWidth, windowHeight); - }); - }); - } - - function renderWindow(node, container, options, windowWidth, windowHeight) { - var clonedWindow = container.contentWindow; - var support = new Support(clonedWindow.document); - var imageLoader = new ImageLoader(options, support); - var bounds = getBounds(node); - var width = options.type === "view" ? windowWidth : documentWidth(clonedWindow.document); - var height = options.type === "view" ? windowHeight : documentHeight(clonedWindow.document); - var renderer = new options.renderer(width, height, imageLoader, options, document); - var parser = new NodeParser(node, renderer, support, imageLoader, options); - return parser.ready.then(function() { - log("Finished rendering"); - var canvas; - - if (options.type === "view") { - canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0}); - } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) { - canvas = renderer.canvas; - } else { - canvas = crop(renderer.canvas, {width: options.width != null ? options.width : bounds.width, height: options.height != null ? options.height : bounds.height, top: bounds.top, left: bounds.left, x: 0, y: 0}); - } - - cleanupContainer(container, options); - return canvas; - }); - } - - function cleanupContainer(container, options) { - if (options.removeContainer) { - container.parentNode.removeChild(container); - log("Cleaned up container"); - } - } - - function crop(canvas, bounds) { - var croppedCanvas = document.createElement("canvas"); - var x1 = Math.min(canvas.width - 1, Math.max(0, bounds.left)); - var x2 = Math.min(canvas.width, Math.max(1, bounds.left + bounds.width)); - var y1 = Math.min(canvas.height - 1, Math.max(0, bounds.top)); - var y2 = Math.min(canvas.height, Math.max(1, bounds.top + bounds.height)); - croppedCanvas.width = bounds.width; - croppedCanvas.height = bounds.height; - var width = x2-x1; - var height = y2-y1; - log("Cropping canvas at:", "left:", bounds.left, "top:", bounds.top, "width:", width, "height:", height); - log("Resulting crop with width", bounds.width, "and height", bounds.height, "with x", x1, "and y", y1); - croppedCanvas.getContext("2d").drawImage(canvas, x1, y1, width, height, bounds.x, bounds.y, width, height); - return croppedCanvas; - } - - function documentWidth (doc) { - return Math.max( - Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), - Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), - Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) - ); - } - - function documentHeight (doc) { - return Math.max( - Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), - Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), - Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) - ); - } - - function absoluteUrl(url) { - var link = document.createElement("a"); - link.href = url; - link.href = link.href; - return link; - } - - },{"./clone":2,"./imageloader":11,"./log":13,"./nodecontainer":14,"./nodeparser":15,"./proxy":16,"./renderers/canvas":20,"./support":22,"./utils":26}],5:[function(_dereq_,module,exports){ - var log = _dereq_('./log'); - var smallImage = _dereq_('./utils').smallImage; - - function DummyImageContainer(src) { - this.src = src; - log("DummyImageContainer for", src); - if (!this.promise || !this.image) { - log("Initiating DummyImageContainer"); - DummyImageContainer.prototype.image = new Image(); - var image = this.image; - DummyImageContainer.prototype.promise = new Promise(function(resolve, reject) { - image.onload = resolve; - image.onerror = reject; - image.src = smallImage(); - if (image.complete === true) { - resolve(image); - } - }); - } - } - - module.exports = DummyImageContainer; - - },{"./log":13,"./utils":26}],6:[function(_dereq_,module,exports){ - var smallImage = _dereq_('./utils').smallImage; - - function Font(family, size) { - var container = document.createElement('div'), - img = document.createElement('img'), - span = document.createElement('span'), - sampleText = 'Hidden Text', - baseline, - middle; - - container.style.visibility = "hidden"; - container.style.fontFamily = family; - container.style.fontSize = size; - container.style.margin = 0; - container.style.padding = 0; - - document.body.appendChild(container); - - img.src = smallImage(); - img.width = 1; - img.height = 1; - - img.style.margin = 0; - img.style.padding = 0; - img.style.verticalAlign = "baseline"; - - span.style.fontFamily = family; - span.style.fontSize = size; - span.style.margin = 0; - span.style.padding = 0; - - span.appendChild(document.createTextNode(sampleText)); - container.appendChild(span); - container.appendChild(img); - baseline = (img.offsetTop - span.offsetTop) + 1; - - container.removeChild(span); - container.appendChild(document.createTextNode(sampleText)); - - container.style.lineHeight = "normal"; - img.style.verticalAlign = "super"; - - middle = (img.offsetTop-container.offsetTop) + 1; - - document.body.removeChild(container); - - this.baseline = baseline; - this.lineWidth = 1; - this.middle = middle; - } - - module.exports = Font; - - },{"./utils":26}],7:[function(_dereq_,module,exports){ - var Font = _dereq_('./font'); - - function FontMetrics() { - this.data = {}; - } - - FontMetrics.prototype.getMetrics = function(family, size) { - if (this.data[family + "-" + size] === undefined) { - this.data[family + "-" + size] = new Font(family, size); - } - return this.data[family + "-" + size]; - }; - - module.exports = FontMetrics; - - },{"./font":6}],8:[function(_dereq_,module,exports){ - var utils = _dereq_('./utils'); - var getBounds = utils.getBounds; - var loadUrlDocument = _dereq_('./proxy').loadUrlDocument; - - function FrameContainer(container, sameOrigin, options) { - this.image = null; - this.src = container; - var self = this; - var bounds = getBounds(container); - this.promise = (!sameOrigin ? this.proxyLoad(options.proxy, bounds, options) : new Promise(function(resolve) { - if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) { - container.contentWindow.onload = container.onload = function() { - resolve(container); - }; - } else { - resolve(container); - } - })).then(function(container) { - var html2canvas = _dereq_('./core'); - return html2canvas(container.contentWindow.document.documentElement, {type: 'view', width: container.width, height: container.height, proxy: options.proxy, javascriptEnabled: options.javascriptEnabled, removeContainer: options.removeContainer, allowTaint: options.allowTaint, imageTimeout: options.imageTimeout / 2}); - }).then(function(canvas) { - return self.image = canvas; - }); - } - - FrameContainer.prototype.proxyLoad = function(proxy, bounds, options) { - var container = this.src; - return loadUrlDocument(container.src, proxy, container.ownerDocument, bounds.width, bounds.height, options); - }; - - module.exports = FrameContainer; - - },{"./core":4,"./proxy":16,"./utils":26}],9:[function(_dereq_,module,exports){ - function GradientContainer(imageData) { - this.src = imageData.value; - this.colorStops = []; - this.type = null; - this.x0 = 0.5; - this.y0 = 0.5; - this.x1 = 0.5; - this.y1 = 0.5; - this.promise = Promise.resolve(true); - } - - GradientContainer.TYPES = { - LINEAR: 1, - RADIAL: 2 - }; - - // TODO: support hsl[a], negative %/length values - // TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle ) - GradientContainer.REGEXP_COLORSTOP = /^\s*(rgba?\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*[0-9\.]+)?\s*\)|[a-z]{3,20}|#[a-f0-9]{3,6})(?:\s+(\d{1,3}(?:\.\d+)?)(%|px)?)?(?:\s|$)/i; - - module.exports = GradientContainer; - - },{}],10:[function(_dereq_,module,exports){ - function ImageContainer(src, cors) { - this.src = src; - this.image = new Image(); - var self = this; - this.tainted = null; - this.promise = new Promise(function(resolve, reject) { - self.image.onload = resolve; - self.image.onerror = reject; - if (cors) { - self.image.crossOrigin = "anonymous"; - } - self.image.src = src; - if (self.image.complete === true) { - resolve(self.image); - } - }); - } - - module.exports = ImageContainer; - - },{}],11:[function(_dereq_,module,exports){ - var log = _dereq_('./log'); - var ImageContainer = _dereq_('./imagecontainer'); - var DummyImageContainer = _dereq_('./dummyimagecontainer'); - var ProxyImageContainer = _dereq_('./proxyimagecontainer'); - var FrameContainer = _dereq_('./framecontainer'); - var SVGContainer = _dereq_('./svgcontainer'); - var SVGNodeContainer = _dereq_('./svgnodecontainer'); - var LinearGradientContainer = _dereq_('./lineargradientcontainer'); - var WebkitGradientContainer = _dereq_('./webkitgradientcontainer'); - var bind = _dereq_('./utils').bind; - - function ImageLoader(options, support) { - this.link = null; - this.options = options; - this.support = support; - this.origin = this.getOrigin(window.location.href); - } - - ImageLoader.prototype.findImages = function(nodes) { - var images = []; - nodes.reduce(function(imageNodes, container) { - switch(container.node.nodeName) { - case "IMG": - return imageNodes.concat([{ - args: [container.node.src], - method: "url" - }]); - case "svg": - case "IFRAME": - return imageNodes.concat([{ - args: [container.node], - method: container.node.nodeName - }]); - } - return imageNodes; - }, []).forEach(this.addImage(images, this.loadImage), this); - return images; - }; - - ImageLoader.prototype.findBackgroundImage = function(images, container) { - container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this); - return images; - }; - - ImageLoader.prototype.addImage = function(images, callback) { - return function(newImage) { - newImage.args.forEach(function(image) { - if (!this.imageExists(images, image)) { - images.splice(0, 0, callback.call(this, newImage)); - log('Added image #' + (images.length), typeof(image) === "string" ? image.substring(0, 100) : image); - } - }, this); - }; - }; - - ImageLoader.prototype.hasImageBackground = function(imageData) { - return imageData.method !== "none"; - }; - - ImageLoader.prototype.loadImage = function(imageData) { - if (imageData.method === "url") { - var src = imageData.args[0]; - if (this.isSVG(src) && !this.support.svg && !this.options.allowTaint) { - return new SVGContainer(src); - } else if (src.match(/data:image\/.*;base64,/i)) { - return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false); - } else if (this.isSameOrigin(src) || this.options.allowTaint === true || this.isSVG(src)) { - return new ImageContainer(src, false); - } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) { - return new ImageContainer(src, true); - } else if (this.options.proxy) { - return new ProxyImageContainer(src, this.options.proxy); - } else { - return new DummyImageContainer(src); - } - } else if (imageData.method === "linear-gradient") { - return new LinearGradientContainer(imageData); - } else if (imageData.method === "gradient") { - return new WebkitGradientContainer(imageData); - } else if (imageData.method === "svg") { - return new SVGNodeContainer(imageData.args[0], this.support.svg); - } else if (imageData.method === "IFRAME") { - return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options); - } else { - return new DummyImageContainer(imageData); - } - }; - - ImageLoader.prototype.isSVG = function(src) { - return src.substring(src.length - 3).toLowerCase() === "svg" || SVGContainer.prototype.isInline(src); - }; - - ImageLoader.prototype.imageExists = function(images, src) { - return images.some(function(image) { - return image.src === src; - }); - }; - - ImageLoader.prototype.isSameOrigin = function(url) { - return (this.getOrigin(url) === this.origin); - }; - - ImageLoader.prototype.getOrigin = function(url) { - var link = this.link || (this.link = document.createElement("a")); - link.href = url; - link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/ - return link.protocol + link.hostname + link.port; - }; - - ImageLoader.prototype.getPromise = function(container) { - return this.timeout(container, this.options.imageTimeout)['catch'](function() { - var dummy = new DummyImageContainer(container.src); - return dummy.promise.then(function(image) { - container.image = image; - }); - }); - }; - - ImageLoader.prototype.get = function(src) { - var found = null; - return this.images.some(function(img) { - return (found = img).src === src; - }) ? found : null; - }; - - ImageLoader.prototype.fetch = function(nodes) { - this.images = nodes.reduce(bind(this.findBackgroundImage, this), this.findImages(nodes)); - this.images.forEach(function(image, index) { - image.promise.then(function() { - log("Succesfully loaded image #"+ (index+1), image); - }, function(e) { - log("Failed loading image #"+ (index+1), image, e); - }); - }); - this.ready = Promise.all(this.images.map(this.getPromise, this)); - log("Finished searching images"); - return this; - }; - - ImageLoader.prototype.timeout = function(container, timeout) { - var timer; - var promise = Promise.race([container.promise, new Promise(function(res, reject) { - timer = setTimeout(function() { - log("Timed out loading image", container); - reject(container); - }, timeout); - })]).then(function(container) { - clearTimeout(timer); - return container; - }); - promise['catch'](function() { - clearTimeout(timer); - }); - return promise; - }; - - module.exports = ImageLoader; - - },{"./dummyimagecontainer":5,"./framecontainer":8,"./imagecontainer":10,"./lineargradientcontainer":12,"./log":13,"./proxyimagecontainer":17,"./svgcontainer":23,"./svgnodecontainer":24,"./utils":26,"./webkitgradientcontainer":27}],12:[function(_dereq_,module,exports){ - var GradientContainer = _dereq_('./gradientcontainer'); - var Color = _dereq_('./color'); - - function LinearGradientContainer(imageData) { - GradientContainer.apply(this, arguments); - this.type = GradientContainer.TYPES.LINEAR; - - var hasDirection = LinearGradientContainer.REGEXP_DIRECTION.test( imageData.args[0] ) || - !GradientContainer.REGEXP_COLORSTOP.test( imageData.args[0] ); - - if (hasDirection) { - imageData.args[0].split(/\s+/).reverse().forEach(function(position, index) { - switch(position) { - case "left": - this.x0 = 0; - this.x1 = 1; - break; - case "top": - this.y0 = 0; - this.y1 = 1; - break; - case "right": - this.x0 = 1; - this.x1 = 0; - break; - case "bottom": - this.y0 = 1; - this.y1 = 0; - break; - case "to": - var y0 = this.y0; - var x0 = this.x0; - this.y0 = this.y1; - this.x0 = this.x1; - this.x1 = x0; - this.y1 = y0; - break; - case "center": - break; // centered by default - // Firefox internally converts position keywords to percentages: - // http://www.w3.org/TR/2010/WD-CSS2-20101207/colors.html#propdef-background-position - default: // percentage or absolute length - // TODO: support absolute start point positions (e.g., use bounds to convert px to a ratio) - var ratio = parseFloat(position, 10) * 1e-2; - if (isNaN(ratio)) { // invalid or unhandled value - break; - } - if (index === 0) { - this.y0 = ratio; - this.y1 = 1 - this.y0; - } else { - this.x0 = ratio; - this.x1 = 1 - this.x0; - } - break; - } - }, this); - } else { - this.y0 = 0; - this.y1 = 1; - } - - this.colorStops = imageData.args.slice(hasDirection ? 1 : 0).map(function(colorStop) { - var colorStopMatch = colorStop.match(GradientContainer.REGEXP_COLORSTOP); - var value = +colorStopMatch[2]; - var unit = value === 0 ? "%" : colorStopMatch[3]; // treat "0" as "0%" - return { - color: new Color(colorStopMatch[1]), - // TODO: support absolute stop positions (e.g., compute gradient line length & convert px to ratio) - stop: unit === "%" ? value / 100 : null - }; - }); - - if (this.colorStops[0].stop === null) { - this.colorStops[0].stop = 0; - } - - if (this.colorStops[this.colorStops.length - 1].stop === null) { - this.colorStops[this.colorStops.length - 1].stop = 1; - } - - // calculates and fills-in explicit stop positions when omitted from rule - this.colorStops.forEach(function(colorStop, index) { - if (colorStop.stop === null) { - this.colorStops.slice(index).some(function(find, count) { - if (find.stop !== null) { - colorStop.stop = ((find.stop - this.colorStops[index - 1].stop) / (count + 1)) + this.colorStops[index - 1].stop; - return true; - } else { - return false; - } - }, this); - } - }, this); - } - - LinearGradientContainer.prototype = Object.create(GradientContainer.prototype); - - // TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle ) - LinearGradientContainer.REGEXP_DIRECTION = /^\s*(?:to|left|right|top|bottom|center|\d{1,3}(?:\.\d+)?%?)(?:\s|$)/i; - - module.exports = LinearGradientContainer; - - },{"./color":3,"./gradientcontainer":9}],13:[function(_dereq_,module,exports){ - var logger = function() { - if (logger.options.logging && window.console && window.console.log) { - Function.prototype.bind.call(window.console.log, (window.console)).apply(window.console, [(Date.now() - logger.options.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0))); - } - }; - - logger.options = {logging: false}; - module.exports = logger; - - },{}],14:[function(_dereq_,module,exports){ - var Color = _dereq_('./color'); - var utils = _dereq_('./utils'); - var getBounds = utils.getBounds; - var parseBackgrounds = utils.parseBackgrounds; - var offsetBounds = utils.offsetBounds; - - function NodeContainer(node, parent) { - this.node = node; - this.parent = parent; - this.stack = null; - this.bounds = null; - this.borders = null; - this.clip = []; - this.backgroundClip = []; - this.offsetBounds = null; - this.visible = null; - this.computedStyles = null; - this.colors = {}; - this.styles = {}; - this.backgroundImages = null; - this.transformData = null; - this.transformMatrix = null; - this.isPseudoElement = false; - this.opacity = null; - } - - NodeContainer.prototype.cloneTo = function(stack) { - stack.visible = this.visible; - stack.borders = this.borders; - stack.bounds = this.bounds; - stack.clip = this.clip; - stack.backgroundClip = this.backgroundClip; - stack.computedStyles = this.computedStyles; - stack.styles = this.styles; - stack.backgroundImages = this.backgroundImages; - stack.opacity = this.opacity; - }; - - NodeContainer.prototype.getOpacity = function() { - return this.opacity === null ? (this.opacity = this.cssFloat('opacity')) : this.opacity; - }; - - NodeContainer.prototype.assignStack = function(stack) { - this.stack = stack; - stack.children.push(this); - }; - - NodeContainer.prototype.isElementVisible = function() { - return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : ( - this.css('display') !== "none" && - this.css('visibility') !== "hidden" && - !this.node.hasAttribute("data-html2canvas-ignore") && - (this.node.nodeName !== "INPUT" || this.node.getAttribute("type") !== "hidden") - ); - }; - - NodeContainer.prototype.css = function(attribute) { - if (!this.computedStyles) { - this.computedStyles = this.isPseudoElement ? this.parent.computedStyle(this.before ? ":before" : ":after") : this.computedStyle(null); - } - - return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]); - }; - - NodeContainer.prototype.prefixedCss = function(attribute) { - var prefixes = ["webkit", "moz", "ms", "o"]; - var value = this.css(attribute); - if (value === undefined) { - prefixes.some(function(prefix) { - value = this.css(prefix + attribute.substr(0, 1).toUpperCase() + attribute.substr(1)); - return value !== undefined; - }, this); - } - return value === undefined ? null : value; - }; - - NodeContainer.prototype.computedStyle = function(type) { - return this.node.ownerDocument.defaultView.getComputedStyle(this.node, type); - }; - - NodeContainer.prototype.cssInt = function(attribute) { - var value = parseInt(this.css(attribute), 10); - return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html - }; - - NodeContainer.prototype.color = function(attribute) { - return this.colors[attribute] || (this.colors[attribute] = new Color(this.css(attribute))); - }; - - NodeContainer.prototype.cssFloat = function(attribute) { - var value = parseFloat(this.css(attribute)); - return (isNaN(value)) ? 0 : value; - }; - - NodeContainer.prototype.fontWeight = function() { - var weight = this.css("fontWeight"); - switch(parseInt(weight, 10)){ - case 401: - weight = "bold"; - break; - case 400: - weight = "normal"; - break; - } - return weight; - }; - - NodeContainer.prototype.parseClip = function() { - var matches = this.css('clip').match(this.CLIP); - if (matches) { - return { - top: parseInt(matches[1], 10), - right: parseInt(matches[2], 10), - bottom: parseInt(matches[3], 10), - left: parseInt(matches[4], 10) - }; - } - return null; - }; - - NodeContainer.prototype.parseBackgroundImages = function() { - return this.backgroundImages || (this.backgroundImages = parseBackgrounds(this.css("backgroundImage"))); - }; - - NodeContainer.prototype.cssList = function(property, index) { - var value = (this.css(property) || '').split(','); - value = value[index || 0] || value[0] || 'auto'; - value = value.trim().split(' '); - if (value.length === 1) { - value = [value[0], isPercentage(value[0]) ? 'auto' : value[0]]; - } - return value; - }; - - NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) { - var size = this.cssList("backgroundSize", index); - var width, height; - - if (isPercentage(size[0])) { - width = bounds.width * parseFloat(size[0]) / 100; - } else if (/contain|cover/.test(size[0])) { - var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height; - return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio}; - } else { - width = parseInt(size[0], 10); - } - - if (size[0] === 'auto' && size[1] === 'auto') { - height = image.height; - } else if (size[1] === 'auto') { - height = width / image.width * image.height; - } else if (isPercentage(size[1])) { - height = bounds.height * parseFloat(size[1]) / 100; - } else { - height = parseInt(size[1], 10); - } - - if (size[0] === 'auto') { - width = height / image.height * image.width; - } - - return {width: width, height: height}; - }; - - NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) { - var position = this.cssList('backgroundPosition', index); - var left, top; - - if (isPercentage(position[0])){ - left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100); - } else { - left = parseInt(position[0], 10); - } - - if (position[1] === 'auto') { - top = left / image.width * image.height; - } else if (isPercentage(position[1])){ - top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100; - } else { - top = parseInt(position[1], 10); - } - - if (position[0] === 'auto') { - left = top / image.height * image.width; - } - - return {left: left, top: top}; - }; - - NodeContainer.prototype.parseBackgroundRepeat = function(index) { - return this.cssList("backgroundRepeat", index)[0]; - }; - - NodeContainer.prototype.parseTextShadows = function() { - var textShadow = this.css("textShadow"); - var results = []; - - if (textShadow && textShadow !== 'none') { - var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY); - for (var i = 0; shadows && (i < shadows.length); i++) { - var s = shadows[i].match(this.TEXT_SHADOW_VALUES); - results.push({ - color: new Color(s[0]), - offsetX: s[1] ? parseFloat(s[1].replace('px', '')) : 0, - offsetY: s[2] ? parseFloat(s[2].replace('px', '')) : 0, - blur: s[3] ? s[3].replace('px', '') : 0 - }); - } - } - return results; - }; - - NodeContainer.prototype.parseTransform = function() { - if (!this.transformData) { - if (this.hasTransform()) { - var offset = this.parseBounds(); - var origin = this.prefixedCss("transformOrigin").split(" ").map(removePx).map(asFloat); - origin[0] += offset.left; - origin[1] += offset.top; - this.transformData = { - origin: origin, - matrix: this.parseTransformMatrix() - }; - } else { - this.transformData = { - origin: [0, 0], - matrix: [1, 0, 0, 1, 0, 0] - }; - } - } - return this.transformData; - }; - - NodeContainer.prototype.parseTransformMatrix = function() { - if (!this.transformMatrix) { - var transform = this.prefixedCss("transform"); - var matrix = transform ? parseMatrix(transform.match(this.MATRIX_PROPERTY)) : null; - this.transformMatrix = matrix ? matrix : [1, 0, 0, 1, 0, 0]; - } - return this.transformMatrix; - }; - - NodeContainer.prototype.parseBounds = function() { - return this.bounds || (this.bounds = this.hasTransform() ? offsetBounds(this.node) : getBounds(this.node)); - }; - - NodeContainer.prototype.hasTransform = function() { - return this.parseTransformMatrix().join(",") !== "1,0,0,1,0,0" || (this.parent && this.parent.hasTransform()); - }; - - NodeContainer.prototype.getValue = function() { - var value = this.node.value || ""; - if (this.node.tagName === "SELECT") { - value = selectionValue(this.node); - } else if (this.node.type === "password") { - value = Array(value.length + 1).join('\u2022'); // jshint ignore:line - } - return value.length === 0 ? (this.node.placeholder || "") : value; - }; - - NodeContainer.prototype.MATRIX_PROPERTY = /(matrix|matrix3d)\((.+)\)/; - NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g; - NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g; - NodeContainer.prototype.CLIP = /^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/; - - function selectionValue(node) { - var option = node.options[node.selectedIndex || 0]; - return option ? (option.text || "") : ""; - } - - function parseMatrix(match) { - if (match && match[1] === "matrix") { - return match[2].split(",").map(function(s) { - return parseFloat(s.trim()); - }); - } else if (match && match[1] === "matrix3d") { - var matrix3d = match[2].split(",").map(function(s) { - return parseFloat(s.trim()); - }); - return [matrix3d[0], matrix3d[1], matrix3d[4], matrix3d[5], matrix3d[12], matrix3d[13]]; - } - } - - function isPercentage(value) { - return value.toString().indexOf("%") !== -1; - } - - function removePx(str) { - return str.replace("px", ""); - } - - function asFloat(str) { - return parseFloat(str); - } - - module.exports = NodeContainer; - - },{"./color":3,"./utils":26}],15:[function(_dereq_,module,exports){ - var log = _dereq_('./log'); - var punycode = _dereq_('punycode'); - var NodeContainer = _dereq_('./nodecontainer'); - var TextContainer = _dereq_('./textcontainer'); - var PseudoElementContainer = _dereq_('./pseudoelementcontainer'); - var FontMetrics = _dereq_('./fontmetrics'); - var Color = _dereq_('./color'); - var StackingContext = _dereq_('./stackingcontext'); - var utils = _dereq_('./utils'); - var bind = utils.bind; - var getBounds = utils.getBounds; - var parseBackgrounds = utils.parseBackgrounds; - var offsetBounds = utils.offsetBounds; - - function NodeParser(element, renderer, support, imageLoader, options) { - log("Starting NodeParser"); - this.renderer = renderer; - this.options = options; - this.range = null; - this.support = support; - this.renderQueue = []; - this.stack = new StackingContext(true, 1, element.ownerDocument, null); - var parent = new NodeContainer(element, null); - if (options.background) { - renderer.rectangle(0, 0, renderer.width, renderer.height, new Color(options.background)); - } - if (element === element.ownerDocument.documentElement) { - // http://www.w3.org/TR/css3-background/#special-backgrounds - var canvasBackground = new NodeContainer(parent.color('backgroundColor').isTransparent() ? element.ownerDocument.body : element.ownerDocument.documentElement, null); - renderer.rectangle(0, 0, renderer.width, renderer.height, canvasBackground.color('backgroundColor')); - } - parent.visibile = parent.isElementVisible(); - this.createPseudoHideStyles(element.ownerDocument); - this.disableAnimations(element.ownerDocument); - this.nodes = flatten([parent].concat(this.getChildren(parent)).filter(function(container) { - return container.visible = container.isElementVisible(); - }).map(this.getPseudoElements, this)); - this.fontMetrics = new FontMetrics(); - log("Fetched nodes, total:", this.nodes.length); - log("Calculate overflow clips"); - this.calculateOverflowClips(); - log("Start fetching images"); - this.images = imageLoader.fetch(this.nodes.filter(isElement)); - this.ready = this.images.ready.then(bind(function() { - log("Images loaded, starting parsing"); - log("Creating stacking contexts"); - this.createStackingContexts(); - log("Sorting stacking contexts"); - this.sortStackingContexts(this.stack); - this.parse(this.stack); - log("Render queue created with " + this.renderQueue.length + " items"); - return new Promise(bind(function(resolve) { - if (!options.async) { - this.renderQueue.forEach(this.paint, this); - resolve(); - } else if (typeof(options.async) === "function") { - options.async.call(this, this.renderQueue, resolve); - } else if (this.renderQueue.length > 0){ - this.renderIndex = 0; - this.asyncRenderer(this.renderQueue, resolve); - } else { - resolve(); - } - }, this)); - }, this)); - } - - NodeParser.prototype.calculateOverflowClips = function() { - this.nodes.forEach(function(container) { - if (isElement(container)) { - if (isPseudoElement(container)) { - container.appendToDOM(); - } - container.borders = this.parseBorders(container); - var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : []; - var cssClip = container.parseClip(); - if (cssClip && ["absolute", "fixed"].indexOf(container.css('position')) !== -1) { - clip.push([["rect", - container.bounds.left + cssClip.left, - container.bounds.top + cssClip.top, - cssClip.right - cssClip.left, - cssClip.bottom - cssClip.top - ]]); - } - container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip; - container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip; - if (isPseudoElement(container)) { - container.cleanDOM(); - } - } else if (isTextNode(container)) { - container.clip = hasParentClip(container) ? container.parent.clip : []; - } - if (!isPseudoElement(container)) { - container.bounds = null; - } - }, this); - }; - - function hasParentClip(container) { - return container.parent && container.parent.clip.length; - } - - NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) { - asyncTimer = asyncTimer || Date.now(); - this.paint(queue[this.renderIndex++]); - if (queue.length === this.renderIndex) { - resolve(); - } else if (asyncTimer + 20 > Date.now()) { - this.asyncRenderer(queue, resolve, asyncTimer); - } else { - setTimeout(bind(function() { - this.asyncRenderer(queue, resolve); - }, this), 0); - } - }; - - NodeParser.prototype.createPseudoHideStyles = function(document) { - this.createStyles(document, '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + ':before { content: "" !important; display: none !important; }' + - '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER + ':after { content: "" !important; display: none !important; }'); - }; - - NodeParser.prototype.disableAnimations = function(document) { - this.createStyles(document, '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' + - '-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}'); - }; - - NodeParser.prototype.createStyles = function(document, styles) { - var hidePseudoElements = document.createElement('style'); - hidePseudoElements.innerHTML = styles; - document.body.appendChild(hidePseudoElements); - }; - - NodeParser.prototype.getPseudoElements = function(container) { - var nodes = [[container]]; - if (container.node.nodeType === Node.ELEMENT_NODE) { - var before = this.getPseudoElement(container, ":before"); - var after = this.getPseudoElement(container, ":after"); - - if (before) { - nodes.push(before); - } - - if (after) { - nodes.push(after); - } - } - return flatten(nodes); - }; - - function toCamelCase(str) { - return str.replace(/(\-[a-z])/g, function(match){ - return match.toUpperCase().replace('-',''); - }); - } - - NodeParser.prototype.getPseudoElement = function(container, type) { - var style = container.computedStyle(type); - if(!style || !style.content || style.content === "none" || style.content === "-moz-alt-content" || style.display === "none") { - return null; - } - - var content = stripQuotes(style.content); - var isImage = content.substr(0, 3) === 'url'; - var pseudoNode = document.createElement(isImage ? 'img' : 'html2canvaspseudoelement'); - var pseudoContainer = new PseudoElementContainer(pseudoNode, container, type); - - for (var i = style.length-1; i >= 0; i--) { - var property = toCamelCase(style.item(i)); - pseudoNode.style[property] = style[property]; - } - - pseudoNode.className = PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + " " + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER; - - if (isImage) { - pseudoNode.src = parseBackgrounds(content)[0].args[0]; - return [pseudoContainer]; - } else { - var text = document.createTextNode(content); - pseudoNode.appendChild(text); - return [pseudoContainer, new TextContainer(text, pseudoContainer)]; - } - }; - - - NodeParser.prototype.getChildren = function(parentContainer) { - return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) { - var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement); - return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container; - }, this)); - }; - - NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) { - var stack = new StackingContext(hasOwnStacking, container.getOpacity(), container.node, container.parent); - container.cloneTo(stack); - var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack; - parentStack.contexts.push(stack); - container.stack = stack; - }; - - NodeParser.prototype.createStackingContexts = function() { - this.nodes.forEach(function(container) { - if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container) || container.hasTransform())) { - this.newStackingContext(container, true); - } else if (isElement(container) && ((isPositioned(container) && zIndex0(container)) || isInlineBlock(container) || isFloating(container))) { - this.newStackingContext(container, false); - } else { - container.assignStack(container.parent.stack); - } - }, this); - }; - - NodeParser.prototype.isBodyWithTransparentRoot = function(container) { - return container.node.nodeName === "BODY" && container.parent.color('backgroundColor').isTransparent(); - }; - - NodeParser.prototype.isRootElement = function(container) { - return container.parent === null; - }; - - NodeParser.prototype.sortStackingContexts = function(stack) { - stack.contexts.sort(zIndexSort(stack.contexts.slice(0))); - stack.contexts.forEach(this.sortStackingContexts, this); - }; - - NodeParser.prototype.parseTextBounds = function(container) { - return function(text, index, textList) { - if (container.parent.css("textDecoration").substr(0, 4) !== "none" || text.trim().length !== 0) { - if (this.support.rangeBounds && !container.parent.hasTransform()) { - var offset = textList.slice(0, index).join("").length; - return this.getRangeBounds(container.node, offset, text.length); - } else if (container.node && typeof(container.node.data) === "string") { - var replacementNode = container.node.splitText(text.length); - var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform()); - container.node = replacementNode; - return bounds; - } - } else if(!this.support.rangeBounds || container.parent.hasTransform()){ - container.node = container.node.splitText(text.length); - } - return {}; - }; - }; - - NodeParser.prototype.getWrapperBounds = function(node, transform) { - var wrapper = node.ownerDocument.createElement('html2canvaswrapper'); - var parent = node.parentNode, - backupText = node.cloneNode(true); - - wrapper.appendChild(node.cloneNode(true)); - parent.replaceChild(wrapper, node); - var bounds = transform ? offsetBounds(wrapper) : getBounds(wrapper); - parent.replaceChild(backupText, wrapper); - return bounds; - }; - - NodeParser.prototype.getRangeBounds = function(node, offset, length) { - var range = this.range || (this.range = node.ownerDocument.createRange()); - range.setStart(node, offset); - range.setEnd(node, offset + length); - return range.getBoundingClientRect(); - }; - - function ClearTransform() {} - - NodeParser.prototype.parse = function(stack) { - // http://www.w3.org/TR/CSS21/visuren.html#z-index - var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first). - var descendantElements = stack.children.filter(isElement); - var descendantNonFloats = descendantElements.filter(not(isFloating)); - var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants. - var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats. - var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. - var stackLevel0 = stack.contexts.concat(descendantNonFloats.filter(isPositioned)).filter(zIndex0); // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0. - var text = stack.children.filter(isTextNode).filter(hasText); - var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first). - negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats) - .concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) { - this.renderQueue.push(container); - if (isStackingContext(container)) { - this.parse(container); - this.renderQueue.push(new ClearTransform()); - } - }, this); - }; - - NodeParser.prototype.paint = function(container) { - try { - if (container instanceof ClearTransform) { - this.renderer.ctx.restore(); - } else if (isTextNode(container)) { - if (isPseudoElement(container.parent)) { - container.parent.appendToDOM(); - } - this.paintText(container); - if (isPseudoElement(container.parent)) { - container.parent.cleanDOM(); - } - } else { - this.paintNode(container); - } - } catch(e) { - log(e); - if (this.options.strict) { - throw e; - } - } - }; - - NodeParser.prototype.paintNode = function(container) { - if (isStackingContext(container)) { - this.renderer.setOpacity(container.opacity); - this.renderer.ctx.save(); - if (container.hasTransform()) { - this.renderer.setTransform(container.parseTransform()); - } - } - - if (container.node.nodeName === "INPUT" && container.node.type === "checkbox") { - this.paintCheckbox(container); - } else if (container.node.nodeName === "INPUT" && container.node.type === "radio") { - this.paintRadio(container); - } else { - this.paintElement(container); - } - }; - - NodeParser.prototype.paintElement = function(container) { - var bounds = container.parseBounds(); - this.renderer.clip(container.backgroundClip, function() { - this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth)); - }, this); - - this.renderer.clip(container.clip, function() { - this.renderer.renderBorders(container.borders.borders); - }, this); - - this.renderer.clip(container.backgroundClip, function() { - switch (container.node.nodeName) { - case "svg": - case "IFRAME": - var imgContainer = this.images.get(container.node); - if (imgContainer) { - this.renderer.renderImage(container, bounds, container.borders, imgContainer); - } else { - log("Error loading <" + container.node.nodeName + ">", container.node); - } - break; - case "IMG": - var imageContainer = this.images.get(container.node.src); - if (imageContainer) { - this.renderer.renderImage(container, bounds, container.borders, imageContainer); - } else { - log("Error loading <img>", container.node.src); - } - break; - case "CANVAS": - this.renderer.renderImage(container, bounds, container.borders, {image: container.node}); - break; - case "SELECT": - case "INPUT": - case "TEXTAREA": - this.paintFormValue(container); - break; - } - }, this); - }; - - NodeParser.prototype.paintCheckbox = function(container) { - var b = container.parseBounds(); - - var size = Math.min(b.width, b.height); - var bounds = {width: size - 1, height: size - 1, top: b.top, left: b.left}; - var r = [3, 3]; - var radius = [r, r, r, r]; - var borders = [1,1,1,1].map(function(w) { - return {color: new Color('#A5A5A5'), width: w}; - }); - - var borderPoints = calculateCurvePoints(bounds, radius, borders); - - this.renderer.clip(container.backgroundClip, function() { - this.renderer.rectangle(bounds.left + 1, bounds.top + 1, bounds.width - 2, bounds.height - 2, new Color("#DEDEDE")); - this.renderer.renderBorders(calculateBorders(borders, bounds, borderPoints, radius)); - if (container.node.checked) { - this.renderer.font(new Color('#424242'), 'normal', 'normal', 'bold', (size - 3) + "px", 'arial'); - this.renderer.text("\u2714", bounds.left + size / 6, bounds.top + size - 1); - } - }, this); - }; - - NodeParser.prototype.paintRadio = function(container) { - var bounds = container.parseBounds(); - - var size = Math.min(bounds.width, bounds.height) - 2; - - this.renderer.clip(container.backgroundClip, function() { - this.renderer.circleStroke(bounds.left + 1, bounds.top + 1, size, new Color('#DEDEDE'), 1, new Color('#A5A5A5')); - if (container.node.checked) { - this.renderer.circle(Math.ceil(bounds.left + size / 4) + 1, Math.ceil(bounds.top + size / 4) + 1, Math.floor(size / 2), new Color('#424242')); - } - }, this); - }; - - NodeParser.prototype.paintFormValue = function(container) { - var value = container.getValue(); - if (value.length > 0) { - var document = container.node.ownerDocument; - var wrapper = document.createElement('html2canvaswrapper'); - var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color', - 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', - 'width', 'height', 'borderLeftStyle', 'borderTopStyle', 'borderLeftWidth', 'borderTopWidth', - 'boxSizing', 'whiteSpace', 'wordWrap']; - - properties.forEach(function(property) { - try { - wrapper.style[property] = container.css(property); - } catch(e) { - // Older IE has issues with "border" - log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); - } - }); - var bounds = container.parseBounds(); - wrapper.style.position = "fixed"; - wrapper.style.left = bounds.left + "px"; - wrapper.style.top = bounds.top + "px"; - wrapper.textContent = value; - document.body.appendChild(wrapper); - this.paintText(new TextContainer(wrapper.firstChild, container)); - document.body.removeChild(wrapper); - } - }; - - NodeParser.prototype.paintText = function(container) { - container.applyTextTransform(); - var characters = punycode.ucs2.decode(container.node.data); - var textList = (!this.options.letterRendering || noLetterSpacing(container)) && !hasUnicode(container.node.data) ? getWords(characters) : characters.map(function(character) { - return punycode.ucs2.encode([character]); - }); - - var weight = container.parent.fontWeight(); - var size = container.parent.css('fontSize'); - var family = container.parent.css('fontFamily'); - var shadows = container.parent.parseTextShadows(); - - this.renderer.font(container.parent.color('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family); - if (shadows.length) { - // TODO: support multiple text shadows - this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur); - } else { - this.renderer.clearShadow(); - } - - this.renderer.clip(container.parent.clip, function() { - textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) { - if (bounds && /^\s*$/.test(textList[index]) === false) { - this.renderer.text(textList[index], bounds.left, bounds.bottom); - this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size)); - } - }, this); - }, this); - }; - - NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) { - switch(container.css("textDecoration").split(" ")[0]) { - case "underline": - // Draws a line at the baseline of the font - // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size - this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.color("color")); - break; - case "overline": - this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.color("color")); - break; - case "line-through": - // TODO try and find exact position for line-through - this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.color("color")); - break; - } - }; - - var borderColorTransforms = { - inset: [ - ["darken", 0.60], - ["darken", 0.10], - ["darken", 0.10], - ["darken", 0.60] - ] - }; - - NodeParser.prototype.parseBorders = function(container) { - var nodeBounds = container.parseBounds(); - var radius = getBorderRadiusData(container); - var borders = ["Top", "Right", "Bottom", "Left"].map(function(side, index) { - var style = container.css('border' + side + 'Style'); - var color = container.color('border' + side + 'Color'); - if (style === "inset" && color.isBlack()) { - color = new Color([255, 255, 255, color.a]); // this is wrong, but - } - var colorTransform = borderColorTransforms[style] ? borderColorTransforms[style][index] : null; - return { - width: container.cssInt('border' + side + 'Width'), - color: colorTransform ? color[colorTransform[0]](colorTransform[1]) : color, - args: null - }; - }); - var borderPoints = calculateCurvePoints(nodeBounds, radius, borders); - - return { - clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds), - borders: calculateBorders(borders, nodeBounds, borderPoints, radius) - }; - }; - - function calculateBorders(borders, nodeBounds, borderPoints, radius) { - return borders.map(function(border, borderSide) { - if (border.width > 0) { - var bx = nodeBounds.left; - var by = nodeBounds.top; - var bw = nodeBounds.width; - var bh = nodeBounds.height - (borders[2].width); - - switch(borderSide) { - case 0: - // top border - bh = borders[0].width; - border.args = drawSide({ - c1: [bx, by], - c2: [bx + bw, by], - c3: [bx + bw - borders[1].width, by + bh], - c4: [bx + borders[3].width, by + bh] - }, radius[0], radius[1], - borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner); - break; - case 1: - // right border - bx = nodeBounds.left + nodeBounds.width - (borders[1].width); - bw = borders[1].width; - - border.args = drawSide({ - c1: [bx + bw, by], - c2: [bx + bw, by + bh + borders[2].width], - c3: [bx, by + bh], - c4: [bx, by + borders[0].width] - }, radius[1], radius[2], - borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner); - break; - case 2: - // bottom border - by = (by + nodeBounds.height) - (borders[2].width); - bh = borders[2].width; - border.args = drawSide({ - c1: [bx + bw, by + bh], - c2: [bx, by + bh], - c3: [bx + borders[3].width, by], - c4: [bx + bw - borders[3].width, by] - }, radius[2], radius[3], - borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner); - break; - case 3: - // left border - bw = borders[3].width; - border.args = drawSide({ - c1: [bx, by + bh + borders[2].width], - c2: [bx, by], - c3: [bx + bw, by + borders[0].width], - c4: [bx + bw, by + bh] - }, radius[3], radius[0], - borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner); - break; - } - } - return border; - }); - } - - NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) { - var backgroundClip = container.css('backgroundClip'), - borderArgs = []; - - switch(backgroundClip) { - case "content-box": - case "padding-box": - parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width); - parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width); - parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width); - parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width); - break; - - default: - parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top); - parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top); - parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height); - parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height); - break; - } - - return borderArgs; - }; - - function getCurvePoints(x, y, r1, r2) { - var kappa = 4 * ((Math.sqrt(2) - 1) / 3); - var ox = (r1) * kappa, // control point offset horizontal - oy = (r2) * kappa, // control point offset vertical - xm = x + r1, // x-middle - ym = y + r2; // y-middle - return { - topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}), - topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}), - bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}), - bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y}) - }; - } - - function calculateCurvePoints(bounds, borderRadius, borders) { - var x = bounds.left, - y = bounds.top, - width = bounds.width, - height = bounds.height, - - tlh = borderRadius[0][0] < width / 2 ? borderRadius[0][0] : width / 2, - tlv = borderRadius[0][1] < height / 2 ? borderRadius[0][1] : height / 2, - trh = borderRadius[1][0] < width / 2 ? borderRadius[1][0] : width / 2, - trv = borderRadius[1][1] < height / 2 ? borderRadius[1][1] : height / 2, - brh = borderRadius[2][0] < width / 2 ? borderRadius[2][0] : width / 2, - brv = borderRadius[2][1] < height / 2 ? borderRadius[2][1] : height / 2, - blh = borderRadius[3][0] < width / 2 ? borderRadius[3][0] : width / 2, - blv = borderRadius[3][1] < height / 2 ? borderRadius[3][1] : height / 2; - - var topWidth = width - trh, - rightHeight = height - brv, - bottomWidth = width - brh, - leftHeight = height - blv; - - return { - topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5), - topLeftInner: getCurvePoints(x + borders[3].width, y + borders[0].width, Math.max(0, tlh - borders[3].width), Math.max(0, tlv - borders[0].width)).topLeft.subdivide(0.5), - topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5), - topRightInner: getCurvePoints(x + Math.min(topWidth, width + borders[3].width), y + borders[0].width, (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, trv - borders[0].width).topRight.subdivide(0.5), - bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5), - bottomRightInner: getCurvePoints(x + Math.min(bottomWidth, width - borders[3].width), y + Math.min(rightHeight, height + borders[0].width), Math.max(0, brh - borders[1].width), brv - borders[2].width).bottomRight.subdivide(0.5), - bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5), - bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), blv - borders[2].width).bottomLeft.subdivide(0.5) - }; - } - - function bezierCurve(start, startControl, endControl, end) { - var lerp = function (a, b, t) { - return { - x: a.x + (b.x - a.x) * t, - y: a.y + (b.y - a.y) * t - }; - }; - - return { - start: start, - startControl: startControl, - endControl: endControl, - end: end, - subdivide: function(t) { - var ab = lerp(start, startControl, t), - bc = lerp(startControl, endControl, t), - cd = lerp(endControl, end, t), - abbc = lerp(ab, bc, t), - bccd = lerp(bc, cd, t), - dest = lerp(abbc, bccd, t); - return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)]; - }, - curveTo: function(borderArgs) { - borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]); - }, - curveToReversed: function(borderArgs) { - borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]); - } - }; - } - - function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { - var borderArgs = []; - - if (radius1[0] > 0 || radius1[1] > 0) { - borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]); - outer1[1].curveTo(borderArgs); - } else { - borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]); - } - - if (radius2[0] > 0 || radius2[1] > 0) { - borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]); - outer2[0].curveTo(borderArgs); - borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]); - inner2[0].curveToReversed(borderArgs); - } else { - borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]); - borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]); - } - - if (radius1[0] > 0 || radius1[1] > 0) { - borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]); - inner1[1].curveToReversed(borderArgs); - } else { - borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]); - } - - return borderArgs; - } - - function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) { - if (radius1[0] > 0 || radius1[1] > 0) { - borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]); - corner1[0].curveTo(borderArgs); - corner1[1].curveTo(borderArgs); - } else { - borderArgs.push(["line", x, y]); - } - - if (radius2[0] > 0 || radius2[1] > 0) { - borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]); - } - } - - function negativeZIndex(container) { - return container.cssInt("zIndex") < 0; - } - - function positiveZIndex(container) { - return container.cssInt("zIndex") > 0; - } - - function zIndex0(container) { - return container.cssInt("zIndex") === 0; - } - - function inlineLevel(container) { - return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1; - } - - function isStackingContext(container) { - return (container instanceof StackingContext); - } - - function hasText(container) { - return container.node.data.trim().length > 0; - } - - function noLetterSpacing(container) { - return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing"))); - } - - function getBorderRadiusData(container) { - return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) { - var value = container.css('border' + side + 'Radius'); - var arr = value.split(" "); - if (arr.length <= 1) { - arr[1] = arr[0]; - } - return arr.map(asInt); - }); - } - - function renderableNode(node) { - return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE); - } - - function isPositionedForStacking(container) { - var position = container.css("position"); - var zIndex = (["absolute", "relative", "fixed"].indexOf(position) !== -1) ? container.css("zIndex") : "auto"; - return zIndex !== "auto"; - } - - function isPositioned(container) { - return container.css("position") !== "static"; - } - - function isFloating(container) { - return container.css("float") !== "none"; - } - - function isInlineBlock(container) { - return ["inline-block", "inline-table"].indexOf(container.css("display")) !== -1; - } - - function not(callback) { - var context = this; - return function() { - return !callback.apply(context, arguments); - }; - } - - function isElement(container) { - return container.node.nodeType === Node.ELEMENT_NODE; - } - - function isPseudoElement(container) { - return container.isPseudoElement === true; - } - - function isTextNode(container) { - return container.node.nodeType === Node.TEXT_NODE; - } - - function zIndexSort(contexts) { - return function(a, b) { - return (a.cssInt("zIndex") + (contexts.indexOf(a) / contexts.length)) - (b.cssInt("zIndex") + (contexts.indexOf(b) / contexts.length)); - }; - } - - function hasOpacity(container) { - return container.getOpacity() < 1; - } - - function asInt(value) { - return parseInt(value, 10); - } - - function getWidth(border) { - return border.width; - } - - function nonIgnoredElement(nodeContainer) { - return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR", "OPTION"].indexOf(nodeContainer.node.nodeName) === -1); - } - - function flatten(arrays) { - return [].concat.apply([], arrays); - } - - function stripQuotes(content) { - var first = content.substr(0, 1); - return (first === content.substr(content.length - 1) && first.match(/'|"/)) ? content.substr(1, content.length - 2) : content; - } - - function getWords(characters) { - var words = [], i = 0, onWordBoundary = false, word; - while(characters.length) { - if (isWordBoundary(characters[i]) === onWordBoundary) { - word = characters.splice(0, i); - if (word.length) { - words.push(punycode.ucs2.encode(word)); - } - onWordBoundary =! onWordBoundary; - i = 0; - } else { - i++; - } - - if (i >= characters.length) { - word = characters.splice(0, i); - if (word.length) { - words.push(punycode.ucs2.encode(word)); - } - } - } - return words; - } - - function isWordBoundary(characterCode) { - return [ - 32, // <space> - 13, // \r - 10, // \n - 9, // \t - 45 // - - ].indexOf(characterCode) !== -1; - } - - function hasUnicode(string) { - return (/[^\u0000-\u00ff]/).test(string); - } - - module.exports = NodeParser; - - },{"./color":3,"./fontmetrics":7,"./log":13,"./nodecontainer":14,"./pseudoelementcontainer":18,"./stackingcontext":21,"./textcontainer":25,"./utils":26,"punycode":1}],16:[function(_dereq_,module,exports){ - var XHR = _dereq_('./xhr'); - var utils = _dereq_('./utils'); - var log = _dereq_('./log'); - var createWindowClone = _dereq_('./clone'); - var decode64 = utils.decode64; - - function Proxy(src, proxyUrl, document) { - var supportsCORS = ('withCredentials' in new XMLHttpRequest()); - if (!proxyUrl) { - return Promise.reject("No proxy configured"); - } - var callback = createCallback(supportsCORS); - var url = createProxyUrl(proxyUrl, src, callback); - - return supportsCORS ? XHR(url) : (jsonp(document, url, callback).then(function(response) { - return decode64(response.content); - })); - } - var proxyCount = 0; - - function ProxyURL(src, proxyUrl, document) { - var supportsCORSImage = ('crossOrigin' in new Image()); - var callback = createCallback(supportsCORSImage); - var url = createProxyUrl(proxyUrl, src, callback); - return (supportsCORSImage ? Promise.resolve(url) : jsonp(document, url, callback).then(function(response) { - return "data:" + response.type + ";base64," + response.content; - })); - } - - function jsonp(document, url, callback) { - return new Promise(function(resolve, reject) { - var s = document.createElement("script"); - var cleanup = function() { - delete window.html2canvas.proxy[callback]; - document.body.removeChild(s); - }; - window.html2canvas.proxy[callback] = function(response) { - cleanup(); - resolve(response); - }; - s.src = url; - s.onerror = function(e) { - cleanup(); - reject(e); - }; - document.body.appendChild(s); - }); - } - - function createCallback(useCORS) { - return !useCORS ? "html2canvas_" + Date.now() + "_" + (++proxyCount) + "_" + Math.round(Math.random() * 100000) : ""; - } - - function createProxyUrl(proxyUrl, src, callback) { - return proxyUrl + "?url=" + encodeURIComponent(src) + (callback.length ? "&callback=html2canvas.proxy." + callback : ""); - } - - function documentFromHTML(src) { - return function(html) { - var parser = new DOMParser(), doc; - try { - doc = parser.parseFromString(html, "text/html"); - } catch(e) { - log("DOMParser not supported, falling back to createHTMLDocument"); - doc = document.implementation.createHTMLDocument(""); - try { - doc.open(); - doc.write(html); - doc.close(); - } catch(ee) { - log("createHTMLDocument write not supported, falling back to document.body.innerHTML"); - doc.body.innerHTML = html; // ie9 doesnt support writing to documentElement - } - } - - var b = doc.querySelector("base"); - if (!b || !b.href.host) { - var base = doc.createElement("base"); - base.href = src; - doc.head.insertBefore(base, doc.head.firstChild); - } - - return doc; - }; - } - - function loadUrlDocument(src, proxy, document, width, height, options) { - return new Proxy(src, proxy, window.document).then(documentFromHTML(src)).then(function(doc) { - return createWindowClone(doc, document, width, height, options, 0, 0); - }); - } - - exports.Proxy = Proxy; - exports.ProxyURL = ProxyURL; - exports.loadUrlDocument = loadUrlDocument; - - },{"./clone":2,"./log":13,"./utils":26,"./xhr":28}],17:[function(_dereq_,module,exports){ - var ProxyURL = _dereq_('./proxy').ProxyURL; - - function ProxyImageContainer(src, proxy) { - var link = document.createElement("a"); - link.href = src; - src = link.href; - this.src = src; - this.image = new Image(); - var self = this; - this.promise = new Promise(function(resolve, reject) { - self.image.crossOrigin = "Anonymous"; - self.image.onload = resolve; - self.image.onerror = reject; - - new ProxyURL(src, proxy, document).then(function(url) { - self.image.src = url; - })['catch'](reject); - }); - } - - module.exports = ProxyImageContainer; - - },{"./proxy":16}],18:[function(_dereq_,module,exports){ - var NodeContainer = _dereq_('./nodecontainer'); - - function PseudoElementContainer(node, parent, type) { - NodeContainer.call(this, node, parent); - this.isPseudoElement = true; - this.before = type === ":before"; - } - - PseudoElementContainer.prototype.cloneTo = function(stack) { - PseudoElementContainer.prototype.cloneTo.call(this, stack); - stack.isPseudoElement = true; - stack.before = this.before; - }; - - PseudoElementContainer.prototype = Object.create(NodeContainer.prototype); - - PseudoElementContainer.prototype.appendToDOM = function() { - if (this.before) { - this.parent.node.insertBefore(this.node, this.parent.node.firstChild); - } else { - this.parent.node.appendChild(this.node); - } - this.parent.node.className += " " + this.getHideClass(); - }; - - PseudoElementContainer.prototype.cleanDOM = function() { - this.node.parentNode.removeChild(this.node); - this.parent.node.className = this.parent.node.className.replace(this.getHideClass(), ""); - }; - - PseudoElementContainer.prototype.getHideClass = function() { - return this["PSEUDO_HIDE_ELEMENT_CLASS_" + (this.before ? "BEFORE" : "AFTER")]; - }; - - PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = "___html2canvas___pseudoelement_before"; - PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER = "___html2canvas___pseudoelement_after"; - - module.exports = PseudoElementContainer; - - },{"./nodecontainer":14}],19:[function(_dereq_,module,exports){ - var log = _dereq_('./log'); - - function Renderer(width, height, images, options, document) { - this.width = width; - this.height = height; - this.images = images; - this.options = options; - this.document = document; - } - - Renderer.prototype.renderImage = function(container, bounds, borderData, imageContainer) { - var paddingLeft = container.cssInt('paddingLeft'), - paddingTop = container.cssInt('paddingTop'), - paddingRight = container.cssInt('paddingRight'), - paddingBottom = container.cssInt('paddingBottom'), - borders = borderData.borders; - - var width = bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight); - var height = bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom); - this.drawImage( - imageContainer, - 0, - 0, - imageContainer.image.width || width, - imageContainer.image.height || height, - bounds.left + paddingLeft + borders[3].width, - bounds.top + paddingTop + borders[0].width, - width, - height - ); - }; - - Renderer.prototype.renderBackground = function(container, bounds, borderData) { - if (bounds.height > 0 && bounds.width > 0) { - this.renderBackgroundColor(container, bounds); - this.renderBackgroundImage(container, bounds, borderData); - } - }; - - Renderer.prototype.renderBackgroundColor = function(container, bounds) { - var color = container.color("backgroundColor"); - if (!color.isTransparent()) { - this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, color); - } - }; - - Renderer.prototype.renderBorders = function(borders) { - borders.forEach(this.renderBorder, this); - }; - - Renderer.prototype.renderBorder = function(data) { - if (!data.color.isTransparent() && data.args !== null) { - this.drawShape(data.args, data.color); - } - }; - - Renderer.prototype.renderBackgroundImage = function(container, bounds, borderData) { - var backgroundImages = container.parseBackgroundImages(); - backgroundImages.reverse().forEach(function(backgroundImage, index, arr) { - switch(backgroundImage.method) { - case "url": - var image = this.images.get(backgroundImage.args[0]); - if (image) { - this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1), borderData); - } else { - log("Error loading background-image", backgroundImage.args[0]); - } - break; - case "linear-gradient": - case "gradient": - var gradientImage = this.images.get(backgroundImage.value); - if (gradientImage) { - this.renderBackgroundGradient(gradientImage, bounds, borderData); - } else { - log("Error loading background-image", backgroundImage.args[0]); - } - break; - case "none": - break; - default: - log("Unknown background-image type", backgroundImage.args[0]); - } - }, this); - }; - - Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index, borderData) { - var size = container.parseBackgroundSize(bounds, imageContainer.image, index); - var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size); - var repeat = container.parseBackgroundRepeat(index); - switch (repeat) { - case "repeat-x": - case "repeat no-repeat": - this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + borderData[3], bounds.top + position.top + borderData[0], 99999, size.height, borderData); - break; - case "repeat-y": - case "no-repeat repeat": - this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + borderData[0], size.width, 99999, borderData); - break; - case "no-repeat": - this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + position.top + borderData[0], size.width, size.height, borderData); - break; - default: - this.renderBackgroundRepeat(imageContainer, position, size, {top: bounds.top, left: bounds.left}, borderData[3], borderData[0]); - break; - } - }; - - module.exports = Renderer; - - },{"./log":13}],20:[function(_dereq_,module,exports){ - var Renderer = _dereq_('../renderer'); - var LinearGradientContainer = _dereq_('../lineargradientcontainer'); - var log = _dereq_('../log'); - - function CanvasRenderer(width, height) { - Renderer.apply(this, arguments); - this.canvas = this.options.canvas || this.document.createElement("canvas"); - if (!this.options.canvas) { - this.canvas.width = width; - this.canvas.height = height; - } - this.ctx = this.canvas.getContext("2d"); - this.taintCtx = this.document.createElement("canvas").getContext("2d"); - this.ctx.textBaseline = "bottom"; - this.variables = {}; - log("Initialized CanvasRenderer with size", width, "x", height); - } - - CanvasRenderer.prototype = Object.create(Renderer.prototype); - - CanvasRenderer.prototype.setFillStyle = function(fillStyle) { - this.ctx.fillStyle = typeof(fillStyle) === "object" && !!fillStyle.isColor ? fillStyle.toString() : fillStyle; - return this.ctx; - }; - - CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) { - this.setFillStyle(color).fillRect(left, top, width, height); - }; - - CanvasRenderer.prototype.circle = function(left, top, size, color) { - this.setFillStyle(color); - this.ctx.beginPath(); - this.ctx.arc(left + size / 2, top + size / 2, size / 2, 0, Math.PI*2, true); - this.ctx.closePath(); - this.ctx.fill(); - }; - - CanvasRenderer.prototype.circleStroke = function(left, top, size, color, stroke, strokeColor) { - this.circle(left, top, size, color); - this.ctx.strokeStyle = strokeColor.toString(); - this.ctx.stroke(); - }; - - CanvasRenderer.prototype.drawShape = function(shape, color) { - this.shape(shape); - this.setFillStyle(color).fill(); - }; - - CanvasRenderer.prototype.taints = function(imageContainer) { - if (imageContainer.tainted === null) { - this.taintCtx.drawImage(imageContainer.image, 0, 0); - try { - this.taintCtx.getImageData(0, 0, 1, 1); - imageContainer.tainted = false; - } catch(e) { - this.taintCtx = document.createElement("canvas").getContext("2d"); - imageContainer.tainted = true; - } - } - - return imageContainer.tainted; - }; - - CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx, dy, dw, dh) { - if (!this.taints(imageContainer) || this.options.allowTaint) { - this.ctx.drawImage(imageContainer.image, sx, sy, sw, sh, dx, dy, dw, dh); - } - }; - - CanvasRenderer.prototype.clip = function(shapes, callback, context) { - this.ctx.save(); - shapes.filter(hasEntries).forEach(function(shape) { - this.shape(shape).clip(); - }, this); - callback.call(context); - this.ctx.restore(); - }; - - CanvasRenderer.prototype.shape = function(shape) { - this.ctx.beginPath(); - shape.forEach(function(point, index) { - if (point[0] === "rect") { - this.ctx.rect.apply(this.ctx, point.slice(1)); - } else { - this.ctx[(index === 0) ? "moveTo" : point[0] + "To" ].apply(this.ctx, point.slice(1)); - } - }, this); - this.ctx.closePath(); - return this.ctx; - }; - - CanvasRenderer.prototype.font = function(color, style, variant, weight, size, family) { - this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ").split(",")[0]; - }; - - CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) { - this.setVariable("shadowColor", color.toString()) - .setVariable("shadowOffsetY", offsetX) - .setVariable("shadowOffsetX", offsetY) - .setVariable("shadowBlur", blur); - }; - - CanvasRenderer.prototype.clearShadow = function() { - this.setVariable("shadowColor", "rgba(0,0,0,0)"); - }; - - CanvasRenderer.prototype.setOpacity = function(opacity) { - this.ctx.globalAlpha = opacity; - }; - - CanvasRenderer.prototype.setTransform = function(transform) { - this.ctx.translate(transform.origin[0], transform.origin[1]); - this.ctx.transform.apply(this.ctx, transform.matrix); - this.ctx.translate(-transform.origin[0], -transform.origin[1]); - }; - - CanvasRenderer.prototype.setVariable = function(property, value) { - if (this.variables[property] !== value) { - this.variables[property] = this.ctx[property] = value; - } - - return this; - }; - - CanvasRenderer.prototype.text = function(text, left, bottom) { - this.ctx.fillText(text, left, bottom); - }; - - CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, size, bounds, left, top, width, height, borderData) { - var shape = [ - ["line", Math.round(left), Math.round(top)], - ["line", Math.round(left + width), Math.round(top)], - ["line", Math.round(left + width), Math.round(height + top)], - ["line", Math.round(left), Math.round(height + top)] - ]; - this.clip([shape], function() { - this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]); - }, this); - }; - - CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, size, bounds, borderLeft, borderTop) { - var offsetX = Math.round(bounds.left + backgroundPosition.left + borderLeft), offsetY = Math.round(bounds.top + backgroundPosition.top + borderTop); - this.setFillStyle(this.ctx.createPattern(this.resizeImage(imageContainer, size), "repeat")); - this.ctx.translate(offsetX, offsetY); - this.ctx.fill(); - this.ctx.translate(-offsetX, -offsetY); - }; - - CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) { - if (gradientImage instanceof LinearGradientContainer) { - var gradient = this.ctx.createLinearGradient( - bounds.left + bounds.width * gradientImage.x0, - bounds.top + bounds.height * gradientImage.y0, - bounds.left + bounds.width * gradientImage.x1, - bounds.top + bounds.height * gradientImage.y1); - gradientImage.colorStops.forEach(function(colorStop) { - gradient.addColorStop(colorStop.stop, colorStop.color.toString()); - }); - this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, gradient); - } - }; - - CanvasRenderer.prototype.resizeImage = function(imageContainer, size) { - var image = imageContainer.image; - if(image.width === size.width && image.height === size.height) { - return image; - } - - var ctx, canvas = document.createElement('canvas'); - canvas.width = size.width; - canvas.height = size.height; - ctx = canvas.getContext("2d"); - ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height ); - return canvas; - }; - - function hasEntries(array) { - return array.length > 0; - } - - module.exports = CanvasRenderer; - - },{"../lineargradientcontainer":12,"../log":13,"../renderer":19}],21:[function(_dereq_,module,exports){ - var NodeContainer = _dereq_('./nodecontainer'); - - function StackingContext(hasOwnStacking, opacity, element, parent) { - NodeContainer.call(this, element, parent); - this.ownStacking = hasOwnStacking; - this.contexts = []; - this.children = []; - this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity; - } - - StackingContext.prototype = Object.create(NodeContainer.prototype); - - StackingContext.prototype.getParentStack = function(context) { - var parentStack = (this.parent) ? this.parent.stack : null; - return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack; - }; - - module.exports = StackingContext; - - },{"./nodecontainer":14}],22:[function(_dereq_,module,exports){ - function Support(document) { - this.rangeBounds = this.testRangeBounds(document); - this.cors = this.testCORS(); - this.svg = this.testSVG(); - } - - Support.prototype.testRangeBounds = function(document) { - var range, testElement, rangeBounds, rangeHeight, support = false; - - if (document.createRange) { - range = document.createRange(); - if (range.getBoundingClientRect) { - testElement = document.createElement('boundtest'); - testElement.style.height = "123px"; - testElement.style.display = "block"; - document.body.appendChild(testElement); - - range.selectNode(testElement); - rangeBounds = range.getBoundingClientRect(); - rangeHeight = rangeBounds.height; - - if (rangeHeight === 123) { - support = true; - } - document.body.removeChild(testElement); - } - } - - return support; - }; - - Support.prototype.testCORS = function() { - return typeof((new Image()).crossOrigin) !== "undefined"; - }; - - Support.prototype.testSVG = function() { - var img = new Image(); - var canvas = document.createElement("canvas"); - var ctx = canvas.getContext("2d"); - img.src = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>"; - - try { - ctx.drawImage(img, 0, 0); - canvas.toDataURL(); - } catch(e) { - return false; - } - return true; - }; - - module.exports = Support; - - },{}],23:[function(_dereq_,module,exports){ - var XHR = _dereq_('./xhr'); - var decode64 = _dereq_('./utils').decode64; - - function SVGContainer(src) { - this.src = src; - this.image = null; - var self = this; - - this.promise = this.hasFabric().then(function() { - return (self.isInline(src) ? Promise.resolve(self.inlineFormatting(src)) : XHR(src)); - }).then(function(svg) { - return new Promise(function(resolve) { - window.html2canvas.svg.fabric.loadSVGFromString(svg, self.createCanvas.call(self, resolve)); - }); - }); - } - - SVGContainer.prototype.hasFabric = function() { - return !window.html2canvas.svg || !window.html2canvas.svg.fabric ? Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg")) : Promise.resolve(); - }; - - SVGContainer.prototype.inlineFormatting = function(src) { - return (/^data:image\/svg\+xml;base64,/.test(src)) ? this.decode64(this.removeContentType(src)) : this.removeContentType(src); - }; - - SVGContainer.prototype.removeContentType = function(src) { - return src.replace(/^data:image\/svg\+xml(;base64)?,/,''); - }; - - SVGContainer.prototype.isInline = function(src) { - return (/^data:image\/svg\+xml/i.test(src)); - }; - - SVGContainer.prototype.createCanvas = function(resolve) { - var self = this; - return function (objects, options) { - var canvas = new window.html2canvas.svg.fabric.StaticCanvas('c'); - self.image = canvas.lowerCanvasEl; - canvas - .setWidth(options.width) - .setHeight(options.height) - .add(window.html2canvas.svg.fabric.util.groupSVGElements(objects, options)) - .renderAll(); - resolve(canvas.lowerCanvasEl); - }; - }; - - SVGContainer.prototype.decode64 = function(str) { - return (typeof(window.atob) === "function") ? window.atob(str) : decode64(str); - }; - - module.exports = SVGContainer; - - },{"./utils":26,"./xhr":28}],24:[function(_dereq_,module,exports){ - var SVGContainer = _dereq_('./svgcontainer'); - - function SVGNodeContainer(node, _native) { - this.src = node; - this.image = null; - var self = this; - - this.promise = _native ? new Promise(function(resolve, reject) { - self.image = new Image(); - self.image.onload = resolve; - self.image.onerror = reject; - self.image.src = "data:image/svg+xml," + (new XMLSerializer()).serializeToString(node); - if (self.image.complete === true) { - resolve(self.image); - } - }) : this.hasFabric().then(function() { - return new Promise(function(resolve) { - window.html2canvas.svg.fabric.parseSVGDocument(node, self.createCanvas.call(self, resolve)); - }); - }); - } - - SVGNodeContainer.prototype = Object.create(SVGContainer.prototype); - - module.exports = SVGNodeContainer; - - },{"./svgcontainer":23}],25:[function(_dereq_,module,exports){ - var NodeContainer = _dereq_('./nodecontainer'); - - function TextContainer(node, parent) { - NodeContainer.call(this, node, parent); - } - - TextContainer.prototype = Object.create(NodeContainer.prototype); - - TextContainer.prototype.applyTextTransform = function() { - this.node.data = this.transform(this.parent.css("textTransform")); - }; - - TextContainer.prototype.transform = function(transform) { - var text = this.node.data; - switch(transform){ - case "lowercase": - return text.toLowerCase(); - case "capitalize": - return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize); - case "uppercase": - return text.toUpperCase(); - default: - return text; - } - }; - - function capitalize(m, p1, p2) { - if (m.length > 0) { - return p1 + p2.toUpperCase(); - } - } - - module.exports = TextContainer; - - },{"./nodecontainer":14}],26:[function(_dereq_,module,exports){ - exports.smallImage = function smallImage() { - return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - }; - - exports.bind = function(callback, context) { - return function() { - return callback.apply(context, arguments); - }; - }; - - /* - * base64-arraybuffer - * https://github.com/niklasvh/base64-arraybuffer - * - * Copyright (c) 2012 Niklas von Hertzen - * Licensed under the MIT license. - */ - - exports.decode64 = function(base64) { - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - var len = base64.length, i, encoded1, encoded2, encoded3, encoded4, byte1, byte2, byte3; - - var output = ""; - - for (i = 0; i < len; i+=4) { - encoded1 = chars.indexOf(base64[i]); - encoded2 = chars.indexOf(base64[i+1]); - encoded3 = chars.indexOf(base64[i+2]); - encoded4 = chars.indexOf(base64[i+3]); - - byte1 = (encoded1 << 2) | (encoded2 >> 4); - byte2 = ((encoded2 & 15) << 4) | (encoded3 >> 2); - byte3 = ((encoded3 & 3) << 6) | encoded4; - if (encoded3 === 64) { - output += String.fromCharCode(byte1); - } else if (encoded4 === 64 || encoded4 === -1) { - output += String.fromCharCode(byte1, byte2); - } else{ - output += String.fromCharCode(byte1, byte2, byte3); - } - } - - return output; - }; - - exports.getBounds = function(node) { - if (node.getBoundingClientRect) { - var clientRect = node.getBoundingClientRect(); - var width = node.offsetWidth == null ? clientRect.width : node.offsetWidth; - return { - top: clientRect.top, - bottom: clientRect.bottom || (clientRect.top + clientRect.height), - right: clientRect.left + width, - left: clientRect.left, - width: width, - height: node.offsetHeight == null ? clientRect.height : node.offsetHeight - }; - } - return {}; - }; - - exports.offsetBounds = function(node) { - var parent = node.offsetParent ? exports.offsetBounds(node.offsetParent) : {top: 0, left: 0}; - - return { - top: node.offsetTop + parent.top, - bottom: node.offsetTop + node.offsetHeight + parent.top, - right: node.offsetLeft + parent.left + node.offsetWidth, - left: node.offsetLeft + parent.left, - width: node.offsetWidth, - height: node.offsetHeight - }; - }; - - exports.parseBackgrounds = function(backgroundImage) { - var whitespace = ' \r\n\t', - method, definition, prefix, prefix_i, block, results = [], - mode = 0, numParen = 0, quote, args; - var appendResult = function() { - if(method) { - if (definition.substr(0, 1) === '"') { - definition = definition.substr(1, definition.length - 2); - } - if (definition) { - args.push(definition); - } - if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) { - prefix = method.substr(0, prefix_i); - method = method.substr(prefix_i); - } - results.push({ - prefix: prefix, - method: method.toLowerCase(), - value: block, - args: args, - image: null - }); - } - args = []; - method = prefix = definition = block = ''; - }; - args = []; - method = prefix = definition = block = ''; - backgroundImage.split("").forEach(function(c) { - if (mode === 0 && whitespace.indexOf(c) > -1) { - return; - } - switch(c) { - case '"': - if(!quote) { - quote = c; - } else if(quote === c) { - quote = null; - } - break; - case '(': - if(quote) { - break; - } else if(mode === 0) { - mode = 1; - block += c; - return; - } else { - numParen++; - } - break; - case ')': - if (quote) { - break; - } else if(mode === 1) { - if(numParen === 0) { - mode = 0; - block += c; - appendResult(); - return; - } else { - numParen--; - } - } - break; - - case ',': - if (quote) { - break; - } else if(mode === 0) { - appendResult(); - return; - } else if (mode === 1) { - if (numParen === 0 && !method.match(/^url$/i)) { - args.push(definition); - definition = ''; - block += c; - return; - } - } - break; - } - - block += c; - if (mode === 0) { - method += c; - } else { - definition += c; - } - }); - - appendResult(); - return results; - }; - - },{}],27:[function(_dereq_,module,exports){ - var GradientContainer = _dereq_('./gradientcontainer'); - - function WebkitGradientContainer(imageData) { - GradientContainer.apply(this, arguments); - this.type = imageData.args[0] === "linear" ? GradientContainer.TYPES.LINEAR : GradientContainer.TYPES.RADIAL; - } - - WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype); - - module.exports = WebkitGradientContainer; - - },{"./gradientcontainer":9}],28:[function(_dereq_,module,exports){ - function XHR(url) { - return new Promise(function(resolve, reject) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url); - - xhr.onload = function() { - if (xhr.status === 200) { - resolve(xhr.responseText); - } else { - reject(new Error(xhr.statusText)); - } - }; - - xhr.onerror = function() { - reject(new Error("Network Error")); - }; - - xhr.send(); - }); - } - - module.exports = XHR; - - },{}]},{},[4])(4) - }); - - /************************************************ - * Title : custom font * - * Start Data : 2017. 01. 22. * - * Comment : TEXT API * - ************************************************/ - - /****************************** - * jsPDF extension API Design * - * ****************************/ - (function(jsPDF){ - var PLUS = '+'.charCodeAt(0); - var SLASH = '/'.charCodeAt(0); - var NUMBER = '0'.charCodeAt(0); - var LOWER = 'a'.charCodeAt(0); - var UPPER = 'A'.charCodeAt(0); - var PLUS_URL_SAFE = '-'.charCodeAt(0); - var SLASH_URL_SAFE = '_'.charCodeAt(0); - - /*****************************************************************/ - /* function : b64ToByteArray */ - /* comment : Base64 encoded TTF file contents (b64) are decoded */ - /* by Byte array and stored. */ - /*****************************************************************/ - var b64ToByteArray = function(b64) { - var i, j, l, tmp, placeHolders, arr; - if (b64.length % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - // the number of equal signs (place holders) - // if there are two placeholders, than the two characters before it - // represent one byte - // if there is only one, then the three characters before it represent 2 bytes - // this is just a cheap hack to not do indexOf twice - var len = b64.length; - placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0; - // base64 is 4/3 + up to two characters of the original data - arr = new Uint8Array(b64.length * 3 / 4 - placeHolders); - // if there are placeholders, only get up to the last complete 4 chars - l = placeHolders > 0 ? b64.length - 4 : b64.length; - var L = 0; - - function push(v) { - arr[L++] = v; - } - for (i = 0, j = 0; i < l; i += 4, j += 3) { - tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)); - push((tmp & 0xFF0000) >> 16); - push((tmp & 0xFF00) >> 8); - push(tmp & 0xFF); - } - if (placeHolders === 2) { - tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4); - push(tmp & 0xFF); - } - else if (placeHolders === 1) { - tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2); - push((tmp >> 8) & 0xFF); - push(tmp & 0xFF); - } - return arr - }; - - /***************************************************************/ - /* function : decode */ - /* comment : Change the base64 encoded font's content to match */ - /* the base64 index value. */ - /***************************************************************/ - var decode = function(elt) { - var code = elt.charCodeAt(0); - if (code === PLUS || code === PLUS_URL_SAFE) return 62 // '+' - if (code === SLASH || code === SLASH_URL_SAFE) return 63 // '/' - if (code < NUMBER) return -1 //no match - if (code < NUMBER + 10) return code - NUMBER + 26 + 26 - if (code < UPPER + 26) return code - UPPER - if (code < LOWER + 26) return code - LOWER + 26 - }; - - jsPDF.API.TTFFont = (function () { - /************************************************************************/ - /* function : open */ - /* comment : Decode the encoded ttf content and create a TTFFont object. */ - /************************************************************************/ - TTFFont.open = function (filename, name, vfs, encoding) { - var contents; - contents = b64ToByteArray(vfs); - return new TTFFont(contents, name, encoding); - }; - /***************************************************************/ - /* function : TTFFont gernerator */ - /* comment : Decode TTF contents are parsed, Data, */ - /* Subset object is created, and registerTTF function is called.*/ - /***************************************************************/ - function TTFFont(rawData, name, encoding) { - var data; - this.rawData = rawData; - data = this.contents = new Data(rawData); - this.contents.pos = 4; - if (data.readString(4) === 'ttcf') { - if (!name) { - throw new Error("Must specify a font name for TTC files."); - } - throw new Error("Font " + name + " not found in TTC file."); - } - else { - data.pos = 0; - this.parse(); - this.subset = new Subset(this); - this.registerTTF(); - } - } - /********************************************************/ - /* function : parse */ - /* comment : TTF Parses the file contents by each table.*/ - /********************************************************/ - TTFFont.prototype.parse = function () { - this.directory = new Directory(this.contents); - this.head = new HeadTable(this); - this.name = new NameTable(this); - this.cmap = new CmapTable(this); - this.hhea = new HheaTable(this); - this.maxp = new MaxpTable(this); - this.hmtx = new HmtxTable(this); - this.post = new PostTable(this); - this.os2 = new OS2Table(this); - this.loca = new LocaTable(this); - this.glyf = new GlyfTable(this); - this.ascender = (this.os2.exists && this.os2.ascender) || this.hhea.ascender; - this.decender = (this.os2.exists && this.os2.decender) || this.hhea.decender; - this.lineGap = (this.os2.exists && this.os2.lineGap) || this.hhea.lineGap; - return this.bbox = [this.head.xMin, this.head.yMin, this.head.xMax, this.head.yMax]; - }; - /***************************************************************/ - /* function : registerTTF */ - /* comment : Get the value to assign pdf font descriptors. */ - /***************************************************************/ - TTFFont.prototype.registerTTF = function () { - var e, hi, low, raw, _ref; - this.scaleFactor = 1000.0 / this.head.unitsPerEm; - this.bbox = (function () { - var _i, _len, _ref, _results; - _ref = this.bbox; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - e = _ref[_i]; - _results.push(Math.round(e * this.scaleFactor)); - } - return _results; - }).call(this); - this.stemV = 0; - if (this.post.exists) { - raw = this.post.italic_angle; - hi = raw >> 16; - low = raw & 0xFF; - if (hi & 0x8000 !== 0) { - hi = -((hi ^ 0xFFFF) + 1); - } - this.italicAngle = +("" + hi + "." + low); - } - else { - this.italicAngle = 0; - } - this.ascender = Math.round(this.ascender * this.scaleFactor); - this.decender = Math.round(this.decender * this.scaleFactor); - this.lineGap = Math.round(this.lineGap * this.scaleFactor); - this.capHeight = (this.os2.exists && this.os2.capHeight) || this.ascender; - this.xHeight = (this.os2.exists && this.os2.xHeight) || 0; - this.familyClass = (this.os2.exists && this.os2.familyClass || 0) >> 8; - this.isSerif = (_ref = this.familyClass) === 1 || _ref === 2 || _ref === 3 || _ref === 4 || _ref === 5 || _ref === 7; - this.isScript = this.familyClass === 10; - this.flags = 0; - if (this.post.isFixedPitch) { - this.flags |= 1 << 0; - } - if (this.isSerif) { - this.flags |= 1 << 1; - } - if (this.isScript) { - this.flags |= 1 << 3; - } - if (this.italicAngle !== 0) { - this.flags |= 1 << 6; - } - this.flags |= 1 << 5; - if (!this.cmap.unicode) { - throw new Error('No unicode cmap for font'); - } - }; - TTFFont.prototype.characterToGlyph = function (character) { - var _ref; - return ((_ref = this.cmap.unicode) != null ? _ref.codeMap[character] : void 0) || 0; - }; - TTFFont.prototype.widthOfGlyph = function (glyph) { - var scale; - scale = 1000.0 / this.head.unitsPerEm; - return this.hmtx.forGlyph(glyph).advance * scale; - }; - TTFFont.prototype.widthOfString = function (string, size, charSpace) { - var charCode, i, scale, width, _i, _ref, charSpace; - string = '' + string; - width = 0; - for (i = _i = 0, _ref = string.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - charCode = string.charCodeAt(i); - width += (this.widthOfGlyph(this.characterToGlyph(charCode)) + charSpace * (1000/ size)) || 0; - } - scale = size / 1000; - return width * scale; - }; - TTFFont.prototype.lineHeight = function (size, includeGap) { - var gap; - if (includeGap == null) { - includeGap = false; - } - gap = includeGap ? this.lineGap : 0; - return (this.ascender + gap - this.decender) / 1000 * size; - }; - return TTFFont; - })(); - - /************************************************************************************************/ - /* function : Data */ - /* comment : The ttf data decoded and stored in an array is read and written to the Data object.*/ - /************************************************************************************************/ - var Data = (function () { - function Data(data) { - this.data = data != null ? data : []; - this.pos = 0; - this.length = this.data.length; - } - Data.prototype.readByte = function () { - return this.data[this.pos++]; - }; - Data.prototype.writeByte = function (byte) { - return this.data[this.pos++] = byte; - }; - Data.prototype.readUInt32 = function () { - var b1, b2, b3, b4; - b1 = this.readByte() * 0x1000000; - b2 = this.readByte() << 16; - b3 = this.readByte() << 8; - b4 = this.readByte(); - return b1 + b2 + b3 + b4; - }; - Data.prototype.writeUInt32 = function (val) { - this.writeByte((val >>> 24) & 0xff); - this.writeByte((val >> 16) & 0xff); - this.writeByte((val >> 8) & 0xff); - return this.writeByte(val & 0xff); - }; - Data.prototype.readInt32 = function () { - var int; - int = this.readUInt32(); - if (int >= 0x80000000) { - return int - 0x100000000; - } - else { - return int; - } - }; - Data.prototype.writeInt32 = function (val) { - if (val < 0) { - val += 0x100000000; - } - return this.writeUInt32(val); - }; - Data.prototype.readUInt16 = function () { - var b1, b2; - b1 = this.readByte() << 8; - b2 = this.readByte(); - return b1 | b2; - }; - Data.prototype.writeUInt16 = function (val) { - this.writeByte((val >> 8) & 0xff); - return this.writeByte(val & 0xff); - }; - Data.prototype.readInt16 = function () { - var int; - int = this.readUInt16(); - if (int >= 0x8000) { - return int - 0x10000; - } - else { - return int; - } - }; - Data.prototype.writeInt16 = function (val) { - if (val < 0) { - val += 0x10000; - } - return this.writeUInt16(val); - }; - Data.prototype.readString = function (length) { - var i, ret, _i; - ret = []; - for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) { - ret[i] = String.fromCharCode(this.readByte()); - } - return ret.join(''); - }; - Data.prototype.writeString = function (val) { - var i, _i, _ref, _results; - _results = []; - for (i = _i = 0, _ref = val.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - _results.push(this.writeByte(val.charCodeAt(i))); - } - return _results; - }; - /*Data.prototype.stringAt = function (pos, length) { - this.pos = pos; - return this.readString(length); - };*/ - Data.prototype.readShort = function () { - return this.readInt16(); - }; - Data.prototype.writeShort = function (val) { - return this.writeInt16(val); - }; - Data.prototype.readLongLong = function () { - var b1, b2, b3, b4, b5, b6, b7, b8; - b1 = this.readByte(); - b2 = this.readByte(); - b3 = this.readByte(); - b4 = this.readByte(); - b5 = this.readByte(); - b6 = this.readByte(); - b7 = this.readByte(); - b8 = this.readByte(); - if (b1 & 0x80) { - return ((b1 ^ 0xff) * 0x100000000000000 + (b2 ^ 0xff) * 0x1000000000000 + (b3 ^ 0xff) * 0x10000000000 + (b4 ^ 0xff) * 0x100000000 + (b5 ^ 0xff) * 0x1000000 + (b6 ^ 0xff) * 0x10000 + (b7 ^ 0xff) * 0x100 + (b8 ^ 0xff) + 1) * -1; - } - return b1 * 0x100000000000000 + b2 * 0x1000000000000 + b3 * 0x10000000000 + b4 * 0x100000000 + b5 * 0x1000000 + b6 * 0x10000 + b7 * 0x100 + b8; - }; - /*Data.prototype.writeLongLong = function (val) { - var high, low; - high = Math.floor(val / 0x100000000); - low = val & 0xffffffff; - this.writeByte((high >> 24) & 0xff); - this.writeByte((high >> 16) & 0xff); - this.writeByte((high >> 8) & 0xff); - this.writeByte(high & 0xff); - this.writeByte((low >> 24) & 0xff); - this.writeByte((low >> 16) & 0xff); - this.writeByte((low >> 8) & 0xff); - return this.writeByte(low & 0xff); - };*/ - Data.prototype.readInt = function () { - return this.readInt32(); - }; - Data.prototype.writeInt = function (val) { - return this.writeInt32(val); - }; - /*Data.prototype.slice = function (start, end) { - return this.data.slice(start, end); - };*/ - Data.prototype.read = function (bytes) { - var buf, i, _i; - buf = []; - for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { - buf.push(this.readByte()); - } - return buf; - }; - Data.prototype.write = function (bytes) { - var byte, _i, _len, _results; - _results = []; - for (_i = 0, _len = bytes.length; _i < _len; _i++) { - byte = bytes[_i]; - _results.push(this.writeByte(byte)); - } - return _results; - }; - return Data; - })(); - - var Directory = (function () { - var checksum; - - /*****************************************************************************************************/ - /* function : Directory generator */ - /* comment : Initialize the offset, tag, length, and checksum for each table for the font to be used.*/ - /*****************************************************************************************************/ - function Directory(data) { - var entry, i, _i, _ref; - this.scalarType = data.readInt(); - this.tableCount = data.readShort(); - this.searchRange = data.readShort(); - this.entrySelector = data.readShort(); - this.rangeShift = data.readShort(); - this.tables = {}; - for (i = _i = 0, _ref = this.tableCount; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - entry = { - tag: data.readString(4) - , checksum: data.readInt() - , offset: data.readInt() - , length: data.readInt() - }; - this.tables[entry.tag] = entry; - } - } - /********************************************************************************************************/ - /* function : encode */ - /* comment : It encodes and stores the font table object and information used for the directory object. */ - /********************************************************************************************************/ - Directory.prototype.encode = function (tables) { - var adjustment, directory, directoryLength, entrySelector, headOffset, log2, offset, rangeShift, searchRange, sum, table, tableCount, tableData, tag; - tableCount = Object.keys(tables).length; - log2 = Math.log(2); - searchRange = Math.floor(Math.log(tableCount) / log2) * 16; - entrySelector = Math.floor(searchRange / log2); - rangeShift = tableCount * 16 - searchRange; - directory = new Data; - directory.writeInt(this.scalarType); - directory.writeShort(tableCount); - directory.writeShort(searchRange); - directory.writeShort(entrySelector); - directory.writeShort(rangeShift); - directoryLength = tableCount * 16; - offset = directory.pos + directoryLength; - headOffset = null; - tableData = []; - for (tag in tables) { - table = tables[tag]; - directory.writeString(tag); - directory.writeInt(checksum(table)); - directory.writeInt(offset); - directory.writeInt(table.length); - tableData = tableData.concat(table); - if (tag === 'head') { - headOffset = offset; - } - offset += table.length; - while (offset % 4) { - tableData.push(0); - offset++; - } - } - directory.write(tableData); - sum = checksum(directory.data); - adjustment = 0xB1B0AFBA - sum; - directory.pos = headOffset + 8; - directory.writeUInt32(adjustment); - return directory.data; - }; - /***************************************************************/ - /* function : checksum */ - /* comment : Duplicate the table for the tag. */ - /***************************************************************/ - checksum = function (data) { - var i, sum, tmp, _i, _ref; - data = __slice.call(data); - while (data.length % 4) { - data.push(0); - } - tmp = new Data(data); - sum = 0; - for (i = _i = 0, _ref = data.length; _i < _ref; i = _i += 4) { - sum += tmp.readUInt32(); - } - return sum & 0xFFFFFFFF; - }; - return Directory; - })(); - - var Table, __hasProp = {}.hasOwnProperty - , __extends = function (child, parent) { - for (var key in parent) { - if (__hasProp.call(parent, key)) child[key] = parent[key]; - } - - function ctor() { - this.constructor = child; - } - ctor.prototype = parent.prototype; - child.prototype = new ctor(); - child.__super__ = parent.prototype; - return child; - }; - /***************************************************************/ - /* function : Table */ - /* comment : Save info for each table, and parse the table. */ - /***************************************************************/ - Table = (function () { - function Table(file) { - var info; - this.file = file; - info = this.file.directory.tables[this.tag]; - this.exists = !!info; - if (info) { - this.offset = info.offset, this.length = info.length; - this.parse(this.file.contents); - } - } - Table.prototype.parse = function () {}; - Table.prototype.encode = function () {}; - Table.prototype.raw = function () { - if (!this.exists) { - return null; - } - this.file.contents.pos = this.offset; - return this.file.contents.read(this.length); - }; - return Table; - })(); - - var HeadTable = (function (_super) { - __extends(HeadTable, _super); - - function HeadTable() { - return HeadTable.__super__.constructor.apply(this, arguments); - } - HeadTable.prototype.tag = 'head'; - HeadTable.prototype.parse = function (data) { - data.pos = this.offset; - this.version = data.readInt(); - this.revision = data.readInt(); - this.checkSumAdjustment = data.readInt(); - this.magicNumber = data.readInt(); - this.flags = data.readShort(); - this.unitsPerEm = data.readShort(); - this.created = data.readLongLong(); - this.modified = data.readLongLong(); - this.xMin = data.readShort(); - this.yMin = data.readShort(); - this.xMax = data.readShort(); - this.yMax = data.readShort(); - this.macStyle = data.readShort(); - this.lowestRecPPEM = data.readShort(); - this.fontDirectionHint = data.readShort(); - this.indexToLocFormat = data.readShort(); - return this.glyphDataFormat = data.readShort(); - }; - /*HeadTable.prototype.encode = function (loca) { - var table; - table = new Data; - table.writeInt(this.version); - table.writeInt(this.revision); - table.writeInt(this.checkSumAdjustment); - table.writeInt(this.magicNumber); - table.writeShort(this.flags); - table.writeShort(this.unitsPerEm); - table.writeLongLong(this.created); - table.writeLongLong(this.modified); - table.writeShort(this.xMin); - table.writeShort(this.yMin); - table.writeShort(this.xMax); - table.writeShort(this.yMax); - table.writeShort(this.macStyle); - table.writeShort(this.lowestRecPPEM); - table.writeShort(this.fontDirectionHint); - table.writeShort(loca.type); - table.writeShort(this.glyphDataFormat); - return table.data; - };*/ - return HeadTable; - })(Table); - - /************************************************************************************/ - /* function : CmapEntry */ - /* comment : Cmap Initializes and encodes object information (required by pdf spec).*/ - /************************************************************************************/ - var CmapEntry = (function () { - function CmapEntry(data, offset) { - var code, count, endCode, glyphId, glyphIds, i, idDelta, idRangeOffset, index, saveOffset, segCount, segCountX2, start, startCode, tail, _i, _j, _k, _len; - this.platformID = data.readUInt16(); - this.encodingID = data.readShort(); - this.offset = offset + data.readInt(); - saveOffset = data.pos; - data.pos = this.offset; - this.format = data.readUInt16(); - this.length = data.readUInt16(); - this.language = data.readUInt16(); - this.isUnicode = (this.platformID === 3 && this.encodingID === 1 && this.format === 4) || this.platformID === 0 && this.format === 4; - this.codeMap = {}; - switch (this.format) { - case 0: - for (i = _i = 0; _i < 256; i = ++_i) { - this.codeMap[i] = data.readByte(); - } - break; - case 4: - segCountX2 = data.readUInt16(); - segCount = segCountX2 / 2; - data.pos += 6; - endCode = (function () { - var _j, _results; - _results = []; - for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) { - _results.push(data.readUInt16()); - } - return _results; - })(); - data.pos += 2; - startCode = (function () { - var _j, _results; - _results = []; - for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) { - _results.push(data.readUInt16()); - } - return _results; - })(); - idDelta = (function () { - var _j, _results; - _results = []; - for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) { - _results.push(data.readUInt16()); - } - return _results; - })(); - idRangeOffset = (function () { - var _j, _results; - _results = []; - for (i = _j = 0; 0 <= segCount ? _j < segCount : _j > segCount; i = 0 <= segCount ? ++_j : --_j) { - _results.push(data.readUInt16()); - } - return _results; - })(); - count = (this.length - data.pos + this.offset) / 2; - glyphIds = (function () { - var _j, _results; - _results = []; - for (i = _j = 0; 0 <= count ? _j < count : _j > count; i = 0 <= count ? ++_j : --_j) { - _results.push(data.readUInt16()); - } - return _results; - })(); - for (i = _j = 0, _len = endCode.length; _j < _len; i = ++_j) { - tail = endCode[i]; - start = startCode[i]; - for (code = _k = start; start <= tail ? _k <= tail : _k >= tail; code = start <= tail ? ++_k : --_k) { - if (idRangeOffset[i] === 0) { - glyphId = code + idDelta[i]; - } - else { - index = idRangeOffset[i] / 2 + (code - start) - (segCount - i); - glyphId = glyphIds[index] || 0; - if (glyphId !== 0) { - glyphId += idDelta[i]; - } - } - this.codeMap[code] = glyphId & 0xFFFF; - } - } - } - data.pos = saveOffset; - } - CmapEntry.encode = function (charmap, encoding) { - var charMap, code, codeMap, codes, delta, deltas, diff, endCode, endCodes, entrySelector, glyphIDs, i, id, indexes, last, map, nextID, offset, old, rangeOffsets, rangeShift, result, searchRange, segCount, segCountX2, startCode, startCodes, startGlyph, subtable, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _len7, _m, _n, _name, _o, _p, _q; - subtable = new Data; - codes = Object.keys(charmap).sort(function (a, b) { - return a - b; - }); - switch (encoding) { - case 'macroman': - id = 0; - indexes = (function () { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 256; i = ++_i) { - _results.push(0); - } - return _results; - })(); - map = { - 0: 0 - }; - codeMap = {}; - for (_i = 0, _len = codes.length; _i < _len; _i++) { - code = codes[_i]; - if (map[_name = charmap[code]] == null) { - map[_name] = ++id; - } - codeMap[code] = { - old: charmap[code] - , "new": map[charmap[code]] - }; - indexes[code] = map[charmap[code]]; - } - subtable.writeUInt16(1); - subtable.writeUInt16(0); - subtable.writeUInt32(12); - subtable.writeUInt16(0); - subtable.writeUInt16(262); - subtable.writeUInt16(0); - subtable.write(indexes); - return result = { - charMap: codeMap - , subtable: subtable.data - , maxGlyphID: id + 1 - }; - case 'unicode': - startCodes = []; - endCodes = []; - nextID = 0; - map = {}; - charMap = {}; - last = diff = null; - for (_j = 0, _len1 = codes.length; _j < _len1; _j++) { - code = codes[_j]; - old = charmap[code]; - if (map[old] == null) { - map[old] = ++nextID; - } - charMap[code] = { - old: old - , "new": map[old] - }; - delta = map[old] - code; - if ((last == null) || delta !== diff) { - if (last) { - endCodes.push(last); - } - startCodes.push(code); - diff = delta; - } - last = code; - } - if (last) { - endCodes.push(last); - } - endCodes.push(0xFFFF); - startCodes.push(0xFFFF); - segCount = startCodes.length; - segCountX2 = segCount * 2; - searchRange = 2 * Math.pow(Math.log(segCount) / Math.LN2, 2); - entrySelector = Math.log(searchRange / 2) / Math.LN2; - rangeShift = 2 * segCount - searchRange; - deltas = []; - rangeOffsets = []; - glyphIDs = []; - for (i = _k = 0, _len2 = startCodes.length; _k < _len2; i = ++_k) { - startCode = startCodes[i]; - endCode = endCodes[i]; - if (startCode === 0xFFFF) { - deltas.push(0); - rangeOffsets.push(0); - break; - } - startGlyph = charMap[startCode]["new"]; - if (startCode - startGlyph >= 0x8000) { - deltas.push(0); - rangeOffsets.push(2 * (glyphIDs.length + segCount - i)); - for (code = _l = startCode; startCode <= endCode ? _l <= endCode : _l >= endCode; code = startCode <= endCode ? ++_l : --_l) { - glyphIDs.push(charMap[code]["new"]); - } - } - else { - deltas.push(startGlyph - startCode); - rangeOffsets.push(0); - } - } - subtable.writeUInt16(3); - subtable.writeUInt16(1); - subtable.writeUInt32(12); - subtable.writeUInt16(4); - subtable.writeUInt16(16 + segCount * 8 + glyphIDs.length * 2); - subtable.writeUInt16(0); - subtable.writeUInt16(segCountX2); - subtable.writeUInt16(searchRange); - subtable.writeUInt16(entrySelector); - subtable.writeUInt16(rangeShift); - for (_m = 0, _len3 = endCodes.length; _m < _len3; _m++) { - code = endCodes[_m]; - subtable.writeUInt16(code); - } - subtable.writeUInt16(0); - for (_n = 0, _len4 = startCodes.length; _n < _len4; _n++) { - code = startCodes[_n]; - subtable.writeUInt16(code); - } - for (_o = 0, _len5 = deltas.length; _o < _len5; _o++) { - delta = deltas[_o]; - subtable.writeUInt16(delta); - } - for (_p = 0, _len6 = rangeOffsets.length; _p < _len6; _p++) { - offset = rangeOffsets[_p]; - subtable.writeUInt16(offset); - } - for (_q = 0, _len7 = glyphIDs.length; _q < _len7; _q++) { - id = glyphIDs[_q]; - subtable.writeUInt16(id); - } - return result = { - charMap: charMap - , subtable: subtable.data - , maxGlyphID: nextID + 1 - }; - } - }; - return CmapEntry; - })(); - - var CmapTable = (function (_super) { - __extends(CmapTable, _super); - - function CmapTable() { - return CmapTable.__super__.constructor.apply(this, arguments); - } - CmapTable.prototype.tag = 'cmap'; - CmapTable.prototype.parse = function (data) { - var entry, i, tableCount, _i; - data.pos = this.offset; - this.version = data.readUInt16(); - tableCount = data.readUInt16(); - this.tables = []; - this.unicode = null; - for (i = _i = 0; 0 <= tableCount ? _i < tableCount : _i > tableCount; i = 0 <= tableCount ? ++_i : --_i) { - entry = new CmapEntry(data, this.offset); - this.tables.push(entry); - if (entry.isUnicode) { - if (this.unicode == null) { - this.unicode = entry; - } - } - } - return true; - }; - /*************************************************************************/ - /* function : encode */ - /* comment : Encode the cmap table corresponding to the input character. */ - /*************************************************************************/ - CmapTable.encode = function (charmap, encoding) { - var result, table; - if (encoding == null) { - encoding = 'macroman'; - } - result = CmapEntry.encode(charmap, encoding); - table = new Data; - table.writeUInt16(0); - table.writeUInt16(1); - result.table = table.data.concat(result.subtable); - return result; - }; - return CmapTable; - })(Table); - - var HheaTable = (function (_super) { - __extends(HheaTable, _super); - - function HheaTable() { - return HheaTable.__super__.constructor.apply(this, arguments); - } - HheaTable.prototype.tag = 'hhea'; - HheaTable.prototype.parse = function (data) { - data.pos = this.offset; - this.version = data.readInt(); - this.ascender = data.readShort(); - this.decender = data.readShort(); - this.lineGap = data.readShort(); - this.advanceWidthMax = data.readShort(); - this.minLeftSideBearing = data.readShort(); - this.minRightSideBearing = data.readShort(); - this.xMaxExtent = data.readShort(); - this.caretSlopeRise = data.readShort(); - this.caretSlopeRun = data.readShort(); - this.caretOffset = data.readShort(); - data.pos += 4 * 2; - this.metricDataFormat = data.readShort(); - return this.numberOfMetrics = data.readUInt16(); - }; - /*HheaTable.prototype.encode = function (ids) { - var i, table, _i, _ref; - table = new Data; - table.writeInt(this.version); - table.writeShort(this.ascender); - table.writeShort(this.decender); - table.writeShort(this.lineGap); - table.writeShort(this.advanceWidthMax); - table.writeShort(this.minLeftSideBearing); - table.writeShort(this.minRightSideBearing); - table.writeShort(this.xMaxExtent); - table.writeShort(this.caretSlopeRise); - table.writeShort(this.caretSlopeRun); - table.writeShort(this.caretOffset); - for (i = _i = 0, _ref = 4 * 2; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - table.writeByte(0); - } - table.writeShort(this.metricDataFormat); - table.writeUInt16(ids.length); - return table.data; - };*/ - return HheaTable; - })(Table); - - var OS2Table = (function (_super) { - __extends(OS2Table, _super); - - function OS2Table() { - return OS2Table.__super__.constructor.apply(this, arguments); - } - OS2Table.prototype.tag = 'OS/2'; - OS2Table.prototype.parse = function (data) { - var i; - data.pos = this.offset; - this.version = data.readUInt16(); - this.averageCharWidth = data.readShort(); - this.weightClass = data.readUInt16(); - this.widthClass = data.readUInt16(); - this.type = data.readShort(); - this.ySubscriptXSize = data.readShort(); - this.ySubscriptYSize = data.readShort(); - this.ySubscriptXOffset = data.readShort(); - this.ySubscriptYOffset = data.readShort(); - this.ySuperscriptXSize = data.readShort(); - this.ySuperscriptYSize = data.readShort(); - this.ySuperscriptXOffset = data.readShort(); - this.ySuperscriptYOffset = data.readShort(); - this.yStrikeoutSize = data.readShort(); - this.yStrikeoutPosition = data.readShort(); - this.familyClass = data.readShort(); - this.panose = (function () { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 10; i = ++_i) { - _results.push(data.readByte()); - } - return _results; - })(); - this.charRange = (function () { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 4; i = ++_i) { - _results.push(data.readInt()); - } - return _results; - })(); - this.vendorID = data.readString(4); - this.selection = data.readShort(); - this.firstCharIndex = data.readShort(); - this.lastCharIndex = data.readShort(); - if (this.version > 0) { - this.ascent = data.readShort(); - this.descent = data.readShort(); - this.lineGap = data.readShort(); - this.winAscent = data.readShort(); - this.winDescent = data.readShort(); - this.codePageRange = (function () { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 2; i = ++_i) { - _results.push(data.readInt()); - } - return _results; - })(); - if (this.version > 1) { - this.xHeight = data.readShort(); - this.capHeight = data.readShort(); - this.defaultChar = data.readShort(); - this.breakChar = data.readShort(); - return this.maxContext = data.readShort(); - } - } - }; - /*OS2Table.prototype.encode = function () { - return this.raw(); - };*/ - return OS2Table; - })(Table); - - var PostTable = (function (_super) { - __extends(PostTable, _super); - - function PostTable() { - return PostTable.__super__.constructor.apply(this, arguments); - } - PostTable.prototype.tag = 'post'; - PostTable.prototype.parse = function (data) { - var i, length, numberOfGlyphs, _i, _results; - data.pos = this.offset; - this.format = data.readInt(); - this.italicAngle = data.readInt(); - this.underlinePosition = data.readShort(); - this.underlineThickness = data.readShort(); - this.isFixedPitch = data.readInt(); - this.minMemType42 = data.readInt(); - this.maxMemType42 = data.readInt(); - this.minMemType1 = data.readInt(); - this.maxMemType1 = data.readInt(); - switch (this.format) { - case 0x00010000: - break; - case 0x00020000: - numberOfGlyphs = data.readUInt16(); - this.glyphNameIndex = []; - for (i = _i = 0; 0 <= numberOfGlyphs ? _i < numberOfGlyphs : _i > numberOfGlyphs; i = 0 <= numberOfGlyphs ? ++_i : --_i) { - this.glyphNameIndex.push(data.readUInt16()); - } - this.names = []; - _results = []; - while (data.pos < this.offset + this.length) { - length = data.readByte(); - _results.push(this.names.push(data.readString(length))); - } - return _results; - break; - case 0x00025000: - numberOfGlyphs = data.readUInt16(); - return this.offsets = data.read(numberOfGlyphs); - case 0x00030000: - break; - case 0x00040000: - return this.map = (function () { - var _j, _ref, _results1; - _results1 = []; - for (i = _j = 0, _ref = this.file.maxp.numGlyphs; 0 <= _ref ? _j < _ref : _j > _ref; i = 0 <= _ref ? ++_j : --_j) { - _results1.push(data.readUInt32()); - } - return _results1; - }).call(this); - } - }; - return PostTable; - })(Table); - - /*********************************************************************************************************/ - /* function : NameEntry */ - /* comment : Store copyright information, platformID, encodingID, and languageID in the NameEntry object.*/ - /*********************************************************************************************************/ - var NameEntry = (function () { - function NameEntry(raw, entry) { - this.raw = raw; - this.length = raw.length; - this.platformID = entry.platformID; - this.encodingID = entry.encodingID; - this.languageID = entry.languageID; - } - return NameEntry; - })(); - - var NameTable = (function (_super) { - __extends(NameTable, _super); - - function NameTable() { - return NameTable.__super__.constructor.apply(this, arguments); - } - NameTable.prototype.tag = 'name'; - NameTable.prototype.parse = function (data) { - var count, entries, entry, format, i, name, stringOffset, strings, text, _i, _j, _len, _name; - data.pos = this.offset; - format = data.readShort(); - count = data.readShort(); - stringOffset = data.readShort(); - entries = []; - for (i = _i = 0; 0 <= count ? _i < count : _i > count; i = 0 <= count ? ++_i : --_i) { - entries.push({ - platformID: data.readShort() - , encodingID: data.readShort() - , languageID: data.readShort() - , nameID: data.readShort() - , length: data.readShort() - , offset: this.offset + stringOffset + data.readShort() - }); - } - strings = {}; - for (i = _j = 0, _len = entries.length; _j < _len; i = ++_j) { - entry = entries[i]; - data.pos = entry.offset; - text = data.readString(entry.length); - name = new NameEntry(text, entry); - if (strings[_name = entry.nameID] == null) { - strings[_name] = []; - } - strings[entry.nameID].push(name); - } - this.strings = strings; - this.copyright = strings[0]; - this.fontFamily = strings[1]; - this.fontSubfamily = strings[2]; - this.uniqueSubfamily = strings[3]; - this.fontName = strings[4]; - this.version = strings[5]; - this.postscriptName = strings[6][0].raw.replace(/[\x00-\x19\x80-\xff]/g, ""); - this.trademark = strings[7]; - this.manufacturer = strings[8]; - this.designer = strings[9]; - this.description = strings[10]; - this.vendorUrl = strings[11]; - this.designerUrl = strings[12]; - this.license = strings[13]; - this.licenseUrl = strings[14]; - this.preferredFamily = strings[15]; - this.preferredSubfamily = strings[17]; - this.compatibleFull = strings[18]; - return this.sampleText = strings[19]; - }; - /*NameTable.prototype.encode = function () { - var id, list, nameID, nameTable, postscriptName, strCount, strTable, string, strings, table, val, _i, _len, _ref; - strings = {}; - _ref = this.strings; - for (id in _ref) { - val = _ref[id]; - strings[id] = val; - } - postscriptName = new NameEntry("" + subsetTag + "+" + this.postscriptName, { - platformID: 1 - , encodingID: 0 - , languageID: 0 - }); - strings[6] = [postscriptName]; - subsetTag = successorOf(subsetTag); - strCount = 0; - for (id in strings) { - list = strings[id]; - if (list != null) { - strCount += list.length; - } - } - table = new Data; - strTable = new Data; - table.writeShort(0); - table.writeShort(strCount); - table.writeShort(6 + 12 * strCount); - for (nameID in strings) { - list = strings[nameID]; - if (list != null) { - for (_i = 0, _len = list.length; _i < _len; _i++) { - string = list[_i]; - table.writeShort(string.platformID); - table.writeShort(string.encodingID); - table.writeShort(string.languageID); - table.writeShort(nameID); - table.writeShort(string.length); - table.writeShort(strTable.pos); - strTable.writeString(string.raw); - } - } - } - return nameTable = { - postscriptName: postscriptName.raw - , table: table.data.concat(strTable.data) - }; - };*/ - return NameTable; - })(Table); - - var MaxpTable = (function (_super) { - __extends(MaxpTable, _super); - - function MaxpTable() { - return MaxpTable.__super__.constructor.apply(this, arguments); - } - MaxpTable.prototype.tag = 'maxp'; - MaxpTable.prototype.parse = function (data) { - data.pos = this.offset; - this.version = data.readInt(); - this.numGlyphs = data.readUInt16(); - this.maxPoints = data.readUInt16(); - this.maxContours = data.readUInt16(); - this.maxCompositePoints = data.readUInt16(); - this.maxComponentContours = data.readUInt16(); - this.maxZones = data.readUInt16(); - this.maxTwilightPoints = data.readUInt16(); - this.maxStorage = data.readUInt16(); - this.maxFunctionDefs = data.readUInt16(); - this.maxInstructionDefs = data.readUInt16(); - this.maxStackElements = data.readUInt16(); - this.maxSizeOfInstructions = data.readUInt16(); - this.maxComponentElements = data.readUInt16(); - return this.maxComponentDepth = data.readUInt16(); - }; - /*MaxpTable.prototype.encode = function (ids) { - var table; - table = new Data; - table.writeInt(this.version); - table.writeUInt16(ids.length); - table.writeUInt16(this.maxPoints); - table.writeUInt16(this.maxContours); - table.writeUInt16(this.maxCompositePoints); - table.writeUInt16(this.maxComponentContours); - table.writeUInt16(this.maxZones); - table.writeUInt16(this.maxTwilightPoints); - table.writeUInt16(this.maxStorage); - table.writeUInt16(this.maxFunctionDefs); - table.writeUInt16(this.maxInstructionDefs); - table.writeUInt16(this.maxStackElements); - table.writeUInt16(this.maxSizeOfInstructions); - table.writeUInt16(this.maxComponentElements); - table.writeUInt16(this.maxComponentDepth); - return table.data; - };*/ - return MaxpTable; - })(Table); - - var HmtxTable = (function (_super) { - __extends(HmtxTable, _super); - - function HmtxTable() { - return HmtxTable.__super__.constructor.apply(this, arguments); - } - HmtxTable.prototype.tag = 'hmtx'; - HmtxTable.prototype.parse = function (data) { - var i, last, lsbCount, m, _i, _j, _ref, _results; - data.pos = this.offset; - this.metrics = []; - for (i = _i = 0, _ref = this.file.hhea.numberOfMetrics; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - this.metrics.push({ - advance: data.readUInt16() - , lsb: data.readInt16() - }); - } - lsbCount = this.file.maxp.numGlyphs - this.file.hhea.numberOfMetrics; - this.leftSideBearings = (function () { - var _j, _results; - _results = []; - for (i = _j = 0; 0 <= lsbCount ? _j < lsbCount : _j > lsbCount; i = 0 <= lsbCount ? ++_j : --_j) { - _results.push(data.readInt16()); - } - return _results; - })(); - this.widths = (function () { - var _j, _len, _ref1, _results; - _ref1 = this.metrics; - _results = []; - for (_j = 0, _len = _ref1.length; _j < _len; _j++) { - m = _ref1[_j]; - _results.push(m.advance); - } - return _results; - }).call(this); - last = this.widths[this.widths.length - 1]; - _results = []; - for (i = _j = 0; 0 <= lsbCount ? _j < lsbCount : _j > lsbCount; i = 0 <= lsbCount ? ++_j : --_j) { - _results.push(this.widths.push(last)); - } - return _results; - }; - /***************************************************************/ - /* function : forGlyph */ - /* comment : Returns the advance width and lsb for this glyph. */ - /***************************************************************/ - HmtxTable.prototype.forGlyph = function (id) { - var metrics; - if (id in this.metrics) { - return this.metrics[id]; - } - return metrics = { - advance: this.metrics[this.metrics.length - 1].advance - , lsb: this.leftSideBearings[id - this.metrics.length] - }; - }; - /*HmtxTable.prototype.encode = function (mapping) { - var id, metric, table, _i, _len; - table = new Data; - for (_i = 0, _len = mapping.length; _i < _len; _i++) { - id = mapping[_i]; - metric = this.forGlyph(id); - table.writeUInt16(metric.advance); - table.writeUInt16(metric.lsb); - } - return table.data; - };*/ - return HmtxTable; - })(Table); - - var __slice = [].slice; - - var GlyfTable = (function (_super) { - __extends(GlyfTable, _super); - - function GlyfTable() { - return GlyfTable.__super__.constructor.apply(this, arguments); - } - GlyfTable.prototype.tag = 'glyf'; - GlyfTable.prototype.parse = function (data) { - return this.cache = {}; - }; - GlyfTable.prototype.glyphFor = function (id) { - id = id; - var data, index, length, loca, numberOfContours, raw, xMax, xMin, yMax, yMin; - if (id in this.cache) { - return this.cache[id]; - } - loca = this.file.loca; - data = this.file.contents; - index = loca.indexOf(id); - length = loca.lengthOf(id); - if (length === 0) { - return this.cache[id] = null; - } - data.pos = this.offset + index; - raw = new Data(data.read(length)); - numberOfContours = raw.readShort(); - xMin = raw.readShort(); - yMin = raw.readShort(); - xMax = raw.readShort(); - yMax = raw.readShort(); - if (numberOfContours === -1) { - this.cache[id] = new CompoundGlyph(raw, xMin, yMin, xMax, yMax); - } - else { - this.cache[id] = new SimpleGlyph(raw, numberOfContours, xMin, yMin, xMax, yMax); - } - return this.cache[id]; - }; - GlyfTable.prototype.encode = function (glyphs, mapping, old2new) { - var glyph, id, offsets, table, _i, _len; - table = []; - offsets = []; - for (_i = 0, _len = mapping.length; _i < _len; _i++) { - id = mapping[_i]; - glyph = glyphs[id]; - offsets.push(table.length); - if (glyph) { - table = table.concat(glyph.encode(old2new)); - } - } - offsets.push(table.length); - return { - table: table - , offsets: offsets - }; - }; - return GlyfTable; - })(Table); - - var SimpleGlyph = (function () { - /**************************************************************************/ - /* function : SimpleGlyph */ - /* comment : Stores raw, xMin, yMin, xMax, and yMax values for this glyph.*/ - /**************************************************************************/ - function SimpleGlyph(raw, numberOfContours, xMin, yMin, xMax, yMax) { - this.raw = raw; - this.numberOfContours = numberOfContours; - this.xMin = xMin; - this.yMin = yMin; - this.xMax = xMax; - this.yMax = yMax; - this.compound = false; - } - SimpleGlyph.prototype.encode = function () { - return this.raw.data; - }; - return SimpleGlyph; - })(); - - var CompoundGlyph = (function () { - var ARG_1_AND_2_ARE_WORDS, MORE_COMPONENTS, WE_HAVE_AN_X_AND_Y_SCALE, WE_HAVE_A_SCALE, WE_HAVE_A_TWO_BY_TWO; - ARG_1_AND_2_ARE_WORDS = 0x0001; - WE_HAVE_A_SCALE = 0x0008; - MORE_COMPONENTS = 0x0020; - WE_HAVE_AN_X_AND_Y_SCALE = 0x0040; - WE_HAVE_A_TWO_BY_TWO = 0x0080; - - /********************************************************************************************************************/ - /* function : CompoundGlypg generator */ - /* comment : It stores raw, xMin, yMin, xMax, yMax, glyph id, and glyph offset for the corresponding compound glyph.*/ - /********************************************************************************************************************/ - function CompoundGlyph(raw, xMin, yMin, xMax, yMax) { - var data, flags; - this.raw = raw; - this.xMin = xMin; - this.yMin = yMin; - this.xMax = xMax; - this.yMax = yMax; - this.compound = true; - this.glyphIDs = []; - this.glyphOffsets = []; - data = this.raw; - while (true) { - flags = data.readShort(); - this.glyphOffsets.push(data.pos); - this.glyphIDs.push(data.readShort()); - if (!(flags & MORE_COMPONENTS)) { - break; - } - if (flags & ARG_1_AND_2_ARE_WORDS) { - data.pos += 4; - } - else { - data.pos += 2; - } - if (flags & WE_HAVE_A_TWO_BY_TWO) { - data.pos += 8; - } - else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { - data.pos += 4; - } - else if (flags & WE_HAVE_A_SCALE) { - data.pos += 2; - } - } - } - /****************************************************************************************************************/ - /* function : CompoundGlypg encode */ - /* comment : After creating a table for the characters you typed, you call directory.encode to encode the table.*/ - /****************************************************************************************************************/ - CompoundGlyph.prototype.encode = function (mapping) { - var i, id, result, _i, _len, _ref; - result = new Data(__slice.call(this.raw.data)); - _ref = this.glyphIDs; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - id = _ref[i]; - result.pos = this.glyphOffsets[i]; - } - return result.data; - }; - return CompoundGlyph; - })(); - - var LocaTable = (function (_super) { - __extends(LocaTable, _super); - - function LocaTable() { - return LocaTable.__super__.constructor.apply(this, arguments); - } - LocaTable.prototype.tag = 'loca'; - LocaTable.prototype.parse = function (data) { - var format, i; - data.pos = this.offset; - format = this.file.head.indexToLocFormat; - if (format === 0) { - return this.offsets = (function () { - var _i, _ref, _results; - _results = []; - for (i = _i = 0, _ref = this.length; _i < _ref; i = _i += 2) { - _results.push(data.readUInt16() * 2); - } - return _results; - }).call(this); - } - else { - return this.offsets = (function () { - var _i, _ref, _results; - _results = []; - for (i = _i = 0, _ref = this.length; _i < _ref; i = _i += 4) { - _results.push(data.readUInt32()); - } - return _results; - }).call(this); - } - }; - LocaTable.prototype.indexOf = function (id) { - return this.offsets[id]; - }; - LocaTable.prototype.lengthOf = function (id) { - return this.offsets[id + 1] - this.offsets[id]; - }; - LocaTable.prototype.encode = function (offsets, activeGlyphs) { - var LocaTable = new Uint32Array(this.offsets.length); - var glyfPtr = 0; - var listGlyf = 0; - for (var k = 0; k < LocaTable.length; ++k) { - LocaTable[k] = glyfPtr; - if (listGlyf < activeGlyphs.length && activeGlyphs[listGlyf] == k) { - ++listGlyf; - LocaTable[k] = glyfPtr; - var start = this.offsets[k]; - var len = this.offsets[k + 1] - start; - if (len > 0) { - glyfPtr += len; - } - } - } - var newLocaTable = new Array(LocaTable.length * 4); - for (var j = 0; j < LocaTable.length; ++j) { - newLocaTable[4 * j + 3] = (LocaTable[j] & 0x000000ff); - newLocaTable[4 * j + 2] = (LocaTable[j] & 0x0000ff00) >> 8; - newLocaTable[4 * j + 1] = (LocaTable[j] & 0x00ff0000) >> 16; - newLocaTable[4 * j] = (LocaTable[j] & 0xff000000) >> 24; - } - return newLocaTable; - }; - return LocaTable; - })(Table); - - /************************************************************************************/ - /* function : invert */ - /* comment : Change the object's (key: value) to create an object with (value: key).*/ - /************************************************************************************/ - var invert = function (object) { - var key, ret, val; - ret = {}; - for (key in object) { - val = object[key]; - ret[val] = key; - } - return ret; - }; - - /*var successorOf = function (input) { - var added, alphabet, carry, i, index, isUpperCase, last, length, next, result; - alphabet = 'abcdefghijklmnopqrstuvwxyz'; - length = alphabet.length; - result = input; - i = input.length; - while (i >= 0) { - last = input.charAt(--i); - if (isNaN(last)) { - index = alphabet.indexOf(last.toLowerCase()); - if (index === -1) { - next = last; - carry = true; - } - else { - next = alphabet.charAt((index + 1) % length); - isUpperCase = last === last.toUpperCase(); - if (isUpperCase) { - next = next.toUpperCase(); - } - carry = index + 1 >= length; - if (carry && i === 0) { - added = isUpperCase ? 'A' : 'a'; - result = added + next + result.slice(1); - break; - } - } - } - else { - next = +last + 1; - carry = next > 9; - if (carry) { - next = 0; - } - if (carry && i === 0) { - result = '1' + next + result.slice(1); - break; - } - } - result = result.slice(0, i) + next + result.slice(i + 1); - if (!carry) { - break; - } - } - return result; - };*/ - - var Subset = (function () { - function Subset(font) { - this.font = font; - this.subset = {}; - this.unicodes = {}; - this.next = 33; - } - /*Subset.prototype.use = function (character) { - var i, _i, _ref; - if (typeof character === 'string') { - for (i = _i = 0, _ref = character.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - this.use(character.charCodeAt(i)); - } - return; - } - if (!this.unicodes[character]) { - this.subset[this.next] = character; - return this.unicodes[character] = this.next++; - } - };*/ - /*Subset.prototype.encodeText = function (text) { - var char, i, string, _i, _ref; - string = ''; - for (i = _i = 0, _ref = text.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - char = this.unicodes[text.charCodeAt(i)]; - string += String.fromCharCode(char); - } - return string; - };*/ - /***************************************************************/ - /* function : generateCmap */ - /* comment : Returns the unicode cmap for this font. */ - /***************************************************************/ - Subset.prototype.generateCmap = function () { - var mapping, roman, unicode, unicodeCmap, _ref; - unicodeCmap = this.font.cmap.tables[0].codeMap; - mapping = {}; - _ref = this.subset; - for (roman in _ref) { - unicode = _ref[roman]; - mapping[roman] = unicodeCmap[unicode]; - } - return mapping; - }; - /*Subset.prototype.glyphIDs = function () { - var ret, roman, unicode, unicodeCmap, val, _ref; - unicodeCmap = this.font.cmap.tables[0].codeMap; - ret = [0]; - _ref = this.subset; - for (roman in _ref) { - unicode = _ref[roman]; - val = unicodeCmap[unicode]; - if ((val != null) && __indexOf.call(ret, val) < 0) { - ret.push(val); - } - } - return ret.sort(); - };*/ - /******************************************************************/ - /* function : glyphsFor */ - /* comment : Returns simple glyph objects for the input character.*/ - /******************************************************************/ - Subset.prototype.glyphsFor = function (glyphIDs) { - var additionalIDs, glyph, glyphs, id, _i, _len, _ref; - glyphs = {}; - for (_i = 0, _len = glyphIDs.length; _i < _len; _i++) { - id = glyphIDs[_i]; - glyphs[id] = this.font.glyf.glyphFor(id); - } - additionalIDs = []; - for (id in glyphs) { - glyph = glyphs[id]; - if (glyph != null ? glyph.compound : void 0) { - additionalIDs.push.apply(additionalIDs, glyph.glyphIDs); - } - } - if (additionalIDs.length > 0) { - _ref = this.glyphsFor(additionalIDs); - for (id in _ref) { - glyph = _ref[id]; - glyphs[id] = glyph; - } - } - return glyphs; - }; - /***************************************************************/ - /* function : encode */ - /* comment : Encode various tables for the characters you use. */ - /***************************************************************/ - Subset.prototype.encode = function (glyID) { - var cmap, code, glyf, glyphs, id, ids, loca, new2old, newIDs, nextGlyphID, old2new, oldID, oldIDs, tables, _ref; - cmap = CmapTable.encode(this.generateCmap(), 'unicode'); - glyphs = this.glyphsFor(glyID); - old2new = { - 0: 0 - }; - _ref = cmap.charMap; - for (code in _ref) { - ids = _ref[code]; - old2new[ids.old] = ids["new"]; - } - nextGlyphID = cmap.maxGlyphID; - for (oldID in glyphs) { - if (!(oldID in old2new)) { - old2new[oldID] = nextGlyphID++; - } - } - new2old = invert(old2new); - newIDs = Object.keys(new2old).sort(function (a, b) { - return a - b; - }); - oldIDs = (function () { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = newIDs.length; _i < _len; _i++) { - id = newIDs[_i]; - _results.push(new2old[id]); - } - return _results; - })(); - glyf = this.font.glyf.encode(glyphs, oldIDs, old2new); - loca = this.font.loca.encode(glyf.offsets, oldIDs); - tables = { - cmap: this.font.cmap.raw() - , glyf: glyf.table - , loca: loca - , hmtx: this.font.hmtx.raw() - , hhea: this.font.hhea.raw() - , maxp: this.font.maxp.raw() - , post: this.font.post.raw() - , name: this.font.name.raw() - , head: this.font.head.raw() - }; - if (this.font.os2.exists) { - tables['OS/2'] = this.font.os2.raw(); - } - return this.font.directory.encode(tables); - }; - return Subset; - })(); - - jsPDF.API.PDFObject = (function () { - var pad; - - function PDFObject() {} - pad = function (str, length) { - return (Array(length + 1).join('0') + str).slice(-length); - }; - /*****************************************************************************/ - /* function : convert */ - /* comment :Converts pdf tag's / FontBBox and array values in / W to strings */ - /*****************************************************************************/ - PDFObject.convert = function (object) { - var e, items, key, out, val; - if (Array.isArray(object)) { - items = ((function () { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = object.length; _i < _len; _i++) { - e = object[_i]; - _results.push(PDFObject.convert(e)); - } - return _results; - })()).join(' '); - return '[' + items + ']'; - } - else if (typeof object === 'string') { - return '/' + object; - } - else if (object != null ? object.isString : void 0) { - return '(' + object + ')'; - } - else if (object instanceof Date) { - return '(D:' + pad(object.getUTCFullYear(), 4) + pad(object.getUTCMonth(), 2) + pad(object.getUTCDate(), 2) + pad(object.getUTCHours(), 2) + pad(object.getUTCMinutes(), 2) + pad(object.getUTCSeconds(), 2) + 'Z)'; - } - else if ({}.toString.call(object) === '[object Object]') { - out = ['<<']; - for (key in object) { - val = object[key]; - out.push('/' + key + ' ' + PDFObject.convert(val)); - } - out.push('>>'); - return out.join('\n'); - } - else { - return '' + object; - } - }; - return PDFObject; - })(); - })(jsPDF); - - // Generated by CoffeeScript 1.4.0 - - /* - # PNG.js - # Copyright (c) 2011 Devon Govett - # MIT LICENSE - # - # - */ - - - (function(global) { - var PNG; - - PNG = (function() { - var APNG_BLEND_OP_SOURCE, APNG_DISPOSE_OP_BACKGROUND, APNG_DISPOSE_OP_PREVIOUS, makeImage, scratchCanvas, scratchCtx; - - PNG.load = function(url, canvas, callback) { - var xhr; - if (typeof canvas === 'function') { - callback = canvas; - } - xhr = new XMLHttpRequest; - xhr.open("GET", url, true); - xhr.responseType = "arraybuffer"; - xhr.onload = function() { - var data, png; - data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer); - png = new PNG(data); - if (typeof (canvas != null ? canvas.getContext : void 0) === 'function') { - png.render(canvas); - } - return typeof callback === "function" ? callback(png) : void 0; - }; - return xhr.send(null); - }; - - APNG_DISPOSE_OP_BACKGROUND = 1; - - APNG_DISPOSE_OP_PREVIOUS = 2; - - APNG_BLEND_OP_SOURCE = 0; - - function PNG(data) { - var chunkSize, colors, palLen, delayDen, delayNum, frame, i, index, key, section, palShort, text, _i, _j, _ref; - this.data = data; - this.pos = 8; - this.palette = []; - this.imgData = []; - this.transparency = {}; - this.animation = null; - this.text = {}; - frame = null; - while (true) { - chunkSize = this.readUInt32(); - section = ((function() { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 4; i = ++_i) { - _results.push(String.fromCharCode(this.data[this.pos++])); - } - return _results; - }).call(this)).join(''); - switch (section) { - case 'IHDR': - this.width = this.readUInt32(); - this.height = this.readUInt32(); - this.bits = this.data[this.pos++]; - this.colorType = this.data[this.pos++]; - this.compressionMethod = this.data[this.pos++]; - this.filterMethod = this.data[this.pos++]; - this.interlaceMethod = this.data[this.pos++]; - break; - case 'acTL': - this.animation = { - numFrames: this.readUInt32(), - numPlays: this.readUInt32() || Infinity, - frames: [] - }; - break; - case 'PLTE': - this.palette = this.read(chunkSize); - break; - case 'fcTL': - if (frame) { - this.animation.frames.push(frame); - } - this.pos += 4; - frame = { - width: this.readUInt32(), - height: this.readUInt32(), - xOffset: this.readUInt32(), - yOffset: this.readUInt32() - }; - delayNum = this.readUInt16(); - delayDen = this.readUInt16() || 100; - frame.delay = 1000 * delayNum / delayDen; - frame.disposeOp = this.data[this.pos++]; - frame.blendOp = this.data[this.pos++]; - frame.data = []; - break; - case 'IDAT': - case 'fdAT': - if (section === 'fdAT') { - this.pos += 4; - chunkSize -= 4; - } - data = (frame != null ? frame.data : void 0) || this.imgData; - for (i = _i = 0; 0 <= chunkSize ? _i < chunkSize : _i > chunkSize; i = 0 <= chunkSize ? ++_i : --_i) { - data.push(this.data[this.pos++]); - } - break; - case 'tRNS': - this.transparency = {}; - switch (this.colorType) { - case 3: - palLen = this.palette.length/3; - this.transparency.indexed = this.read(chunkSize); - if(this.transparency.indexed.length > palLen) - throw new Error('More transparent colors than palette size'); - /* - * According to the PNG spec trns should be increased to the same size as palette if shorter - */ - //palShort = 255 - this.transparency.indexed.length; - palShort = palLen - this.transparency.indexed.length; - if (palShort > 0) { - for (i = _j = 0; 0 <= palShort ? _j < palShort : _j > palShort; i = 0 <= palShort ? ++_j : --_j) { - this.transparency.indexed.push(255); - } - } - break; - case 0: - this.transparency.grayscale = this.read(chunkSize)[0]; - break; - case 2: - this.transparency.rgb = this.read(chunkSize); - } - break; - case 'tEXt': - text = this.read(chunkSize); - index = text.indexOf(0); - key = String.fromCharCode.apply(String, text.slice(0, index)); - this.text[key] = String.fromCharCode.apply(String, text.slice(index + 1)); - break; - case 'IEND': - if (frame) { - this.animation.frames.push(frame); - } - this.colors = (function() { - switch (this.colorType) { - case 0: - case 3: - case 4: - return 1; - case 2: - case 6: - return 3; - } - }).call(this); - this.hasAlphaChannel = (_ref = this.colorType) === 4 || _ref === 6; - colors = this.colors + (this.hasAlphaChannel ? 1 : 0); - this.pixelBitlength = this.bits * colors; - this.colorSpace = (function() { - switch (this.colors) { - case 1: - return 'DeviceGray'; - case 3: - return 'DeviceRGB'; - } - }).call(this); - this.imgData = new Uint8Array(this.imgData); - return; - default: - this.pos += chunkSize; - } - this.pos += 4; - if (this.pos > this.data.length) { - throw new Error("Incomplete or corrupt PNG file"); - } - } - return; - } - - PNG.prototype.read = function(bytes) { - var i, _i, _results; - _results = []; - for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { - _results.push(this.data[this.pos++]); - } - return _results; - }; - - PNG.prototype.readUInt32 = function() { - var b1, b2, b3, b4; - b1 = this.data[this.pos++] << 24; - b2 = this.data[this.pos++] << 16; - b3 = this.data[this.pos++] << 8; - b4 = this.data[this.pos++]; - return b1 | b2 | b3 | b4; - }; - - PNG.prototype.readUInt16 = function() { - var b1, b2; - b1 = this.data[this.pos++] << 8; - b2 = this.data[this.pos++]; - return b1 | b2; - }; - - - PNG.prototype.decodePixels = function(data) { - var pixelBytes = this.pixelBitlength / 8; - var fullPixels = new Uint8Array(this.width * this.height * pixelBytes); - var pos = 0; - var _this = this; - - if (data == null) { - data = this.imgData; - } - if (data.length === 0) { - return new Uint8Array(0); - } - - data = new FlateStream(data); - data = data.getBytes(); - function pass (x0, y0, dx, dy) { - var abyte, c, col, i, left, length, p, pa, paeth, pb, pc, pixels, row, scanlineLength, upper, upperLeft, _i, _j, _k, _l, _m; - var w = Math.ceil((_this.width - x0) / dx), h = Math.ceil((_this.height - y0) / dy); - var isFull = _this.width == w && _this.height == h; - scanlineLength = pixelBytes * w; - pixels = isFull ? fullPixels : new Uint8Array(scanlineLength * h); - length = data.length; - row = 0; - c = 0; - while (row < h && pos < length) { - switch (data[pos++]) { - case 0: - for (i = _i = 0; _i < scanlineLength; i = _i += 1) { - pixels[c++] = data[pos++]; - } - break; - case 1: - for (i = _j = 0; _j < scanlineLength; i = _j += 1) { - abyte = data[pos++]; - left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; - pixels[c++] = (abyte + left) % 256; - } - break; - case 2: - for (i = _k = 0; _k < scanlineLength; i = _k += 1) { - abyte = data[pos++]; - col = (i - (i % pixelBytes)) / pixelBytes; - upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; - pixels[c++] = (upper + abyte) % 256; - } - break; - case 3: - for (i = _l = 0; _l < scanlineLength; i = _l += 1) { - abyte = data[pos++]; - col = (i - (i % pixelBytes)) / pixelBytes; - left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; - upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; - pixels[c++] = (abyte + Math.floor((left + upper) / 2)) % 256; - } - break; - case 4: - for (i = _m = 0; _m < scanlineLength; i = _m += 1) { - abyte = data[pos++]; - col = (i - (i % pixelBytes)) / pixelBytes; - left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; - if (row === 0) { - upper = upperLeft = 0; - } else { - upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; - upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i % pixelBytes)]; - } - p = left + upper - upperLeft; - pa = Math.abs(p - left); - pb = Math.abs(p - upper); - pc = Math.abs(p - upperLeft); - if (pa <= pb && pa <= pc) { - paeth = left; - } else if (pb <= pc) { - paeth = upper; - } else { - paeth = upperLeft; - } - pixels[c++] = (abyte + paeth) % 256; - } - break; - default: - throw new Error("Invalid filter algorithm: " + data[pos - 1]); - } - if (!isFull) { - var fullPos = ((y0 + row * dy) * _this.width + x0) * pixelBytes; - var partPos = row * scanlineLength; - for (i = 0; i < w; i += 1) { - for (var j = 0; j < pixelBytes; j += 1) - fullPixels[fullPos++] = pixels[partPos++]; - fullPos += (dx - 1) * pixelBytes; - } - } - row++; - } - } - if (_this.interlaceMethod == 1) { - /* - 1 6 4 6 2 6 4 6 - 7 7 7 7 7 7 7 7 - 5 6 5 6 5 6 5 6 - 7 7 7 7 7 7 7 7 - 3 6 4 6 3 6 4 6 - 7 7 7 7 7 7 7 7 - 5 6 5 6 5 6 5 6 - 7 7 7 7 7 7 7 7 - */ - pass(0, 0, 8, 8); // 1 - /* NOTE these seem to follow the pattern: - * pass(x, 0, 2*x, 2*x); - * pass(0, x, x, 2*x); - * with x being 4, 2, 1. - */ - pass(4, 0, 8, 8); // 2 - pass(0, 4, 4, 8); // 3 - - pass(2, 0, 4, 4); // 4 - pass(0, 2, 2, 4); // 5 - - pass(1, 0, 2, 2); // 6 - pass(0, 1, 1, 2); // 7 - } else { - pass(0, 0, 1, 1); - } - return fullPixels; - }; - - PNG.prototype.decodePalette = function() { - var c, i, length, palette, pos, ret, transparency, _i, _ref, _ref1; - palette = this.palette; - transparency = this.transparency.indexed || []; - ret = new Uint8Array((transparency.length || 0) + palette.length); - pos = 0; - length = palette.length; - c = 0; - for (i = _i = 0, _ref = palette.length; _i < _ref; i = _i += 3) { - ret[pos++] = palette[i]; - ret[pos++] = palette[i + 1]; - ret[pos++] = palette[i + 2]; - ret[pos++] = (_ref1 = transparency[c++]) != null ? _ref1 : 255; - } - return ret; - }; - - PNG.prototype.copyToImageData = function(imageData, pixels) { - var alpha, colors, data, i, input, j, k, length, palette, v, _ref; - colors = this.colors; - palette = null; - alpha = this.hasAlphaChannel; - if (this.palette.length) { - palette = (_ref = this._decodedPalette) != null ? _ref : this._decodedPalette = this.decodePalette(); - colors = 4; - alpha = true; - } - data = imageData.data || imageData; - length = data.length; - input = palette || pixels; - i = j = 0; - if (colors === 1) { - while (i < length) { - k = palette ? pixels[i / 4] * 4 : j; - v = input[k++]; - data[i++] = v; - data[i++] = v; - data[i++] = v; - data[i++] = alpha ? input[k++] : 255; - j = k; - } - } else { - while (i < length) { - k = palette ? pixels[i / 4] * 4 : j; - data[i++] = input[k++]; - data[i++] = input[k++]; - data[i++] = input[k++]; - data[i++] = alpha ? input[k++] : 255; - j = k; - } - } - }; - - PNG.prototype.decode = function() { - var ret; - ret = new Uint8Array(this.width * this.height * 4); - this.copyToImageData(ret, this.decodePixels()); - return ret; - }; - - try { - scratchCanvas = global.document.createElement('canvas'); - scratchCtx = scratchCanvas.getContext('2d'); - } catch(e) { - return -1; - } - - makeImage = function(imageData) { - var img; - scratchCtx.width = imageData.width; - scratchCtx.height = imageData.height; - scratchCtx.clearRect(0, 0, imageData.width, imageData.height); - scratchCtx.putImageData(imageData, 0, 0); - img = new Image; - img.src = scratchCanvas.toDataURL(); - return img; - }; - - PNG.prototype.decodeFrames = function(ctx) { - var frame, i, imageData, pixels, _i, _len, _ref, _results; - if (!this.animation) { - return; - } - _ref = this.animation.frames; - _results = []; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - frame = _ref[i]; - imageData = ctx.createImageData(frame.width, frame.height); - pixels = this.decodePixels(new Uint8Array(frame.data)); - this.copyToImageData(imageData, pixels); - frame.imageData = imageData; - _results.push(frame.image = makeImage(imageData)); - } - return _results; - }; - - PNG.prototype.renderFrame = function(ctx, number) { - var frame, frames, prev; - frames = this.animation.frames; - frame = frames[number]; - prev = frames[number - 1]; - if (number === 0) { - ctx.clearRect(0, 0, this.width, this.height); - } - if ((prev != null ? prev.disposeOp : void 0) === APNG_DISPOSE_OP_BACKGROUND) { - ctx.clearRect(prev.xOffset, prev.yOffset, prev.width, prev.height); - } else if ((prev != null ? prev.disposeOp : void 0) === APNG_DISPOSE_OP_PREVIOUS) { - ctx.putImageData(prev.imageData, prev.xOffset, prev.yOffset); - } - if (frame.blendOp === APNG_BLEND_OP_SOURCE) { - ctx.clearRect(frame.xOffset, frame.yOffset, frame.width, frame.height); - } - return ctx.drawImage(frame.image, frame.xOffset, frame.yOffset); - }; - - PNG.prototype.animate = function(ctx) { - var doFrame, frameNumber, frames, numFrames, numPlays, _ref, - _this = this; - frameNumber = 0; - _ref = this.animation, numFrames = _ref.numFrames, frames = _ref.frames, numPlays = _ref.numPlays; - return (doFrame = function() { - var f, frame; - f = frameNumber++ % numFrames; - frame = frames[f]; - _this.renderFrame(ctx, f); - if (numFrames > 1 && frameNumber / numFrames < numPlays) { - return _this.animation._timeout = setTimeout(doFrame, frame.delay); - } - })(); - }; - - PNG.prototype.stopAnimation = function() { - var _ref; - return clearTimeout((_ref = this.animation) != null ? _ref._timeout : void 0); - }; - - PNG.prototype.render = function(canvas) { - var ctx, data; - if (canvas._png) { - canvas._png.stopAnimation(); - } - canvas._png = this; - canvas.width = this.width; - canvas.height = this.height; - ctx = canvas.getContext("2d"); - if (this.animation) { - this.decodeFrames(ctx); - return this.animate(ctx); - } else { - data = ctx.createImageData(this.width, this.height); - this.copyToImageData(data, this.decodePixels()); - return ctx.putImageData(data, 0, 0); - } - }; - - return PNG; - - })(); - - global.PNG = PNG; - - }(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 - - /* - * Extracted from pdf.js - * https://github.com/andreasgal/pdf.js - * - * Copyright (c) 2011 Mozilla Foundation - * - * Contributors: Andreas Gal <gal@mozilla.com> - * Chris G Jones <cjones@mozilla.com> - * Shaon Barman <shaon.barman@gmail.com> - * Vivien Nicolas <21@vingtetun.org> - * Justin D'Arcangelo <justindarc@gmail.com> - * Yury Delendik - * - * - */ - - var DecodeStream = (function() { - function constructor() { - this.pos = 0; - this.bufferLength = 0; - this.eof = false; - this.buffer = null; - } - - constructor.prototype = { - ensureBuffer: function decodestream_ensureBuffer(requested) { - var buffer = this.buffer; - var current = buffer ? buffer.byteLength : 0; - if (requested < current) - return buffer; - var size = 512; - while (size < requested) - size <<= 1; - var buffer2 = new Uint8Array(size); - for (var i = 0; i < current; ++i) - buffer2[i] = buffer[i]; - return this.buffer = buffer2; - }, - getByte: function decodestream_getByte() { - var pos = this.pos; - while (this.bufferLength <= pos) { - if (this.eof) - return null; - this.readBlock(); - } - return this.buffer[this.pos++]; - }, - getBytes: function decodestream_getBytes(length) { - var pos = this.pos; - - if (length) { - this.ensureBuffer(pos + length); - var end = pos + length; - - while (!this.eof && this.bufferLength < end) - this.readBlock(); - - var bufEnd = this.bufferLength; - if (end > bufEnd) - end = bufEnd; - } else { - while (!this.eof) - this.readBlock(); - - var end = this.bufferLength; - } - - this.pos = end; - return this.buffer.subarray(pos, end); - }, - lookChar: function decodestream_lookChar() { - var pos = this.pos; - while (this.bufferLength <= pos) { - if (this.eof) - return null; - this.readBlock(); - } - return String.fromCharCode(this.buffer[this.pos]); - }, - getChar: function decodestream_getChar() { - var pos = this.pos; - while (this.bufferLength <= pos) { - if (this.eof) - return null; - this.readBlock(); - } - return String.fromCharCode(this.buffer[this.pos++]); - }, - makeSubStream: function decodestream_makeSubstream(start, length, dict) { - var end = start + length; - while (this.bufferLength <= end && !this.eof) - this.readBlock(); - return new Stream(this.buffer, start, length, dict); - }, - skip: function decodestream_skip(n) { - if (!n) - n = 1; - this.pos += n; - }, - reset: function decodestream_reset() { - this.pos = 0; - } - }; - - return constructor; - })(); - - var FlateStream = (function() { - if (typeof Uint32Array === 'undefined') { - return undefined; - } - var codeLenCodeMap = new Uint32Array([ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 - ]); - - var lengthDecode = new Uint32Array([ - 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, - 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, - 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073, - 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102 - ]); - - var distDecode = new Uint32Array([ - 0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, - 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, - 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01, - 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001 - ]); - - var fixedLitCodeTab = [new Uint32Array([ - 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, - 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, - 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0, - 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0, - 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8, - 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8, - 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8, - 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8, - 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4, - 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4, - 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4, - 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4, - 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc, - 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec, - 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc, - 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc, - 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2, - 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2, - 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2, - 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2, - 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca, - 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea, - 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da, - 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa, - 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6, - 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6, - 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6, - 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6, - 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce, - 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee, - 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de, - 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe, - 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1, - 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1, - 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1, - 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1, - 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9, - 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9, - 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9, - 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9, - 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5, - 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5, - 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5, - 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5, - 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd, - 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed, - 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd, - 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd, - 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3, - 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3, - 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3, - 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3, - 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb, - 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb, - 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db, - 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb, - 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7, - 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7, - 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7, - 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7, - 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf, - 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef, - 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df, - 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff - ]), 9]; - - var fixedDistCodeTab = [new Uint32Array([ - 0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, - 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, - 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d, - 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000 - ]), 5]; - - function error(e) { - throw new Error(e) - } - - function constructor(bytes) { - //var bytes = stream.getBytes(); - var bytesPos = 0; - - var cmf = bytes[bytesPos++]; - var flg = bytes[bytesPos++]; - if (cmf == -1 || flg == -1) - error('Invalid header in flate stream'); - if ((cmf & 0x0f) != 0x08) - error('Unknown compression method in flate stream'); - if ((((cmf << 8) + flg) % 31) != 0) - error('Bad FCHECK in flate stream'); - if (flg & 0x20) - error('FDICT bit set in flate stream'); - - this.bytes = bytes; - this.bytesPos = bytesPos; - - this.codeSize = 0; - this.codeBuf = 0; - - DecodeStream.call(this); - } - - constructor.prototype = Object.create(DecodeStream.prototype); - - constructor.prototype.getBits = function(bits) { - var codeSize = this.codeSize; - var codeBuf = this.codeBuf; - var bytes = this.bytes; - var bytesPos = this.bytesPos; - - var b; - while (codeSize < bits) { - if (typeof (b = bytes[bytesPos++]) == 'undefined') - error('Bad encoding in flate stream'); - codeBuf |= b << codeSize; - codeSize += 8; - } - b = codeBuf & ((1 << bits) - 1); - this.codeBuf = codeBuf >> bits; - this.codeSize = codeSize -= bits; - this.bytesPos = bytesPos; - return b; - }; - - constructor.prototype.getCode = function(table) { - var codes = table[0]; - var maxLen = table[1]; - var codeSize = this.codeSize; - var codeBuf = this.codeBuf; - var bytes = this.bytes; - var bytesPos = this.bytesPos; - - while (codeSize < maxLen) { - var b; - if (typeof (b = bytes[bytesPos++]) == 'undefined') - error('Bad encoding in flate stream'); - codeBuf |= (b << codeSize); - codeSize += 8; - } - var code = codes[codeBuf & ((1 << maxLen) - 1)]; - var codeLen = code >> 16; - var codeVal = code & 0xffff; - if (codeSize == 0 || codeSize < codeLen || codeLen == 0) - error('Bad encoding in flate stream'); - this.codeBuf = (codeBuf >> codeLen); - this.codeSize = (codeSize - codeLen); - this.bytesPos = bytesPos; - return codeVal; - }; - - constructor.prototype.generateHuffmanTable = function(lengths) { - var n = lengths.length; - - // find max code length - var maxLen = 0; - for (var i = 0; i < n; ++i) { - if (lengths[i] > maxLen) - maxLen = lengths[i]; - } - - // build the table - var size = 1 << maxLen; - var codes = new Uint32Array(size); - for (var len = 1, code = 0, skip = 2; - len <= maxLen; - ++len, code <<= 1, skip <<= 1) { - for (var val = 0; val < n; ++val) { - if (lengths[val] == len) { - // bit-reverse the code - var code2 = 0; - var t = code; - for (var i = 0; i < len; ++i) { - code2 = (code2 << 1) | (t & 1); - t >>= 1; - } - - // fill the table entries - for (var i = code2; i < size; i += skip) - codes[i] = (len << 16) | val; - - ++code; - } - } - } - - return [codes, maxLen]; - }; - - constructor.prototype.readBlock = function() { - function repeat(stream, array, len, offset, what) { - var repeat = stream.getBits(len) + offset; - while (repeat-- > 0) - array[i++] = what; - } - - // read block header - var hdr = this.getBits(3); - if (hdr & 1) - this.eof = true; - hdr >>= 1; - - if (hdr == 0) { // uncompressed block - var bytes = this.bytes; - var bytesPos = this.bytesPos; - var b; - - if (typeof (b = bytes[bytesPos++]) == 'undefined') - error('Bad block header in flate stream'); - var blockLen = b; - if (typeof (b = bytes[bytesPos++]) == 'undefined') - error('Bad block header in flate stream'); - blockLen |= (b << 8); - if (typeof (b = bytes[bytesPos++]) == 'undefined') - error('Bad block header in flate stream'); - var check = b; - if (typeof (b = bytes[bytesPos++]) == 'undefined') - error('Bad block header in flate stream'); - check |= (b << 8); - if (check != (~blockLen & 0xffff)) - error('Bad uncompressed block length in flate stream'); - - this.codeBuf = 0; - this.codeSize = 0; - - var bufferLength = this.bufferLength; - var buffer = this.ensureBuffer(bufferLength + blockLen); - var end = bufferLength + blockLen; - this.bufferLength = end; - for (var n = bufferLength; n < end; ++n) { - if (typeof (b = bytes[bytesPos++]) == 'undefined') { - this.eof = true; - break; - } - buffer[n] = b; - } - this.bytesPos = bytesPos; - return; - } - - var litCodeTable; - var distCodeTable; - if (hdr == 1) { // compressed block, fixed codes - litCodeTable = fixedLitCodeTab; - distCodeTable = fixedDistCodeTab; - } else if (hdr == 2) { // compressed block, dynamic codes - var numLitCodes = this.getBits(5) + 257; - var numDistCodes = this.getBits(5) + 1; - var numCodeLenCodes = this.getBits(4) + 4; - - // build the code lengths code table - var codeLenCodeLengths = Array(codeLenCodeMap.length); - var i = 0; - while (i < numCodeLenCodes) - codeLenCodeLengths[codeLenCodeMap[i++]] = this.getBits(3); - var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths); - - // build the literal and distance code tables - var len = 0; - var i = 0; - var codes = numLitCodes + numDistCodes; - var codeLengths = new Array(codes); - while (i < codes) { - var code = this.getCode(codeLenCodeTab); - if (code == 16) { - repeat(this, codeLengths, 2, 3, len); - } else if (code == 17) { - repeat(this, codeLengths, 3, 3, len = 0); - } else if (code == 18) { - repeat(this, codeLengths, 7, 11, len = 0); - } else { - codeLengths[i++] = len = code; - } - } - - litCodeTable = - this.generateHuffmanTable(codeLengths.slice(0, numLitCodes)); - distCodeTable = - this.generateHuffmanTable(codeLengths.slice(numLitCodes, codes)); - } else { - error('Unknown block type in flate stream'); - } - - var buffer = this.buffer; - var limit = buffer ? buffer.length : 0; - var pos = this.bufferLength; - while (true) { - var code1 = this.getCode(litCodeTable); - if (code1 < 256) { - if (pos + 1 >= limit) { - buffer = this.ensureBuffer(pos + 1); - limit = buffer.length; - } - buffer[pos++] = code1; - continue; - } - if (code1 == 256) { - this.bufferLength = pos; - return; - } - code1 -= 257; - code1 = lengthDecode[code1]; - var code2 = code1 >> 16; - if (code2 > 0) - code2 = this.getBits(code2); - var len = (code1 & 0xffff) + code2; - code1 = this.getCode(distCodeTable); - code1 = distDecode[code1]; - code2 = code1 >> 16; - if (code2 > 0) - code2 = this.getBits(code2); - var dist = (code1 & 0xffff) + code2; - if (pos + len >= limit) { - buffer = this.ensureBuffer(pos + len); - limit = buffer.length; - } - for (var k = 0; k < len; ++k, ++pos) - buffer[pos] = buffer[pos - dist]; - } - }; - - return constructor; - })(); - - /** - * JavaScript Polyfill functions for jsPDF - * Collected from public resources by - * https://github.com/diegocr - */ - - (function (global) { - - if (typeof global.console !== "object") { - // Console-polyfill. MIT license. - // https://github.com/paulmillr/console-polyfill - // Make it safe to do console.log() always. - global.console = {}; - - var con = global.console; - var prop, method; - var dummy = function() {}; - var properties = ['memory']; - var methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' + - 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,' + - 'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn').split(','); - while (prop = properties.pop()) if (!con[prop]) con[prop] = {}; - while (method = methods.pop()) if (!con[method]) con[method] = dummy; - } - - var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - - if (typeof global.btoa === 'undefined') { - global.btoa = function(data) { - // discuss at: http://phpjs.org/functions/base64_encode/ - // original by: Tyler Akins (http://rumkin.com) - // improved by: Bayron Guevara - // improved by: Thunder.m - // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // improved by: Rafal Kukawski (http://kukawski.pl) - // bugfixed by: Pellentesque Malesuada - // example 1: base64_encode('Kevin van Zonneveld'); - // returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' - - var o1,o2,o3,h1,h2,h3,h4,bits,i = 0,ac = 0,enc = '',tmp_arr = []; - - if (!data) { - return data; - } - - do { // pack three octets into four hexets - o1 = data.charCodeAt(i++); - o2 = data.charCodeAt(i++); - o3 = data.charCodeAt(i++); - - bits = o1 << 16 | o2 << 8 | o3; - - h1 = bits >> 18 & 0x3f; - h2 = bits >> 12 & 0x3f; - h3 = bits >> 6 & 0x3f; - h4 = bits & 0x3f; - - // use hexets to index into b64, and append result to encoded string - tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); - } while (i < data.length); - - enc = tmp_arr.join(''); - - var r = data.length % 3; - - return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); - }; - } - - if (typeof global.atob === 'undefined') { - global.atob = function(data) { - // discuss at: http://phpjs.org/functions/base64_decode/ - // original by: Tyler Akins (http://rumkin.com) - // improved by: Thunder.m - // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // input by: Aman Gupta - // input by: Brett Zamir (http://brett-zamir.me) - // bugfixed by: Onno Marsman - // bugfixed by: Pellentesque Malesuada - // bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); - // returns 1: 'Kevin van Zonneveld' - - var o1,o2,o3,h1,h2,h3,h4,bits,i = 0,ac = 0,dec = '',tmp_arr = []; - - if (!data) { - return data; - } - - data += ''; - - do { // unpack four hexets into three octets using index points in b64 - h1 = b64.indexOf(data.charAt(i++)); - h2 = b64.indexOf(data.charAt(i++)); - h3 = b64.indexOf(data.charAt(i++)); - h4 = b64.indexOf(data.charAt(i++)); - - bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; - - o1 = bits >> 16 & 0xff; - o2 = bits >> 8 & 0xff; - o3 = bits & 0xff; - - if (h3 == 64) { - tmp_arr[ac++] = String.fromCharCode(o1); - } else if (h4 == 64) { - tmp_arr[ac++] = String.fromCharCode(o1, o2); - } else { - tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); - } - } while (i < data.length); - - dec = tmp_arr.join(''); - - return dec; - }; - } - - if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisArg */) { - if (this === void 0 || this === null || typeof fun !== "function") - throw new TypeError(); - - var t = Object(this), len = t.length >>> 0, res = new Array(len); - var thisArg = arguments.length > 1 ? arguments[1] : void 0; - for (var i = 0; i < len; i++) { - // NOTE: Absolute correctness would demand Object.defineProperty - // be used. But this method is fairly new, and failure is - // possible only if Object.prototype or Array.prototype - // has a property |i| (very unlikely), so use a less-correct - // but more portable alternative. - if (i in t) - res[i] = fun.call(thisArg, t[i], i, t); - } - - return res; - }; - } - - - if(!Array.isArray) { - Array.isArray = function(arg) { - return Object.prototype.toString.call(arg) === '[object Array]'; - }; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = function(fun, thisArg) { - - if (this === void 0 || this === null || typeof fun !== "function") - throw new TypeError(); - - var t = Object(this), len = t.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in t) - fun.call(thisArg, t[i], i, t); - } - }; - } - - if (!Object.keys) { - Object.keys = (function () { - - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = ['toString','toLocaleString','valueOf','hasOwnProperty', - 'isPrototypeOf','propertyIsEnumerable','constructor'], - dontEnumsLength = dontEnums.length; - - return function (obj) { - if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { - throw new TypeError(); - } - var result = [], prop, i; - - for (prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - - if (hasDontEnumBug) { - for (i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - return result; - }; - }()); - } - - if (typeof Object.assign != 'function') { - Object.assign = function(target) { - if (target == null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - - target = Object(target); - for (var index = 1; index < arguments.length; index++) { - var source = arguments[index]; - if (source != null) { - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - } - return target; - }; - } - - if (!String.prototype.trim) { - String.prototype.trim = function () { - return this.replace(/^\s+|\s+$/g, ''); - }; - } - if (!String.prototype.trimLeft) { - String.prototype.trimLeft = function() { - return this.replace(/^\s+/g, ""); - }; - } - if (!String.prototype.trimRight) { - String.prototype.trimRight = function() { - return this.replace(/\s+$/g, ""); - }; - } - - - }(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 - - return jsPDF; - -}))); diff --git a/thirdpartylibs.xml b/thirdpartylibs.xml index 2a687fde90a8a8d7bb17bb990f54a7775890d19a..2a0accfc9b565c6d430f3e98a73d8cdee2e3c26a 100644 --- a/thirdpartylibs.xml +++ b/thirdpartylibs.xml @@ -36,5 +36,12 @@ <license>MIT</license> <licenseversion></licenseversion> </library> + <library> + <location>amd/src/jspdf.js</location> + <name>jsPDF</name> + <version>1.4.1</version> + <license>MIT</license> + <licenseversion></licenseversion> + </library> </libraries>