vendor: 5,244 bytes, lines 1-227
1(function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 typeof define === 'function' && define.amd ? define(factory) : 4 (global.jspdf = factory()); 5}(this, (function () { 'use strict'; 6 7var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 8 return typeof obj; 9} : function (obj) { 10 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 11}; 12 13 14 15 16 17var asyncGenerator = function () { 18 function AwaitValue(value) { 19 this.value = value; 20 } 21 22 function AsyncGenerator(gen) { 23 var front, back; 24 25 function send(key, arg) { 26 return new Promise(function (resolve, reject) { 27 var request = { 28 key: key, 29 arg: arg, 30 resolve: resolve, 31 reject: reject, 32 next: null 33 }; 34 35 if (back) { 36 back = back.next = request; 37 } else { 38 front = back = request; 39 resume(key, arg); 40 } 41 }); 42 } 43 44 function resume(key, arg) { 45 try { 46 var result = gen[key](arg); 47 var value = result.value; 48 49 if (value instanceof AwaitValue) { 50 Promise.resolve(value.value).then(function (arg) { 51 resume("next", arg); 52 }, function (arg) { 53 resume("throw", arg); 54 }); 55 } else { 56 settle(result.done ? "return" : "normal", result.value); 57 } 58 } catch (err) { 59 settle("throw", err); 60 } 61 } 62 63 function settle(type, value) { 64 switch (type) { 65 case "return": 66 front.resolve({ 67 value: value, 68 done: true 69 }); 70 break; 71 72 case "throw": 73 front.reject(value); 74 break; 75 76 default: 77 front.resolve({ 78 value: value, 79 done: false 80 }); 81 break; 82 } 83 84 front = front.next; 85 86 if (front) { 87 resume(front.key, front.arg); 88 } else { 89 back = null; 90 } 91 } 92 93 this._invoke = send; 94 95 if (typeof gen.return !== "function") { 96 this.return = undefined; 97 } 98 } 99 100 if (typeof Symbol === "function" && Symbol.asyncIterator) { 101 AsyncGenerator.prototype[Symbol.asyncIterator] = function () { 102 return this; 103 }; 104 } 105 106 AsyncGenerator.prototype.next = function (arg) { 107 return this._invoke("next", arg); 108 }; 109 110 AsyncGenerator.prototype.throw = function (arg) { 111 return this._invoke("throw", arg); 112 }; 113 114 AsyncGenerator.prototype.return = function (arg) { 115 return this._invoke("return", arg); 116 }; 117 118 return { 119 wrap: function (fn) { 120 return function () { 121 return new AsyncGenerator(fn.apply(this, arguments)); 122 }; 123 }, 124 await: function (value) { 125 return new AwaitValue(value); 126 } 127 }; 128}(); 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144var get$1 = function get$1(object, property, receiver) { 145 if (object === null) object = Function.prototype; 146 var desc = Object.getOwnPropertyDescriptor(object, property); 147 148 if (desc === undefined) { 149 var parent = Object.getPrototypeOf(object); 150 151 if (parent === null) { 152 return undefined; 153 } else { 154 return get$1(parent, property, receiver); 155 } 156 } else if ("value" in desc) { 157 return desc.value; 158 } else { 159 var getter = desc.get; 160 161 if (getter === undefined) { 162 return undefined; 163 } 164 165 return getter.call(receiver); 166 } 167}; 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185var set$1 = function set$1(object, property, value, receiver) { 186 var desc = Object.getOwnPropertyDescriptor(object, property); 187 188 if (desc === undefined) { 189 var parent = Object.getPrototypeOf(object); 190 191 if (parent !== null) { 192 set$1(parent, property, value, receiver); 193 } 194 } else if ("value" in desc && desc.writable) { 195 desc.value = value; 196 } else { 197 var setter = desc.set; 198 199 if (setter !== undefined) { 200 setter.call(receiver, value); 201 } 202 } 203 204 return value; 205}; 206 207/** @preserve 208 * jsPDF - PDF Document creation from JavaScript 209 * Version 1.3.3 Built on 2017-02-23T15:31:28.692Z 210 * CommitID c2fa0d3c14 211 * 212 * Copyright (c) 2010-2016 James Hall <[email protected]>, https://github.com/MrRio/jsPDF 213 * 2010 Aaron Spike, https://github.com/acspike 214 * 2012 Willow Systems Corporation, willow-systems.com 215 * 2012 Pablo Hess, https://github.com/pablohess 216 * 2012 Florian Jenett, https://github.com/fjenett 217 * 2013 Warren Weckesser, https://github.com/warrenweckesser 218 * 2013 Youssef Beddad, https://github.com/lifof 219 * 2013 Lee Driscoll, https://github.com/lsdriscoll 220 * 2013 Stefan Slonevskiy, https://github.com/stefslon 221 * 2013 Jeremy Morel, https://github.com/jmorel 222 * 2013 Christoph Hartmann, https://github.com/chris-rock 223 * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria 224 * 2014 James Makes, https://github.com/dollaruw 225 * 2014 Diego Casorran, https://github.com/diegocr 226 * 2014 Steven Spungin, https://github.com/Flamenco 227 * 2014 Kenneth Glassey, https://github.com/Gavver
227s 228 * 229 * Licensed under the MIT License 230 * 231 * Contributor(s): 232 * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, 233 * kim3er, mfo, alnorth, Flamenco 234 */ 235 236/** 237 * Creates new jsPDF document object instance. 238 * 239 * @class 240 * @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l") 241 * @param unit Measurement unit to be used when coordinates are specified. 242 * One of "pt" (points), "mm" (Default), "cm", "in" 243 * @param format One of 'pageFormats' as shown below, default: a4 244 * @returns {jsPDF} 245 * @name jsPDF 246 */ 247var jsPDF = function (global) { 248 'use strict'; 249 250 var pdfVersion = '1.3', 251 pageFormats = { // Size in pt of various paper formats 252 'a0': [2383.94, 3370.39], 253 'a1': [1683.78, 2383.94], 254 'a2': [1190.55, 1683.78], 255 'a3': [841.89, 1190.55], 256 'a4': [595.28, 841.89], 257 'a5': [419.53, 595.28], 258 'a6': [297.64, 419.53], 259 'a7': [209.76, 297.64], 260 'a8': [147.40, 209.76], 261 'a9': [104.88, 147.40], 262 'a10': [73.70, 104.88], 263 'b0': [2834.65, 4008.19], 264 'b1': [2004.09, 2834.65], 265 'b2': [1417.32, 2004.09], 266 'b3': [1000.63, 1417.32], 267 'b4': [708.66, 1000.63], 268 'b5': [498.90, 708.66], 269 'b6': [354.33, 498.90], 270 'b7': [249.45, 354.33], 271 'b8': [175.75, 249.45], 272 'b9': [124.72, 175.75], 273 'b10': [87.87, 124.72], 274 'c0': [2599.37, 3676.54], 275 'c1': [1836.85, 2599.37], 276 'c2': [1298.27, 1836.85], 277 'c3': [918.43, 1298.27], 278 'c4': [649.13, 918.43], 279 'c5': [459.21, 649.13], 280 'c6': [323.15, 459.21], 281 'c7': [229.61, 323.15], 282 'c8': [161.57, 229.61], 283 'c9': [113.39, 161.57], 284 'c10': [79.37, 113.39], 285 'dl': [311.81, 623.62], 286 'letter': [612, 792], 287 'government-letter': [576, 756], 288 'legal': [612, 1008], 289 'junior-legal': [576, 360], 290 'ledger': [1224, 792], 291 'tabloid': [792, 1224], 292 'credit-card': [153, 243] 293 }; 294 295 /** 296 * jsPDF's Internal PubSub Implementation. 297 * See mrrio.github.io/jsPDF/doc/symbols/PubSub.html 298 * Backward compatible rewritten on 2014 by 299 * Diego Casorran, https://github.com/diegocr 300 * 301 * @class 302 * @name PubSub 303 * @ignore This should not be in the public docs. 304 */ 305 function PubSub(context) { 306 var topics = {}; 307 308 this.subscribe = function (topic, callback, once) { 309 if (typeof callback !== 'function') { 310 return false; 311 } 312 313 if (!topics.hasOwnProperty(topic)) { 314 topics[topic] = {}; 315 } 316 317 var id = Math.random().toString(35); 318 topics[topic][id] = [callback, !!once]; 319 320 return id; 321 }; 322 323 this.unsubscribe = function (token) { 324 for (var topic in topics) { 325 if (topics[topic][token]) { 326 delete topics[topic][token]; 327 return true; 328 } 329 } 330 return false; 331 }; 332 333 this.publish = function (topic) { 334 if (topics.hasOwnProperty(topic)) { 335 var args = Array.prototype.slice.call(arguments, 1), 336 idr = []; 337 338 for (var id in topics[topic]) { 339 var sub = topics[topic][id]; 340 try { 341 sub[0].apply(context, args); 342 } catch (ex) { 343 if (global.console) { 344 console.error('jsPDF PubSub Error', ex.message, ex); 345 } 346 } 347 if (sub[1]) idr.push(id); 348 } 349 if (idr.length) idr.forEach(this.unsubscribe); 350 } 351 }; 352 } 353 354 /** 355 * @constructor 356 * @private 357 */ 358 function jsPDF(orientation, unit, format, compressPdf) { 359 var options = {}; 360 361 if ((typeof orientation === 'undefined' ? 'undefined' : _typeof(orientation)) === 'object') { 362 options = orientation; 363 364 orientation = options.orientation; 365 unit = options.unit || unit; 366 format = options.format || format; 367 compressPdf = options.compress || options.compressPdf || compressPdf; 368 } 369 370 // Default options 371 unit = unit || 'mm'; 372 format = format || 'a4'; 373 orientation = ('' + (orientation || 'P')).toLowerCase(); 374 375 var format_as_string = ('' + format).toLowerCase(), 376 compress = !!compressPdf && typeof Uint8Array === 'function', 377 textColor = options.textColor || '0 g', 378 drawColor = options.drawColor || '0 G', 379 activeFontSize = options.fontSize || 16, 380 lineHeightProportion = options.lineHeight || 1.15, 381 lineWidth = options.lineWidth || 0.200025, 382 // 2mm 383 objectNumber = 2, 384 // 'n' Current object number 385 outToPages = !1, 386 // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content 387 offsets = [], 388 // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes. 389 fonts = {}, 390 // collection of font objects, where key is fontKey - a dynamically created label for a given font. 391 fontmap = {}, 392 // mapping structure fontName > fontStyle > font key - performance layer. See addFont() 393 activeFontKey, 394 // will be string representing the KEY of the font as combination of fontName + fontStyle 395 k, 396 // Scale factor 397 tmp, 398 page = 0, 399 currentPage, 400 pages = [], 401 pagesContext = [], 402 // same index as pages and pagedim 403 pagedim = [], 404 content = [], 405 additionalObjects = [], 406 lineCapID = 0, 407 lineJoinID = 0, 408 content_length = 0, 409 pageWidth, 410 pageHeight, 411 pageMode, 412 zoomMode, 413 layoutMode, 414 documentProperties = { 415 'title': '', 416 'subject': '', 417 'author': '', 418 'keywords': '', 419 'creator': '' 420 }, 421 API = {}, 422 events = new PubSub(API), 423 424
425 ///////////////////// 426 // Private functions 427 ///////////////////// 428 f2 = function f2(number) { 429 return number.toFixed(2); // Ie, %.2f 430 }, 431 f3 = function f3(number) { 432 return number.toFixed(3); // Ie, %.3f 433 }, 434 padd2 = function padd2(number) { 435 return ('0' + parseInt(number)).slice(-2); 436 }, 437 out = function out(string) { 438 if (outToPages) { 439 /* set by beginPage */ 440 pages[currentPage].push(string); 441 } else { 442 // +1 for '\n' that will be used to join 'content' 443 content_length += string.length + 1; 444 content.push(string); 445 } 446 }, 447 newObject = function newObject() { 448 // Begin a new object 449 objectNumber++; 450 offsets[objectNumber] = content_length; 451 out(objectNumber + ' 0 obj'); 452 return objectNumber; 453 }, 454 455 // Does not output the object until after the pages have been output. 456 // Returns an object containing the objectId and content. 457 // All pages have been added so the object ID can be estimated to start right after. 458 // This does not modify the current objectNumber; It must be updated after the newObjects are output. 459 newAdditionalObject = function newAdditionalObject() { 460 var objId = pages.length * 2 + 1; 461 objId += additionalObjects.length; 462 var obj = { 463 objId: objId, 464 content: '' 465 }; 466 additionalObjects.push(obj); 467 return obj; 468 }, 469 470 // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data 471 newObjectDeferred = function newObjectDeferred() { 472 objectNumber++; 473 offsets[objectNumber] = function () { 474 return content_length; 475 }; 476 return objectNumber; 477 }, 478 newObjectDeferredBegin = function newObjectDeferredBegin(oid) { 479 offsets[oid] = content_length; 480 }, 481 putStream = function putStream(str) { 482 out('stream'); 483 out(str); 484 out('endstream'); 485 }, 486 putPages = function putPages() { 487 var n, 488 p, 489 arr, 490 i, 491 deflater, 492 adler32, 493 adler32cs, 494 wPt, 495 hPt, 496 pageObjectNumbers = []; 497 498 adler32cs = global.adler32cs || jsPDF.adler32cs; 499 if (compress && typeof adler32cs === 'undefined') { 500 compress = false; 501 } 502 503 // outToPages = false as set in endDocument(). out() writes to content. 504 505 for (n = 1; n <= page; n++) { 506 pageObjectNumbers.push(newObject()); 507 wPt = (pageWidth = pagedim[n].width) * k; 508 hPt = (pageHeight = pagedim[n].height) * k; 509 out('<</Type /Page'); 510 out('/Parent 1 0 R'); 511 out('/Resources 2 0 R'); 512 out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']'); 513 // Added for annotation plugin 514 events.publish('putPage', { 515 pageNumber: n, 516 page: pages[n] 517 }); 518 out('/Contents ' + (objectNumber + 1) + ' 0 R'); 519 out('>>'); 520 out('endobj'); 521 522 // Page content 523 p = pages[n].join('\n'); 524 newObject(); 525 if (compress) { 526 arr = []; 527 i = p.length; 528 while (i--) { 529 arr[i] = p.charCodeAt(i); 530 } 531 adler32 = adler32cs.from(p); 532 deflater = new Deflater(6); 533 deflater.append(new Uint8Array(arr)); 534 p = deflater.flush(); 535 arr = new Uint8Array(p.length + 6); 536 arr.set(new Uint8Array([120, 156])), arr.set(p, 2); 537 arr.set(new Uint8Array([adler32 & 0xFF, adler32 >> 8 & 0xFF, adler32 >> 16 & 0xFF, adler32 >> 24 & 0xFF]), p.length + 2); 538 p = String.fromCharCode.apply(null, arr); 539 out('<</Length ' + p.length + ' /Filter [/FlateDecode]>>'); 540 } else { 541 out('<</Length ' + p.length + '>>'); 542 } 543 putStream(p); 544 out('endobj'); 545 } 546 offsets[1] = content_length; 547 out('1 0 obj'); 548 out('<</Type /Pages'); 549 var kids = '/Kids ['; 550 for (i = 0; i < page; i++) { 551 kids += pageObjectNumbers[i] + ' 0 R '; 552 } 553 out(kids + ']'); 554 out('/Count ' + page); 555 out('>>'); 556 out('endobj'); 557 events.publish('postPutPages'); 558 }, 559 putFont = function putFont(font) { 560 font.objectNumber = newObject(); 561 out('<</BaseFont/' + font.PostScriptName + '/Type/Font'); 562 if (typeof font.encoding === 'string') { 563 out('/Encoding/' + font.encoding); 564 } 565 out('/Subtype/Type1>>'); 566 out('endobj'); 567 }, 568 putFonts = function putFonts() { 569 for (var fontKey in fonts) { 570 if (fonts.hasOwnProperty(fontKey)) { 571 putFont(fonts[fontKey]); 572 } 573 } 574 }, 575 putXobjectDict = function putXobjectDict() { 576 // Loop through images, or other data objects 577 events.publish('putXobjectDict'); 578 }, 579 putResourceDictionary = function putResourceDictionary() { 580 out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); 581 out('/Font <<'); 582 583 // Do this for each font, the '1' bit is the index of the font 584 for (var fontKey in fonts) { 585 if (fonts.hasOwnProperty(fontKey)) { 586 out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R'); 587 } 588 } 589 out('>>'); 590 out('/XObject <<'); 591 putXobjectDict(); 592 out('>>'); 593 }, 594 putResources = function putResources() { 595 putFonts(); 596 events.publish('putResources'); 597 // Resource dictionary 598 offsets[2] = content_length; 599 out('2 0 obj'); 600 out('<<'); 601 putResourceDictionary(); 602 out('>>'); 603 out('endobj'); 604 events.publish('postPutResources'); 605 }, 606 putAdditionalObjects = function putAdditionalObjects() { 607 events.publish('putAdditionalObjects'); 608 for (var i = 0; i < additionalObjects.length; i++) { 609 var obj = additionalObjects[i]; 610 offsets[obj.objId] = content_length; 611 out(obj.objId + ' 0 obj'); 612 out(obj.content); 613 out('endobj'); 614 } 615 objectNumber += additionalObjects.length; 616 events.publish('postPutAdditionalObjects'); 617 }, 618 addToFontDictionary = function addToFontDictionary(fontKey, fontName, fontStyle) { 619 // this is mapping structure for quick font key lookup. 620 // returns the KEY of the font (ex: "F1") for a given 621 // pair of font name and type (ex: "Arial". "Italic") 622 if (!fontmap.hasOwnProperty(fontName)) { 623 fontmap[fontName] = {}; 624 } 625 fontmap[fontName][fontStyle] = fontKey; 626 }, 627 628 /** 629 * FontObject describes a particular font as member of an instnace of jsPDF 630 * 631 * It's a collection of properties like 'id' (to be used in PDF stream), 632 * 'fontName' (font's family name), 'fontStyle' (font's style variant label) 633 * 634 * @class 635 * @public 636 * @property id {String} PDF-document-instance-specific label assinged to the font. 637 * @property PostScriptName {String} PDF specification full name for the font 638 * @property encoding {Object} Encoding_name-to-Font_metrics_object mapping. 639 * @name FontObject 640 * @ignore This should not be in the public docs. 641 */ 642 addFont = function addFont(PostScriptName, fontName, fontStyle, encoding) { 643 var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10), 644 645 // This is FontObject 646 font = fonts[fontKey] = { 647 'id': fontKey, 648 'PostScriptName': PostScriptName, 649 'fontName': fontName, 650 'fontStyle': fontStyle, 651 'encoding': encoding, 652 'metadata': {} 653 }; 654 addToFontDictionary(fontKey, fontName, fontStyle); 655 events.publish('addFont', font); 656 657 return fontKey; 658 }, 659 addFonts = function addFonts() { 660 661 var HELVETICA = "helvetica", 662 TIMES = "times", 663 COURIER = "courier", 664 NORMAL = "normal", 665 BOLD = "bold", 666 ITALIC = "italic", 667 BOLD_ITALIC = "bolditalic", 668 encoding = 'StandardEncoding', 669 ZAPF = "zapfdingbats", 670 standardFonts = [['Helvetica', HELVETICA, NORMAL], ['Helvetica-Bold', HELVETICA, BOLD], ['Helvetica-Oblique', HELVETICA, ITALIC], ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC], ['Courier', COURIER, NORMAL], ['Courier-Bold', COURIER, BOLD], ['Courier-Oblique', COURIER, ITALIC], ['Courier-BoldOblique', COURIER, BOLD_ITALIC], ['Times-Roman', TIMES, NORMAL], ['Times-Bold', TIMES, BOLD], ['Times-Italic', TIMES, ITALIC], ['Times-BoldItalic', TIMES, BOLD_ITALIC], ['ZapfDingbats', ZAPF]]; 671 672 for (var i = 0, l = standardFonts.length; i < l; i++) { 673 var fontKey = addFont(standardFonts[i][0], standardFonts[i][1], standardFonts[i][2], encoding); 674 675 // adding aliases for standard fonts, this time matching the capitalization 676 var parts = standardFonts[i][0].split('-'); 677 addToFontDictionary(fontKey, parts[0], parts[1] || ''); 678 } 679 events.publish('addFonts', { 680 fonts: fonts, 681 dictionary: fontmap 682 }); 683 }, 684 SAFE = function __safeCall(fn) { 685 fn.foo = function __safeCallWrapper() { 686 try { 687 return fn.apply(this, arguments); 688 } catch (e) { 689 var stack = e.stack || ''; 690 if (~stack.indexOf(' at ')) stack = stack.split(" at ")[1]; 691 var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message; 692 if (global.console) { 693 global.console.error(m, e); 694 if (global.alert) alert(m); 695 } else { 696 throw new Error(m); 697 } 698 } 699 }; 700 fn.foo.bar = fn; 701 return fn.foo; 702 }, 703 to8bitStream = function to8bitStream(text, flags) { 704 /** 705 * PDF 1.3 spec:
706 * "For text strings encoded in Unicode, the first two bytes must be 254 followed by 707 * 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts 708 * with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely 709 * to be a meaningful beginning of a word or phrase.) The remainder of the 710 * string consists of Unicode character codes, according to the UTF-16 encoding 711 * specified in the Unicode standard, version 2.0. Commonly used Unicode values 712 * are represented as 2 bytes per character, with the high-order byte appearing first 713 * in the string." 714 * 715 * In other words, if there are chars in a string with char code above 255, we 716 * recode the string to UCS2 BE - string doubles in length and BOM is prepended. 717 * 718 * HOWEVER! 719 * Actual *content* (body) text (as opposed to strings used in document properties etc) 720 * does NOT expect BOM. There, it is treated as a literal GID (Glyph ID) 721 * 722 * Because of Adobe's focus on "you subset your fonts!" you are not supposed to have 723 * a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could 724 * fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode 725 * code page. There, however, all characters in the stream are treated as GIDs, 726 * including BOM, which is the reason we need to skip BOM in content text (i.e. that 727 * that is tied to a font). 728 * 729 * To signal this "special" PDFEscape / to8bitStream handling mode, 730 * API.text() function sets (unless you overwrite it with manual values 731 * given to API.text(.., flags) ) 732 * flags.autoencode = true 733 * flags.noBOM = true 734 * 735 * =================================================================================== 736 * `flags` properties relied upon: 737 * .sourceEncoding = string with encoding label. 738 * "Unicode" by default. = encoding of the incoming text. 739 * pass some non-existing encoding name 740 * (ex: 'Do not touch my strings! I know what I am doing.') 741 * to make encoding code skip the encoding step. 742 * .outputEncoding = Either valid PDF encoding name 743 * (must be supported by jsPDF font metrics, otherwise no encoding) 744 * or a JS object, where key = sourceCharCode, value = outputCharCode 745 * missing keys will be treated as: sourceCharCode === outputCharCode 746 * .noBOM 747 * See comment higher above for explanation for why this is important 748 * .autoencode 749 * See comment higher above for explanation for why this is important 750 */ 751 752 var i, l, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch; 753 754 flags = flags || {}; 755 sourceEncoding = flags.sourceEncoding || 'Unicode'; 756 outputEncoding = flags.outputEncoding; 757 758 // This 'encoding' section relies on font metrics format 759 // attached to font objects by, among others, 760 // "Willow Systems' standard_font_metrics plugin" 761 // see jspdf.plugin.standard_font_metrics.js for format 762 // of the font.metadata.encoding Object. 763 // It should be something like 764 // .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}} 765 // .widths = {0:width, code:width, ..., 'fof':divisor} 766 // .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...} 767 if ((flags.autoencode || outputEncoding) && fonts[activeFontKey].metadata && fonts[activeFontKey].metadata[sourceEncoding] && fonts[activeFontKey].metadata[sourceEncoding].encoding) { 768 encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding; 769 770 // each font has default encoding. Some have it clearly defined. 771 if (!outputEncoding && fonts[activeFontKey].encoding) { 772 outputEncoding = fonts[activeFontKey].encoding; 773 } 774 775 // Hmmm, the above did not work? Let's try again, in different place. 776 if (!outputEncoding && encodingBlock.codePages) { 777 outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default 778 } 779 780 if (typeof outputEncoding === 'string') { 781 outputEncoding = encodingBlock[outputEncoding]; 782 } 783 // we want output encoding to be a JS Object, where 784 // key = sourceEncoding's character code and 785 // value = outputEncoding's character code. 786 if (outputEncoding) { 787 isUnicode = false;
788 newtext = []; 789 for (i = 0, l = text.length; i < l; i++) { 790 ch = outputEncoding[text.charCodeAt(i)]; 791 if (ch) { 792 newtext.push(String.fromCharCode(ch)); 793 } else { 794 newtext.push(text[i]); 795 } 796 797 // since we are looping over chars anyway, might as well 798 // check for residual unicodeness 799 if (newtext[i].charCodeAt(0) >> 8) { 800 /* more than 255 */ 801 isUnicode = true; 802 } 803 } 804 text = newtext.join(''); 805 } 806 } 807 808 i = text.length; 809 // isUnicode may be set to false above. Hence the triple-equal to undefined 810 while (isUnicode === undefined && i !== 0) { 811 if (text.charCodeAt(i - 1) >> 8) { 812 /* more than 255 */ 813 isUnicode = true; 814 } 815 i--; 816 } 817 if (!isUnicode) { 818 return text; 819 } 820 821 newtext = flags.noBOM ? [] : [254, 255]; 822 for (i = 0, l = text.length; i < l; i++) { 823 ch = text.charCodeAt(i); 824 bch = ch >> 8; // divide by 256 825 if (bch >> 8) { 826 /* something left after dividing by 256 second time */ 827 throw new Error("Character at position " + i + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE"); 828 } 829 newtext.push(bch); 830 newtext.push(ch - (bch << 8)); 831 } 832 return String.fromCharCode.apply(undefined, newtext); 833 }, 834 pdfEscape = function pdfEscape(text, flags) { 835 /** 836 * Replace '/', '(', and ')' with pdf-safe versions 837 * 838 * Doing to8bitStream does NOT make this PDF display unicode text. For that 839 * we also need to reference a unicode font and embed it - royal pain in the rear. 840 * 841 * There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars, 842 * which JavaScript Strings are happy to provide. So, while we still cannot display 843 * 2-byte characters property, at least CONDITIONALLY converting (entire string containing) 844 * 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF 845 * is still parseable. 846 * This will allow immediate support for unicode in document properties strings. 847 */ 848 return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); 849 }, 850 putInfo = function putInfo() { 851 out('/Producer (jsPDF ' + jsPDF.version + ')'); 852 for (var key in documentProperties) { 853 if (documentProperties.hasOwnProperty(key) && documentProperties[key]) { 854 out('/' + key.substr(0, 1).toUpperCase() + key.substr(1) + ' (' + pdfEscape(documentProperties[key]) + ')'); 855 } 856 } 857 var created = new Date(), 858 tzoffset = created.getTimezoneOffset(), 859 tzsign = tzoffset < 0 ? '+' : '-', 860 tzhour = Math.floor(Math.abs(tzoffset / 60)), 861 tzmin = Math.abs(tzoffset % 60), 862 tzstr = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join(''); 863 out(['/CreationDate (D:', created.getFullYear(), padd2(created.getMonth() + 1), padd2(created.getDate()), padd2(created.getHours()), padd2(created.getMinutes()), padd2(created.getSeconds()), tzstr, ')'].join('')); 864 }, 865 putCatalog = function putCatalog() { 866 out('/Type /Catalog'); 867 out('/Pages 1 0 R'); 868 // PDF13ref Section 7.2.1 869 if (!zoomMode) zoomMode = 'fullwidth'; 870 switch (zoomMode) { 871 case 'fullwidth': 872 out('/OpenAction [3 0 R /FitH null]'); 873 break; 874 case 'fullheight': 875 out('/OpenAction [3 0 R /FitV null]'); 876 break; 877 case 'fullpage': 878 out('/OpenAction [3 0 R /Fit]'); 879 break; 880 case 'original': 881 out('/OpenAction [3 0 R /XYZ null null 1]'); 882 break; 883 default: 884 var pcn = '' + zoomMode; 885 if (pcn.substr(pcn.length - 1) === '%') zoomMode = parseInt(zoomMode) / 100; 886 if (typeof zoomMode === 'number') { 887 out('/OpenAction [3 0 R /XYZ null null ' + f2(zoomMode) + ']'); 888 } 889 } 890 if (!layoutMode) layoutMode = 'continuous'; 891 switch (layoutMode) { 892 case 'continuous': 893 out('/PageLayout /OneColumn'); 894 break; 895 case 'single': 896 out('/PageLayout /SinglePage'); 897 break; 898 case 'two': 899 case 'twoleft': 900 out('/PageLayout /TwoColumnLeft'); 901 break; 902 case 'tworight': 903 out('/PageLayout /TwoColumnRight'); 904 break; 905 } 906 if (pageMode) { 907 /** 908 * A name object specifying how the document should be displayed when opened: 909 * UseNone : Neither document outline nor thumbnail images visible -- DEFAULT 910 * UseOutlines : Document outline visible 911 * UseThumbs : Thumbnail images visible 912 * FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible 913 */ 914 out('/PageMode /' + pageMode); 915 } 916 events.publish('putCatalog'); 917 }, 918 putTrailer = function putTrailer() { 919 out('/Size ' + (objectNumber + 1)); 920 out('/Root ' + objectNumber + ' 0 R'); 921 out('/Info ' + (objectNumber - 1) + ' 0 R'); 922 }, 923 beginPage = function beginPage(width, height) { 924 // Dimensions are stored as user units and converted to points on output 925 var orientation = typeof height === 'string' && height.toLowerCase(); 926 if (typeof width === 'string') { 927 var format = width.toLowerCase(); 928 if (pageFormats.hasOwnProperty(format)) { 929 width = pageFormats[format][0] / k; 930 height = pageFormats[format][1] / k; 931 } 932 } 933 if (Array.isArray(width)) { 934 height = width[1]; 935 width = width[0]; 936 } 937 if (orientation) { 938 switch (orientation.substr(0, 1)) { 939 case 'l': 940 if (height > width) orientation = 's'; 941 break; 942 case 'p': 943 if (width > height) orientation = 's'; 944 break; 945 } 946 if (orientation === 's') { 947 tmp = width; 948 width = height; 949 height = tmp; 950 } 951 } 952 outToPages = true; 953 pages[++page] = []; 954 pagedim[page] = { 955 width: Number(width) || pageWidth, 956 height: Number(height) || pageHeight 957 }; 958 pagesContext[page] = {}; 959 _setPage(page); 960 }, 961 _addPage = function _addPage() { 962 beginPage.apply(this, arguments); 963 // Set line width 964 out(f2(lineWidth * k) + ' w'); 965 // Set draw color 966 out(drawColor); 967 // resurrecting non-default line caps, joins 968 if (lineCapID !== 0) { 969 out(lineCapID + ' J'); 970 } 971 if (lineJoinID !== 0) { 972 out(lineJoinID + ' j'); 973 } 974 events.publish('addPage', { 975 pageNumber: page 976 }); 977 }, 978 _deletePage = function _deletePage(n) { 979 if (n > 0 && n <= page) { 980 pages.splice(n, 1); 981 pagedim.splice(n, 1); 982 page--; 983 if (currentPage > page) { 984 currentPage = page; 985 } 986 this.setPage(currentPage); 987 } 988 }, 989 _setPage = function _setPage(n) { 990 if (n > 0 && n <= page) { 991 currentPage = n; 992 pageWidth = pagedim[n].width; 993 pageHeight = pagedim[n].height; 994 } 995 }, 996 997 /** 998 * Returns a document-specific font key - a label assigned to a 999 * font name + font type combination at the time the font was added 1000 * to the font inventory. 1001 * 1002 * Font key is used as label for the desired font for a block of text 1003 * to be added to the PDF document stream. 1004 * @private 1005 * @function 1006 * @param fontName {String} can be undefined on "falthy" to indicate "use current" 1007 * @param fontStyle {String} can be undefined on "falthy" to indicate "use current" 1008 * @returns {String} Font key. 1009 */ 1010 _getFont = function _getFont(fontName, fontStyle) { 1011 var key; 1012 1013 fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName; 1014 fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle; 1015 1016 if (fontName !== undefined) { 1017 fontName = fontName.toLowerCase(); 1018 } 1019 switch (fontName) { 1020 case 'sans-serif': 1021 case 'verdana': 1022 case 'arial': 1023 case 'helvetica': 1024 fontName = 'helvetica'; 1025 break; 1026 case 'fixed': 1027 case 'monospace': 1028 case 'terminal': 1029 case 'courier': 1030 fontName = 'courier'; 1031 break; 1032 case 'serif': 1033 case 'cursive': 1034 case 'fantasy': 1035 default: 1036 fontName = 'times'; 1037 break; 1038 } 1039
1040 try { 1041 // get a string like 'F3' - the KEY corresponding tot he font + type combination. 1042 key = fontmap[fontName][fontStyle]; 1043 } catch (e) {} 1044 1045 if (!key) { 1046 //throw new Error("Unable to look up font label for font '" + fontName + "', '" 1047 //+ fontStyle + "'. Refer to getFontList() for available fonts."); 1048 key = fontmap['times'][fontStyle]; 1049 if (key == null) { 1050 key = fontmap['times']['normal']; 1051 } 1052 } 1053 return key; 1054 }, 1055 buildDocument = function buildDocument() { 1056 outToPages = false; // switches out() to content 1057 1058 objectNumber = 2; 1059 content_length = 0; 1060 content = []; 1061 offsets = []; 1062 additionalObjects = []; 1063 // Added for AcroForm 1064 events.publish('buildDocument'); 1065 1066 // putHeader() 1067 out('%PDF-' + pdfVersion); 1068 1069 putPages(); 1070 1071 // Must happen after putPages 1072 // Modifies current object Id 1073 putAdditionalObjects(); 1074 1075 putResources(); 1076 1077 // Info 1078 newObject(); 1079 out('<<'); 1080 putInfo(); 1081 out('>>'); 1082 out('endobj'); 1083 1084 // Catalog 1085 newObject(); 1086 out('<<'); 1087 putCatalog(); 1088 out('>>'); 1089 out('endobj'); 1090 1091 // Cross-ref 1092 var o = content_length, 1093 i, 1094 p = "0000000000"; 1095 out('xref'); 1096 out('0 ' + (objectNumber + 1)); 1097 out(p + ' 65535 f '); 1098 for (i = 1; i <= objectNumber; i++) { 1099 var offset = offsets[i]; 1100 if (typeof offset === 'function') { 1101 out((p + offsets[i]()).slice(-10) + ' 00000 n '); 1102 } else { 1103 out((p + offsets[i]).slice(-10) + ' 00000 n '); 1104 } 1105 } 1106 // Trailer 1107 out('trailer'); 1108 out('<<'); 1109 putTrailer(); 1110 out('>>'); 1111 out('startxref'); 1112 out('' + o); 1113 out('%%EOF'); 1114 1115 outToPages = true; 1116 1117 return content.join('\n'); 1118 }, 1119 getStyle = function getStyle(style) { 1120 // see path-painting operators in PDF spec 1121 var op = 'S'; // stroke 1122 if (style === 'F') { 1123 op = 'f'; // fill 1124 } else if (style === 'FD' || style === 'DF') { 1125 op = 'B'; // both 1126 } else if (style === 'f' || style === 'f*' || style === 'B' || style === 'B*') { 1127 /* 1128 Allow direct use of these PDF path-painting operators: 1129 - f fill using nonzero winding number rule 1130 - f* fill using even-odd rule 1131 - B fill then stroke with fill using non-zero winding number rule 1132 - B* fill then stroke with fill using even-odd rule 1133 */ 1134 op = style; 1135 } 1136 return op; 1137 }, 1138 getArrayBuffer = function getArrayBuffer() { 1139 var data = buildDocument(), 1140 len = data.length, 1141 ab = new ArrayBuffer(len), 1142 u8 = new Uint8Array(ab); 1143 1144 while (len--) { 1145 u8[len] = data.charCodeAt(len); 1146 }return ab; 1147 }, 1148 getBlob = function getBlob() { 1149 return new Blob([getArrayBuffer()], { 1150 type: "application/pdf" 1151 }); 1152 }, 1153 1154 /** 1155 * Generates the PDF document. 1156 * 1157 * If `type` argument is undefined, output is raw body of resulting PDF returned as a string. 1158 * 1159 * @param {String} type A string identifying one of the possible output types. 1160 * @param {Object} options An object providing some additional signalling to PDF generator. 1161 * @function 1162 * @returns {jsPDF} 1163 * @methodOf jsPDF# 1164 * @name output 1165 */ 1166 _output = SAFE(function (type, options) { 1167 var datauri = ('' + type).substr(0, 6) === 'dataur' ? 'data:application/pdf;base64,' + btoa(buildDocument()) : 0; 1168 1169 switch (type) { 1170 case undefined: 1171 return buildDocument(); 1172 case 'save': 1173 if (navigator.getUserMedia) { 1174 if (global.URL === undefined || global.URL.createObjectURL === undefined) { 1175 return API.output('dataurlnewwindow'); 1176 } 1177 } 1178 saveAs(getBlob(), options); 1179 if (typeof saveAs.unload === 'function') { 1180 if (global.setTimeout) { 1181 setTimeout(saveAs.unload, 911); 1182 } 1183 } 1184 break; 1185 case 'arraybuffer': 1186 return getArrayBuffer(); 1187 case 'blob': 1188 return getBlob(); 1189 case 'bloburi': 1190 case 'bloburl': 1191 // User is responsible of calling revokeObjectURL 1192 return global.URL && global.URL.createObjectURL(getBlob()) || void 0; 1193 case 'datauristring': 1194 case 'dataurlstring': 1195 return datauri; 1196 case 'dataurlnewwindow': 1197 var nW = global.open(datauri); 1198 if (nW || typeof safari === "undefined") return nW; 1199 /* pass through */ 1200 case 'datauri': 1201 case 'dataurl': 1202 return global.document.location.href = datauri; 1203 default: 1204 throw new Error('Output type "' + type + '" is not supported.'); 1205 } 1206 // @TODO: Add different output options 1207 }); 1208 1209 switch (unit) { 1210 case 'pt': 1211 k = 1; 1212 break; 1213 case 'mm': 1214 k = 72 / 25.4000508; 1215 break; 1216 case 'cm': 1217 k = 72 / 2.54000508; 1218 break; 1219 case 'in': 1220 k = 72; 1221 break; 1222 case 'px': 1223 k = 96 / 72; 1224 break; 1225 case 'pc': 1226 k = 12; 1227 break; 1228 case 'em': 1229 k = 12; 1230 break; 1231 case 'ex': 1232 k = 6; 1233 break; 1234 default: 1235 throw 'Invalid unit: ' + unit; 1236 } 1237 1238 //--------------------------------------- 1239 // Public API 1240 1241 /** 1242 * Object exposing internal API to plugins 1243 * @public 1244 */ 1245 API.internal = { 1246 'pdfEscape': pdfEscape, 1247 'getStyle': getStyle, 1248 /** 1249 * Returns {FontObject} describing a particular font. 1250 * @public 1251 * @function 1252 * @param fontName {String} (Optional) Font's family name 1253 * @param fontStyle {String} (Optional) Font's style variation name (Example:"Italic") 1254 * @returns {FontObject} 1255 */ 1256 'getFont': function getFont() { 1257 return fonts[_getFont.apply(API, arguments)]; 1258 }, 1259 'getFontSize': function getFontSize() { 1260 return activeFontSize; 1261 }, 1262 'getLineHeight': function getLineHeight() { 1263 return activeFontSize * lineHeightProportion; 1264 },
1265 'write': function write(string1 /*, string2, string3, etc */) { 1266 out(arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' ')); 1267 }, 1268 'getCoordinateString': function getCoordinateString(value) { 1269 return f2(value * k); 1270 }, 1271 'getVerticalCoordinateString': function getVerticalCoordinateString(value) { 1272 return f2((pageHeight - value) * k); 1273 }, 1274 'collections': {}, 1275 'newObject': newObject, 1276 'newAdditionalObject': newAdditionalObject, 1277 'newObjectDeferred': newObjectDeferred, 1278 'newObjectDeferredBegin': newObjectDeferredBegin, 1279 'putStream': putStream, 1280 'events': events, 1281 // ratio that you use in multiplication of a given "size" number to arrive to 'point' 1282 // units of measurement. 1283 // scaleFactor is set at initialization of the document and calculated against the stated 1284 // default measurement units for the document. 1285 // If default is "mm", k is the number that will turn number in 'mm' into 'points' number. 1286 // through multiplication. 1287 'scaleFactor': k, 1288 'pageSize': { 1289 get width() { 1290 return pageWidth; 1291 }, 1292 get height() { 1293 return pageHeight; 1294 } 1295 }, 1296 'output': function output(type, options) { 1297 return _output(type, options); 1298 }, 1299 'getNumberOfPages': function getNumberOfPages() { 1300 return pages.length - 1; 1301 }, 1302 'pages': pages, 1303 'out': out, 1304 'f2': f2, 1305 'getPageInfo': function getPageInfo(pageNumberOneBased) { 1306 var objId = (pageNumberOneBased - 1) * 2 + 3; 1307 return { 1308 objId: objId, 1309 pageNumber: pageNumberOneBased, 1310 pageContext: pagesContext[pageNumberOneBased] 1311 }; 1312 }, 1313 'getCurrentPageInfo': function getCurrentPageInfo() { 1314 var objId = (currentPage - 1) * 2 + 3; 1315 return { 1316 objId: objId, 1317 pageNumber: currentPage, 1318 pageContext: pagesContext[currentPage] 1319 }; 1320 }, 1321 'getPDFVersion': function getPDFVersion() { 1322 return pdfVersion; 1323 } 1324 }; 1325 1326 /** 1327 * Adds (and transfers the focus to) new page to the PDF document. 1328 * @function 1329 * @returns {jsPDF} 1330 * 1331 * @methodOf jsPDF# 1332 * @name addPage 1333 */ 1334 API.addPage = function () { 1335 _addPage.apply(this, arguments); 1336 return this; 1337 }; 1338 /** 1339 * Adds (and transfers the focus to) new page to the PDF document. 1340 * @function 1341 * @returns {jsPDF} 1342 * 1343 * @methodOf jsPDF# 1344 * @name setPage 1345 * @param {Number} page Switch the active page to the page number specified 1346 * @example 1347 * doc = jsPDF() 1348 * doc.addPage() 1349 * doc.addPage() 1350 * doc.text('I am on page 3', 10, 10) 1351 * doc.setPage(1) 1352 * doc.text('I am on page 1', 10, 10) 1353 */ 1354 API.setPage = function () { 1355 _setPage.apply(this, arguments); 1356 return this; 1357 }; 1358 API.insertPage = function (beforePage) { 1359 this.addPage(); 1360 this.movePage(currentPage, beforePage); 1361 return this; 1362 }; 1363 API.movePage = function (targetPage, beforePage) { 1364 if (targetPage > beforePage) { 1365 var tmpPages = pages[targetPage]; 1366 var tmpPagedim = pagedim[targetPage]; 1367 var tmpPagesContext = pagesContext[targetPage]; 1368 for (var i = targetPage; i > beforePage; i--) { 1369 pages[i] = pages[i - 1]; 1370 pagedim[i] = pagedim[i - 1]; 1371 pagesContext[i] = pagesContext[i - 1]; 1372 } 1373 pages[beforePage] = tmpPages; 1374 pagedim[beforePage] = tmpPagedim; 1375 pagesContext[beforePage] = tmpPagesContext; 1376 this.setPage(beforePage); 1377 } else if (targetPage < beforePage) { 1378 var tmpPages = pages[targetPage]; 1379 var tmpPagedim = pagedim[targetPage]; 1380 var tmpPagesContext = pagesContext[targetPage]; 1381 for (var i = targetPage; i < beforePage; i++) { 1382 pages[i] = pages[i + 1]; 1383 pagedim[i] = pagedim[i + 1]; 1384 pagesContext[i] = pagesContext[i + 1]; 1385 } 1386 pages[beforePage] = tmpPages; 1387 pagedim[beforePage] = tmpPagedim; 1388 pagesContext[beforePage] = tmpPagesContext; 1389 this.setPage(beforePage); 1390 } 1391 return this; 1392 }; 1393 1394 API.deletePage = function () { 1395 _deletePage.apply(this, arguments); 1396 return this; 1397 }; 1398 1399 /** 1400 * Set the display mode options of the page like zoom and layout. 1401 * 1402 * @param {integer|String} zoom You can pass an integer or percentage as 1403 * a string. 2 will scale the document up 2x, '200%' will scale up by the 1404 * same amount. You can also set it to 'fullwidth', 'fullheight', 1405 * 'fullpage', or 'original'. 1406 * 1407 * Only certain PDF readers support this, such as Adobe Acrobat 1408 * 1409 * @param {String} layout Layout mode can be: 'continuous' - this is the 1410 * default continuous scroll. 'single' - the single page mode only shows one 1411 * page at a time. 'twoleft' - two column left mode, first page starts on 1412 * the left, and 'tworight' - pages are laid out in two columns, with the 1413 * first page on the right. This would be used for books. 1414 * @param {String} pmode 'UseOutlines' - it shows the 1415 * outline of the document on the left. 'UseThumbs' - shows thumbnails along 1416 * the left. 'FullScreen' - prompts the user to enter fullscreen mode. 1417 * 1418 * @function 1419 * @returns {jsPDF} 1420 * @name setDisplayMode 1421 */ 1422 API.setDisplayMode = function (zoom, layout, pmode) { 1423 zoomMode = zoom; 1424 layoutMode = layout; 1425 pageMode = pmode; 1426
1427 var validPageModes = [undefined, null, 'UseNone', 'UseOutlines', 'UseThumbs', 'FullScreen']; 1428 if (validPageModes.indexOf(pmode) == -1) { 1429 throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "' + pmode + '" is not recognized.'); 1430 } 1431 return this; 1432 }, 1433 1434 /** 1435 * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings. 1436 * 1437 * @function 1438 * @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. 1439 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1440 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1441 * @param {Object} flags 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. 1442 * @returns {jsPDF} 1443 * @methodOf jsPDF# 1444 * @name text 1445 */ 1446 API.text = function (text, x, y, flags, angle, align) { 1447 /** 1448 * Inserts something like this into PDF 1449 * BT 1450 * /F1 16 Tf % Font name + size 1451 * 16 TL % How many units down for next line in multiline text 1452 * 0 g % color 1453 * 28.35 813.54 Td % position 1454 * (line one) Tj 1455 * T* (line two) Tj 1456 * T* (line three) Tj 1457 * ET 1458 */ 1459 function ESC(s) { 1460 s = s.split("\t").join(Array(options.TabLen || 9).join(" ")); 1461 return pdfEscape(s, flags); 1462 } 1463 1464 // Pre-August-2012 the order of arguments was function(x, y, text, flags) 1465 // in effort to make all calls have similar signature like 1466 // function(data, coordinates... , miscellaneous) 1467 // this method had its args flipped. 1468 // code below allows backward compatibility with old arg order. 1469 if (typeof text === 'number') { 1470 tmp = y; 1471 y = x; 1472 x = text; 1473 text = tmp; 1474 } 1475 1476 // If there are any newlines in text, we assume 1477 // the user wanted to print multiple lines, so break the 1478 // text up into an array. If the text is already an array, 1479 // we assume the user knows what they are doing. 1480 // Convert text into an array anyway to simplify 1481 // later code. 1482 if (typeof text === 'string') { 1483 if (text.match(/[\n\r]/)) { 1484 text = text.split(/\r\n|\r|\n/g); 1485 } else { 1486 text = [text]; 1487 } 1488 } 1489 if (typeof angle === 'string') { 1490 align = angle; 1491 angle = null; 1492 } 1493 if (typeof flags === 'string') { 1494 align = flags; 1495 flags = null; 1496 } 1497 if (typeof flags === 'number') { 1498 angle = flags; 1499 flags = null; 1500 } 1501 var xtra = '', 1502 mode = 'Td', 1503 todo; 1504 if (angle) { 1505 angle *= Math.PI / 180; 1506 var c = Math.cos(angle), 1507 s = Math.sin(angle); 1508 xtra = [f2(c), f2(s), f2(s * -1), f2(c), ''].join(" "); 1509 mode = 'Tm'; 1510 } 1511 flags = flags || {}; 1512 if (!('noBOM' in flags)) flags.noBOM = true; 1513 if (!('autoencode' in flags)) flags.autoencode = true; 1514 1515 var strokeOption = ''; 1516 var pageContext = this.internal.getCurrentPageInfo().pageContext; 1517 if (true === flags.stroke) { 1518 if (pageContext.lastTextWasStroke !== true) { 1519 strokeOption = '1 Tr\n'; 1520 pageContext.lastTextWasStroke = true; 1521 } 1522 } else { 1523 if (pageContext.lastTextWasStroke) { 1524 strokeOption = '0 Tr\n'; 1525 } 1526 pageContext.lastTextWasStroke = false; 1527 } 1528 1529 if (typeof this._runningPageHeight === 'undefined') { 1530 this._runningPageHeight = 0; 1531 } 1532 1533 if (typeof text === 'string') { 1534 text = ESC(text); 1535 } else if (Object.prototype.toString.call(text) === '[object Array]') { 1536 // we don't want to destroy original text array, so cloning it 1537 var sa = text.concat(), 1538 da = [],
1539 len = sa.length; 1540 // we do array.join('text that must not be PDFescaped") 1541 // thus, pdfEscape each component separately 1542 while (len--) { 1543 da.push(ESC(sa.shift())); 1544 } 1545 var linesLeft = Math.ceil((pageHeight - y - this._runningPageHeight) * k / (activeFontSize * lineHeightProportion)); 1546 if (0 <= linesLeft && linesLeft < da.length + 1) { 1547 //todo = da.splice(linesLeft-1); 1548 } 1549 1550 if (align) { 1551 var left, 1552 prevX, 1553 maxLineLength, 1554 leading = activeFontSize * lineHeightProportion, 1555 lineWidths = text.map(function (v) { 1556 return this.getStringUnitWidth(v) * activeFontSize / k; 1557 }, this); 1558 maxLineLength = Math.max.apply(Math, lineWidths); 1559 // The first line uses the "main" Td setting, 1560 // and the subsequent lines are offset by the 1561 // previous line's x coordinate. 1562 if (align === "center") { 1563 // The passed in x coordinate defines 1564 // the center point. 1565 left = x - maxLineLength / 2; 1566 x -= lineWidths[0] / 2; 1567 } else if (align === "right") { 1568 // The passed in x coordinate defines the 1569 // rightmost point of the text. 1570 left = x - maxLineLength; 1571 x -= lineWidths[0]; 1572 } else { 1573 throw new Error('Unrecognized alignment option, use "center" or "right".'); 1574 } 1575 prevX = x; 1576 text = da[0]; 1577 for (var i = 1, len = da.length; i < len; i++) { 1578 var delta = maxLineLength - lineWidths[i]; 1579 if (align === "center") delta /= 2; 1580 // T* = x-offset leading Td ( text ) 1581 text += ") Tj\n" + (left - prevX + delta) + " -" + leading + " Td (" + da[i]; 1582 prevX = left + delta; 1583 } 1584 } else { 1585 text = da.join(") Tj\nT* ("); 1586 } 1587 } else { 1588 throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.'); 1589 } 1590 // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates 1591 1592 // BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET 1593 // if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations) 1594 // Thus, there is NO useful, *reliable* concept of "default" font for a page. 1595 // The fact that "default" (reuse font used before) font worked before in basic cases is an accident 1596 // - readers dealing smartly with brokenness of jsPDF's markup. 1597 1598 var curY; 1599 1600 if (todo) { 1601 //this.addPage(); 1602 //this._runningPageHeight += y - (activeFontSize * 1.7 / k); 1603 //curY = f2(pageHeight - activeFontSize * 1.7 /k); 1604 } else { 1605 curY = f2((pageHeight - y) * k); 1606 } 1607 //curY = f2((pageHeight - (y - this._runningPageHeight)) * k); 1608 1609 // if (curY < 0){ 1610 // console.log('auto page break'); 1611 // this.addPage(); 1612 // this._runningPageHeight = y - (activeFontSize * 1.7 / k); 1613 // curY = f2(pageHeight - activeFontSize * 1.7 /k); 1614 // } 1615 1616 out('BT\n/' + activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size 1617 activeFontSize * lineHeightProportion + ' TL\n' + // line spacing 1618 strokeOption + // stroke option 1619 textColor + '\n' + xtra + f2(x * k) + ' ' + curY + ' ' + mode + '\n(' + text + ') Tj\nET'); 1620 1621 if (todo) { 1622 //this.text( todo, x, activeFontSize * 1.7 / k); 1623 //this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7 / k)); 1624 this.text(todo, x, y); // + (activeFontSize * 1.7 / k)); 1625 } 1626 1627 return this; 1628 }; 1629 1630 /** 1631 * Letter spacing method to print text with gaps 1632 * 1633 * @function 1634 * @param {String|Array} text String to be added to the page. 1635 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1636 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1637 * @param {Number} spacing Spacing (in units declared at inception) 1638 * @returns {jsPDF} 1639 * @methodOf jsPDF# 1640 * @name lstext 1641 * @deprecated We'll be removing this function. It doesn't take character width into account. 1642 */ 1643 API.lstext = function (text, x, y, spacing) { 1644 console.warn('jsPDF.lstext is deprecated'); 1645 for (var i = 0, len = text.length; i < len; i++, x += spacing) { 1646 this.text(text[i], x, y); 1647 }return this; 1648 }; 1649 1650 API.line = function (x1, y1, x2, y2) { 1651 return this.lines([[x2 - x1, y2 - y1]], x1, y1); 1652 }; 1653 1654 API.clip = function () { 1655 // By patrick-roberts, github.com/MrRio/jsPDF/issues/328 1656 // Call .clip() after calling .rect() with a style argument of null 1657 out('W'); // clip 1658 out('S'); // stroke path; necessary for clip to work 1659 }; 1660 1661 /** 1662 * This fixes the previous function clip(). Perhaps the 'stroke path' hack was due to the missing 'n' instruction? 1663 * We introduce the fixed version so as to not break API. 1664 * @param fillRule 1665 */ 1666 API.clip_fixed = function (fillRule) { 1667 // Call .clip() after calling drawing ops with a style argument of null 1668 // W is the PDF clipping op 1669 if ('evenodd' === fillRule) { 1670 out('W*'); 1671 } else { 1672 out('W'); 1673 } 1674 // End the path object without filling or stroking it. 1675 // This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path 1676 // (see Section 4.4.3, âClipping Path Operatorsâ) 1677 out('n'); 1678 }; 1679 1680 /** 1681 * Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates. 1682 * All data points in `lines` are relative to last line origin. 1683 * `x`, `y` become x1,y1 for first line / curve in the set. 1684 * For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point. 1685 * 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. 1686 * 1687 * @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line 1688 * @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves). 1689 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1690 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1691 * @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. 1692 * @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. 1693 * @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point. 1694 * @function 1695 * @returns {jsPDF} 1696 * @methodOf jsPDF# 1697 * @name lines 1698 */ 1699 API.lines = function (lines, x, y, scale, style, closed) { 1700 var scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4; 1701 1702 // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style) 1703 // in effort to make all calls have similar signature like 1704 // function(content, coordinateX, coordinateY , miscellaneous) 1705 // this method had its args flipped. 1706 // code below allows backward compatibility with old arg order. 1707 if (typeof lines === 'number') { 1708 tmp = y; 1709 y = x; 1710 x = lines; 1711 lines = tmp; 1712 } 1713 1714 scale = scale || [1, 1]; 1715 1716 // starting point
1717 out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m '); 1718 1719 scalex = scale[0]; 1720 scaley = scale[1]; 1721 l = lines.length; 1722 //, x2, y2 // bezier only. In page default measurement "units", *after* scaling 1723 //, x3, y3 // bezier only. In page default measurement "units", *after* scaling 1724 // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling 1725 x4 = x; // last / ending point = starting point for first item. 1726 y4 = y; // last / ending point = starting point for first item. 1727 1728 for (i = 0; i < l; i++) { 1729 leg = lines[i]; 1730 if (leg.length === 2) { 1731 // simple line 1732 x4 = leg[0] * scalex + x4; // here last x4 was prior ending point 1733 y4 = leg[1] * scaley + y4; // here last y4 was prior ending point 1734 out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l'); 1735 } else { 1736 // bezier curve 1737 x2 = leg[0] * scalex + x4; // here last x4 is prior ending point 1738 y2 = leg[1] * scaley + y4; // here last y4 is prior ending point 1739 x3 = leg[2] * scalex + x4; // here last x4 is prior ending point 1740 y3 = leg[3] * scaley + y4; // here last y4 is prior ending point 1741 x4 = leg[4] * scalex + x4; // here last x4 was prior ending point 1742 y4 = leg[5] * scaley + y4; // here last y4 was prior ending point 1743 out(f3(x2 * k) + ' ' + f3((pageHeight - y2) * k) + ' ' + f3(x3 * k) + ' ' + f3((pageHeight - y3) * k) + ' ' + f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' c'); 1744 } 1745 } 1746 1747 if (closed) { 1748 out(' h'); 1749 } 1750 1751 // stroking / filling / both the path 1752 if (style !== null) { 1753 out(getStyle(style)); 1754 } 1755 return this; 1756 }; 1757 1758 /** 1759 * Adds a rectangle to PDF 1760 * 1761 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1762 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1763 * @param {Number} w Width (in units declared at inception of PDF document) 1764 * @param {Number} h Height (in units declared at inception of PDF document) 1765 * @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. 1766 * @function 1767 * @returns {jsPDF} 1768 * @methodOf jsPDF# 1769 * @name rect 1770 */ 1771 API.rect = function (x, y, w, h, style) { 1772 var op = getStyle(style); 1773 out([f2(x * k), f2((pageHeight - y) * k), f2(w * k), f2(-h * k), 're'].join(' ')); 1774 1775 if (style !== null) { 1776 out(getStyle(style)); 1777 } 1778 1779 return this; 1780 }; 1781 1782 /** 1783 * Adds a triangle to PDF 1784 * 1785 * @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page 1786 * @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page 1787 * @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page 1788 * @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page 1789 * @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page 1790 * @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page 1791 * @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. 1792 * @function 1793 * @returns {jsPDF} 1794 * @methodOf jsPDF# 1795 * @name triangle 1796 */ 1797 API.triangle = function (x1, y1, x2, y2, x3, y3, style) { 1798 this.lines([[x2 - x1, y2 - y1], // vector to point 2 1799 [x3 - x2, y3 - y2], // vector to point 3 1800 [x1 - x3, y1 - y3] // closing vector back to point 1 1801 ], x1, y1, // start of path 1802 [1, 1], style, true); 1803 return this; 1804 }; 1805 1806 /** 1807 * Adds a rectangle with rounded corners to PDF 1808 * 1809 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1810 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1811 * @param {Number} w Width (in units declared at inception of PDF document) 1812 * @param {Number} h Height (in units declared at inception of PDF document) 1813 * @param {Number} rx Radius along x axis (in units declared at inception of PDF document) 1814 * @param {Number} rx Radius along y axis (in units declared at inception of PDF document) 1815 * @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. 1816 * @function 1817 * @returns {jsPDF} 1818 * @methodOf jsPDF# 1819 * @name roundedRect 1820 */ 1821 API.roundedRect = function (x, y, w, h, rx, ry, style) {
1822 var MyArc = 4 / 3 * (Math.SQRT2 - 1); 1823 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 1824 [1, 1], style); 1825 return this; 1826 }; 1827 1828 /** 1829 * Adds an ellipse to PDF 1830 * 1831 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1832 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1833 * @param {Number} rx Radius along x axis (in units declared at inception of PDF document) 1834 * @param {Number} rx Radius along y axis (in units declared at inception of PDF document) 1835 * @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. 1836 * @function 1837 * @returns {jsPDF} 1838 * @methodOf jsPDF# 1839 * @name ellipse 1840 */ 1841 API.ellipse = function (x, y, rx, ry, style) { 1842 var lx = 4 / 3 * (Math.SQRT2 - 1) * rx, 1843 ly = 4 / 3 * (Math.SQRT2 - 1) * ry; 1844 1845 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(' ')); 1846 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(' ')); 1847 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(' ')); 1848 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(' ')); 1849 1850 if (style !== null) { 1851 out(getStyle(style)); 1852 } 1853 1854 return this; 1855 }; 1856 1857 /** 1858 * Adds an circle to PDF 1859 * 1860 * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page 1861 * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page 1862 * @param {Number} r Radius (in units declared at inception of PDF document) 1863 * @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. 1864 * @function 1865 * @returns {jsPDF} 1866 * @methodOf jsPDF# 1867 * @name circle 1868 */ 1869 API.circle = function (x, y, r, style) { 1870 return this.ellipse(x, y, r, r, style); 1871 }; 1872 1873 /** 1874 * Adds a properties to the PDF document 1875 * 1876 * @param {Object} A property_name-to-property_value object structure. 1877 * @function 1878 * @returns {jsPDF} 1879 * @methodOf jsPDF# 1880 * @name setProperties 1881 */ 1882 API.setProperties = function (properties) { 1883 // copying only those properties we can render. 1884 for (var property in documentProperties) { 1885 if (documentProperties.hasOwnProperty(property) && properties[property]) { 1886 documentProperties[property] = properties[property]; 1887 } 1888 } 1889 return this; 1890 }; 1891 1892 /** 1893 * Sets font size for upcoming text elements. 1894 * 1895 * @param {Number} size Font size in points. 1896 * @function 1897 * @returns {jsPDF} 1898 * @methodOf jsPDF# 1899 * @name setFontSize 1900 */ 1901 API.setFontSize = function (size) { 1902 activeFontSize = size; 1903 return this; 1904 }; 1905 1906 /** 1907 * Sets text font face, variant for upcoming text elements.
1908 * See output of jsPDF.getFontList() for possible font names, styles. 1909 * 1910 * @param {String} fontName Font name or family. Example: "times" 1911 * @param {String} fontStyle Font style or variant. Example: "italic" 1912 * @function 1913 * @returns {jsPDF} 1914 * @methodOf jsPDF# 1915 * @name setFont 1916 */ 1917 API.setFont = function (fontName, fontStyle) { 1918 activeFontKey = _getFont(fontName, fontStyle); 1919 // if font is not found, the above line blows up and we never go further 1920 return this; 1921 }; 1922 1923 /** 1924 * Switches font style or variant for upcoming text elements, 1925 * while keeping the font face or family same. 1926 * See output of jsPDF.getFontList() for possible font names, styles. 1927 * 1928 * @param {String} style Font style or variant. Example: "italic" 1929 * @function 1930 * @returns {jsPDF} 1931 * @methodOf jsPDF# 1932 * @name setFontStyle 1933 */ 1934 API.setFontStyle = API.setFontType = function (style) { 1935 activeFontKey = _getFont(undefined, style); 1936 // if font is not found, the above line blows up and we never go further 1937 return this; 1938 }; 1939 1940 /** 1941 * Returns an object - a tree of fontName to fontStyle relationships available to 1942 * active PDF document. 1943 * 1944 * @public 1945 * @function 1946 * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... } 1947 * @methodOf jsPDF# 1948 * @name getFontList 1949 */ 1950 API.getFontList = function () { 1951 // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added. 1952 var list = {}, 1953 fontName, 1954 fontStyle, 1955 tmp; 1956 1957 for (fontName in fontmap) { 1958 if (fontmap.hasOwnProperty(fontName)) { 1959 list[fontName] = tmp = []; 1960 for (fontStyle in fontmap[fontName]) { 1961 if (fontmap[fontName].hasOwnProperty(fontStyle)) { 1962 tmp.push(fontStyle); 1963 } 1964 } 1965 } 1966 } 1967 1968 return list; 1969 }; 1970 1971 /** 1972 * Add a custom font. 1973 * 1974 * @param {String} Postscript name of the Font. Example: "Menlo-Regular" 1975 * @param {String} Name of font-family from @font-face definition. Example: "Menlo Regular" 1976 * @param {String} Font style. Example: "normal" 1977 * @function 1978 * @returns the {fontKey} (same as the internal method) 1979 * @methodOf jsPDF# 1980 * @name addFont 1981 */ 1982 API.addFont = function (postScriptName, fontName, fontStyle) { 1983 addFont(postScriptName, fontName, fontStyle, 'StandardEncoding'); 1984 }; 1985 1986 /** 1987 * Sets line width for upcoming lines. 1988 * 1989 * @param {Number} width Line width (in units declared at inception of PDF document) 1990 * @function 1991 * @returns {jsPDF} 1992 * @methodOf jsPDF# 1993 * @name setLineWidth 1994 */ 1995 API.setLineWidth = function (width) { 1996 out((width * k).toFixed(2) + ' w'); 1997 return this; 1998 }; 1999 2000 /** 2001 * Sets the stroke color for upcoming elements. 2002 * 2003 * Depending on the number of arguments given, Gray, RGB, or CMYK 2004 * color space is implied. 2005 * 2006 * When only ch1 is given, "Gray" color space is implied and it 2007 * must be a value in the range from 0.00 (solid black) to to 1.00 (white) 2008 * if values are communicated as String types, or in range from 0 (black) 2009 * to 255 (white) if communicated as Number type. 2010 * The RGB-like 0-255 range is provided for backward compatibility. 2011 * 2012 * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each 2013 * value must be in the range from 0.00 (minimum intensity) to to 1.00 2014 * (max intensity) if values are communicated as String types, or 2015 * from 0 (min intensity) to to 255 (max intensity) if values are communicated 2016 * as Number types. 2017 * The RGB-like 0-255 range is provided for backward compatibility. 2018 * 2019 * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each 2020 * value must be a in the range from 0.00 (0% concentration) to to 2021 * 1.00 (100% concentration) 2022 * 2023 * Because JavaScript treats fixed point numbers badly (rounds to 2024 * floating point nearest to binary representation) it is highly advised to 2025 * communicate the fractional numbers as String types, not JavaScript Number type. 2026 * 2027 * @param {Number|String} ch1 Color channel value 2028 * @param {Number|String} ch2 Color channel value 2029 * @param {Number|String} ch3 Color channel value 2030 * @param {Number|String} ch4 Color channel value 2031 * 2032 * @function 2033 * @returns {jsPDF} 2034 * @methodOf jsPDF# 2035 * @name setDrawColor 2036 */ 2037 API.setDrawColor = function (ch1, ch2, ch3, ch4) { 2038 var color; 2039 if (ch2 === undefined || ch4 === undefined && ch1 === ch2 === ch3) { 2040 // Gray color space. 2041 if (typeof ch1 === 'string') { 2042 color = ch1 + ' G'; 2043 } else { 2044 color = f2(ch1 / 255) + ' G'; 2045 } 2046 } else if (ch4 === undefined) { 2047 // RGB 2048 if (typeof ch1 === 'string') { 2049 color = [ch1, ch2, ch3, 'RG'].join(' '); 2050 } else { 2051 color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' '); 2052 } 2053 } else { 2054 // CMYK 2055 if (typeof ch1 === 'string') { 2056 color = [ch1, ch2, ch3, ch4, 'K'].join(' '); 2057 } else { 2058 color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' '); 2059 } 2060 } 2061 2062 out(color); 2063 return this; 2064 }; 2065 2066 /** 2067 * Sets the fill color for upcoming elements. 2068 *
2069 * Depending on the number of arguments given, Gray, RGB, or CMYK 2070 * color space is implied. 2071 * 2072 * When only ch1 is given, "Gray" color space is implied and it 2073 * must be a value in the range from 0.00 (solid black) to to 1.00 (white) 2074 * if values are communicated as String types, or in range from 0 (black) 2075 * to 255 (white) if communicated as Number type. 2076 * The RGB-like 0-255 range is provided for backward compatibility. 2077 * 2078 * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each 2079 * value must be in the range from 0.00 (minimum intensity) to to 1.00 2080 * (max intensity) if values are communicated as String types, or 2081 * from 0 (min intensity) to to 255 (max intensity) if values are communicated 2082 * as Number types. 2083 * The RGB-like 0-255 range is provided for backward compatibility. 2084 * 2085 * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each 2086 * value must be a in the range from 0.00 (0% concentration) to to 2087 * 1.00 (100% concentration) 2088 * 2089 * Because JavaScript treats fixed point numbers badly (rounds to 2090 * floating point nearest to binary representation) it is highly advised to 2091 * communicate the fractional numbers as String types, not JavaScript Number type. 2092 * 2093 * @param {Number|String} ch1 Color channel value 2094 * @param {Number|String} ch2 Color channel value 2095 * @param {Number|String} ch3 Color channel value 2096 * @param {Number|String} ch4 Color channel value 2097 * 2098 * @function 2099 * @returns {jsPDF} 2100 * @methodOf jsPDF# 2101 * @name setFillColor 2102 */ 2103 API.setFillColor = function (ch1, ch2, ch3, ch4) { 2104 var color; 2105 2106 if (ch2 === undefined || ch4 === undefined && ch1 === ch2 === ch3) { 2107 // Gray color space. 2108 if (typeof ch1 === 'string') { 2109 color = ch1 + ' g'; 2110 } else { 2111 color = f2(ch1 / 255) + ' g'; 2112 } 2113 } else if (ch4 === undefined || (typeof ch4 === 'undefined' ? 'undefined' : _typeof(ch4)) === 'object') { 2114 // RGB 2115 if (typeof ch1 === 'string') { 2116 color = [ch1, ch2, ch3, 'rg'].join(' '); 2117 } else { 2118 color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' '); 2119 } 2120 if (ch4 && ch4.a === 0) { 2121 //TODO Implement transparency. 2122 //WORKAROUND use white for now 2123 color = ['255', '255', '255', 'rg'].join(' '); 2124 } 2125 } else { 2126 // CMYK 2127 if (typeof ch1 === 'string') { 2128 color = [ch1, ch2, ch3, ch4, 'k'].join(' '); 2129 } else { 2130 color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' '); 2131 } 2132 } 2133 2134 out(color); 2135 return this; 2136 }; 2137 2138 /** 2139 * Sets the text color for upcoming elements. 2140 * If only one, first argument is given, 2141 * treats the value as gray-scale color value. 2142 * 2143 * @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF' 2144 * @param {Number} g Green channel color value in range 0-255 2145 * @param {Number} b Blue channel color value in range 0-255 2146 * @function 2147 * @returns {jsPDF} 2148 * @methodOf jsPDF# 2149 * @name setTextColor 2150 */ 2151 API.setTextColor = function (r, g, b) { 2152 if (typeof r === 'string' && /^#[0-9A-Fa-f]{6}$/.test(r)) { 2153 var hex = parseInt(r.substr(1), 16); 2154 r = hex >> 16 & 255; 2155 g = hex >> 8 & 255; 2156 b = hex & 255; 2157 } 2158 2159 if (r === 0 && g === 0 && b === 0 || typeof g === 'undefined') { 2160 textColor = f3(r / 255) + ' g'; 2161 } else { 2162 textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' '); 2163 } 2164 return this; 2165 }; 2166 2167 /** 2168 * Is an Object providing a mapping from human-readable to 2169 * integer flag values designating the varieties of line cap 2170 * and join styles. 2171 * 2172 * @returns {Object} 2173 * @fieldOf jsPDF# 2174 * @name CapJoinStyles 2175 */ 2176 API.CapJoinStyles = { 2177 0: 0, 2178 'butt': 0, 2179 'but': 0, 2180 'miter': 0, 2181 1: 1, 2182 'round': 1, 2183 'rounded': 1, 2184 'circle': 1, 2185 2: 2, 2186 'projecting': 2, 2187 'project': 2, 2188 'square': 2, 2189 'bevel': 2 2190 }; 2191 2192 /** 2193 * Sets the line cap styles 2194 * See {jsPDF.CapJoinStyles} for variants 2195 * 2196 * @param {String|Number} style A string or number identifying the type of line cap 2197 * @function 2198 * @returns {jsPDF} 2199 * @methodOf jsPDF# 2200 * @name setLineCap 2201 */ 2202 API.setLineCap = function (style) { 2203 var id = this.CapJoinStyles[style]; 2204 if (id === undefined) { 2205 throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); 2206 } 2207 lineCapID = id; 2208 out(id + ' J'); 2209 2210 return this; 2211 }; 2212 2213 /** 2214 * Sets the line join styles 2215 * See {jsPDF.CapJoinStyles} for variants 2216 * 2217 * @param {String|Number} style A string or number identifying the type of line join 2218 * @function 2219 * @returns {jsPDF} 2220 * @methodOf jsPDF# 2221 * @name setLineJoin 2222 */ 2223 API.setLineJoin = function (style) { 2224 var id = this.CapJoinStyles[style]; 2225 if (id === undefined) { 2226 throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); 2227 } 2228 lineJoinID = id; 2229 out(id + ' j'); 2230 2231 return this; 2232 }; 2233 2234 // Output is both an internal (for plugins) and external function 2235 API.output = _output; 2236 2237 /** 2238 * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf') 2239 * @param {String} filename The filename including extension. 2240 * 2241 * @function 2242 * @returns {jsPDF} 2243 * @methodOf jsPDF# 2244 * @name save 2245 */ 2246 API.save = function (filename) { 2247 API.output('save', filename); 2248 }; 2249 2250 // applying plugins (more methods) ON TOP of built-in API. 2251 // this is intentional as we allow plugins to override 2252 // built-ins 2253 for (var plugin in jsPDF.API) { 2254 if (jsPDF.API.hasOwnProperty(plugin)) { 2255 if (plugin === 'events' && jsPDF.API.events.length) { 2256 (function (events, newEvents) { 2257 2258 // jsPDF.API.events is a JS Array of Arrays 2259 // where each Array is a pair of event name, handler 2260 // Events were added by plugins to the jsPDF instantiator. 2261 // These are always added to the new instance and some ran 2262 // during instantiation. 2263 var eventname, handler_and_args, i; 2264 2265 for (i = newEvents.length - 1; i !== -1; i--) { 2266 // subscribe takes 3 args: 'topic', function, runonce_flag 2267 // if undefined, runonce is false. 2268 // users can attach callback directly, 2269 // or they can attach an array with [callback, runonce_flag] 2270 // that's what the "apply" magic is for below.
2271 eventname = newEvents[i][0]; 2272 handler_and_args = newEvents[i][1]; 2273 events.subscribe.apply(events, [eventname].concat(typeof handler_and_args === 'function' ? [handler_and_args] : handler_and_args)); 2274 } 2275 })(events, jsPDF.API.events); 2276 } else { 2277 API[plugin] = jsPDF.API[plugin]; 2278 } 2279 } 2280 } 2281 2282 ////////////////////////////////////////////////////// 2283 // continuing initialization of jsPDF Document object 2284 ////////////////////////////////////////////////////// 2285 // Add the first page automatically 2286 addFonts(); 2287 activeFontKey = 'F1'; 2288 _addPage(format, orientation); 2289 2290 events.publish('initialized'); 2291 return API; 2292 } 2293 2294 /** 2295 * jsPDF.API is a STATIC property of jsPDF class. 2296 * jsPDF.API is an object you can add methods and properties to. 2297 * The methods / properties you add will show up in new jsPDF objects. 2298 * 2299 * One property is prepopulated. It is the 'events' Object. Plugin authors can add topics, 2300 * callbacks to this object. These will be reassigned to all new instances of jsPDF. 2301 * Examples: 2302 * jsPDF.API.events['initialized'] = function(){ 'this' is API object } 2303 * jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object } 2304 * 2305 * @static 2306 * @public 2307 * @memberOf jsPDF 2308 * @name API 2309 * 2310 * @example 2311 * jsPDF.API.mymethod = function(){ 2312 * // 'this' will be ref to internal API object. see jsPDF source 2313 * // , so you can refer to built-in methods like so: 2314 * // this.line(....) 2315 * // this.text(....) 2316 * } 2317 * var pdfdoc = new jsPDF() 2318 * pdfdoc.mymethod() // <- !!!!!! 2319 */ 2320 jsPDF.API = { 2321 events: [] 2322 }; 2323 jsPDF.version = "1.x-master"; 2324 2325 if (typeof define === 'function' && define.amd) { 2326 define('jsPDF', function () { 2327 return jsPDF; 2328 }); 2329 } else if (typeof module !== 'undefined' && module.exports) { 2330 module.exports = jsPDF; 2331 } else { 2332 global.jsPDF = jsPDF; 2333 } 2334 return jsPDF; 2335}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || undefined); 2336 2337 2338/** 2339 * jsPDF AcroForm Plugin 2340 * Copyright (c) 2016 Alexander Weidt, https://github.com/BiggA94 2341 * 2342 * Licensed under the MIT License. 2343 * http://opensource.org/licenses/mit-license 2344 */ 2345 2346(window.AcroForm = function (jsPDFAPI) { 2347 'use strict'; 2348 2349 var AcroForm = window.AcroForm; 2350 2351 AcroForm.scale = function (x) { 2352 return x * (acroformPlugin.internal.scaleFactor / 1); // 1 = (96 / 72) 2353 }; 2354 AcroForm.antiScale = function (x) { 2355 return 1 / acroformPlugin.internal.scaleFactor * x; 2356 }; 2357 2358 var acroformPlugin = { 2359 fields: [], 2360 xForms: [], 2361 /** 2362 * acroFormDictionaryRoot contains information about the AcroForm Dictionary 2363 * 0: The Event-Token, the AcroFormDictionaryCallback has 2364 * 1: The Object ID of the Root 2365 */ 2366 acroFormDictionaryRoot: null, 2367 /** 2368 * After the PDF gets evaluated, the reference to the root has to be reset, 2369 * this indicates, whether the root has already been printed out 2370 */ 2371 printedOut: false, 2372 internal: null 2373 }; 2374 2375 jsPDF.API.acroformPlugin = acroformPlugin; 2376 2377 var annotReferenceCallback = function annotReferenceCallback() { 2378 for (var i in this.acroformPlugin.acroFormDictionaryRoot.Fields) { 2379 var formObject = this.acroformPlugin.acroFormDictionaryRoot.Fields[i]; 2380 // add Annot Reference! 2381 if (formObject.hasAnnotation) { 2382 // If theres an Annotation Widget in the Form Object, put the Reference in the /Annot array 2383 createAnnotationReference.call(this, formObject); 2384 } 2385 } 2386 }; 2387 2388 var createAcroForm = function createAcroForm() { 2389 if (this.acroformPlugin.acroFormDictionaryRoot) { 2390 //return; 2391 throw new Error("Exception while creating AcroformDictionary"); 2392 } 2393 2394 // The Object Number of the AcroForm Dictionary 2395 this.acroformPlugin.acroFormDictionaryRoot = new AcroForm.AcroFormDictionary(); 2396 2397 this.acroformPlugin.internal = this.internal; 2398 2399 // add Callback for creating the AcroForm Dictionary 2400 this.acroformPlugin.acroFormDictionaryRoot._eventID = this.internal.events.subscribe('postPutResources', AcroFormDictionaryCallback); 2401 2402 this.internal.events.subscribe('buildDocument', annotReferenceCallback); //buildDocument 2403 2404 // Register event, that is triggered when the DocumentCatalog is written, in order to add /AcroForm 2405 this.internal.events.subscribe('putCatalog', putCatalogCallback); 2406 2407 // Register event, that creates all Fields 2408 this.internal.events.subscribe('postPutPages', createFieldCallback); 2409 }; 2410 2411 /** 2412 * Create the Reference to the widgetAnnotation, so that it gets referenced in the Annot[] int the+ 2413 * (Requires the Annotation Plugin) 2414 */ 2415 var createAnnotationReference = function createAnnotationReference(object) { 2416 var options = { 2417 type: 'reference', 2418 object: object 2419 }; 2420 jsPDF.API.annotationPlugin.annotations[this.internal.getPageInfo(object.page).pageNumber].push(options); 2421 }; 2422
2423 var putForm = function putForm(formObject) { 2424 if (this.acroformPlugin.printedOut) { 2425 this.acroformPlugin.printedOut = false; 2426 this.acroformPlugin.acroFormDictionaryRoot = null; 2427 } 2428 if (!this.acroformPlugin.acroFormDictionaryRoot) { 2429 createAcroForm.call(this); 2430 } 2431 this.acroformPlugin.acroFormDictionaryRoot.Fields.push(formObject); 2432 }; 2433 2434 // Callbacks 2435 2436 var putCatalogCallback = function putCatalogCallback() { 2437 //Put reference to AcroForm to DocumentCatalog 2438 if (typeof this.acroformPlugin.acroFormDictionaryRoot != 'undefined') { 2439 // for safety, shouldn't normally be the case 2440 this.internal.write('/AcroForm ' + this.acroformPlugin.acroFormDictionaryRoot.objId + ' ' + 0 + ' R'); 2441 } else { 2442 console.log('Root missing...'); 2443 } 2444 }; 2445 2446 /** 2447 * Adds /Acroform X 0 R to Document Catalog, 2448 * and creates the AcroForm Dictionary 2449 */ 2450 var AcroFormDictionaryCallback = function AcroFormDictionaryCallback() { 2451 // Remove event 2452 this.internal.events.unsubscribe(this.acroformPlugin.acroFormDictionaryRoot._eventID); 2453 2454 delete this.acroformPlugin.acroFormDictionaryRoot._eventID; 2455 2456 this.acroformPlugin.printedOut = true; 2457 }; 2458 2459 /** 2460 * Creates the single Fields and writes them into the Document 2461 * 2462 * If fieldArray is set, use the fields that are inside it instead of the fields from the AcroRoot 2463 * (for the FormXObjects...) 2464 */ 2465 var createFieldCallback = function createFieldCallback(fieldArray) { 2466 var standardFields = !fieldArray; 2467 2468 if (!fieldArray) { 2469 // in case there is no fieldArray specified, we want to print out the Fields of the AcroForm 2470 // Print out Root 2471 this.internal.newObjectDeferredBegin(this.acroformPlugin.acroFormDictionaryRoot.objId); 2472 this.internal.out(this.acroformPlugin.acroFormDictionaryRoot.getString()); 2473 } 2474 2475 var fieldArray = fieldArray || this.acroformPlugin.acroFormDictionaryRoot.Kids; 2476 2477 for (var i in fieldArray) { 2478 var key = i; 2479 var form = fieldArray[i]; 2480 2481 var oldRect = form.Rect; 2482 2483 if (form.Rect) { 2484 form.Rect = AcroForm.internal.calculateCoordinates.call(this, form.Rect); 2485 } 2486 2487 // Start Writing the Object 2488 this.internal.newObjectDeferredBegin(form.objId); 2489 2490 var content = ""; 2491 content += form.objId + " 0 obj\n"; 2492 2493 content += "<<\n" + form.getContent(); 2494 2495 form.Rect = oldRect; 2496 2497 if (form.hasAppearanceStream && !form.appearanceStreamContent) { 2498 // Calculate Appearance 2499 var appearance = AcroForm.internal.calculateAppearanceStream.call(this, form); 2500 content += "/AP << /N " + appearance + " >>\n"; 2501 2502 this.acroformPlugin.xForms.push(appearance); 2503 } 2504 2505 // Assume AppearanceStreamContent is a Array with N,R,D (at least one of them!) 2506 if (form.appearanceStreamContent) { 2507 content += "/AP << "; 2508 // Iterate over N,R and D 2509 for (var k in form.appearanceStreamContent) { 2510 var value = form.appearanceStreamContent[k]; 2511 content += "/" + k + " "; 2512 content += "<< "; 2513 if (Object.keys(value).length >= 1 || Array.isArray(value)) { 2514 // appearanceStream is an Array or Object! 2515 for (var i in value) { 2516 var obj = value[i]; 2517 if (typeof obj === 'function') { 2518 // if Function is referenced, call it in order to get the FormXObject 2519 obj = obj.call(this, form); 2520 } 2521 content += "/" + i + " " + obj + " "; 2522 2523 // In case the XForm is already used, e.g. OffState of CheckBoxes, don't add it 2524 if (!(this.acroformPlugin.xForms.indexOf(obj) >= 0)) this.acroformPlugin.xForms.push(obj); 2525 } 2526 } else { 2527 var obj = value; 2528 if (typeof obj === 'function') { 2529 // if Function is referenced, call it in order to get the FormXObject 2530 obj = obj.call(this, form); 2531 } 2532 content += "/" + i + " " + obj + " \n"; 2533 if (!(this.acroformPlugin.xForms.indexOf(obj) >= 0)) this.acroformPlugin.xForms.push(obj); 2534 } 2535 content += " >>\n"; 2536 } 2537 2538 // appearance stream is a normal Object.. 2539 content += ">>\n"; 2540 } 2541 2542 content += ">>\nendobj\n"; 2543 2544 this.internal.out(content); 2545 } 2546 if (standardFields) { 2547 createXFormObjectCallback.call(this, this.acroformPlugin.xForms); 2548 } 2549 }; 2550 2551 var createXFormObjectCallback = function createXFormObjectCallback(fieldArray) { 2552 for (var i in fieldArray) { 2553 var key = i; 2554 var form = fieldArray[i]; 2555 // Start Writing the Object 2556 this.internal.newObjectDeferredBegin(form && form.objId); 2557 2558 var content = ""; 2559 content += form ? form.getString() : ''; 2560 this.internal.out(content); 2561 2562 delete fieldArray[key]; 2563 } 2564 }; 2565 2566 // Public: 2567 2568 jsPDFAPI.addField = function (fieldObject) { 2569 //var opt = parseOptions(fieldObject); 2570 if (fieldObject instanceof AcroForm.TextField) { 2571 addTextField.call(this, fieldObject); 2572 } else if (fieldObject instanceof AcroForm.ChoiceField) { 2573 addChoiceField.call(this, fieldObject); 2574 } else if (fieldObject instanceof AcroForm.Button) { 2575 addButton.call(this, fieldObject); 2576 } else if (fieldObject instanceof AcroForm.ChildClass) { 2577 putForm.call(this, fieldObject); 2578 } else if (fieldObject) { 2579 // try to put.. 2580 putForm.call(this, fieldObject); 2581 } 2582 fieldObject.page = this.acroformPlugin.internal.getCurrentPageInfo().pageNumber; 2583 return this; 2584 }; 2585 2586 // ############### sort in: 2587 2588 /** 2589 * Button 2590 * FT = Btn 2591 */ 2592 var addButton = function addButton(options) { 2593 var options = options || new AcroForm.Field(); 2594 2595 options.FT = '/Btn'; 2596 2597 /** 2598 * Calculating the Ff entry: 2599 * 2600 * The Ff entry contains flags, that have to be set bitwise 2601 * In the Following the number in the Comment is the BitPosition 2602 */ 2603 var flags = options.Ff || 0; 2604 2605 // 17, Pushbutton 2606 if (options.pushbutton) { 2607 // Options.pushbutton should be 1 or 0 2608 flags = AcroForm.internal.setBitPosition(flags, 17); 2609 delete options.pushbutton; 2610 } 2611 2612 //16, Radio 2613 if (options.radio) { 2614 //flags = options.Ff | options.radio << 15; 2615 flags = AcroForm.internal.setBitPosition(flags, 16); 2616 delete options.radio; 2617 } 2618 2619 // 15, NoToggleToOff (Radio buttons only 2620 if (options.noToggleToOff) { 2621 //flags = options.Ff | options.noToggleToOff << 14; 2622 flags = AcroForm.internal.setBitPosition(flags, 15); 2623 //delete options.noToggleToOff; 2624 } 2625 2626 // In case, there is no Flag set, it is a check-box 2627 options.Ff = flags; 2628 2629 putForm.call(this, options); 2630 }; 2631 2632 var addTextField = function addTextField(options) { 2633 var options = options || new AcroForm.Field(); 2634 2635 options.FT = '/Tx'; 2636 2637 /** 2638 * Calculating the Ff entry: 2639 * 2640 * The Ff entry contains flags, that have to be set bitwise 2641 * In the Following the number in the Comment is the BitPosition 2642 */ 2643 2644 var flags = options.Ff || 0; 2645 2646 // 13, multiline 2647 if (options.multiline) { 2648 // Set Flag 2649 flags = flags | 1 << 12; 2650 // Remove multiline from FieldObject 2651 //delete options.multiline; 2652 } 2653 2654 // 14, Password 2655 if (options.password) { 2656 flags = flags | 1 << 13; 2657 //delete options.password; 2658 } 2659 2660 // 21, FileSelect, PDF 1.4... 2661 if (options.fileSelect) { 2662 flags = flags | 1 << 20; 2663 //delete options.fileSelect; 2664 } 2665 2666 // 23, DoNotSpellCheck, PDF 1.4... 2667 if (options.doNotSpellCheck) { 2668 flags = flags | 1 << 22; 2669 //delete options.doNotSpellCheck; 2670 } 2671 2672 // 24, DoNotScroll, PDF 1.4... 2673 if (options.doNotScroll) { 2674 flags = flags | 1 << 23; 2675 //delete options.doNotScroll; 2676 } 2677 2678 options.Ff = options.Ff || flags; 2679
2680 // Add field 2681 putForm.call(this, options); 2682 }; 2683 2684 var addChoiceField = function addChoiceField(opt) { 2685 var options = opt || new AcroForm.Field(); 2686 2687 options.FT = '/Ch'; 2688 2689 /** 2690 * Calculating the Ff entry: 2691 * 2692 * The Ff entry contains flags, that have to be set bitwise 2693 * In the Following the number in the Comment is the BitPosition 2694 */ 2695 2696 var flags = options.Ff || 0; 2697 2698 // 18, Combo (If not set, the choiceField is a listBox!!) 2699 if (options.combo) { 2700 // Set Flag 2701 flags = AcroForm.internal.setBitPosition(flags, 18); 2702 // Remove combo from FieldObject 2703 delete options.combo; 2704 } 2705 2706 // 19, Edit 2707 if (options.edit) { 2708 flags = AcroForm.internal.setBitPosition(flags, 19); 2709 delete options.edit; 2710 } 2711 2712 // 20, Sort 2713 if (options.sort) { 2714 flags = AcroForm.internal.setBitPosition(flags, 20); 2715 delete options.sort; 2716 } 2717 2718 // 22, MultiSelect (PDF 1.4) 2719 if (options.multiSelect && this.internal.getPDFVersion() >= 1.4) { 2720 flags = AcroForm.internal.setBitPosition(flags, 22); 2721 delete options.multiSelect; 2722 } 2723 2724 // 23, DoNotSpellCheck (PDF 1.4) 2725 if (options.doNotSpellCheck && this.internal.getPDFVersion() >= 1.4) { 2726 flags = AcroForm.internal.setBitPosition(flags, 23); 2727 delete options.doNotSpellCheck; 2728 } 2729 2730 options.Ff = flags; 2731 2732 //options.hasAnnotation = true; 2733 2734 // Add field 2735 putForm.call(this, options); 2736 }; 2737})(jsPDF.API); 2738 2739var AcroForm = window.AcroForm; 2740 2741AcroForm.internal = {}; 2742 2743AcroForm.createFormXObject = function (formObject) { 2744 var xobj = new AcroForm.FormXObject(); 2745 var height = AcroForm.Appearance.internal.getHeight(formObject) || 0; 2746 var width = AcroForm.Appearance.internal.getWidth(formObject) || 0; 2747 xobj.BBox = [0, 0, width, height]; 2748 return xobj; 2749}; 2750 2751// Contains Methods for creating standard appearances 2752AcroForm.Appearance = { 2753 CheckBox: { 2754 createAppearanceStream: function createAppearanceStream() { 2755 var appearance = { 2756 N: { 2757 On: AcroForm.Appearance.CheckBox.YesNormal 2758 }, 2759 D: { 2760 On: AcroForm.Appearance.CheckBox.YesPushDown, 2761 Off: AcroForm.Appearance.CheckBox.OffPushDown 2762 } 2763 }; 2764 2765 return appearance; 2766 }, 2767 /** 2768 * If any other icons are needed, the number between the brackets can be changed 2769 * @returns {string} 2770 */ 2771 createMK: function createMK() { 2772 // 3-> Hook 2773 return "<< /CA (3)>>"; 2774 }, 2775 /** 2776 * Returns the standard On Appearance for a CheckBox 2777 * @returns {AcroForm.FormXObject} 2778 */ 2779 YesPushDown: function YesPushDown(formObject) { 2780 var xobj = AcroForm.createFormXObject(formObject); 2781 var stream = ""; 2782 // F13 is ZapfDingbats (Symbolic) 2783 formObject.Q = 1; // set text-alignment as centered 2784 var calcRes = AcroForm.internal.calculateX(formObject, "3", "ZapfDingbats", 50); 2785 stream += "0.749023 g\n\ 2786 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ 2787 f\n\ 2788 BMC\n\ 2789 q\n\ 2790 0 0 1 rg\n\ 2791 /F13 " + calcRes.fontSize + " Tf 0 g\n\ 2792 BT\n"; 2793 stream += calcRes.text; 2794 stream += "ET\n\ 2795 Q\n\ 2796 EMC\n"; 2797 xobj.stream = stream; 2798 return xobj; 2799 }, 2800 2801 YesNormal: function YesNormal(formObject) { 2802 var xobj = AcroForm.createFormXObject(formObject); 2803 var stream = ""; 2804 formObject.Q = 1; // set text-alignment as centered 2805 var calcRes = AcroForm.internal.calculateX(formObject, "3", "ZapfDingbats", AcroForm.Appearance.internal.getHeight(formOb
2805ject) * 0.9); 2806 stream += "1 g\n\ 28070 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ 2808f\n\ 2809q\n\ 28100 0 1 rg\n\ 28110 0 " + (AcroForm.Appearance.internal.getWidth(formObject) - 1) + " " + (AcroForm.Appearance.internal.getHeight(formObject) - 1) + " re\n\ 2812W\n\ 2813n\n\ 28140 g\n\ 2815BT\n\ 2816/F13 " + calcRes.fontSize + " Tf 0 g\n"; 2817 stream += calcRes.text; 2818 stream += "ET\n\ 2819 Q\n"; 2820 xobj.stream = stream; 2821 return xobj; 2822 }, 2823 2824 /** 2825 * Returns the standard Off Appearance for a CheckBox 2826 * @returns {AcroForm.FormXObject} 2827 */ 2828 OffPushDown: function OffPushDown(formObject) { 2829 var xobj = AcroForm.createFormXObject(formObject); 2830 var stream = ""; 2831 stream += "0.749023 g\n\ 2832 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ 2833 f\n"; 2834 xobj.stream = stream; 2835 return xobj; 2836 } 2837 }, 2838 2839 RadioButton: { 2840 Circle: { 2841 createAppearanceStream: function createAppearanceStream(name) { 2842 var appearanceStreamContent = { 2843 D: { 2844 'Off': AcroForm.Appearance.RadioButton.Circle.OffPushDown 2845 }, 2846 N: {} 2847 }; 2848 appearanceStreamContent.N[name] = AcroForm.Appearance.RadioButton.Circle.YesNormal; 2849 appearanceStreamContent.D[name] = AcroForm.Appearance.RadioButton.Circle.YesPushDown; 2850 return appearanceStreamContent; 2851 }, 2852 createMK: function createMK() { 2853 return "<< /CA (l)>>"; 2854 }, 2855 2856 YesNormal: function YesNormal(formObject) { 2857 var xobj = AcroForm.createFormXObject(formObject); 2858 var stream = ""; 2859 // Make the Radius of the Circle relative to min(height, width) of formObject 2860 var DotRadius = AcroForm.Appearance.internal.getWidth(formObject) <= AcroForm.Appearance.internal.getHeight(formObject) ? AcroForm.Appearance.internal.getWidth(formObject) / 4 : AcroForm.Appearance.internal.getHeight(formObject) / 4; 2861 // The Borderpadding... 2862 DotRadius *= 0.9; 2863 var c = AcroForm.Appearance.internal.Bezier_C; 2864 /* 2865 The Following is a Circle created with Bezier-Curves. 2866 */ 2867 stream += "q\n\ 28681 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ 2869" + DotRadius + " 0 m\n\ 2870" + DotRadius + " " + DotRadius * c + " " + DotRadius * c + " " + DotRadius + " 0 " + DotRadius + " c\n\ 2871-" + DotRadius * c + " " + DotRadius + " -" + DotRadius + " " + DotRadius * c + " -" + DotRadius + " 0 c\n\ 2872-" + DotRadius + " -" + DotRadius * c + " -" + DotRadius * c + " -" + DotRadius + " 0 -" + DotRadius + " c\n\ 2873" + DotRadius * c + " -" + DotRadius + " " + DotRadius + " -" + DotRadius * c + " " + DotRadius + " 0 c\n\ 2874f\n\ 2875Q\n"; 2876 xobj.stream = stream; 2877 return xobj; 2878 }, 2879 YesPushDown: function YesPushDown(formObject) { 2880 var xobj = AcroForm.createFormXObject(formObject); 2881 var stream = ""; 2882 var DotRadius = AcroForm.Appearance.internal.getWidth(formObject) <= AcroForm.Appearance.internal.getHeight(formObject) ? AcroForm.Appearance.internal.getWidth(formObject) / 4 : AcroForm.Appearance.internal.getHeight(formObject) / 4; 2883 // The Borderpadding... 2884 DotRadius *= 0.9; 2885 // Save results for later use; no need to waste processor ticks on doing math 2886 var k = DotRadius * 2; 2887 // var c = AcroForm.Appearance.internal.Bezier_C; 2888 var kc = k * AcroForm.Appearance.internal.Bezier_C; 2889 var dc = DotRadius * AcroForm.Appearance.internal.Bezier_C; 2890 // stream += "0.749023 g\n\ 2891 // q\n\ 2892 // 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formOb
2892ject) / 2 + " cm\n\ 2893 // " + DotRadius * 2 + " 0 m\n\ 2894 // " + DotRadius * 2 + " " + DotRadius * 2 * c + " " + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 " + DotRadius * 2 + " c\n\ 2895 // -" + DotRadius * 2 * c + " " + DotRadius * 2 + " -" + DotRadius * 2 + " " + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 c\n\ 2896 // -" + DotRadius * 2 + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 -" + DotRadius * 2 + " c\n\ 2897 // " + DotRadius * 2 * c + " -" + DotRadius * 2 + " " + DotRadius * 2 + " -" + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 c\n\ 2898 // f\n\ 2899 // Q\n\ 2900 // 0 g\n\ 2901 // q\n\ 2902 // 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ 2903 // " + DotRadius + " 0 m\n\ 2904 // " + DotRadius + " " + DotRadius * c + " " + DotRadius * c + " " + DotRadius + " 0 " + DotRadius + " c\n\ 2905 // -" + DotRadius * c + " " + DotRadius + " -" + DotRadius + " " + DotRadius * c + " -" + DotRadius + " 0 c\n\ 2906 // -" + DotRadius + " -" + DotRadius * c + " -" + DotRadius * c + " -" + DotRadius + " 0 -" + DotRadius + " c\n\ 2907 // " + DotRadius * c + " -" + DotRadius + " " + DotRadius + " -" + DotRadius * c + " " + DotRadius + " 0 c\n\ 2908 // f\n\ 2909 // Q\n"; 2910 2911 // FASTER VERSION with less processor ticks spent on math operations 2912 stream += "0.749023 g\n\ 2913 q\n\ 2914 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ 2915" + k + " 0 m\n\ 2916" + k + " " + kc + " " + kc + " " + k + " 0 " + k + " c\n\ 2917-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c\n\ 2918-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c\n\ 2919" + kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c\n\ 2920 f\n\ 2921 Q\n\ 2922 0 g\n\ 2923 q\n\ 2924 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ 2925" + DotRadius + " 0 m\n\ 2926" + DotRadius + " " + dc + " " + dc + " " + DotRadius + " 0 " + DotRadius + " c\n\ 2927-" + dc + " " + DotRadius + " -" + DotRadius + " " + dc + " -" + DotRadius + " 0 c\n\ 2928-" + DotRadius + " -" + dc + " -" + dc + " -" + DotRadius + " 0 -" + DotRadius + " c\n\ 2929" + dc + " -" + DotRadius + " " + DotRadius + " -" + dc + " " + DotRadius + " 0 c\n\ 2930 f\n\ 2931 Q\n"; 2932 xobj.stream = stream; 2933 return xobj; 2934 }, 2935 OffPushDown: function OffPushDown(formObject) { 2936 var xobj = AcroForm.createFormXObject(formObject); 2937 var stream = ""; 2938 var DotRadius = AcroForm.Appearance.internal.getWidth(formObject) <= AcroForm.Appearance.internal.getHeight(formObject) ? AcroForm.Appearance.internal.getWidth(formObject) / 4 : AcroForm.Appearance.internal.getHeight(formObject) / 4; 2939 // The Borderpadding... 2940 DotRadius *= 0.9; 2941 // Save results for later use; no need to waste processor ticks on doing math 2942 var k = DotRadius * 2; 2943 // var c = AcroForm.Appearance.internal.Bezier_C; 2944 var kc = k * AcroForm.Appearance.internal.Bezier_C; 2945 // stream += "0.749023 g\n\ 2946 // q\n\ 2947 // 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ 2948 // " + DotRadius * 2 + " 0 m\n\ 2949 // " + DotRadius * 2 + " " + DotRadius * 2 * c + " " + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 " + DotRadius * 2 + " c\n\ 2950 // -" + DotRadius * 2 * c + " " + DotRadius * 2 + " -" + DotRadius * 2 + " " + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 c\n\ 2951 // -" + DotRadius * 2 + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 -" + DotRadius * 2 + " c\n\ 2952 // " + DotRadius * 2 * c + " -" + DotRadius * 2 + " " + DotRadius * 2 + " -" + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 c\n\ 2953 // f\n\ 2954 // Q\n"; 2955 2956 // FASTER VERSION with less processor ticks spent on math operations 2957 stream += "0.749023 g\n\ 2958 q\n\ 2959 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formOb
2959ject) / 2 + " cm\n\ 2960" + k + " 0 m\n\ 2961" + k + " " + kc + " " + kc + " " + k + " 0 " + k + " c\n\ 2962-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c\n\ 2963-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c\n\ 2964" + kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c\n\ 2965 f\n\ 2966 Q\n"; 2967 xobj.stream = stream; 2968 return xobj; 2969 } 2970 }, 2971 2972 Cross: { 2973 /** 2974 * Creates the Actual AppearanceDictionary-References 2975 * @param name 2976 * @returns 2977 */ 2978 createAppearanceStream: function createAppearanceStream(name) { 2979 var appearanceStreamContent = { 2980 D: { 2981 'Off': AcroForm.Appearance.RadioButton.Cross.OffPushDown 2982 }, 2983 N: {} 2984 }; 2985 appearanceStreamContent.N[name] = AcroForm.Appearance.RadioButton.Cross.YesNormal; 2986 appearanceStreamContent.D[name] = AcroForm.Appearance.RadioButton.Cross.YesPushDown; 2987 return appearanceStreamContent; 2988 }, 2989 createMK: function createMK() { 2990 return "<< /CA (8)>>"; 2991 }, 2992 2993 YesNormal: function YesNormal(formObject) { 2994 var xobj = AcroForm.createFormXObject(formObject); 2995 var stream = ""; 2996 var cross = AcroForm.Appearance.internal.calculateCross(formObject); 2997 stream += "q\n\ 2998 1 1 " + (AcroForm.Appearance.internal.getWidth(formObject) - 2) + " " + (AcroForm.Appearance.internal.getHeight(formObject) - 2) + " re\n\ 2999 W\n\ 3000 n\n\ 3001 " + cross.x1.x + " " + cross.x1.y + " m\n\ 3002 " + cross.x2.x + " " + cross.x2.y + " l\n\ 3003 " + cross.x4.x + " " + cross.x4.y + " m\n\ 3004 " + cross.x3.x + " " + cross.x3.y + " l\n\ 3005 s\n\ 3006 Q\n"; 3007 xobj.stream = stream; 3008 return xobj; 3009 }, 3010 YesPushDown: function YesPushDown(formObject) { 3011 var xobj = AcroForm.createFormXObject(formObject); 3012 var cross = AcroForm.Appearance.internal.calculateCross(formObject); 3013 var stream = ""; 3014 stream += "0.749023 g\n\ 3015 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ 3016 f\n\ 3017 q\n\ 3018 1 1 " + (AcroForm.Appearance.internal.getWidth(formObject) - 2) + " " + (AcroForm.Appearance.internal.getHeight(formObject) - 2) + " re\n\ 3019 W\n\ 3020 n\n\ 3021 " + cross.x1.x + " " + cross.x1.y + " m\n\ 3022 " + cross.x2.x + " " + cross.x2.y + " l\n\ 3023 " + cross.x4.x + " " + cross.x4.y + " m\n\ 3024 " + cross.x3.x + " " + cross.x3.y + " l\n\ 3025 s\n\ 3026 Q\n"; 3027 xobj.stream = stream; 3028 return xobj; 3029 }, 3030 OffPushDown: function OffPushDown(formObject) { 3031 var xobj = AcroForm.createFormXObject(formObject); 3032 var stream = ""; 3033 stream += "0.749023 g\n\ 3034 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ 3035 f\n"; 3036 xobj.stream = stream; 3037 return xobj; 3038 } 3039 } 3040 }, 3041 3042 /** 3043 * Returns the standard Appearance 3044 * @returns {AcroForm.FormXObject} 3045 */ 3046 createDefaultAppearanceStream: function createDefaultAppearanceStream(formObject) { 3047 var stream = ""; 3048 // Set Helvetica to Standard Font (size: auto) 3049 // Color: Black 3050 stream += "/Helv 0 Tf 0 g"; 3051 return stream; 3052 } 3053}; 3054 3055AcroForm.Appearance.internal = { 3056 Bezier_C: 0.551915024494, 3057 3058 calculateCross: function calculateCross(formObject) { 3059 var min = function min(x, y) { 3060 return x > y ? y : x; 3061 }; 3062 3063 var width = AcroForm.Appearance.internal.getWidth(formObject); 3064 var height = AcroForm.Appearance.internal.getHeight(formOb
3064ject); 3065 var a = min(width, height); 3066 var crossSize = a; 3067 var borderPadding = 2; // The Padding in px 3068 3069 3070 var cross = { 3071 x1: { // upperLeft 3072 x: (width - a) / 2, 3073 y: (height - a) / 2 + a }, 3074 x2: { // lowerRight 3075 x: (width - a) / 2 + a, 3076 y: (height - a) / 2 //borderPadding 3077 }, 3078 x3: { // lowerLeft 3079 x: (width - a) / 2, 3080 y: (height - a) / 2 //borderPadding 3081 }, 3082 x4: { // upperRight 3083 x: (width - a) / 2 + a, 3084 y: (height - a) / 2 + a } 3085 }; 3086 3087 return cross; 3088 } 3089}; 3090AcroForm.Appearance.internal.getWidth = function (formObject) { 3091 return formObject.Rect[2]; //(formObject.Rect[2] - formObject.Rect[0]) || 0; 3092}; 3093AcroForm.Appearance.internal.getHeight = function (formObject) { 3094 return formObject.Rect[3]; //(formObject.Rect[1] - formObject.Rect[3]) || 0; 3095}; 3096 3097// ########################## 3098 3099//### For inheritance: 3100AcroForm.internal.inherit = function (child, parent) { 3101 var ObjectCreate = Object.create || function (o) { 3102 var F = function F() {}; 3103 F.prototype = o; 3104 return new F(); 3105 }; 3106 child.prototype = Object.create(parent.prototype); 3107 child.prototype.constructor = child; 3108}; 3109 3110// ### Handy Functions: 3111 3112AcroForm.internal.arrayToPdfArray = function (array) { 3113 if (Array.isArray(array)) { 3114 var content = ' ['; 3115 for (var i in array) { 3116 var element = array[i].toString(); 3117 content += element; 3118 content += i < array.length - 1 ? ' ' : ''; 3119 } 3120 content += ']'; 3121 3122 return content; 3123 } 3124}; 3125 3126AcroForm.internal.toPdfString = function (string) { 3127 string = string || ""; 3128 3129 // put Bracket at the Beginning of the String 3130 if (string.indexOf('(') !== 0) { 3131 string = '(' + string; 3132 } 3133 3134 if (string.substring(string.length - 1) != ')') { 3135 string += '('; 3136 } 3137 return string; 3138}; 3139 3140// ########################## 3141// Classes 3142// ########################## 3143 3144 3145AcroForm.PDFObject = function () { 3146 // The Object ID in the PDF Object Model 3147 // todo 3148 var _objId; 3149 Object.defineProperty(this, 'objId', { 3150 get: function get() { 3151 if (!_objId) { 3152 if (this.internal) { 3153 _objId = this.internal.newObjectDeferred(); 3154 } else if (jsPDF.API.acroformPlugin.internal) { 3155 // todo - find better option, that doesn't rely on a Global Static var 3156 _objId = jsPDF.API.acroformPlugin.internal.newObjectDeferred(); 3157 } 3158 } 3159 if (!_objId) { 3160 console.log("Couldn't create Object ID"); 3161 } 3162 return _objId; 3163 }, 3164 configurable: false 3165 }); 3166}; 3167 3168AcroForm.PDFObject.prototype.toString = function () { 3169 return this.objId + " 0 R"; 3170}; 3171 3172AcroForm.PDFObject.prototype.getString = function () { 3173 var res = this.objId + " 0 obj\n<<"; 3174 var content = this.getContent(); 3175 3176 res += content + ">>\n"; 3177 if (this.stream) { 3178 res += "stream\n"; 3179 res += this.stream; 3180 res += "endstream\n"; 3181 } 3182 res += "endobj\n"; 3183 return res; 3184}; 3185 3186AcroForm.PDFObject.prototype.getContent = function () { 3187 /** 3188 * Prints out all enumerable Variables from the Object 3189 * @param fieldObject 3190 * @returns {string} 3191 */ 3192 var createContentFromFieldObject = function createContentFromFieldObject(fieldObject) { 3193 var content = ''; 3194 3195 var keys = Object.keys(fieldObject).filter(function (key) { 3196 return key != 'content' && key != 'appearanceStreamContent' && key.substring(0, 1) != "_"; 3197 }); 3198 3199 for (var i in keys) { 3200 var key = keys[i]; 3201 var value = fieldObject[key]; 3202 3203 /*if (key == 'Rect' && value) { 3204 value = AcroForm.internal.calculateCoordinates.call(jsPDF.API.acroformPlugin.internal, value); 3205 }*/ 3206 3207 if (value) { 3208 if (Array.isArray(value)) { 3209 content += '/' + key + ' ' + AcroForm.internal.arrayToPdfArray(value) + "\n"; 3210 } else if (value instanceof AcroForm.PDFObject) { 3211 // In case it is a reference to another PDFObject, take the referennce number 3212 content += '/' + key + ' ' + value.objId + " 0 R" + "\n"; 3213 } else { 3214 content += '/' + key + ' ' + value + '\n'; 3215 } 3216 } 3217 } 3218 return content; 3219 }; 3220 3221 var object = ""; 3222 3223 object += createContentFromFieldObject(this); 3224 return object; 3225}; 3226 3227AcroForm.FormXObject = function () { 3228 AcroForm.PDFObject.call(this); 3229 this.Type = "/XObject"; 3230 this.Subtype = "/Form"; 3231 this.FormType = 1; 3232 this.BBox; 3233 this.Matrix; 3234 this.Resources = "2 0 R"; 3235 this.PieceInfo; 3236 var _stream; 3237 Object.defineProperty(this, 'Length', { 3238 enumerable: true, 3239 get: function get() { 3240 return _stream !== undefined ? _stream.length : 0; 3241 } 3242 });
3243 Object.defineProperty(this, 'stream', { 3244 enumerable: false, 3245 set: function set(val) { 3246 _stream = val; 3247 }, 3248 get: function get() { 3249 if (_stream) { 3250 return _stream; 3251 } else { 3252 return null; 3253 } 3254 } 3255 }); 3256}; 3257 3258AcroForm.internal.inherit(AcroForm.FormXObject, AcroForm.PDFObject); 3259 3260AcroForm.AcroFormDictionary = function () { 3261 AcroForm.PDFObject.call(this); 3262 var _Kids = []; 3263 Object.defineProperty(this, 'Kids', { 3264 enumerable: false, 3265 configurable: true, 3266 get: function get() { 3267 if (_Kids.length > 0) { 3268 return _Kids; 3269 } else { 3270 return; 3271 } 3272 } 3273 }); 3274 Object.defineProperty(this, 'Fields', { 3275 enumerable: true, 3276 configurable: true, 3277 get: function get() { 3278 return _Kids; 3279 } 3280 }); 3281 // Default Appearance 3282 this.DA; 3283}; 3284 3285AcroForm.internal.inherit(AcroForm.AcroFormDictionary, AcroForm.PDFObject); 3286 3287// ##### The Objects, the User can Create: 3288 3289 3290// The Field Object contains the Variables, that every Field needs 3291// Rectangle for Appearance: lower_left_X, lower_left_Y, width, height 3292AcroForm.Field = function () { 3293 'use strict'; 3294 3295 AcroForm.PDFObject.call(this); 3296 3297 var _Rect; 3298 Object.defineProperty(this, 'Rect', { 3299 enumerable: true, 3300 configurable: false, 3301 get: function get() { 3302 if (!_Rect) { 3303 return; 3304 } 3305 var tmp = _Rect; 3306 //var calculatedRes = AcroForm.internal.calculateCoordinates(_Rect); // do later! 3307 return tmp; 3308 }, 3309 set: function set(val) { 3310 _Rect = val; 3311 } 3312 }); 3313 3314 var _FT = ""; 3315 Object.defineProperty(this, 'FT', { 3316 enumerable: true, 3317 set: function set(val) { 3318 _FT = val; 3319 }, 3320 get: function get() { 3321 return _FT; 3322 } 3323 }); 3324 /** 3325 * The Partial name of the Field Object. 3326 * It has to be unique. 3327 */ 3328 var _T; 3329 3330 Object.defineProperty(this, 'T', { 3331 enumerable: true, 3332 configurable: false, 3333 set: function set(val) { 3334 _T = val; 3335 }, 3336 get: function get() { 3337 if (!_T || _T.length < 1) { 3338 if (this instanceof AcroForm.ChildClass) { 3339 // In case of a Child from a Radio´Group, you don't need a FieldName!!! 3340 return; 3341 } 3342 return "(FieldObject" + AcroForm.Field.FieldNum++ + ")"; 3343 } 3344 if (_T.substring(0, 1) == "(" && _T.substring(_T.length - 1)) { 3345 return _T; 3346 } 3347 return "(" + _T + ")"; 3348 } 3349 }); 3350 3351 var _DA; 3352 // Defines the default appearance (Needed for variable Text) 3353 Object.defineProperty(this, 'DA', { 3354 enumerable: true, 3355 get: function get() { 3356 if (!_DA) { 3357 return; 3358 } 3359 return '(' + _DA + ')'; 3360 }, 3361 set: function set(val) { 3362 _DA = val; 3363 } 3364 }); 3365 3366 var _DV; 3367 // Defines the default value 3368 Object.defineProperty(this, 'DV', { 3369 enumerable: true, 3370 configurable: true, 3371 get: function get() { 3372 if (!_DV) { 3373 return; 3374 } 3375 return _DV; 3376 }, 3377 set: function set(val) { 3378 _DV = val; 3379 } 3380 }); 3381 3382 //this.Type = "/Annot"; 3383 //this.Subtype = "/Widget"; 3384 Object.defineProperty(this, 'Type', { 3385 enumerable: true, 3386 get: function get() { 3387 return this.hasAnnotation ? "/Annot" : null; 3388 } 3389 }); 3390 3391 Object.defineProperty(this, 'Subtype', { 3392 enumerable: true, 3393 get: function get() { 3394 return this.hasAnnotation ? "/Widget" : null; 3395 } 3396 }); 3397 3398 /** 3399 * 3400 * @type {Array} 3401 */ 3402 this.BG; 3403 3404 Object.defineProperty(this, 'hasAnnotation', { 3405 enumerable: false, 3406 get: function get() { 3407 if (this.Rect || this.BC || this.BG) { 3408 return true; 3409 } 3410 return false;
3411 } 3412 }); 3413 3414 Object.defineProperty(this, 'hasAppearanceStream', { 3415 enumerable: false, 3416 configurable: true, 3417 writable: true 3418 }); 3419 3420 Object.defineProperty(this, 'page', { 3421 enumerable: false, 3422 configurable: true, 3423 writable: true 3424 }); 3425}; 3426AcroForm.Field.FieldNum = 0; 3427 3428AcroForm.internal.inherit(AcroForm.Field, AcroForm.PDFObject); 3429 3430AcroForm.ChoiceField = function () { 3431 AcroForm.Field.call(this); 3432 // Field Type = Choice Field 3433 this.FT = "/Ch"; 3434 // options 3435 this.Opt = []; 3436 this.V = '()'; 3437 // Top Index 3438 this.TI = 0; 3439 /** 3440 * Defines, whether the 3441 * @type {boolean} 3442 */ 3443 this.combo = false; 3444 /** 3445 * Defines, whether the Choice Field is an Edit Field. 3446 * An Edit Field is automatically an Combo Field. 3447 */ 3448 Object.defineProperty(this, 'edit', { 3449 enumerable: true, 3450 set: function set(val) { 3451 if (val == true) { 3452 this._edit = true; 3453 // ComboBox has to be true 3454 this.combo = true; 3455 } else { 3456 this._edit = false; 3457 } 3458 }, 3459 get: function get() { 3460 if (!this._edit) { 3461 return false; 3462 } 3463 return this._edit; 3464 }, 3465 configurable: false 3466 }); 3467 this.hasAppearanceStream = true; 3468 Object.defineProperty(this, 'V', { 3469 get: function get() { 3470 AcroForm.internal.toPdfString(); 3471 } 3472 }); 3473}; 3474AcroForm.internal.inherit(AcroForm.ChoiceField, AcroForm.Field); 3475window["ChoiceField"] = AcroForm.ChoiceField; 3476 3477AcroForm.ListBox = function () { 3478 AcroForm.ChoiceField.call(this); 3479 //var combo = true; 3480}; 3481AcroForm.internal.inherit(AcroForm.ListBox, AcroForm.ChoiceField); 3482window["ListBox"] = AcroForm.ListBox; 3483 3484AcroForm.ComboBox = function () { 3485 AcroForm.ListBox.call(this); 3486 this.combo = true; 3487}; 3488AcroForm.internal.inherit(AcroForm.ComboBox, AcroForm.ListBox); 3489window["ComboBox"] = AcroForm.ComboBox; 3490 3491AcroForm.EditBox = function () { 3492 AcroForm.ComboBox.call(this); 3493 this.edit = true; 3494}; 3495AcroForm.internal.inherit(AcroForm.EditBox, AcroForm.ComboBox); 3496window["EditBox"] = AcroForm.EditBox; 3497 3498AcroForm.Button = function () { 3499 AcroForm.Field.call(this); 3500 this.FT = "/Btn"; 3501 //this.hasAnnotation = true; 3502}; 3503AcroForm.internal.inherit(AcroForm.Button, AcroForm.Field); 3504window["Button"] = AcroForm.Button; 3505 3506AcroForm.PushButton = function () { 3507 AcroForm.Button.call(this); 3508 this.pushbutton = true; 3509}; 3510AcroForm.internal.inherit(AcroForm.PushButton, AcroForm.Button); 3511window["PushButton"] = AcroForm.PushButton; 3512 3513AcroForm.RadioButton = function () { 3514 AcroForm.Button.call(this); 3515 this.radio = true; 3516 var _Kids = []; 3517 Object.defineProperty(this, 'Kids', { 3518 enumerable: true, 3519 get: function get() { 3520 if (_Kids.length > 0) { 3521 return _Kids; 3522 } 3523 } 3524 }); 3525 3526 Object.defineProperty(this, '__Kids', { 3527 get: function get() { 3528 return _Kids; 3529 } 3530 }); 3531 3532 var _noToggleToOff; 3533 3534 Object.defineProperty(this, 'noToggleToOff', { 3535 enumerable: false, 3536 get: function get() { 3537 return _noToggleToOff; 3538 }, 3539 set: function set(val) { 3540 _noToggleToOff = val; 3541 } 3542 }); 3543 3544 //this.hasAnnotation = false; 3545}; 3546AcroForm.internal.inherit(AcroForm.RadioButton, AcroForm.Button); 3547window["RadioButton"] = AcroForm.RadioButton; 3548 3549/* 3550 * The Child classs of a RadioButton (the radioGroup) 3551 * -> The single Buttons 3552 */ 3553AcroForm.ChildClass = function (parent, name) { 3554 AcroForm.Field.call(this); 3555 this.Parent = parent; 3556 3557 // todo: set AppearanceType as variable that can be set from the outside... 3558 this._AppearanceType = AcroForm.Appearance.RadioButton.Circle; // The Default appearanceType is the Circle 3559 this.appearanceStreamContent = this._AppearanceType.createAppearanceStream(name); 3560 3561 // Set Print in the Annot Flag 3562 this.F = AcroForm.internal.setBitPosition(this.F, 3, 1); 3563 3564 // Set AppearanceCharacteristicsDictionary with default appearance if field is not interacting with user 3565 this.MK = this._AppearanceType.createMK(); // (8) -> Cross, (1)-> Circle, ()-> nothing 3566 3567 // Default Appearance is Off 3568 this.AS = "/Off"; // + name; 3569 3570 this._Name = name; 3571}; 3572AcroForm.internal.inherit(AcroForm.ChildClass, AcroForm.Field); 3573 3574AcroForm.RadioButton.prototype.setAppearance = function (appearance) { 3575 if (!('createAppearanceStream' in appearance && 'createMK' in appearance)) { 3576 console.log("Couldn't assign Appearance to RadioButton. Appearance was Invalid!"); 3577 return; 3578 } 3579 for (var i in this.__Kids) { 3580 var child = this.__Kids[i]; 3581 3582 child.appearanceStreamContent = appearance.createAppearanceStream(child._Name); 3583 child.MK = appearance.createMK(); 3584 } 3585}; 3586 3587AcroForm.RadioButton.prototype.createOption = function (name) { 3588 var parent = this; 3589 var kidCount = this.__Kids.length; 3590 3591 // Create new Child for RadioGroup 3592 var child = new AcroForm.ChildClass(parent, name); 3593 // Add to Parent 3594 this.__Kids.push(child); 3595 3596 jsPDF.API.addField(child); 3597 3598 return child; 3599}; 3600 3601AcroForm.CheckBox = function () { 3602 Button.call(this); 3603 this.appearanceStreamContent = AcroForm.Appearance.CheckBox.createAppearanceStream(); 3604 this.MK = AcroForm.Appearance.CheckBox.createMK(); 3605 this.AS = "/On"; 3606 this.V = "/On"; 3607}; 3608AcroForm.internal.inherit(AcroForm.CheckBox, AcroForm.Button); 3609window["CheckBox"] = AcroForm.CheckBox; 3610 3611AcroForm.TextField = function () { 3612 AcroForm.Field.call(this); 3613 this.DA = AcroForm.Appearance.createDefaultAppearanceStream();
3614 this.F = 4; 3615 var _V; 3616 Object.defineProperty(this, 'V', { 3617 get: function get() { 3618 if (_V) { 3619 return "(" + _V + ")"; 3620 } else { 3621 return _V; 3622 } 3623 }, 3624 enumerable: true, 3625 set: function set(val) { 3626 _V = val; 3627 } 3628 }); 3629 3630 var _DV; 3631 Object.defineProperty(this, 'DV', { 3632 get: function get() { 3633 if (_DV) { 3634 return "(" + _DV + ")"; 3635 } else { 3636 return _DV; 3637 } 3638 }, 3639 enumerable: true, 3640 set: function set(val) { 3641 _DV = val; 3642 } 3643 }); 3644 3645 var _multiline = false; 3646 Object.defineProperty(this, 'multiline', { 3647 enumerable: false, 3648 get: function get() { 3649 return _multiline; 3650 }, 3651 set: function set(val) { 3652 _multiline = val; 3653 } 3654 }); 3655 3656 //this.multiline = false; 3657 //this.password = false; 3658 /** 3659 * For PDF 1.4 3660 * @type {boolean} 3661 */ 3662 //this.fileSelect = false; 3663 /** 3664 * For PDF 1.4 3665 * @type {boolean} 3666 */ 3667 //this.doNotSpellCheck = false; 3668 /** 3669 * For PDF 1.4 3670 * @type {boolean} 3671 */ 3672 //this.doNotScroll = false; 3673 3674 var _MaxLen = false; 3675 Object.defineProperty(this, 'MaxLen', { 3676 enumerable: true, 3677 get: function get() { 3678 return _MaxLen; 3679 }, 3680 set: function set(val) { 3681 _MaxLen = val; 3682 } 3683 }); 3684 3685 Object.defineProperty(this, 'hasAppearanceStream', { 3686 enumerable: false, 3687 get: function get() { 3688 return this.V || this.DV; 3689 } 3690 }); 3691}; 3692AcroForm.internal.inherit(AcroForm.TextField, AcroForm.Field); 3693window["TextField"] = AcroForm.TextField; 3694 3695AcroForm.PasswordField = function () { 3696 TextField.call(this); 3697 Object.defineProperty(this, 'password', { 3698 value: true, 3699 enumerable: false, 3700 configurable: false, 3701 writable: false 3702 }); 3703}; 3704AcroForm.internal.inherit(AcroForm.PasswordField, AcroForm.TextField); 3705window["PasswordField"] = AcroForm.PasswordField; 3706 3707// ############ internal functions 3708 3709/* 3710 * small workaround for calculating the TextMetric approximately 3711 * @param text 3712 * @param fontsize 3713 * @returns {TextMetrics} (Has Height and Width) 3714 */ 3715AcroForm.internal.calculateFontSpace = function (text, fontsize, fonttype) { 3716 var fonttype = fonttype || "helvetica"; 3717 //re-use canvas object for speed improvements 3718 var canvas = AcroForm.internal.calculateFontSpace.canvas || (AcroForm.internal.calculateFontSpace.canvas = document.createElement('canvas')); 3719 3720 var context = canvas.getContext('2d'); 3721 context.save(); 3722 var newFont = fontsize + " " + fonttype; 3723 context.font = newFont; 3724 var res = context.measureText(text); 3725 context.fontcolor = 'black'; 3726 // Calculate height: 3727 var context = canvas.getContext('2d'); 3728 res.height = context.measureText("3").width * 1.5; // 3 because in ZapfDingbats its a Hook and a 3 in normal fonts 3729 context.restore(); 3730 3731 var width = res.width; 3732 3733 return res; 3734}; 3735 3736AcroForm.internal.calculateX = function (formObject, text, font, maxFontSize) { 3737 var maxFontSize = maxFontSize || 12; 3738 var font = font || "helvetica"; 3739 var returnValue = { 3740 text: "", 3741 fontSize: "" 3742 }; 3743 // Remove Brackets 3744 text = text.substr(0, 1) == '(' ? text.substr(1) : text; 3745 text = text.substr(text.length - 1) == ')' ? text.substr(0, text.length - 1) : text; 3746 // split into array of words 3747 var textSplit = text.split(' '); 3748 3749 /** 3750 * the color could be ((alpha)||(r,g,b)||(c,m,y,k)) 3751 * @type {string} 3752 */ 3753 var color = "0 g\n"; 3754 var fontSize = maxFontSize; // The Starting fontSize (The Maximum) 3755 var lineSpacing = 2; 3756 var borderPadding = 2; 3757 3758 var height = AcroForm.Appearance.internal.getHeight(formObject) || 0; 3759 height = height < 0 ? -height : height; 3760 var width = AcroForm.Appearance.internal.getWidth(formObject) || 0; 3761 width = width < 0 ? -width : width; 3762 3763 var isSmallerThanWidth = function isSmallerThanWidth(i, lastLine, fontSize) { 3764 if (i + 1 < textSplit.length) { 3765 var tmp = lastLine + " " + textSplit[i + 1]; 3766 var TextWidth = AcroForm.internal.calculateFontSpace(tmp, fontSize + "px", font).width; 3767 var FieldWidth = width - 2 * borderPadding; 3768 return TextWidth <= FieldWidth; 3769 } else { 3770 return false;
3771 } 3772 }; 3773 3774 fontSize++; 3775 FontSize: while (true) { 3776 var text = ""; 3777 fontSize--; 3778 var textHeight = AcroForm.internal.calculateFontSpace("3", fontSize + "px", font).height; 3779 var startY = formObject.multiline ? height - fontSize : (height - textHeight) / 2; 3780 startY += lineSpacing; 3781 var startX = -borderPadding; 3782 3783 var lastX = startX, 3784 lastY = startY; 3785 var firstWordInLine = 0, 3786 lastWordInLine = 0; 3787 var lastLength = 0; 3788 3789 var y = 0; 3790 if (fontSize == 0) { 3791 // In case, the Text doesn't fit at all 3792 fontSize = 12; 3793 text = "(...) Tj\n"; 3794 text += "% Width of Text: " + AcroForm.internal.calculateFontSpace(text, "1px").width + ", FieldWidth:" + width + "\n"; 3795 break; 3796 } 3797 3798 lastLength = AcroForm.internal.calculateFontSpace(textSplit[0] + " ", fontSize + "px", font).width; 3799 3800 var lastLine = ""; 3801 var lineCount = 0; 3802 Line: for (var i in textSplit) { 3803 lastLine += textSplit[i] + " "; 3804 // Remove last blank 3805 lastLine = lastLine.substr(lastLine.length - 1) == " " ? lastLine.substr(0, lastLine.length - 1) : lastLine; 3806 var key = parseInt(i); 3807 lastLength = AcroForm.internal.calculateFontSpace(lastLine + " ", fontSize + "px", font).width; 3808 var nextLineIsSmaller = isSmallerThanWidth(key, lastLine, fontSize); 3809 var isLastWord = i >= textSplit.length - 1; 3810 if (nextLineIsSmaller && !isLastWord) { 3811 lastLine += " "; 3812 continue; // Line 3813 } else if (!nextLineIsSmaller && !isLastWord) { 3814 if (!formObject.multiline) { 3815 continue FontSize; 3816 } else { 3817 if ((textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) { 3818 // If the Text is higher than the FieldObject 3819 continue FontSize; 3820 } 3821 lastWordInLine = key; 3822 // go on 3823 } 3824 } else if (isLastWord) { 3825 lastWordInLine = key; 3826 } else { 3827 if (formObject.multiline && (textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) { 3828 // If the Text is higher than the FieldObject 3829 continue FontSize; 3830 } 3831 } 3832 3833 var line = ''; 3834 3835 for (var x = firstWordInLine; x <= lastWordInLine; x++) { 3836 line += textSplit[x] + ' '; 3837 } 3838 3839 // Remove last blank 3840 line = line.substr(line.length - 1) == " " ? line.substr(0, line.length - 1) : line; 3841 //lastLength -= blankSpace.width; 3842 lastLength = AcroForm.internal.calculateFontSpace(line, fontSize + "px", font).width; 3843 3844 // Calculate startX 3845 switch (formObject.Q) { 3846 case 2: 3847 // Right justified 3848 startX = width - lastLength - borderPadding; 3849 break; 3850 case 1: 3851 // Q = 1 := Text-Alignment: Center 3852 startX = (width - lastLength) / 2; 3853 break; 3854 case 0: 3855 default: 3856 startX = borderPadding; 3857 break; 3858 } 3859 text += startX + ' ' + lastY + ' Td\n'; 3860 text += '(' + line + ') Tj\n'; 3861 // reset X in PDF 3862 text += -startX + ' 0 Td\n'; 3863 3864 // After a Line, adjust y position 3865 lastY = -(fontSize + lineSpacing); 3866 lastX = startX; 3867 3868 // Reset for next iteration step 3869 lastLength = 0; 3870 firstWordInLine = lastWordInLine + 1; 3871 lineCount++; 3872 3873 lastLine = ""; 3874 continue Line; 3875 } 3876 break; 3877 } 3878 3879 returnValue.text = text; 3880 returnValue.fontSize = fontSize; 3881 3882 return returnValue; 3883}; 3884 3885AcroForm.internal.calculateAppearanceStream = function (formObject) {
3886 if (formObject.appearanceStreamContent) { 3887 // If appearanceStream is already set, use it 3888 return formObject.appearanceStreamContent; 3889 } 3890 3891 if (!formObject.V && !formObject.DV) { 3892 return; 3893 } 3894 3895 // else calculate it 3896 3897 var stream = ''; 3898 3899 var text = formObject.V || formObject.DV; 3900 3901 var calcRes = AcroForm.internal.calculateX(formObject, text); 3902 3903 stream += '/Tx BMC\n' + 'q\n' + 3904 //color + '\n' + 3905 '/F1 ' + calcRes.fontSize + ' Tf\n' + 3906 // Text Matrix 3907 '1 0 0 1 0 0 Tm\n'; 3908 // Begin Text 3909 stream += 'BT\n'; 3910 stream += calcRes.text; 3911 // End Text 3912 stream += 'ET\n'; 3913 stream += 'Q\n' + 'EMC\n'; 3914 3915 var appearanceStreamContent = new AcroForm.createFormXObject(formObject); 3916 3917 appearanceStreamContent.stream = stream; 3918 3919 var appearance = { 3920 N: { 3921 'Normal': appearanceStreamContent 3922 } 3923 }; 3924 3925 return appearanceStreamContent; 3926}; 3927 3928/* 3929 * Converts the Parameters from x,y,w,h to lowerLeftX, lowerLeftY, upperRightX, upperRightY 3930 * @param x 3931 * @param y 3932 * @param w 3933 * @param h 3934 * @returns {*[]} 3935 */ 3936AcroForm.internal.calculateCoordinates = function (x, y, w, h) { 3937 var coordinates = {}; 3938 3939 if (this.internal) { 3940 var mmtopx = function mmtopx(x) { 3941 return x * this.internal.scaleFactor; 3942 }; 3943 3944 if (Array.isArray(x)) { 3945 x[0] = AcroForm.scale(x[0]); 3946 x[1] = AcroForm.scale(x[1]); 3947 x[2] = AcroForm.scale(x[2]); 3948 x[3] = AcroForm.scale(x[3]); 3949 3950 coordinates.lowerLeft_X = x[0] || 0; 3951 coordinates.lowerLeft_Y = mmtopx.call(this, this.internal.pageSize.height) - x[3] - x[1] || 0; 3952 coordinates.upperRight_X = x[0] + x[2] || 0; 3953 coordinates.upperRight_Y = mmtopx.call(this, this.internal.pageSize.height) - x[1] || 0; 3954 } else { 3955 x = AcroForm.scale(x); 3956 y = AcroForm.scale(y); 3957 w = AcroForm.scale(w); 3958 h = AcroForm.scale(h); 3959 coordinates.lowerLeft_X = x || 0; 3960 coordinates.lowerLeft_Y = this.internal.pageSize.height - y || 0; 3961 coordinates.upperRight_X = x + w || 0; 3962 coordinates.upperRight_Y = this.internal.pageSize.height - y + h || 0; 3963 } 3964 } else { 3965 // old method, that is fallback, if we can't get the pageheight, the coordinate-system starts from lower left 3966 if (Array.isArray(x)) { 3967 coordinates.lowerLeft_X = x[0] || 0; 3968 coordinates.lowerLeft_Y = x[1] || 0; 3969 coordinates.upperRight_X = x[0] + x[2] || 0; 3970 coordinates.upperRight_Y = x[1] + x[3] || 0; 3971 } else { 3972 coordinates.lowerLeft_X = x || 0; 3973 coordinates.lowerLeft_Y = y || 0; 3974 coordinates.upperRight_X = x + w || 0; 3975 coordinates.upperRight_Y = y + h || 0; 3976 } 3977 } 3978 3979 return [coordinates.lowerLeft_X, coordinates.lowerLeft_Y, coordinates.upperRight_X, coordinates.upperRight_Y]; 3980}; 3981 3982AcroForm.internal.calculateColor = function (r, g, b) { 3983 var color = new Array(3); 3984 color.r = r | 0; 3985 color.g = g | 0; 3986 color.b = b | 0; 3987 return color; 3988}; 3989 3990AcroForm.internal.getBitPosition = function (variable, position) { 3991 variable = variable || 0; 3992 var bitMask = 1; 3993 bitMask = bitMask << position - 1; 3994 return variable | bitMask; 3995}; 3996 3997AcroForm.internal.setBitPosition = function (variable, position, value) { 3998 variable = variable || 0; 3999 value = value || 1; 4000 4001 var bitMask = 1; 4002 bitMask = bitMask << position - 1; 4003 4004 if (value == 1) { 4005 // Set the Bit to 1 4006 var variable = variable | bitMask; 4007 } else { 4008 // Set the Bit to 0 4009 var variable = variable & ~bitMask; 4010 } 4011 4012 return variable; 4013}; 4014 4015/** 4016 * jsPDF addHTML PlugIn 4017 * Copyright (c) 2014 Diego Casorran 4018 * 4019 * Licensed under the MIT License. 4020 * http://opensource.org/licenses/mit-license 4021 */ 4022 4023(function (jsPDFAPI) { 4024 'use strict'; 4025 4026 /** 4027 * Renders an HTML element to canvas object which added to the PDF 4028 * 4029 * This feature requires [html2canvas](https://github.com/niklasvh/html2canvas) 4030 * or [rasterizeHTML](https://github.com/cburgmer/rasterizeHTML.js) 4031 * 4032 * @returns {jsPDF} 4033 * @name addHTML 4034 * @param element {Mixed} HTML Element, or anything supported by html2canvas. 4035 * @param x {Number} starting X coordinate in jsPDF instance's declared units. 4036 * @param y {Number} starting Y coordinate in jsPDF instance's declared units. 4037 * @param options {Object} Additional options, check the code below. 4038 * @param callback {Function} to call when the rendering has finished. 4039 * NOTE: Every parameter is optional except 'element' and 'callback', in such 4040 * case the image is positioned at 0x0 covering the whole PDF document 4041 * size. Ie, to easily take screenshots of webpages saving them to PDF. 4042 * @deprecated This is being replace with a vector-supporting API. See 4043 * [this link](https://cdn.rawgit.com/MrRio/jsPDF/master/examples/html2pdf/showcase_supported_html.html) 4044 */ 4045 4046 jsPDFAPI.addHTML = function (element, x, y, options, callback) { 4047 'use strict'; 4048 4049 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'); 4050 4051 if (typeof x !== 'number') { 4052 options = x; 4053 callback = y; 4054 } 4055 4056 if (typeof options === 'function') { 4057 callback = options; 4058 options = null; 4059 } 4060 4061 var I = this.internal, 4062 K = I.scaleFactor, 4063 W = I.pageSize.width, 4064 H = I.pageSize.height; 4065 4066 options = options || {}; 4067 options.onrendered = function (obj) { 4068 x = parseInt(x) || 0; 4069 y = parseInt(y) || 0; 4070 var dim = options.dim || {}; 4071 var h = dim.h || 0; 4072 var w = dim.w || Math.min(W, obj.width / K) - x; 4073 4074 var format = 'JPEG'; 4075 if (options.format) format = options.format; 4076 4077 if (obj.height > H && options.pagesplit) { 4078 var crop = function () { 4079 var cy = 0; 4080 while (1) { 4081 var canvas = document.createElement('canvas'); 4082 canvas.width = Math.min(W * K, obj.width); 4083 canvas.height = Math.min(H * K, obj.height - cy); 4084 var ctx = canvas.getContext('2d'); 4085 ctx.drawImage(obj, 0, cy, obj.width, canvas.height, 0, 0, canvas.width, canvas.height); 4086 var args = [canvas, x, cy ? 0 : y, canvas.width / K, canvas.height / K, format, null, 'SLOW']; 4087 this.addImage.apply(this, args); 4088 cy += canvas.height; 4089 if (cy >= obj.height) break; 4090 this.addPage(); 4091 }
4092 callback(w, cy, null, args); 4093 }.bind(this); 4094 if (obj.nodeName === 'CANVAS') { 4095 var img = new Image(); 4096 img.onload = crop; 4097 img.src = obj.toDataURL("image/png"); 4098 obj = img; 4099 } else { 4100 crop(); 4101 } 4102 } else { 4103 var alias = Math.random().toString(35); 4104 var args = [obj, x, y, w, h, format, alias, 'SLOW']; 4105 4106 this.addImage.apply(this, args); 4107 4108 callback(w, h, alias, args); 4109 } 4110 }.bind(this); 4111 4112 if (typeof html2canvas !== 'undefined' && !options.rstz) { 4113 return html2canvas(element, options); 4114 } 4115 4116 if (typeof rasterizeHTML !== 'undefined') { 4117 var meth = 'drawDocument'; 4118 if (typeof element === 'string') { 4119 meth = /^http/.test(element) ? 'drawURL' : 'drawHTML'; 4120 } 4121 options.width = options.width || W * K; 4122 return rasterizeHTML[meth](element, void 0, options).then(function (r) { 4123 options.onrendered(r.image); 4124 }, function (e) { 4125 callback(null, e); 4126 }); 4127 } 4128 4129 return null; 4130 }; 4131})(jsPDF.API); 4132 4133/** @preserve 4134 * jsPDF addImage plugin 4135 * Copyright (c) 2012 Jason Siefken, https://github.com/siefkenj/ 4136 * 2013 Chris Dowling, https://github.com/gingerchris 4137 * 2013 Trinh Ho, https://github.com/ineedfat 4138 * 2013 Edwin Alejandro Perez, https://github.com/eaparango 4139 * 2013 Norah Smith, https://github.com/burnburnrocket 4140 * 2014 Diego Casorran, https://github.com/diegocr 4141 * 2014 James Robb, https://github.com/jamesbrobb 4142 * 4143 * 4144 */ 4145 4146(function (jsPDFAPI) { 4147 'use strict'; 4148 4149 var namespace = 'addImage_', 4150 supported_image_types = ['jpeg', 'jpg', 'png']; 4151 4152 // Image functionality ported from pdf.js 4153 var putImage = function putImage(img) { 4154 4155 var objectNumber = this.internal.newObject(), 4156 out = this.internal.write, 4157 putStream = this.internal.putStream; 4158 4159 img['n'] = objectNumber; 4160 4161 out('<</Type /XObject'); 4162 out('/Subtype /Image'); 4163 out('/Width ' + img['w']); 4164 out('/Height ' + img['h']); 4165 if (img['cs'] === this.color_spaces.INDEXED) { 4166 out('/ColorSpace [/Indexed /DeviceRGB ' 4167 // if an indexed png defines more than one colour with transparency, we've created a smask 4168 + (img['pal'].length / 3 - 1) + ' ' + ('smask' in img ? objectNumber + 2 : objectNumber + 1) + ' 0 R]'); 4169 } else { 4170 out('/ColorSpace /' + img['cs']); 4171 if (img['cs'] === this.color_spaces.DEVICE_CMYK) { 4172 out('/Decode [1 0 1 0 1 0 1 0]'); 4173 } 4174 } 4175 out('/BitsPerComponent ' + img['bpc']); 4176 if ('f' in img) { 4177 out('/Filter /' + img['f']); 4178 } 4179 if ('dp' in img) { 4180 out('/DecodeParms <<' + img['dp'] + '>>'); 4181 } 4182 if ('trns' in img && img['trns'].constructor == Array) { 4183 var trns = '', 4184 i = 0, 4185 len = img['trns'].length; 4186 for (; i < len; i++) { 4187 trns += img['trns'][i] + ' ' + img['trns'][i] + ' '; 4188 }out('/Mask [' + trns + ']'); 4189 } 4190 if ('smask' in img) { 4191 out('/SMask ' + (objectNumber + 1) + ' 0 R'); 4192 } 4193 out('/Length ' + img['data'].length + '>>'); 4194 4195 putStream(img['data']); 4196 4197 out('endobj'); 4198 4199 // Soft mask 4200 if ('smask' in img) { 4201 var dp = '/Predictor ' + img['p'] + ' /Colors 1 /BitsPerComponent ' + img['bpc'] + ' /Columns ' + img['w']; 4202 var smask = { 'w': img['w'], 'h': img['h'], 'cs': 'DeviceGray', 'bpc': img['bpc'], 'dp': dp, 'data': img['smask'] }; 4203 if ('f' in img) smask.f = img['f']; 4204 putImage.call(this, smask); 4205 } 4206 4207 //Palette 4208 if (img['cs'] === this.color_spaces.INDEXED) { 4209 4210 this.internal.newObject(); 4211 //out('<< /Filter / ' + img['f'] +' /Length ' + img['pal'].length + '>>'); 4212 //putStream(zlib.compress(img['pal'])); 4213 out('<< /Length ' + img['pal'].length + '>>'); 4214 putStream(this.arrayBufferToBinaryString(new Uint8Array(img['pal']))); 4215 out('endobj'); 4216 } 4217 }, 4218 putResourcesCallback = function putResourcesCallback() { 4219 var images = this.internal.collections[namespace + 'images']; 4220 for (var i in images) { 4221 putImage.call(this, images[i]); 4222 } 4223 }, 4224 putXObjectsDictCallback = function putXObjectsDictCallback() { 4225 var images = this.internal.collections[namespace + 'images'], 4226 out = this.internal.write, 4227 image; 4228 for (var i in images) { 4229 image = images[i]; 4230 out('/I' + image['i'], image['n'], '0', 'R'); 4231 } 4232 }, 4233 checkCompressValue = function checkCompressValue(value) { 4234 if (value && typeof value === 'string') value = value.toUpperCase(); 4235 return value in jsPDFAPI.image_compression ? value : jsPDFAPI.image_compression.NONE; 4236 }, 4237 getImages = function getImages() { 4238 var images = this.internal.collections[namespace + 'images']; 4239 //first run, so initialise stuff 4240 if (!images) { 4241 this.internal.collections[namespace + 'images'] = images = {}; 4242 this.internal.events.subscribe('putResources', putResourcesCallback); 4243 this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback); 4244 } 4245 4246 return images; 4247 }, 4248 getImageIndex = function getImageIndex(images) { 4249 var imageIndex = 0; 4250 4251 if (images) { 4252 // this is NOT the first time this method is ran on this instance of jsPDF object. 4253 imageIndex = Object.keys ? Object.keys(images).length : function (o) { 4254 var i = 0; 4255 for (var e in o) { 4256 if (o.hasOwnProperty(e)) { 4257 i++; 4258 } 4259 } 4260 return i; 4261 }(images); 4262 } 4263 4264 return imageIndex; 4265 }, 4266 notDefined = function notDefined(value) { 4267 return typeof value === 'undefined' || value === null; 4268 }, 4269 generateAliasFromData = function generateAliasFromData(data) {
4270 return typeof data === 'string' && jsPDFAPI.sHashCode(data); 4271 }, 4272 doesNotSupportImageType = function doesNotSupportImageType(type) { 4273 return supported_image_types.indexOf(type) === -1; 4274 }, 4275 processMethodNotEnabled = function processMethodNotEnabled(type) { 4276 return typeof jsPDFAPI['process' + type.toUpperCase()] !== 'function'; 4277 }, 4278 isDOMElement = function isDOMElement(object) { 4279 return (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && object.nodeType === 1; 4280 }, 4281 createDataURIFromElement = function createDataURIFromElement(element, format, angle) { 4282 4283 //if element is an image which uses data url definition, just return the dataurl 4284 if (element.nodeName === 'IMG' && element.hasAttribute('src')) { 4285 var src = '' + element.getAttribute('src'); 4286 if (!angle && src.indexOf('data:image/') === 0) return src; 4287 4288 // only if the user doesn't care about a format 4289 if (!format && /\.png(?:[?#].*)?$/i.test(src)) format = 'png'; 4290 } 4291 4292 if (element.nodeName === 'CANVAS') { 4293 var canvas = element; 4294 } else { 4295 var canvas = document.createElement('canvas'); 4296 canvas.width = element.clientWidth || element.width; 4297 canvas.height = element.clientHeight || element.height; 4298 4299 var ctx = canvas.getContext('2d'); 4300 if (!ctx) { 4301 throw 'addImage requires canvas to be supported by browser.'; 4302 }
4303 if (angle) { 4304 var x, 4305 y, 4306 b, 4307 c, 4308 s, 4309 w, 4310 h, 4311 to_radians = Math.PI / 180, 4312 angleInRadians; 4313 4314 if ((typeof angle === 'undefined' ? 'undefined' : _typeof(angle)) === 'object') { 4315 x = angle.x; 4316 y = angle.y; 4317 b = angle.bg; 4318 angle = angle.angle; 4319 } 4320 angleInRadians = angle * to_radians; 4321 c = Math.abs(Math.cos(angleInRadians)); 4322 s = Math.abs(Math.sin(angleInRadians)); 4323 w = canvas.width; 4324 h = canvas.height; 4325 canvas.width = h * s + w * c; 4326 canvas.height = h * c + w * s; 4327 4328 if (isNaN(x)) x = canvas.width / 2; 4329 if (isNaN(y)) y = canvas.height / 2; 4330 4331 ctx.clearRect(0, 0, canvas.width, canvas.height); 4332 ctx.fillStyle = b || 'white'; 4333 ctx.fillRect(0, 0, canvas.width, canvas.height); 4334 ctx.save(); 4335 ctx.translate(x, y); 4336 ctx.rotate(angleInRadians); 4337 ctx.drawImage(element, -(w / 2), -(h / 2)); 4338 ctx.rotate(-angleInRadians); 4339 ctx.translate(-x, -y); 4340 ctx.restore(); 4341 } else { 4342 ctx.drawImage(element, 0, 0, canvas.width, canvas.height); 4343 } 4344 } 4345 return canvas.toDataURL(('' + format).toLowerCase() == 'png' ? 'image/png' : 'image/jpeg'); 4346 }, 4347 checkImagesForAlias = function checkImagesForAlias(alias, images) { 4348 var cached_info; 4349 if (images) { 4350 for (var e in images) { 4351 if (alias === images[e].alias) { 4352 cached_info = images[e]; 4353 break; 4354 } 4355 } 4356 } 4357 return cached_info; 4358 }, 4359 determineWidthAndHeight = function determineWidthAndHeight(w, h, info) { 4360 if (!w && !h) { 4361 w = -96; 4362 h = -96; 4363 } 4364 if (w < 0) { 4365 w = -1 * info['w'] * 72 / w / this.internal.scaleFactor; 4366 } 4367 if (h < 0) { 4368 h = -1 * info['h'] * 72 / h / this.internal.scaleFactor; 4369 } 4370 if (w === 0) { 4371 w = h * info['w'] / info['h']; 4372 } 4373 if (h === 0) { 4374 h = w * info['h'] / info['w']; 4375 } 4376 4377 return [w, h]; 4378 }, 4379 writeImageToPDF = function writeImageToPDF(x, y, w, h, info, index, images) { 4380 var dims = determineWidthAndHeight.call(this, w, h, info), 4381 coord = this.internal.getCoordinateString, 4382 vcoord = this.internal.getVerticalCoordinateString; 4383 4384 w = dims[0]; 4385 h = dims[1]; 4386 4387 images[index] = info; 4388 4389 this.internal.write('q', coord(w), '0 0', coord(h) // TODO: check if this should be shifted by vcoord 4390 , coord(x), vcoord(y + h), 'cm /I' + info['i'], 'Do Q'); 4391 }; 4392 4393 /** 4394 * COLOR SPACES 4395 */ 4396 jsPDFAPI.color_spaces = { 4397 DEVICE_RGB: 'DeviceRGB', 4398 DEVICE_GRAY: 'DeviceGray', 4399 DEVICE_CMYK: 'DeviceCMYK', 4400 CAL_GREY: 'CalGray', 4401 CAL_RGB: 'CalRGB', 4402 LAB: 'Lab', 4403 ICC_BASED: 'ICCBased', 4404 INDEXED: 'Indexed', 4405 PATTERN: 'Pattern', 4406 SEPARATION: 'Separation', 4407 DEVICE_N: 'DeviceN' 4408 }; 4409 4410 /** 4411 * DECODE METHODS 4412 */ 4413 jsPDFAPI.decode = { 4414 DCT_DECODE: 'DCTDecode', 4415 FLATE_DECODE: 'FlateDecode', 4416 LZW_DECODE: 'LZWDecode', 4417 JPX_DECODE: 'JPXDecode', 4418 JBIG2_DECODE: 'JBIG2Decode', 4419 ASCII85_DECODE: 'ASCII85Decode', 4420 ASCII_HEX_DECODE: 'ASCIIHexDecode', 4421 RUN_LENGTH_DECODE: 'RunLengthDecode', 4422 CCITT_FAX_DECODE: 'CCITTFaxDecode' 4423 }; 4424 4425 /** 4426 * IMAGE COMPRESSION TYPES 4427 */ 4428 jsPDFAPI.image_compression = { 4429 NONE: 'NONE', 4430 FAST: 'FAST', 4431 MEDIUM: 'MEDIUM', 4432 SLOW: 'SLOW' 4433 }; 4434 4435 jsPDFAPI.sHashCode = function (str) { 4436 return Array.prototype.reduce && str.split("").reduce(function (a, b) { 4437 a = (a << 5) - a + b.charCodeAt(0);return a & a; 4438 }, 0); 4439 }; 4440 4441 jsPDFAPI.isString = function (object) { 4442 return typeof object === 'string'; 4443 }; 4444 4445 /** 4446 * Strips out and returns info from a valid base64 data URI 4447 * @param {String[dataURI]} a valid data URI of format 'data:[<MIME-type>][;base64],<data>' 4448 * @returns an Array containing the following 4449 * [0] the complete data URI 4450 * [1] <MIME-type> 4451 * [2] format - the second part of the mime-type i.e 'png' in 'image/png' 4452 * [4] <data> 4453 */ 4454 jsPDFAPI.extractInfoFromBase64DataURI = function (dataURI) { 4455 return (/^data:([\w]+?\/([\w]+?));base64,(.+?)$/g.exec(dataURI) 4456 ); 4457 }; 4458 4459 /** 4460 * Check to see if ArrayBuffer is supported 4461 */ 4462 jsPDFAPI.supportsArrayBuffer = function () { 4463 return typeof ArrayBuffer !== 'undefined' && typeof Uint8Array !== 'undefined'; 4464 }; 4465 4466 /** 4467 * Tests supplied object to determine if ArrayBuffer 4468 * @param {Object[object]} 4469 */ 4470 jsPDFAPI.isArrayBuffer = function (object) { 4471 if (!this.supportsArrayBuffer()) return false;
4472 return object instanceof ArrayBuffer; 4473 }; 4474 4475 /** 4476 * Tests supplied object to determine if it implements the ArrayBufferView (TypedArray) interface 4477 * @param {Object[object]} 4478 */ 4479 jsPDFAPI.isArrayBufferView = function (object) { 4480 if (!this.supportsArrayBuffer()) return false; 4481 if (typeof Uint32Array === 'undefined') return false; 4482 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; 4483 }; 4484 4485 /** 4486 * Exactly what it says on the tin 4487 */ 4488 jsPDFAPI.binaryStringToUint8Array = function (binary_string) { 4489 /* 4490 * not sure how efficient this will be will bigger files. Is there a native method? 4491 */ 4492 var len = binary_string.length; 4493 var bytes = new Uint8Array(len); 4494 for (var i = 0; i < len; i++) { 4495 bytes[i] = binary_string.charCodeAt(i); 4496 } 4497 return bytes; 4498 }; 4499 4500 /** 4501 * @see this discussion 4502 * http://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers 4503 * 4504 * As stated, i imagine the method below is highly inefficent for large files. 4505 * 4506 * Also of note from Mozilla, 4507 * 4508 * "However, this is slow and error-prone, due to the need for multiple conversions (especially if the binary data is not actually byte-format data, but, for example, 32-bit integers or floats)." 4509 * 4510 * https://developer.mozilla.org/en-US/Add-ons/Code_snippets/StringView 4511 * 4512 * Although i'm strugglig to see how StringView solves this issue? Doesn't appear to be a direct method for conversion? 4513 * 4514 * Async method using Blob and FileReader could be best, but i'm not sure how to fit it into the flow? 4515 */ 4516 jsPDFAPI.arrayBufferToBinaryString = function (buffer) { 4517 /*if('TextDecoder' in window){ 4518 var decoder = new TextDecoder('ascii'); 4519 return decoder.decode(buffer); 4520 }*/ 4521 4522 if (this.isArrayBuffer(buffer)) buffer = new Uint8Array(buffer); 4523 4524 var binary_string = ''; 4525 var len = buffer.byteLength; 4526 for (var i = 0; i < len; i++) { 4527 binary_string += String.fromCharCode(buffer[i]); 4528 } 4529 return binary_string; 4530 /* 4531 * Another solution is the method below - convert array buffer straight to base64 and then use atob 4532 */ 4533 //return atob(this.arrayBufferToBase64(buffer)); 4534 }; 4535 4536 /** 4537 * Converts an ArrayBuffer directly to base64 4538 * 4539 * Taken from here 4540 * 4541 * http://jsperf.com/encoding-xhr-image-data/31 4542 * 4543 * Need to test if this is a better solution for larger files 4544 * 4545 */ 4546 jsPDFAPI.arrayBufferToBase64 = function (arrayBuffer) { 4547 var base64 = ''; 4548 var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 4549 4550 var bytes = new Uint8Array(arrayBuffer); 4551 var byteLength = bytes.byteLength; 4552 var byteRemainder = byteLength % 3; 4553 var mainLength = byteLength - byteRemainder; 4554 4555 var a, b, c, d; 4556 var chunk; 4557 4558 // Main loop deals with bytes in chunks of 3 4559 for (var i = 0; i < mainLength; i = i + 3) { 4560 // Combine the three bytes into a single integer 4561 chunk = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2]; 4562 4563 // Use bitmasks to extract 6-bit segments from the triplet 4564 a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 4565 b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 4566 c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 4567 d = chunk & 63; // 63 = 2^6 - 1 4568 4569 // Convert the raw binary segments to the appropriate ASCII encoding 4570 base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; 4571 } 4572 4573 // Deal with the remaining bytes and padding 4574 if (byteRemainder == 1) { 4575 chunk = bytes[mainLength]; 4576 4577 a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 4578 4579 // Set the 4 least significant bits to zero 4580 b = (chunk & 3) << 4; // 3 = 2^2 - 1 4581 4582 base64 += encodings[a] + encodings[b] + '=='; 4583 } else if (byteRemainder == 2) { 4584 chunk = bytes[mainLength] << 8 | bytes[mainLength + 1]; 4585 4586 a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 4587 b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 4588 4589 // Set the 2 least significant bits to zero 4590 c = (chunk & 15) << 2; // 15 = 2^4 - 1 4591 4592 base64 += encodings[a] + encodings[b] + encodings[c] + '='; 4593 } 4594 4595 return base64; 4596 }; 4597
4598 jsPDFAPI.createImageInfo = function (data, wd, ht, cs, bpc, f, imageIndex, alias, dp, trns, pal, smask, p) { 4599 var info = { 4600 alias: alias, 4601 w: wd, 4602 h: ht, 4603 cs: cs, 4604 bpc: bpc, 4605 i: imageIndex, 4606 data: data 4607 // n: objectNumber will be added by putImage code 4608 }; 4609 4610 if (f) info.f = f; 4611 if (dp) info.dp = dp; 4612 if (trns) info.trns = trns; 4613 if (pal) info.pal = pal; 4614 if (smask) info.smask = smask; 4615 if (p) info.p = p; // predictor parameter for PNG compression 4616 4617 return info; 4618 }; 4619 4620 jsPDFAPI.addImage = function (imageData, format, x, y, w, h, alias, compression, rotation) { 4621 'use strict'; 4622 4623 if (typeof format !== 'string') { 4624 var tmp = h; 4625 h = w; 4626 w = y; 4627 y = x; 4628 x = format; 4629 format = tmp; 4630 } 4631 4632 if ((typeof imageData === 'undefined' ? 'undefined' : _typeof(imageData)) === 'object' && !isDOMElement(imageData) && "imageData" in imageData) { 4633 var options = imageData; 4634 4635 imageData = options.imageData; 4636 format = options.format || format; 4637 x = options.x || x || 0; 4638 y = options.y || y || 0; 4639 w = options.w || w; 4640 h = options.h || h; 4641 alias = options.alias || alias; 4642 compression = options.compression || compression; 4643 rotation = options.rotation || options.angle || rotation; 4644 } 4645 4646 if (isNaN(x) || isNaN(y)) { 4647 console.error('jsPDF.addImage: Invalid coordinates', arguments); 4648 throw new Error('Invalid coordinates passed to jsPDF.addImage'); 4649 } 4650 4651 var images = getImages.call(this), 4652 info; 4653 4654 if (!(info = checkImagesForAlias(imageData, images))) { 4655 var dataAsBinaryString; 4656 4657 if (isDOMElement(imageData)) imageData = createDataURIFromElement(imageData, format, rotation); 4658 4659 if (notDefined(alias)) alias = generateAliasFromData(imageData); 4660 4661 if (!(info = checkImagesForAlias(alias, images))) { 4662 4663 if (this.isString(imageData)) { 4664 4665 var base64Info = this.extractInfoFromBase64DataURI(imageData); 4666 4667 if (base64Info) { 4668 4669 format = base64Info[2]; 4670 imageData = atob(base64Info[3]); //convert to binary string 4671 } else { 4672 4673 if (imageData.charCodeAt(0) === 0x89 && imageData.charCodeAt(1) === 0x50 && imageData.charCodeAt(2) === 0x4e && imageData.charCodeAt(3) === 0x47) format = 'png'; 4674 } 4675 } 4676 format = (format || 'JPEG').toLowerCase(); 4677 4678 if (doesNotSupportImageType(format)) throw new Error('addImage currently only supports formats ' + supported_image_types + ', not \'' + format + '\''); 4679 4680 if (processMethodNotEnabled(format)) throw new Error('please ensure that the plugin for \'' + format + '\' support is added'); 4681 4682 /** 4683 * need to test if it's more efficient to convert all binary strings 4684 * to TypedArray - or should we just leave and process as string? 4685 */ 4686 if (this.supportsArrayBuffer()) { 4687 // no need to convert if imageData is already uint8array 4688 if (!(imageData instanceof Uint8Array)) { 4689 dataAsBinaryString = imageData; 4690 imageData = this.binaryStringToUint8Array(imageData); 4691 } 4692 } 4693 4694 info = this['process' + format.toUpperCase()](imageData, getImageIndex(images), alias, checkCompressValue(compression), dataAsBinaryString); 4695 4696 if (!info) throw new Error('An unkwown error occurred whilst processing the image'); 4697 } 4698 } 4699 4700 writeImageToPDF.call(this, x, y, w, h, info, info.i, images); 4701 4702 return this; 4703 }; 4704 4705 /** 4706 * JPEG SUPPORT 4707 **/ 4708 4709 //takes a string imgData containing the raw bytes of 4710 //a jpeg image and returns [width, height] 4711 //Algorithm from: http://www.64lines.com/jpeg-width-height 4712 var getJpegSize = function getJpegSize(imgData) { 4713 'use strict'; 4714 4715 var width, height, numcomponents; 4716 // Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00 4717 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) { 4718 throw new Error('getJpegSize requires a binary string jpeg file'); 4719 } 4720 var blockLength = imgData.charCodeAt(4) * 256 + imgData.charCodeAt(5); 4721 var i = 4, 4722 len = imgData.length; 4723 while (i < len) { 4724 i += blockLength; 4725 if (imgData.charCodeAt(i) !== 0xff) { 4726 throw new Error('getJpegSize could not find the size of the image'); 4727 } 4728 if (imgData.charCodeAt(i + 1) === 0xc0 || //(SOF) Huffman - Baseline DCT 4729 imgData.charCodeAt(i + 1) === 0xc1 || //(SOF) Huffman - Extended sequential DCT 4730 imgData.charCodeAt(i + 1) === 0xc2 || // Progressive DCT (SOF2) 4731 imgData.charCodeAt(i + 1) === 0xc3 || // Spatial (sequential) lossless (SOF3) 4732 imgData.charCodeAt(i + 1) === 0xc4 || // Differential sequential DCT (SOF5) 4733 imgData.charCodeAt(i + 1) === 0xc5 || // Differential progressive DCT (SOF6) 4734 imgData.charCodeAt(i + 1) === 0xc6 || // Differential spatial (SOF7) 4735 imgData.charCodeAt(i + 1) === 0xc7) { 4736 height = imgData.charCodeAt(i + 5) * 256 + imgData.charCodeAt(i + 6); 4737 width = imgData.charCodeAt(i + 7) * 256 + imgData.charCodeAt(i + 8); 4738 numcomponents = imgData.charCodeAt(i + 9); 4739 return [width, height, numcomponents]; 4740 } else { 4741 i += 2; 4742 blockLength = imgData.charCodeAt(i) * 256 + imgData.charCodeAt(i + 1); 4743 } 4744 } 4745 }, 4746 getJpegSizeFromBytes = function getJpegSizeFromBytes(data) { 4747 4748 var hdr = data[0] << 8 | data[1]; 4749 4750 if (hdr !== 0xFFD8) throw new Error('Supplied data is not a JPEG'); 4751 4752 var len = data.length, 4753 block = (data[4] << 8) + data[5], 4754 pos = 4, 4755 bytes, 4756 width, 4757 height, 4758 numcomponents; 4759 4760 while (pos < len) { 4761 pos += block; 4762 bytes = readBytes(data, pos); 4763 block = (bytes[2] << 8) + bytes[3]; 4764 if ((bytes[1] === 0xC0 || bytes[1] === 0xC2) && bytes[0] === 0xFF && block > 7) { 4765 bytes = readBytes(data, pos + 5); 4766 width = (bytes[2] << 8) + bytes[3]; 4767 height = (bytes[0] << 8) + bytes[1]; 4768 numcomponents = bytes[4]; 4769 return { width: width, height: height, numcomponents: numcomponents }; 4770 } 4771 4772 pos += 2; 4773 } 4774 4775 throw new Error('getJpegSizeFromBytes could not find the size of the image'); 4776 }, 4777 readBytes = function readBytes(data, offset) { 4778 return data.subarray(offset, offset + 5); 4779 }; 4780 4781 jsPDFAPI.processJPEG = function (data, index, alias, compression, dataAsBinaryString) { 4782 'use strict'; 4783 4784 var colorSpace = this.color_spaces.DEVICE_RGB, 4785 filter = this.decode.DCT_DECODE, 4786 bpc = 8, 4787 dims; 4788 4789 if (this.isString(data)) { 4790 dims = getJpegSize(data); 4791 return this.createImageInfo(data, dims[0], dims[1], dims[3] == 1 ? this.color_spaces.DEVICE_GRAY : colorSpace, bpc, filter, index, alias); 4792 } 4793 4794 if (this.isArrayBuffer(data)) data = new Uint8Array(data); 4795 4796 if (this.isArrayBufferView(data)) { 4797 4798 dims = getJpegSizeFromBytes(data); 4799 4800 // if we already have a stored binary string rep use that 4801 data = dataAsBinaryString || this.arrayBufferToBinaryString(data); 4802 4803 return this.createImageInfo(data, dims.width, dims.height, dims.numcomponents == 1 ? this.color_spaces.DEVICE_GRAY : colorSpace, bpc, filter, index, alias); 4804 } 4805 4806 return null; 4807 }; 4808
4809 jsPDFAPI.processJPG = function () /*data, index, alias, compression, dataAsBinaryString*/{ 4810 return this.processJPEG.apply(this, arguments); 4811 }; 4812})(jsPDF.API); 4813 4814/** 4815 * jsPDF Annotations PlugIn 4816 * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) [email protected] 4817 * 4818 * Licensed under the MIT License. 4819 * http://opensource.org/licenses/mit-license 4820 */ 4821 4822/** 4823 * There are many types of annotations in a PDF document. Annotations are placed 4824 * on a page at a particular location. They are not 'attached' to an object. 4825 * <br /> 4826 * This plugin current supports <br /> 4827 * <li> Goto Page (set pageNumber and top in options) 4828 * <li> Goto Name (set name and top in options) 4829 * <li> Goto URL (set url in options) 4830 * <p> 4831 * The destination magnification factor can also be specified when goto is a page number or a named destination. (see documentation below) 4832 * (set magFactor in options). XYZ is the default. 4833 * </p> 4834 * <p> 4835 * Links, Text, Popup, and FreeText are supported. 4836 * </p> 4837 * <p> 4838 * Options In PDF spec Not Implemented Yet 4839 * <li> link border 4840 * <li> named target 4841 * <li> page coordinates 4842 * <li> destination page scaling and layout 4843 * <li> actions other than URL and GotoPage 4844 * <li> background / hover actions 4845 * </p> 4846 */ 4847 4848/* 4849 Destination Magnification Factors 4850 See PDF 1.3 Page 386 for meanings and options 4851 4852 [supported] 4853 XYZ (options; left top zoom) 4854 Fit (no options) 4855 FitH (options: top) 4856 FitV (options: left) 4857 4858 [not supported] 4859 FitR 4860 FitB 4861 FitBH 4862 FitBV 4863 */ 4864 4865(function (jsPDFAPI) { 4866 'use strict'; 4867 4868 var annotationPlugin = { 4869 4870 /** 4871 * An array of arrays, indexed by <em>pageNumber</em>. 4872 */ 4873 annotations: [], 4874 4875 f2: function f2(number) { 4876 return number.toFixed(2); 4877 }, 4878 4879 notEmpty: function notEmpty(obj) { 4880 if (typeof obj != 'undefined') { 4881 if (obj != '') { 4882 return true; 4883 } 4884 } 4885 } 4886 }; 4887 4888 jsPDF.API.annotationPlugin = annotationPlugin; 4889 4890 jsPDF.API.events.push(['addPage', function (info) { 4891 this.annotationPlugin.annotations[info.pageNumber] = []; 4892 }]); 4893 4894 jsPDFAPI.events.push(['putPage', function (info) { 4895 //TODO store annotations in pageContext so reorder/remove will not affect them. 4896 var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; 4897 4898 var found = false; 4899 for (var a = 0; a < pageAnnos.length && !found; a++) { 4900 var anno = pageAnnos[a]; 4901 switch (anno.type) { 4902 case 'link': 4903 if (annotationPlugin.notEmpty(anno.options.url) || annotationPlugin.notEmpty(anno.options.pageNumber)) { 4904 found = true; 4905 break; 4906 } 4907 case 'reference': 4908 case 'text': 4909 case 'freetext': 4910 found = true; 4911 break; 4912 } 4913 } 4914 if (found == false) { 4915 return; 4916 } 4917 4918 this.internal.write("/Annots ["); 4919 var f2 = this.annotationPlugin.f2; 4920 var k = this.internal.scaleFactor; 4921 var pageHeight = this.internal.pageSize.height; 4922 var pageInfo = this.internal.getPageInfo(info.pageNumber); 4923 for (var a = 0; a < pageAnnos.length; a++) { 4924 var anno = pageAnnos[a]; 4925 4926 switch (anno.type) { 4927 case 'reference': 4928 // References to Widget Anotations (for AcroForm Fields) 4929 this.internal.write(' ' + anno.object.objId + ' 0 R '); 4930 break; 4931 case 'text': 4932 // Create a an object for both the text and the popup 4933 var objText = this.internal.newAdditionalObject(); 4934 var objPopup = this.internal.newAdditionalObject(); 4935 4936 var title = anno.title || 'Note'; 4937 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) + "] "; 4938 line = '<</Type /Annot /Subtype /' + 'Text' + ' ' + rect + '/Contents (' + anno.contents + ')'; 4939 line += ' /Popup ' + objPopup.objId + " 0 R"; 4940 line += ' /P ' + pageInfo.objId + " 0 R"; 4941 line += ' /T (' + title + ') >>'; 4942 objText.content = line; 4943 4944 var parent = objText.objId + ' 0 R'; 4945 var popoff = 30; 4946 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) + "] "; 4947 //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) + "] "; 4948 line = '<</Type /Annot /Subtype /' + 'Popup' + ' ' + rect + ' /Parent ' + parent; 4949 if (anno.open) { 4950 line += ' /Open true'; 4951 } 4952 line += ' >>'; 4953 objPopup.content = line; 4954 4955 this.internal.write(objText.objId, '0 R', objPopup.objId, '0 R'); 4956 4957 break; 4958 case 'freetext': 4959 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) + "] "; 4960 var color = anno.color || '#000000'; 4961 line = '<</Type /Annot /Subtype /' + 'FreeText' + ' ' + rect + '/Contents (' + anno.contents + ')'; 4962 line += ' /DS(font: Helvetica,sans-serif 12.0pt; text-align:left; color:#' + color + ')'; 4963 line += ' /Border [0 0 0]'; 4964 line += ' >>'; 4965 this.internal.write(line); 4966 break; 4967 case 'link': 4968 if (anno.options.name) { 4969 var loc = this.annotations._nameMap[anno.options.name]; 4970 anno.options.pageNumber = loc.page; 4971 anno.options.top = loc.y; 4972 } else { 4973 if (!anno.options.top) { 4974 anno.options.top = 0; 4975 } 4976 } 4977 4978 //var pageHeight = this.internal.pageSize.height * this.internal.scaleFactor; 4979 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) + "] "; 4980 4981 var line = ''; 4982 if (anno.options.url) { 4983 line = '<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /A <</S /URI /URI (' + anno.options.url + ') >>'; 4984 } else if (anno.options.pageNumber) { 4985 // first page is 0 4986 var info = this.internal.getPageInfo(anno.options.pageNumber); 4987 line = '<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /Dest [' + info.objId + " 0 R"; 4988 anno.options.magFactor = anno.options.magFactor || "XYZ"; 4989 switch (anno.options.magFactor) { 4990 case 'Fit': 4991 line += ' /Fit]'; 4992 break; 4993 case 'FitH': 4994 //anno.options.top = anno.options.top || f2(pageHeight * k); 4995 line += ' /FitH ' + anno.options.top + ']'; 4996 break; 4997 case 'FitV': 4998 anno.options.left = anno.options.left || 0; 4999 line += ' /FitV ' + anno.options.left + ']'; 5000 break; 5001 case 'XYZ': 5002 default: 5003 var top = f2((pageHeight - anno.options.top) * k); // || f2(pageHeight * k); 5004 anno.options.left = anno.options.left || 0; 5005 // 0 or null zoom will not change zoom factor 5006 if (typeof anno.options.zoom === 'undefined') { 5007 anno.options.zoom = 0; 5008 } 5009 line += ' /XYZ ' + anno.options.left + ' ' + top + ' ' + anno.options.zoom + ']'; 5010 break; 5011 } 5012 } else { 5013 // TODO error - should not be here 5014 } 5015 if (line != '') { 5016 line += " >>"; 5017 this.internal.write(line); 5018 } 5019 break; 5020 } 5021 } 5022 this.internal.write("]"); 5023 }]); 5024
5025 jsPDFAPI.createAnnotation = function (options) { 5026 switch (options.type) { 5027 case 'link': 5028 this.link(options.bounds.x, options.bounds.y, options.bounds.w, options.bounds.h, options); 5029 break; 5030 case 'text': 5031 case 'freetext': 5032 this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push(options); 5033 break; 5034 } 5035 }; 5036 5037 /** 5038 * valid options 5039 * <li> pageNumber or url [required] 5040 * <p>If pageNumber is specified, top and zoom may also be specified</p> 5041 */ 5042 jsPDFAPI.link = function (x, y, w, h, options) { 5043 'use strict'; 5044 5045 this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push({ 5046 x: x, 5047 y: y, 5048 w: w, 5049 h: h, 5050 options: options, 5051 type: 'link' 5052 }); 5053 }; 5054 5055 /** 5056 * valid options 5057 * <li> pageNumber or url [required] 5058 * <p>If pageNumber is specified, top and zoom may also be specified</p> 5059 */ 5060 jsPDFAPI.link = function (x, y, w, h, options) { 5061 'use strict'; 5062 5063 this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push({ 5064 x: x, 5065 y: y, 5066 w: w, 5067 h: h, 5068 options: options, 5069 type: 'link' 5070 }); 5071 }; 5072 5073 /** 5074 * Currently only supports single line text. 5075 * Returns the width of the text/link 5076 */ 5077 jsPDFAPI.textWithLink = function (text, x, y, options) { 5078 'use strict'; 5079 5080 var width = this.getTextWidth(text); 5081 var height = this.internal.getLineHeight(); 5082 this.text(text, x, y); 5083 //TODO We really need the text baseline height to do this correctly. 5084 // Or ability to draw text on top, bottom, center, or baseline. 5085 y += height * .2; 5086 this.link(x, y - height, width, height, options); 5087 return width; 5088 }; 5089 5090 //TODO move into external library 5091 jsPDFAPI.getTextWidth = function (text) { 5092 'use strict'; 5093 5094 var fontSize = this.internal.getFontSize(); 5095 var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor; 5096 return txtWidth; 5097 }; 5098 5099 //TODO move into external library 5100 jsPDFAPI.getLineHeight = function () { 5101 return this.internal.getLineHeight(); 5102 }; 5103 5104 return this; 5105})(jsPDF.API); 5106 5107/** 5108 * jsPDF Autoprint Plugin 5109 * 5110 * Licensed under the MIT License. 5111 * http://opensource.org/licenses/mit-license 5112 */ 5113 5114/** 5115* Makes the PDF automatically print. This works in Chrome, Firefox, Acrobat 5116* Reader. 5117* 5118* @returns {jsPDF} 5119* @name autoPrint 5120* @example 5121* var doc = new jsPDF() 5122* doc.text(10, 10, 'This is a test') 5123* doc.autoPrint() 5124* doc.save('autoprint.pdf') 5125*/ 5126 5127(function (jsPDFAPI) { 5128 'use strict'; 5129 5130 jsPDFAPI.autoPrint = function () { 5131 'use strict'; 5132 5133 var refAutoPrintTag; 5134 5135 this.internal.events.subscribe('postPutResources', function () { 5136 refAutoPrintTag = this.internal.newObject(); 5137 this.internal.write("<< /S/Named /Type/Action /N/Print >>", "endobj"); 5138 }); 5139 5140 this.internal.events.subscribe("putCatalog", function () { 5141 this.internal.write("/OpenAction " + refAutoPrintTag + " 0" + " R"); 5142 }); 5143 return this; 5144 }; 5145})(jsPDF.API); 5146 5147/** 5148 * jsPDF Canvas PlugIn 5149 * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) [email protected] 5150 * 5151 * Licensed under the MIT License. 5152 * http://opensource.org/licenses/mit-license 5153 */ 5154 5155/** 5156 * This plugin mimics the HTML5 Canvas 5157 * 5158 * The goal is to provide a way for current canvas users to print directly to a PDF. 5159 */ 5160 5161(function (jsPDFAPI) { 5162 'use strict'; 5163 5164 jsPDFAPI.events.push(['initialized', function () { 5165 this.canvas.pdf = this; 5166 }]); 5167 5168 jsPDFAPI.canvas = { 5169 getContext: function getContext(name) { 5170 this.pdf.context2d._canvas = this; 5171 return this.pdf.context2d; 5172 }, 5173 style: {} 5174 }; 5175 5176 Object.defineProperty(jsPDFAPI.canvas, 'width', { 5177 get: function get() { 5178 return this._width; 5179 }, 5180 set: function set(value) { 5181 this._width = value; 5182 this.getContext('2d').pageWrapX = value + 1; 5183 } 5184 }); 5185 5186 Object.defineProperty(jsPDFAPI.canvas, 'height', { 5187 get: function get() { 5188 return this._height; 5189 }, 5190 set: function set(value) { 5191 this._height = value; 5192 this.getContext('2d').pageWrapY = value + 1; 5193 } 5194 }); 5195 5196 return this; 5197})(jsPDF.API); 5198 5199/** ==================================================================== 5200 * jsPDF Cell plugin 5201 * Copyright (c) 2013 Youssef Beddad, [email protected] 5202 * 2013 Eduardo Menezes de Morais, [email protected] 5203 * 2013 Lee Driscoll, https://github.com/lsdriscoll 5204 * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria 5205 * 2014 James Hall, [email protected] 5206 * 2014 Diego Casorran, https://github.com/diegocr 5207 * 5208 * 5209 * ==================================================================== 5210 */ 5211 5212(function (jsPDFAPI) { 5213 'use strict'; 5214 /*jslint browser:true */ 5215 /*global document: false, jsPDF */ 5216 5217 var fontName, 5218 fontSize, 5219 fontStyle, 5220 padding = 3, 5221 margin = 13, 5222 headerFunction,
5223 lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }, 5224 pages = 1, 5225 setLastCellPosition = function setLastCellPosition(x, y, w, h, ln) { 5226 lastCellPos = { 'x': x, 'y': y, 'w': w, 'h': h, 'ln': ln }; 5227 }, 5228 getLastCellPosition = function getLastCellPosition() { 5229 return lastCellPos; 5230 }, 5231 NO_MARGINS = { left: 0, top: 0, bottom: 0 }; 5232 5233 jsPDFAPI.setHeaderFunction = function (func) { 5234 headerFunction = func; 5235 }; 5236 5237 jsPDFAPI.getTextDimensions = function (txt) { 5238 fontName = this.internal.getFont().fontName; 5239 fontSize = this.table_font_size || this.internal.getFontSize(); 5240 fontStyle = this.internal.getFont().fontStyle; 5241 // 1 pixel = 0.264583 mm and 1 mm = 72/25.4 point 5242 var px2pt = 0.264583 * 72 / 25.4, 5243 dimensions, 5244 text; 5245 5246 text = document.createElement('font'); 5247 text.id = "jsPDFCell"; 5248 5249 try { 5250 text.style.fontStyle = fontStyle; 5251 } catch (e) { 5252 text.style.fontWeight = fontStyle; 5253 } 5254 5255 text.style.fontName = fontName; 5256 text.style.fontSize = fontSize + 'pt'; 5257 try { 5258 text.textContent = txt; 5259 } catch (e) { 5260 text.innerText = txt; 5261 } 5262 5263 document.body.appendChild(text); 5264 5265 dimensions = { w: (text.offsetWidth + 1) * px2pt, h: (text.offsetHeight + 1) * px2pt }; 5266 5267 document.body.removeChild(text); 5268 5269 return dimensions; 5270 }; 5271 5272 jsPDFAPI.cellAddPage = function () { 5273 var margins = this.margins || NO_MARGINS; 5274 5275 this.addPage(); 5276 5277 setLastCellPosition(margins.left, margins.top, undefined, undefined); 5278 //setLastCellPosition(undefined, undefined, undefined, undefined, undefined); 5279 pages += 1; 5280 }; 5281 5282 jsPDFAPI.cellInitialize = function () { 5283 lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }; 5284 pages = 1; 5285 }; 5286 5287 jsPDFAPI.cell = function (x, y, w, h, txt, ln, align) { 5288 var curCell = getLastCellPosition(); 5289 var pgAdded = false; 5290 5291 // If this is not the first cell, we must change its position 5292 if (curCell.ln !== undefined) { 5293 if (curCell.ln === ln) { 5294 //Same line 5295 x = curCell.x + curCell.w; 5296 y = curCell.y; 5297 } else { 5298 //New line 5299 var margins = this.margins || NO_MARGINS; 5300 if (curCell.y + curCell.h + h + margin >= this.internal.pageSize.height - margins.bottom) { 5301 this.cellAddPage(); 5302 pgAdded = true; 5303 if (this.printHeaders && this.tableHeaderRow) { 5304 this.printHeaderRow(ln, true); 5305 } 5306 } 5307 //We ignore the passed y: the lines may have different heights 5308 y = getLastCellPosition().y + getLastCellPosition().h; 5309 if (pgAdded) y = margin + 10; 5310 } 5311 } 5312 5313 if (txt[0] !== undefined) { 5314 if (this.printingHeaderRow) { 5315 this.rect(x, y, w, h, 'FD'); 5316 } else { 5317 this.rect(x, y, w, h); 5318 } 5319 if (align === 'right') { 5320 if (!(txt instanceof Array)) { 5321 txt = [txt]; 5322 } 5323 for (var i = 0; i < txt.length; i++) { 5324 var currentLine = txt[i]; 5325 var textSize = this.getStringUnitWidth(currentLine) * this.internal.getFontSize(); 5326 this.text(currentLine, x + w - textSize - padding, y + this.internal.getLineHeight() * (i + 1)); 5327 } 5328 } else { 5329 this.text(txt, x + padding, y + this.internal.getLineHeight()); 5330 } 5331 } 5332 setLastCellPosition(x, y, w, h, ln); 5333 return this; 5334 }; 5335 5336 /** 5337 * Return the maximum value from an array 5338 * @param array 5339 * @param comparisonFn 5340 * @returns {*} 5341 */ 5342 jsPDFAPI.arrayMax = function (array, comparisonFn) { 5343 var max = array[0], 5344 i, 5345 ln, 5346 item; 5347 5348 for (i = 0, ln = array.length; i < ln; i += 1) { 5349 item = array[i]; 5350 5351 if (comparisonFn) { 5352 if (comparisonFn(max, item) === -1) { 5353 max = item; 5354 } 5355 } else { 5356 if (item > max) { 5357 max = item; 5358 } 5359 } 5360 } 5361 5362 return max; 5363 }; 5364 5365 /** 5366 * Create a table from a set of data. 5367 * @param {Integer} [x] : left-position for top-left corner of table 5368 * @param {Integer} [y] top-position for top-left corner of table 5369 * @param {Object[]} [data] As array of objects containing key-value pairs corresponding to a row of data. 5370 * @param {String[]} [headers] Omit or null to auto-generate headers at a performance cost 5371 * @param {Object} [config.printHeaders] True to print column headers at the top of every page 5372 * @param {Object} [config.autoSize] True to dynamically set the column widths to match the widest cell value 5373 * @param {Object} [config.margins] margin values for left, top, bottom, and width 5374 * @param {Object} [config.fontSize] Integer fontSize to use (optional) 5375 */ 5376 5377 jsPDFAPI.table = function (x, y, data, headers, config) { 5378 if (!data) { 5379 throw 'No data for PDF table'; 5380 } 5381 5382 var headerNames = [], 5383 headerPrompts = [], 5384 header, 5385 i, 5386 ln, 5387 cln, 5388 columnMatrix = {}, 5389 columnWidths = {}, 5390 columnData, 5391 column, 5392 columnMinWidths = [], 5393 j, 5394 tableHeaderConfigs = [], 5395 model, 5396 jln, 5397 func, 5398 5399 5400 //set up defaults. If a value is provided in config, defaults will be overwritten: 5401 autoSize = false, 5402 printHeaders = true, 5403 fontSize = 12, 5404 margins = NO_MARGINS; 5405 5406 margins.width = this.internal.pageSize.width; 5407 5408 if (config) { 5409 //override config defaults if the user has specified non-default behavior: 5410 if (config.autoSize === true) { 5411 autoSize = true; 5412 } 5413 if (config.printHeaders === false) { 5414 printHeaders = false;
5415 } 5416 if (config.fontSize) { 5417 fontSize = config.fontSize; 5418 } 5419 if (config.css && typeof config.css['font-size'] !== "undefined") { 5420 fontSize = config.css['font-size'] * 16; 5421 } 5422 if (config.margins) { 5423 margins = config.margins; 5424 } 5425 } 5426 5427 /** 5428 * @property {Number} lnMod 5429 * Keep track of the current line number modifier used when creating cells 5430 */ 5431 this.lnMod = 0; 5432 lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }, pages = 1; 5433 5434 this.printHeaders = printHeaders; 5435 this.margins = margins; 5436 this.setFontSize(fontSize); 5437 this.table_font_size = fontSize; 5438 5439 // Set header values 5440 if (headers === undefined || headers === null) { 5441 // No headers defined so we derive from data 5442 headerNames = Object.keys(data[0]); 5443 } else if (headers[0] && typeof headers[0] !== 'string') { 5444 var px2pt = 0.264583 * 72 / 25.4; 5445 5446 // Split header configs into names and prompts 5447 for (i = 0, ln = headers.length; i < ln; i += 1) { 5448 header = headers[i]; 5449 headerNames.push(header.name); 5450 headerPrompts.push(header.prompt); 5451 columnWidths[header.name] = header.width * px2pt; 5452 } 5453 } else { 5454 headerNames = headers; 5455 } 5456 5457 if (autoSize) { 5458 // Create a matrix of columns e.g., {column_title: [row1_Record, row2_Record]} 5459 func = function func(rec) { 5460 return rec[header]; 5461 }; 5462 5463 for (i = 0, ln = headerNames.length; i < ln; i += 1) { 5464 header = headerNames[i]; 5465 5466 columnMatrix[header] = data.map(func); 5467 5468 // get header width 5469 columnMinWidths.push(this.getTextDimensions(headerPrompts[i] || header).w); 5470 column = columnMatrix[header]; 5471 5472 // get cell widths 5473 for (j = 0, cln = column.length; j < cln; j += 1) { 5474 columnData = column[j]; 5475 columnMinWidths.push(this.getTextDimensions(columnData).w); 5476 } 5477 5478 // get final column width 5479 columnWidths[header] = jsPDFAPI.arrayMax(columnMinWidths); 5480 5481 //have to reset 5482 columnMinWidths = []; 5483 } 5484 } 5485 5486 // -- Construct the table 5487 5488 if (printHeaders) { 5489 var lineHeight = this.calculateLineHeight(headerNames, columnWidths, headerPrompts.length ? headerPrompts : headerNames); 5490 5491 // Construct the header row 5492 for (i = 0, ln = headerNames.length; i < ln; i += 1) { 5493 header = headerNames[i]; 5494 tableHeaderConfigs.push([x, y, columnWidths[header], lineHeight, String(headerPrompts.length ? headerPrompts[i] : header)]); 5495 } 5496 5497 // Store the table header config 5498 this.setTableHeaderRow(tableHeaderConfigs); 5499 5500 // Print the header for the start of the table 5501 this.printHeaderRow(1, false); 5502 } 5503 5504 // Construct the data rows 5505 for (i = 0, ln = data.length; i < ln; i += 1) { 5506 var lineHeight; 5507 model = data[i]; 5508 lineHeight = this.calculateLineHeight(headerNames, columnWidths, model); 5509 5510 for (j = 0, jln = headerNames.length; j < jln; j += 1) { 5511 header = headerNames[j]; 5512 this.cell(x, y, columnWidths[header], lineHeight, model[header], i + 2, header.align); 5513 } 5514 } 5515 this.lastCellPos = lastCellPos; 5516 this.table_x = x; 5517 this.table_y = y; 5518 return this; 5519 }; 5520 /** 5521 * Calculate the height for containing the highest column 5522 * @param {String[]} headerNames is the header, used as keys to the data 5523 * @param {Integer[]} columnWidths is size of each column 5524 * @param {Object[]} model is the line of data we want to calculate the height of 5525 */
5526 jsPDFAPI.calculateLineHeight = function (headerNames, columnWidths, model) { 5527 var header, 5528 lineHeight = 0; 5529 for (var j = 0; j < headerNames.length; j++) { 5530 header = headerNames[j]; 5531 model[header] = this.splitTextToSize(String(model[header]), columnWidths[header] - padding); 5532 var h = this.internal.getLineHeight() * model[header].length + padding; 5533 if (h > lineHeight) lineHeight = h; 5534 } 5535 return lineHeight; 5536 }; 5537 5538 /** 5539 * Store the config for outputting a table header 5540 * @param {Object[]} config 5541 * An array of cell configs that would define a header row: Each config matches the config used by jsPDFAPI.cell 5542 * except the ln parameter is excluded 5543 */ 5544 jsPDFAPI.setTableHeaderRow = function (config) { 5545 this.tableHeaderRow = config; 5546 }; 5547 5548 /** 5549 * Output the store header row 5550 * @param lineNumber The line number to output the header at 5551 */ 5552 jsPDFAPI.printHeaderRow = function (lineNumber, new_page) { 5553 if (!this.tableHeaderRow) { 5554 throw 'Property tableHeaderRow does not exist.'; 5555 } 5556 5557 var tableHeaderCell, tmpArray, i, ln; 5558 5559 this.printingHeaderRow = true; 5560 if (headerFunction !== undefined) { 5561 var position = headerFunction(this, pages); 5562 setLastCellPosition(position[0], position[1], position[2], position[3], -1); 5563 } 5564 this.setFontStyle('bold'); 5565 var tempHeaderConf = []; 5566 for (i = 0, ln = this.tableHeaderRow.length; i < ln; i += 1) { 5567 this.setFillColor(200, 200, 200); 5568 5569 tableHeaderCell = this.tableHeaderRow[i]; 5570 if (new_page) { 5571 this.margins.top = margin; 5572 tableHeaderCell[1] = this.margins && this.margins.top || 0; 5573 tempHeaderConf.push(tableHeaderCell); 5574 } 5575 tmpArray = [].concat(tableHeaderCell); 5576 this.cell.apply(this, tmpArray.concat(lineNumber)); 5577 } 5578 if (tempHeaderConf.length > 0) { 5579 this.setTableHeaderRow(tempHeaderConf); 5580 } 5581 this.setFontStyle('normal'); 5582 this.printingHeaderRow = false; 5583 }; 5584})(jsPDF.API); 5585 5586/** 5587 * jsPDF Context2D PlugIn Copyright (c) 2014 Steven Spungin (TwelveTone LLC) [email protected] 5588 * 5589 * Licensed under the MIT License. http://opensource.org/licenses/mit-license 5590 */ 5591 5592/** 5593 * This plugin mimics the HTML5 Canvas's context2d. 5594 * 5595 * The goal is to provide a way for current canvas implementations to print directly to a PDF. 5596 */ 5597 5598/** 5599 * TODO implement stroke opacity (refactor from fill() method ) 5600 * TODO transform angle and radii parameters 5601 */ 5602 5603/** 5604 * require('jspdf.js'); require('lib/css_colors.js'); 5605 */ 5606 5607(function (jsPDFAPI) { 5608 'use strict'; 5609 5610 jsPDFAPI.events.push(['initialized', function () { 5611 this.context2d.pdf = this; 5612 this.context2d.internal.pdf = this; 5613 this.context2d.ctx = new context(); 5614 this.context2d.ctxStack = []; 5615 this.context2d.path = []; 5616 }]); 5617 5618 jsPDFAPI.context2d = { 5619 pageWrapXEnabled: false, 5620 pageWrapYEnabled: false, 5621 pageWrapX: 9999999, 5622 pageWrapY: 9999999, 5623 ctx: new context(), 5624 f2: function f2(number) { 5625 return number.toFixed(2); 5626 }, 5627 5628 fillRect: function fillRect(x, y, w, h) { 5629 if (this._isFillTransparent()) { 5630 return; 5631 } 5632 x = this._wrapX(x); 5633 y = this._wrapY(y); 5634 5635 var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); 5636 this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "f"); 5637 }, 5638 5639 strokeRect: function strokeRect(x, y, w, h) {
5640 if (this._isStrokeTransparent()) { 5641 return; 5642 } 5643 x = this._wrapX(x); 5644 y = this._wrapY(y); 5645 5646 var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); 5647 this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "s"); 5648 }, 5649 5650 /** 5651 * We cannot clear PDF commands that were already written to PDF, so we use white instead. <br /> 5652 * As a special case, read a special flag (ignoreClearRect) and do nothing if it is set. 5653 * This results in all calls to clearRect() to do nothing, and keep the canvas transparent. 5654 * This flag is stored in the save/restore context and is managed the same way as other drawing states. 5655 * @param x 5656 * @param y 5657 * @param w 5658 * @param h 5659 */ 5660 clearRect: function clearRect(x, y, w, h) { 5661 if (this.ctx.ignoreClearRect) { 5662 return; 5663 } 5664 5665 x = this._wrapX(x); 5666 y = this._wrapY(y); 5667 5668 var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); 5669 this.save(); 5670 this.setFillStyle('#ffffff'); 5671 //TODO This is hack to fill with white. 5672 this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "f"); 5673 this.restore(); 5674 }, 5675 5676 save: function save() { 5677 this.ctx._fontSize = this.pdf.internal.getFontSize(); 5678 var ctx = new context(); 5679 ctx.copy(this.ctx); 5680 this.ctxStack.push(this.ctx); 5681 this.ctx = ctx; 5682 }, 5683 5684 restore: function restore() { 5685 this.ctx = this.ctxStack.pop(); 5686 this.setFillStyle(this.ctx.fillStyle); 5687 this.setStrokeStyle(this.ctx.strokeStyle); 5688 this.setFont(this.ctx.font); 5689 this.pdf.setFontSize(this.ctx._fontSize); 5690 this.setLineCap(this.ctx.lineCap); 5691 this.setLineWidth(this.ctx.lineWidth); 5692 this.setLineJoin(this.ctx.lineJoin); 5693 }, 5694 5695 rect: function rect(x, y, w, h) { 5696 this.moveTo(x, y); 5697 this.lineTo(x + w, y); 5698 this.lineTo(x + w, y + h); 5699 this.lineTo(x, y + h); 5700 this.lineTo(x, y); //TODO not needed 5701 this.closePath(); 5702 }, 5703 5704 beginPath: function beginPath() { 5705 this.path = []; 5706 }, 5707 5708 closePath: function closePath() { 5709 this.path.push({ 5710 type: 'close' 5711 }); 5712 }, 5713 5714 _getRgba: function _getRgba(style) { 5715 // get the decimal values of r, g, and b; 5716 var rgba = {}; 5717 5718 if (this.internal.rxTransparent.test(style)) { 5719 rgba.r = 0; 5720 rgba.g = 0; 5721 rgba.b = 0; 5722 rgba.a = 0; 5723 } else { 5724 var m = this.internal.rxRgb.exec(style); 5725 if (m != null) { 5726 rgba.r = parseInt(m[1]); 5727 rgba.g = parseInt(m[2]); 5728 rgba.b = parseInt(m[3]); 5729 rgba.a = 1; 5730 } else { 5731 m = this.internal.rxRgba.exec(style); 5732 if (m != null) { 5733 rgba.r = parseInt(m[1]); 5734 rgba.g = parseInt(m[2]); 5735 rgba.b = parseInt(m[3]); 5736 rgba.a = parseFloat(m[4]); 5737 } else { 5738 rgba.a = 1; 5739 if (style.charAt(0) != '#') { 5740 style = CssColors.colorNameToHex(style); 5741 if (!style) { 5742 style = '#000000'; 5743 } 5744 } else {} 5745 5746 if (style.length === 4) { 5747 rgba.r = style.substring(1, 2); 5748 rgba.r += r; 5749 rgba.g = style.substring(2, 3); 5750 rgba.g += g; 5751 rgba.b = style.substring(3, 4); 5752 rgba.b += b; 5753 } else { 5754 rgba.r = style.substring(1, 3); 5755 rgba.g = style.substring(3, 5); 5756 rgba.b = style.substring(5, 7); 5757 } 5758 rgba.r = parseInt(rgba.r, 16); 5759 rgba.g = parseInt(rgba.g, 16); 5760 rgba.b = parseInt(rgba.b, 16); 5761 } 5762 } 5763 } 5764 rgba.style = style; 5765 return rgba; 5766 }, 5767 5768 setFillStyle: function setFillStyle(style) { 5769 // get the decimal values of r, g, and b; 5770 var r, g, b, a; 5771 5772 if (this.internal.rxTransparent.test(style)) { 5773 r = 0; 5774 g = 0; 5775 b = 0; 5776 a = 0; 5777 } else { 5778 var m = this.internal.rxRgb.exec(style); 5779 if (m != null) { 5780 r = parseInt(m[1]); 5781 g = parseInt(m[2]); 5782 b = parseInt(m[3]); 5783 a = 1; 5784 } else { 5785 m = this.internal.rxRgba.exec(style); 5786 if (m != null) { 5787 r = parseInt(m[1]); 5788 g = parseInt(m[2]); 5789 b = parseInt(m[3]); 5790 a = parseFloat(m[4]); 5791 } else { 5792 a = 1; 5793 if (style.charAt(0) != '#') { 5794 style = CssColors.colorNameToHex(style); 5795 if (!style) { 5796 style = '#000000'; 5797 } 5798 } else {} 5799 5800 if (style.length === 4) { 5801 r = style.substring(1, 2); 5802 r += r; 5803 g = style.substring(2, 3); 5804 g += g; 5805 b = style.substring(3, 4); 5806 b += b; 5807 } else { 5808 r = style.substring(1, 3); 5809 g = style.substring(3, 5); 5810 b = style.substring(5, 7); 5811 } 5812 r = parseInt(r, 16); 5813 g = parseInt(g, 16); 5814 b = parseInt(b, 16); 5815 } 5816 } 5817 } 5818 5819 this.ctx.fillStyle = style; 5820 this.ctx._isFillTransparent = a == 0; 5821 this.ctx._fillOpacity = a; 5822 5823 this.pdf.setFillColor(r, g, b, { 5824 a: a 5825 }); 5826 this.pdf.setTextColor(r, g, b, { 5827 a: a 5828 }); 5829 }, 5830 5831 setStrokeStyle: function setStrokeStyle(style) { 5832 var rgba = this._getRgba(style); 5833 5834 this.ctx.strokeStyle = rgba.style; 5835 this.ctx._isStrokeTransparent = rgba.a == 0; 5836 this.ctx._strokeOpacity = rgba.a; 5837 5838 //TODO jsPDF to handle rgba 5839 if (rgba.a === 0) { 5840 this.pdf.setDrawColor(255, 255, 255); 5841 } else if (rgba.a === 1) { 5842 this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b); 5843 } else { 5844 //this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b, {a: rgba.a}); 5845 this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b); 5846 } 5847 }, 5848 5849 fillText: function fillText(text, x, y, maxWidth) {
5850 if (this._isFillTransparent()) { 5851 return; 5852 } 5853 x = this._wrapX(x); 5854 y = this._wrapY(y); 5855 5856 var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 5857 x = xpt[0]; 5858 y = xpt[1]; 5859 var rads = this._matrix_rotation(this.ctx._transform); 5860 var degs = rads * 57.2958; 5861 5862 //TODO only push the clip if it has not been applied to the current PDF context 5863 if (this.ctx._clip_path.length > 0) { 5864 var lines; 5865 if (window.outIntercept) { 5866 lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; 5867 } else { 5868 lines = this.internal.getCurrentPage(); 5869 } 5870 lines.push("q"); 5871 var origPath = this.path; 5872 this.path = this.ctx._clip_path; 5873 this.ctx._clip_path = []; 5874 this._fill(null, true); 5875 this.ctx._clip_path = this.path; 5876 this.path = origPath; 5877 } 5878 5879 var scale; 5880 if (this.pdf.hotfix && this.pdf.hotfix.scale_text) { 5881 scale = this._getTransform()[0]; 5882 } else { 5883 scale = 1; 5884 } 5885 // In some cases the transform was very small (5.715760606202283e-17). Most likely a canvg rounding error. 5886 if (scale < .01) { 5887 this.pdf.text(text, x, this._getBaseline(y), null, degs); 5888 } else { 5889 var oldSize = this.pdf.internal.getFontSize(); 5890 this.pdf.setFontSize(oldSize * scale); 5891 this.pdf.text(text, x, this._getBaseline(y), null, degs); 5892 this.pdf.setFontSize(oldSize); 5893 } 5894 5895 if (this.ctx._clip_path.length > 0) { 5896 lines.push('Q'); 5897 } 5898 }, 5899 5900 strokeText: function strokeText(text, x, y, maxWidth) {
5901 if (this._isStrokeTransparent()) { 5902 return; 5903 } 5904 x = this._wrapX(x); 5905 y = this._wrapY(y); 5906 5907 var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 5908 x = xpt[0]; 5909 y = xpt[1]; 5910 var rads = this._matrix_rotation(this.ctx._transform); 5911 var degs = rads * 57.2958; 5912 5913 //TODO only push the clip if it has not been applied to the current PDF context 5914 if (this.ctx._clip_path.length > 0) { 5915 var lines; 5916 if (window.outIntercept) { 5917 lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; 5918 } else { 5919 lines = this.internal.getCurrentPage(); 5920 } 5921 lines.push("q"); 5922 var origPath = this.path; 5923 this.path = this.ctx._clip_path; 5924 this.ctx._clip_path = []; 5925 this._fill(null, true); 5926 this.ctx._clip_path = this.path; 5927 this.path = origPath; 5928 } 5929 5930 var scale; 5931 if (this.pdf.hotfix && this.pdf.hotfix.scale_text) { 5932 scale = this._getTransform()[0]; 5933 } else { 5934 scale = 1; 5935 } 5936 if (scale === 1) { 5937 this.pdf.text(text, x, this._getBaseline(y), { 5938 stroke: true 5939 }, degs); 5940 } else { 5941 var oldSize = this.pdf.internal.getFontSize(); 5942 this.pdf.setFontSize(oldSize * scale); 5943 this.pdf.text(text, x, this._getBaseline(y), { 5944 stroke: true 5945 }, degs); 5946 this.pdf.setFontSize(oldSize); 5947 } 5948 5949 if (this.ctx._clip_path.length > 0) { 5950 lines.push('Q'); 5951 } 5952 }, 5953 5954 setFont: function setFont(font) { 5955 this.ctx.font = font; 5956 5957 //var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+([\d\.]+)(px|pt|em)\s+["']?(\w+)['"]?/; 5958 var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+([\d\.]+)(px|pt|em)\s+(.*)?/; 5959 m = rx.exec(font); 5960 if (m != null) { 5961 var fontStyle = m[1]; 5962 var fontVariant = m[2]; 5963 var fontWeight = m[3]; 5964 var fontSize = m[4]; 5965 var fontSizeUnit = m[5]; 5966 var fontFamily = m[6]; 5967 5968 if ('px' === fontSizeUnit) { 5969 fontSize = Math.floor(parseFloat(fontSize)); 5970 // fontSize = fontSize * 1.25; 5971 } else if ('em' === fontSizeUnit) { 5972 fontSize = Math.floor(parseFloat(fontSize) * this.pdf.getFontSize()); 5973 } else { 5974 fontSize = Math.floor(parseFloat(fontSize)); 5975 } 5976 5977 this.pdf.setFontSize(fontSize); 5978 5979 if (fontWeight === 'bold' || fontWeight === '700') { 5980 this.pdf.setFontStyle('bold'); 5981 } else { 5982 if (fontStyle === 'italic') { 5983 this.pdf.setFontStyle('italic'); 5984 } else { 5985 this.pdf.setFontStyle('normal'); 5986 } 5987 } 5988 5989 var name = fontFamily; 5990 var parts = name.toLowerCase().split(/\s*,\s*/); 5991 var jsPdfFontName; 5992 5993 if (parts.indexOf('arial') != -1) { 5994 jsPdfFontName = 'Arial'; 5995 } else if (parts.indexOf('verdana') != -1) { 5996 jsPdfFontName = 'Verdana'; 5997 } else if (parts.indexOf('helvetica') != -1) { 5998 jsPdfFontName = 'Helvetica'; 5999 } else if (parts.indexOf('sans-serif') != -1) { 6000 jsPdfFontName = 'sans-serif'; 6001 } else if (parts.indexOf('fixed') != -1) { 6002 jsPdfFontName = 'Fixed'; 6003 } else if (parts.indexOf('monospace') != -1) { 6004 jsPdfFontName = 'Monospace'; 6005 } else if (parts.indexOf('terminal') != -1) { 6006 jsPdfFontName = 'Terminal'; 6007 } else if (parts.indexOf('courier') != -1) { 6008 jsPdfFontName = 'Courier'; 6009 } else if (parts.indexOf('times') != -1) { 6010 jsPdfFontName = 'Times'; 6011 } else if (parts.indexOf('cursive') != -1) { 6012 jsPdfFontName = 'Cursive'; 6013 } else if (parts.indexOf('fantasy') != -1) { 6014 jsPdfFontName = 'Fantasy'; 6015 } else if (parts.indexOf('serif') != -1) { 6016 jsPdfFontName = 'Serif'; 6017 } else { 6018 jsPdfFontName = 'Serif'; 6019 } 6020 6021 //TODO check more cases 6022 var style; 6023 if ('bold' === fontWeight) { 6024 style = 'bold'; 6025 } else { 6026 style = 'normal'; 6027 } 6028 6029 this.pdf.setFont(jsPdfFontName, style); 6030 } else { 6031 var rx = /(\d+)(pt|px|em)\s+(\w+)\s*(\w+)?/; 6032 var m = rx.exec(font); 6033 if (m != null) { 6034 var size = m[1]; 6035 var unit = m[2]; 6036 var name = m[3]; 6037 var style = m[4]; 6038 if (!style) { 6039 style = 'normal'; 6040 } 6041 if ('em' === fontSizeUnit) { 6042 size = Math.floor(parseFloat(fontSize) * this.pdf.getFontSize()); 6043 } else { 6044 size = Math.floor(parseFloat(size)); 6045 } 6046 this.pdf.setFontSize(size); 6047 this.pdf.setFont(name, style); 6048 } 6049 } 6050 }, 6051 6052 setTextBaseline: function setTextBaseline(baseline) { 6053 this.ctx.textBaseline = baseline; 6054 }, 6055 6056 getTextBaseline: function getTextBaseline() { 6057 return this.ctx.textBaseline; 6058 }, 6059 6060 //TODO implement textAlign 6061 setTextAlign: function setTextAlign(align) { 6062 this.ctx.textAlign = align; 6063 }, 6064 6065 getTextAlign: function getTextAlign() { 6066 return this.ctx.textAlign; 6067 }, 6068 6069 setLineWidth: function setLineWidth(width) { 6070 this.ctx.lineWidth = width; 6071 this.pdf.setLineWidth(width); 6072 }, 6073 6074 setLineCap: function setLineCap(style) { 6075 this.ctx.lineCap = style; 6076 this.pdf.setLineCap(style); 6077 }, 6078 6079 setLineJoin: function setLineJoin(style) { 6080 this.ctx.lineJoin = style; 6081 this.pdf.setLineJoin(style); 6082 }, 6083 6084 moveTo: function moveTo(x, y) { 6085 x = this._wrapX(x); 6086 y = this._wrapY(y); 6087 6088 var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 6089 x = xpt[0]; 6090 y = xpt[1]; 6091 6092 var obj = { 6093 type: 'mt', 6094 x: x, 6095 y: y 6096 }; 6097 this.path.push(obj); 6098 }, 6099 6100 _wrapX: function _wrapX(x) { 6101 if (this.pageWrapXEnabled) { 6102 return x % this.pageWrapX; 6103 } else { 6104 return x; 6105 } 6106 }, 6107 6108 _wrapY: function _wrapY(y) { 6109 if (this.pageWrapYEnabled) { 6110 this._gotoPage(this._page(y)); 6111 return (y - this.lastBreak) % this.pageWrapY; 6112 } else { 6113 return y; 6114 } 6115 }, 6116 6117 transform: function transform(a, b, c, d, e, f) { 6118 //TODO apply to current transformation instead of replacing 6119 this.ctx._transform = [a, b, c, d, e, f]; 6120 }, 6121 6122 setTransform: function setTransform(a, b, c, d, e, f) { 6123 this.ctx._transform = [a, b, c, d, e, f]; 6124 }, 6125 6126 _getTransform: function _getTransform() { 6127 return this.ctx._transform; 6128 }, 6129 6130 lastBreak: 0, 6131 // Y Position of page breaks. 6132 pageBreaks: [], 6133 // returns: One-based Page Number 6134 // Should only be used if pageWrapYEnabled is true 6135 _page: function _page(y) { 6136 if (this.pageWrapYEnabled) { 6137 this.lastBreak = 0; 6138 var manualBreaks = 0; 6139 var autoBreaks = 0; 6140 for (var i = 0; i < this.pageBreaks.length; i++) { 6141 if (y >= this.pageBreaks[i]) { 6142 manualBreaks++; 6143 if (this.lastBreak === 0) { 6144 autoBreaks++; 6145 } 6146 var spaceBetweenLastBreak = this.pageBreaks[i] - this.lastBreak; 6147 this.lastBreak = this.pageBreaks[i]; 6148 var pagesSinceLastBreak = Math.floor(spaceBetweenLastBreak / this.pageWrapY); 6149 autoBreaks += pagesSinceLastBreak; 6150 } 6151 } 6152 if (this.lastBreak === 0) { 6153 var pagesSinceLastBreak = Math.floor(y / this.pageWrapY) + 1; 6154 autoBreaks += pagesSinceLastBreak; 6155 } 6156 return autoBreaks + manualBreaks; 6157 } else { 6158 return this.pdf.internal.getCurrentPageInfo().pageNumber; 6159 } 6160 }, 6161 6162 _gotoPage: function _gotoPage(pageOneBased) { 6163 // This is a stub to be overriden if needed 6164 }, 6165 6166 lineTo: function lineTo(x, y) { 6167 x = this._wrapX(x); 6168 y = this._wrapY(y); 6169 6170 var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 6171 x = xpt[0]; 6172 y = xpt[1]; 6173 6174 var obj = { 6175 type: 'lt', 6176 x: x, 6177 y: y 6178 }; 6179 this.path.push(obj); 6180 }, 6181 6182 bezierCurveTo: function bezierCurveTo(x1, y1, x2, y2, x, y) { 6183 x1 = this._wrapX(x1); 6184 y1 = this._wrapY(y1); 6185 x2 = this._wrapX(x2); 6186 y2 = this._wrapY(y2); 6187 x = this._wrapX(x); 6188 y = this._wrapY(y); 6189 6190 var xpt; 6191 xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 6192 x = xpt[0]; 6193 y = xpt[1]; 6194 xpt = this._matrix_map_point(this.ctx._transform, [x1, y1]); 6195 x1 = xpt[0]; 6196 y1 = xpt[1]; 6197 xpt = this._matrix_map_point(this.ctx._transform, [x2, y2]); 6198 x2 = xpt[0]; 6199 y2 = xpt[1]; 6200 6201 var obj = { 6202 type: 'bct', 6203 x1: x1, 6204 y1: y1, 6205 x2: x2, 6206 y2: y2, 6207 x: x, 6208 y: y 6209 }; 6210 this.path.push(obj); 6211 }, 6212 6213 quadraticCurveTo: function quadraticCurveTo(x1, y1, x, y) { 6214 x1 = this._wrapX(x1); 6215 y1 = this._wrapY(y1); 6216 x = this._wrapX(x); 6217 y = this._wrapY(y); 6218 6219 var xpt; 6220 xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 6221 x = xpt[0]; 6222 y = xpt[1]; 6223 xpt = this._matrix_map_point(this.ctx._transform, [x1, y1]); 6224 x1 = xpt[0]; 6225 y1 = xpt[1]; 6226 6227 var obj = { 6228 type: 'qct', 6229 x1: x1, 6230 y1: y1, 6231 x: x, 6232 y: y 6233 }; 6234 this.path.push(obj); 6235 }, 6236 6237 arc: function arc(x, y, radius, startAngle, endAngle, anticlockwise) { 6238 x = this._wrapX(x); 6239 y = this._wrapY(y); 6240 6241 if (!this._matrix_is_identity(this.ctx._transform)) { 6242 var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); 6243 x = xpt[0]; 6244 y = xpt[1]; 6245 6246 var x_radPt0 = this._matrix_map_point(this.ctx._transform, [0, 0]); 6247 var x_radPt = this._matrix_map_point(this.ctx._transform, [0, radius]); 6248 radius = Math.sqrt(Math.pow(x_radPt[0] - x_radPt0[0], 2) + Math.pow(x_radPt[1] - x_radPt0[1], 2)); 6249 6250 //TODO angles need to be transformed 6251 } 6252 6253 var obj = { 6254 type: 'arc', 6255 x: x, 6256 y: y, 6257 radius: radius, 6258 startAngle: startAngle, 6259 endAngle: endAngle, 6260 anticlockwise: anticlockwise 6261 }; 6262 this.path.push(obj); 6263 }, 6264 6265 drawImage: function drawImage(img, x, y, w, h, x2, y2, w2, h2) { 6266 if (x2 !== undefined) { 6267 x = x2; 6268 y = y2; 6269 w = w2; 6270 h = h2; 6271 } 6272 x = this._wrapX(x); 6273 y = this._wrapY(y); 6274 6275 var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); 6276 var xRect2 = this._matrix_map_rect(this.ctx._transform, { x: x2, y: y2, w: w2, h: h2 }); 6277 6278 // TODO implement source clipping and image scaling 6279 var format; 6280 var rx = /data:image\/(\w+).*/i; 6281 var m = rx.exec(img); 6282 if (m != null) { 6283 format = m[1]; 6284 } else { 6285 // format = "jpeg"; 6286 format = "png"; 6287 } 6288 6289 this.pdf.addImage(img, format, xRect.x, xRect.y, xRect.w, xRect.h); 6290 }, 6291 6292 /** 6293 * Multiply the first matrix by the second 6294 * @param m1 6295 * @param m2 6296 * @returns {*[]} 6297 * @private 6298 */ 6299 _matrix_multiply: function _matrix_multiply(m2, m1) { 6300 var sx = m1[0]; 6301 var shy = m1[1]; 6302 var shx = m1[2]; 6303 var sy = m1[3]; 6304 var tx = m1[4]; 6305 var ty = m1[5]; 6306 6307 var t0 = sx * m2[0] + shy * m2[2]; 6308 var t2 = shx * m2[0] + sy * m2[2]; 6309 var t4 = tx * m2[0] + ty * m2[2] + m2[4]; 6310 shy = sx * m2[1] + shy * m2[3]; 6311 sy = shx * m2[1] + sy * m2[3]; 6312 ty = tx * m2[1] + ty * m2[3] + m2[5]; 6313 sx = t0; 6314 shx = t2; 6315 tx = t4; 6316 6317 return [sx, shy, shx, sy, tx, ty]; 6318 }, 6319 6320 _matrix_rotation: function _matrix_rotation(m) { 6321 return Math.atan2(m[2], m[0]); 6322 }, 6323 6324 _matrix_decompose: function _matrix_decompose(matrix) { 6325 6326 var a = matrix[0]; 6327 var b = matrix[1]; 6328 var c = matrix[2]; 6329 var d = matrix[3]; 6330 6331 var scaleX = Math.sqrt(a * a + b * b); 6332 a /= scaleX; 6333 b /= scaleX; 6334 6335 var shear = a * c + b * d; 6336 c -= a * shear; 6337 d -= b * shear; 6338 6339 var scaleY = Math.sqrt(c * c + d * d); 6340 c /= scaleY; 6341 d /= scaleY; 6342 shear /= scaleY; 6343 6344 if (a * d < b * c) { 6345 a = -a; 6346 b = -b; 6347 shear = -shear; 6348 scaleX = -scaleX; 6349 } 6350 6351 return { 6352 scale: [scaleX, 0, 0, scaleY, 0, 0], 6353 translate: [1, 0, 0, 1, matrix[4], matrix[5]], 6354 rotate: [a, b, -b, a, 0, 0], 6355 skew: [1, 0, shear, 1, 0, 0] 6356 }; 6357 }, 6358 6359 _matrix_map_point: function _matrix_map_point(m1, pt) { 6360 var sx = m1[0]; 6361 var shy = m1[1]; 6362 var shx = m1[2]; 6363 var sy = m1[3]; 6364 var tx = m1[4]; 6365 var ty = m1[5]; 6366 6367 var px = pt[0]; 6368 var py = pt[1]; 6369 6370 var x = px * sx + py * shx + tx; 6371 var y = px * shy + py * sy + ty; 6372 return [x, y]; 6373 }, 6374 6375 _matrix_map_point_obj: function _matrix_map_point_obj(m1, pt) { 6376 var xpt = this._matrix_map_point(m1, [pt.x, pt.y]); 6377 return { x: xpt[0], y: xpt[1] }; 6378 }, 6379 6380 _matrix_map_rect: function _matrix_map_rect(m1, rect) { 6381 var p1 = this._matrix_map_point(m1, [rect.x, rect.y]); 6382 var p2 = this._matrix_map_point(m1, [rect.x + rect.w, rect.y + rect.h]); 6383 return { x: p1[0], y: p1[1], w: p2[0] - p1[0], h: p2[1] - p1[1] }; 6384 }, 6385 6386 _matrix_is_identity: function _matrix_is_identity(m1) { 6387 if (m1[0] != 1) { 6388 return false;
6389 } 6390 if (m1[1] != 0) { 6391 return false; 6392 } 6393 if (m1[2] != 0) { 6394 return false; 6395 } 6396 if (m1[3] != 1) { 6397 return false; 6398 } 6399 if (m1[4] != 0) { 6400 return false; 6401 } 6402 if (m1[5] != 0) { 6403 return false; 6404 } 6405 return true; 6406 }, 6407 6408 rotate: function rotate(angle) { 6409 var matrix = [Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0.0, 0.0]; 6410 this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix); 6411 }, 6412 6413 scale: function scale(sx, sy) { 6414 var matrix = [sx, 0.0, 0.0, sy, 0.0, 0.0]; 6415 this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix); 6416 }, 6417 6418 translate: function translate(x, y) { 6419 var matrix = [1.0, 0.0, 0.0, 1.0, x, y]; 6420 this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix); 6421 }, 6422 6423 stroke: function stroke() { 6424 if (this.ctx._clip_path.length > 0) { 6425 6426 var lines; 6427 if (window.outIntercept) { 6428 lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; 6429 } else { 6430 lines = this.internal.getCurrentPage(); 6431 } 6432 lines.push("q"); 6433 6434 var origPath = this.path; 6435 this.path = this.ctx._clip_path; 6436 this.ctx._clip_path = []; 6437 this._stroke(true); 6438 6439 this.ctx._clip_path = this.path; 6440 this.path = origPath; 6441 this._stroke(false); 6442 6443 lines.push("Q"); 6444 } else { 6445 this._stroke(false); 6446 } 6447 }, 6448 6449 _stroke: function _stroke(isClip) {
6450 if (!isClip && this._isStrokeTransparent()) { 6451 return; 6452 } 6453 6454 //TODO opacity 6455 6456 var moves = []; 6457 var closed = false; 6458 6459 var xPath = this.path; 6460 6461 for (var i = 0; i < xPath.length; i++) { 6462 var pt = xPath[i]; 6463 switch (pt.type) { 6464 case 'mt': 6465 moves.push({ start: pt, deltas: [], abs: [] }); 6466 break; 6467 case 'lt': 6468 var delta = [pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y]; 6469 moves[moves.length - 1].deltas.push(delta); 6470 moves[moves.length - 1].abs.push(pt); 6471 break; 6472 case 'bct': 6473 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]; 6474 moves[moves.length - 1].deltas.push(delta); 6475 break; 6476 case 'qct': 6477 // convert to bezier 6478 var x1 = xPath[i - 1].x + 2.0 / 3.0 * (pt.x1 - xPath[i - 1].x); 6479 var y1 = xPath[i - 1].y + 2.0 / 3.0 * (pt.y1 - xPath[i - 1].y); 6480 var x2 = pt.x + 2.0 / 3.0 * (pt.x1 - pt.x); 6481 var y2 = pt.y + 2.0 / 3.0 * (pt.y1 - pt.y); 6482 var x3 = pt.x; 6483 var y3 = pt.y; 6484 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]; 6485 moves[moves.length - 1].deltas.push(delta); 6486 break; 6487 case 'arc': 6488 //TODO this was hack to avoid out-of-bounds issue 6489 // No move-to before drawing the arc 6490 if (moves.length == 0) { 6491 moves.push({ start: { x: 0, y: 0 }, deltas: [], abs: [] }); 6492 } 6493 moves[moves.length - 1].arc = true; 6494 moves[moves.length - 1].abs.push(pt); 6495 break; 6496 case 'close': 6497 closed = true; 6498 break; 6499 } 6500 } 6501 6502 for (var i = 0; i < moves.length; i++) { 6503 var style; 6504 if (i == moves.length - 1) { 6505 style = 's'; 6506 } else { 6507 style = null; 6508 } 6509 if (moves[i].arc) { 6510 var arcs = moves[i].abs; 6511 for (var ii = 0; ii < arcs.length; ii++) { 6512 var arc = arcs[ii]; 6513 var start = arc.startAngle * 360 / (2 * Math.PI); 6514 var end = arc.endAngle * 360 / (2 * Math.PI); 6515 var x = arc.x; 6516 var y = arc.y; 6517 this.internal.arc2(this, x, y, arc.radius, start, end, arc.anticlockwise, style, isClip); 6518 } 6519 } else { 6520 var x = moves[i].start.x; 6521 var y = moves[i].start.y; 6522 if (!isClip) { 6523 this.pdf.lines(moves[i].deltas, x, y, null, style); 6524 } else { 6525 this.pdf.lines(moves[i].deltas, x, y, null, null); 6526 this.pdf.clip_fixed(); 6527 } 6528 } 6529 } 6530 }, 6531 6532 _isFillTransparent: function _isFillTransparent() { 6533 return this.ctx._isFillTransparent || this.globalAlpha == 0; 6534 }, 6535
6536 _isStrokeTransparent: function _isStrokeTransparent() { 6537 return this.ctx._isStrokeTransparent || this.globalAlpha == 0; 6538 }, 6539 6540 fill: function fill(fillRule) { 6541 //evenodd or nonzero (default) 6542 if (this.ctx._clip_path.length > 0) { 6543 6544 var lines; 6545 if (window.outIntercept) { 6546 lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; 6547 } else { 6548 lines = this.internal.getCurrentPage(); 6549 } 6550 lines.push("q"); 6551 6552 var origPath = this.path; 6553 this.path = this.ctx._clip_path; 6554 this.ctx._clip_path = []; 6555 this._fill(fillRule, true); 6556 6557 this.ctx._clip_path = this.path; 6558 this.path = origPath; 6559 this._fill(fillRule, false); 6560 6561 lines.push('Q'); 6562 } else { 6563 this._fill(fillRule, false); 6564 } 6565 }, 6566 6567 _fill: function _fill(fillRule, isClip) { 6568 if (this._isFillTransparent()) { 6569 return; 6570 } 6571 var v2Support = typeof this.pdf.internal.newObject2 === 'function'; 6572 6573 var lines; 6574 if (window.outIntercept) { 6575 lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; 6576 } else { 6577 lines = this.internal.getCurrentPage(); 6578 } 6579 6580 // if (this.ctx._clip_path.length > 0) { 6581 // lines.push('q'); 6582 // var oldPath = this.path; 6583 // this.path = this.ctx._clip_path; 6584 // this.ctx._clip_path = []; 6585 // this._fill(fillRule, true); 6586 // this.ctx._clip_path = this.path; 6587 // this.path = oldPath; 6588 // } 6589 6590 var moves = []; 6591 var outInterceptOld = window.outIntercept; 6592 6593 if (v2Support) { 6594 // Blend and Mask 6595 switch (this.ctx.globalCompositeOperation) { 6596 case 'normal': 6597 case 'source-over': 6598 break; 6599 case 'destination-in': 6600 case 'destination-out': 6601 //TODO this need to be added to the current group or page 6602 // define a mask stream 6603 var obj = this.pdf.internal.newStreamObject(); 6604 6605 // define a mask state 6606 var obj2 = this.pdf.internal.newObject2(); 6607 obj2.push('<</Type /ExtGState'); 6608 obj2.push('/SMask <</S /Alpha /G ' + obj.objId + ' 0 R>>'); // /S /Luminosity will need to define color space 6609 obj2.push('>>'); 6610 6611 // add mask to page resources 6612 var gsName = 'MASK' + obj2.objId; 6613 this.pdf.internal.addGraphicsState(gsName, obj2.objId); 6614 6615 var instruction = '/' + gsName + ' gs'; 6616 // add mask to page, group, or stream 6617 lines.splice(0, 0, 'q'); 6618 lines.splice(1, 0, instruction); 6619 lines.push('Q'); 6620 6621 window.outIntercept = obj; 6622 break; 6623 default: 6624 var dictionaryEntry = '/' + this.pdf.internal.blendModeMap[this.ctx.globalCompositeOperation.toUpperCase()]; 6625 if (dictionaryEntry) { 6626 this.pdf.internal.out(dictionaryEntry + ' gs'); 6627 } 6628 break; 6629 } 6630 } 6631 6632 var alpha = this.ctx.globalAlpha; 6633 if (this.ctx._fillOpacity < 1) { 6634 // TODO combine this with global opacity 6635 alpha = this.ctx._fillOpacity; 6636 } 6637 6638 //TODO check for an opacity graphics state that was already created
6639 //TODO do not set opacity if current value is already active 6640 if (v2Support) { 6641 var objOpac = this.pdf.internal.newObject2(); 6642 objOpac.push('<</Type /ExtGState'); 6643 //objOpac.push(this.ctx.globalAlpha + " CA"); // Stroke 6644 //objOpac.push(this.ctx.globalAlpha + " ca"); // Not Stroke 6645 objOpac.push('/CA ' + alpha); // Stroke 6646 objOpac.push('/ca ' + alpha); // Not Stroke 6647 objOpac.push('>>'); 6648 var gsName = 'GS_O_' + objOpac.objId; 6649 this.pdf.internal.addGraphicsState(gsName, objOpac.objId); 6650 this.pdf.internal.out('/' + gsName + ' gs'); 6651 } 6652 6653 var xPath = this.path; 6654 6655 for (var i = 0; i < xPath.length; i++) { 6656 var pt = xPath[i]; 6657 switch (pt.type) { 6658 case 'mt': 6659 moves.push({ start: pt, deltas: [], abs: [] }); 6660 break; 6661 case 'lt': 6662 var delta = [pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y]; 6663 moves[moves.length - 1].deltas.push(delta); 6664 moves[moves.length - 1].abs.push(pt); 6665 break; 6666 case 'bct': 6667 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]; 6668 moves[moves.length - 1].deltas.push(delta); 6669 break; 6670 case 'qct': 6671 // convert to bezier 6672 var x1 = xPath[i - 1].x + 2.0 / 3.0 * (pt.x1 - xPath[i - 1].x); 6673 var y1 = xPath[i - 1].y + 2.0 / 3.0 * (pt.y1 - xPath[i - 1].y); 6674 var x2 = pt.x + 2.0 / 3.0 * (pt.x1 - pt.x); 6675 var y2 = pt.y + 2.0 / 3.0 * (pt.y1 - pt.y); 6676 var x3 = pt.x; 6677 var y3 = pt.y; 6678 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]; 6679 moves[moves.length - 1].deltas.push(delta); 6680 break; 6681 case 'arc': 6682 //TODO this was hack to avoid out-of-bounds issue when drawing circle 6683 // No move-to before drawing the arc 6684 if (moves.length === 0) { 6685 moves.push({ deltas: [], abs: [] }); 6686 } 6687 moves[moves.length - 1].arc = true; 6688 moves[moves.length - 1].abs.push(pt); 6689 break; 6690 case 'close': 6691 moves.push({ close: true }); 6692 break; 6693 } 6694 } 6695 6696 for (var i = 0; i < moves.length; i++) { 6697 var style; 6698 if (i == moves.length - 1) { 6699 style = 'f'; 6700 if (fillRule === 'evenodd') { 6701 style += '*'; 6702 } 6703 } else { 6704 style = null; 6705 } 6706 6707 if (moves[i].close) { 6708 this.pdf.internal.out('h'); 6709 this.pdf.internal.out('f'); 6710 } else if (moves[i].arc) { 6711 if (moves[i].start) { 6712 this.internal.move2(this, moves[i].start.x, moves[i].start.y); 6713 } 6714 var arcs = moves[i].abs; 6715 for (var ii = 0; ii < arcs.length; ii++) { 6716 var arc = arcs[ii]; 6717 //TODO lines deltas were getting in here 6718 if (typeof arc.startAngle !== 'undefined') { 6719 var start = arc.startAngle * 360 / (2 * Math.PI); 6720 var end = arc.endAngle * 360 / (2 * Math.PI); 6721 var x = arc.x; 6722 var y = arc.y; 6723 if (ii === 0) { 6724 this.internal.move2(this, x, y); 6725 } 6726 this.internal.arc2(this, x, y, arc.radius, start, end, arc.anticlockwise, null, isClip); 6727 if (ii === arcs.length - 1) { 6728 // The original arc move did not occur because of the algorithm 6729 if (moves[i].start) { 6730 var x = moves[i].start.x; 6731 var y = moves[i].start.y; 6732 this.internal.line2(c2d, x, y); 6733 } 6734 } 6735 } else { 6736 this.internal.line2(c2d, arc.x, arc.y); 6737 } 6738 } 6739 } else { 6740 var x = moves[i].start.x; 6741 var y = moves[i].start.y; 6742 if (!isClip) { 6743 this.pdf.lines(moves[i].deltas, x, y, null, style); 6744 } else { 6745 this.pdf.lines(moves[i].deltas, x, y, null, null);
6746 this.pdf.clip_fixed(); 6747 } 6748 } 6749 } 6750 6751 window.outIntercept = outInterceptOld; 6752 6753 // if (this.ctx._clip_path.length > 0) { 6754 // lines.push('Q'); 6755 // } 6756 }, 6757 6758 pushMask: function pushMask() { 6759 var v2Support = typeof this.pdf.internal.newObject2 === 'function'; 6760 6761 if (!v2Support) { 6762 console.log('jsPDF v2 not enabled'); 6763 return; 6764 } 6765 6766 // define a mask stream 6767 var obj = this.pdf.internal.newStreamObject(); 6768 6769 // define a mask state 6770 var obj2 = this.pdf.internal.newObject2(); 6771 obj2.push('<</Type /ExtGState'); 6772 obj2.push('/SMask <</S /Alpha /G ' + obj.objId + ' 0 R>>'); // /S /Luminosity will need to define color space 6773 obj2.push('>>'); 6774 6775 // add mask to page resources 6776 var gsName = 'MASK' + obj2.objId; 6777 this.pdf.internal.addGraphicsState(gsName, obj2.objId); 6778 6779 var instruction = '/' + gsName + ' gs'; 6780 this.pdf.internal.out(instruction); 6781 }, 6782 6783 clip: function clip() { 6784 //TODO do we reset the path, or just copy it? 6785 if (this.ctx._clip_path.length > 0) { 6786 for (var i = 0; i < this.path.length; i++) { 6787 this.ctx._clip_path.push(this.path[i]); 6788 } 6789 } else { 6790 this.ctx._clip_path = this.path; 6791 } 6792 this.path = []; 6793 }, 6794 6795 measureText: function measureText(text) { 6796 var pdf = this.pdf; 6797 return { 6798 getWidth: function getWidth() { 6799 var fontSize = pdf.internal.getFontSize(); 6800 var txtWidth = pdf.getStringUnitWidth(text) * fontSize / pdf.internal.scaleFactor; 6801 // Convert points to pixels 6802 txtWidth *= 1.3333; 6803 return txtWidth; 6804 }, 6805 6806 get width() { 6807 return this.getWidth(text); 6808 } 6809 }; 6810 }, 6811 _getBaseline: function _getBaseline(y) { 6812 var height = parseInt(this.pdf.internal.getFontSize()); 6813 // TODO Get descent from font descriptor 6814 var descent = height * .25; 6815 switch (this.ctx.textBaseline) { 6816 case 'bottom': 6817 return y - descent; 6818 case 'top': 6819 return y + height; 6820 case 'hanging': 6821 return y + height - descent; 6822 case 'middle': 6823 return y + height / 2 - descent; 6824 case 'ideographic': 6825 // TODO not implemented 6826 return y; 6827 case 'alphabetic': 6828 default: 6829 return y; 6830 } 6831 } 6832 }; 6833 6834 var c2d = jsPDFAPI.context2d; 6835 6836 // accessor methods 6837 Object.defineProperty(c2d, 'fillStyle', { 6838 set: function set(value) { 6839 this.setFillStyle(value); 6840 }, 6841 get: function get() { 6842 return this.ctx.fillStyle; 6843 } 6844 }); 6845 Object.defineProperty(c2d, 'strokeStyle', { 6846 set: function set(value) { 6847 this.setStrokeStyle(value); 6848 }, 6849 get: function get() { 6850 return this.ctx.strokeStyle; 6851 } 6852 }); 6853 Object.defineProperty(c2d, 'lineWidth', { 6854 set: function set(value) { 6855 this.setLineWidth(value); 6856 }, 6857 get: function get() { 6858 return this.ctx.lineWidth; 6859 } 6860 }); 6861 Object.defineProperty(c2d, 'lineCap', { 6862 set: function set(val) { 6863 this.setLineCap(val); 6864 }, 6865 get: function get() { 6866 return this.ctx.lineCap; 6867 } 6868 }); 6869 Object.defineProperty(c2d, 'lineJoin', { 6870 set: function set(val) { 6871 this.setLineJoin(val); 6872 }, 6873 get: function get() { 6874 return this.ctx.lineJoin; 6875 } 6876 }); 6877 Object.defineProperty(c2d, 'miterLimit', { 6878 set: function set(val) { 6879 this.ctx.miterLimit = val; 6880 }, 6881 get: function get() { 6882 return this.ctx.miterLimit; 6883 } 6884 });
6885 Object.defineProperty(c2d, 'textBaseline', { 6886 set: function set(value) { 6887 this.setTextBaseline(value); 6888 }, 6889 get: function get() { 6890 return this.getTextBaseline(); 6891 } 6892 }); 6893 Object.defineProperty(c2d, 'textAlign', { 6894 set: function set(value) { 6895 this.setTextAlign(value); 6896 }, 6897 get: function get() { 6898 return this.getTextAlign(); 6899 } 6900 }); 6901 Object.defineProperty(c2d, 'font', { 6902 set: function set(value) { 6903 this.setFont(value); 6904 }, 6905 get: function get() { 6906 return this.ctx.font; 6907 } 6908 }); 6909 Object.defineProperty(c2d, 'globalCompositeOperation', { 6910 set: function set(value) { 6911 this.ctx.globalCompositeOperation = value; 6912 }, 6913 get: function get() { 6914 return this.ctx.globalCompositeOperation; 6915 } 6916 }); 6917 Object.defineProperty(c2d, 'globalAlpha', { 6918 set: function set(value) { 6919 this.ctx.globalAlpha = value; 6920 }, 6921 get: function get() { 6922 return this.ctx.globalAlpha; 6923 } 6924 }); 6925 // Not HTML API 6926 Object.defineProperty(c2d, 'ignoreClearRect', { 6927 set: function set(value) { 6928 this.ctx.ignoreClearRect = value; 6929 }, 6930 get: function get() { 6931 return this.ctx.ignoreClearRect; 6932 } 6933 }); 6934 // End Not HTML API 6935 6936 c2d.internal = {}; 6937 6938 c2d.internal.rxRgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/; 6939 c2d.internal.rxRgba = /rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/; 6940 c2d.internal.rxTransparent = /transparent|rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*0+\s*\)/; 6941 6942 // http://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html 6943 c2d.internal.arc = function (c2d, xc, yc, r, a1, a2, anticlockwise, style) { 6944 var includeMove = true; 6945 6946 var k = this.pdf.internal.scaleFactor; 6947 var pageHeight = this.pdf.internal.pageSize.height; 6948 var f2 = this.pdf.internal.f2; 6949 6950 var a1r = a1 * (Math.PI / 180); 6951 var a2r = a2 * (Math.PI / 180); 6952 var curves = this.createArc(r, a1r, a2r, anticlockwise); 6953 var pathData = null; 6954 6955 for (var i = 0; i < curves.length; i++) { 6956 var curve = curves[i]; 6957 if (includeMove && i === 0) { 6958 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(' ')); 6959 } else { 6960 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(' ')); 6961 } 6962 6963 //c2d._lastPoint = {x: curve.x1 + xc, y: curve.y1 + yc}; 6964 c2d._lastPoint = { x: xc, y: yc }; 6965 // 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' 6966 } 6967 6968 if (style !== null) { 6969 this.pdf.internal.out(this.pdf.internal.getStyle(style)); 6970 } 6971 }; 6972 6973 /** 6974 * 6975 * @param x Edge point X 6976 * @param y Edge point Y 6977 * @param r Radius 6978 * @param a1 start angle 6979 * @param a2 end angle 6980 * @param anticlockwise 6981 * @param style 6982 * @param isClip 6983 */ 6984 c2d.internal.arc2 = function (c2d, x, y, r, a1, a2, anticlockwise, style, isClip) { 6985 // we need to convert from cartesian to polar here methinks. 6986 var centerX = x; // + r; 6987 var centerY = y; 6988 6989 if (!isClip) { 6990 this.arc(c2d, centerX, centerY, r, a1, a2, anticlockwise, style); 6991 } else { 6992 this.arc(c2d, centerX, centerY, r, a1, a2, anticlockwise, null);
6993 this.pdf.clip_fixed(); 6994 } 6995 }; 6996 6997 c2d.internal.move2 = function (c2d, x, y) { 6998 var k = this.pdf.internal.scaleFactor; 6999 var pageHeight = this.pdf.internal.pageSize.height; 7000 var f2 = this.pdf.internal.f2; 7001 7002 this.pdf.internal.out([f2(x * k), f2((pageHeight - y) * k), 'm'].join(' ')); 7003 c2d._lastPoint = { x: x, y: y }; 7004 }; 7005 7006 c2d.internal.line2 = function (c2d, dx, dy) { 7007 var k = this.pdf.internal.scaleFactor; 7008 var pageHeight = this.pdf.internal.pageSize.height; 7009 var f2 = this.pdf.internal.f2; 7010 7011 //var pt = {x: c2d._lastPoint.x + dx, y: c2d._lastPoint.y + dy}; 7012 var pt = { x: dx, y: dy }; 7013 7014 this.pdf.internal.out([f2(pt.x * k), f2((pageHeight - pt.y) * k), 'l'].join(' ')); 7015 //this.pdf.internal.out('f'); 7016 c2d._lastPoint = pt; 7017 }; 7018 7019 /** 7020 * 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. 7021 * 7022 * 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. 7023 */ 7024 7025 c2d.internal.createArc = function (radius, startAngle, endAngle, anticlockwise) { 7026 var EPSILON = 0.00001; // Roughly 1/1000th of a degree, see below 7027 var twoPI = Math.PI * 2; 7028 var piOverTwo = Math.PI / 2.0; 7029 7030 // normalize startAngle, endAngle to [0, 2PI] 7031 var startAngleN = startAngle; 7032 if (startAngleN < twoPI || startAngleN > twoPI) { 7033 startAngleN = startAngleN % twoPI; 7034 } 7035 if (startAngleN < 0) { 7036 startAngleN = twoPI + startAngleN; 7037 } 7038 7039 while (startAngle > endAngle) { 7040 startAngle = startAngle - twoPI; 7041 } 7042 var totalAngle = Math.abs(endAngle - startAngle); 7043 if (totalAngle < twoPI) { 7044 if (anticlockwise) { 7045 totalAngle = twoPI - totalAngle; 7046 } 7047 } 7048 7049 // Compute the sequence of arc curves, up to PI/2 at a time. 7050 var curves = []; 7051 var sgn = anticlockwise ? -1 : +1; 7052 7053 var a1 = startAngleN; 7054 for (; totalAngle > EPSILON;) { 7055 var remain = sgn * Math.min(totalAngle, piOverTwo); 7056 var a2 = a1 + remain; 7057 curves.push(this.createSmallArc(radius, a1, a2)); 7058 totalAngle -= Math.abs(a2 - a1); 7059 a1 = a2; 7060 } 7061 7062 return curves; 7063 }; 7064 7065 c2d.internal.getCurrentPage = function () { 7066 return this.pdf.internal.pages[this.pdf.internal.getCurrentPageInfo().pageNumber]; 7067 }; 7068 7069 /** 7070 * 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. 7071 * 7072 * 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. 7073 * 7074 * 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. 7075 */ 7076 7077 c2d.internal.createSmallArc = function (r, a1, a2) { 7078 // Compute all four points for an arc that subtends the same total angle 7079 // but is centered on the X-axis 7080 7081 var a = (a2 - a1) / 2.0; 7082 7083 var x4 = r * Math.cos(a); 7084 var y4 = r * Math.sin(a); 7085 var x1 = x4; 7086 var y1 = -y4; 7087 7088 var q1 = x1 * x1 + y1 * y1; 7089 var q2 = q1 + x1 * x4 + y1 * y4; 7090 var k2 = 4 / 3 * (Math.sqrt(2 * q1 * q2) - q2) / (x1 * y4 - y1 * x4); 7091 7092 var x2 = x1 - k2 * y1; 7093 var y2 = y1 + k2 * x1; 7094 var x3 = x2; 7095 var y3 = -y2; 7096 7097 // Find the arc points' actual locations by computing x1,y1 and x4,y4 7098 // and rotating the control points by a + a1 7099 7100 var ar = a + a1; 7101 var cos_ar = Math.cos(ar); 7102 var sin_ar = Math.sin(ar); 7103 7104 return { 7105 x1: r * Math.cos(a1), 7106 y1: r * Math.sin(a1), 7107 x2: x2 * cos_ar - y2 * sin_ar, 7108 y2: x2 * sin_ar + y2 * cos_ar, 7109 x3: x3 * cos_ar - y3 * sin_ar, 7110 y3: x3 * sin_ar + y3 * cos_ar, 7111 x4: r * Math.cos(a2), 7112 y4: r * Math.sin(a2) 7113 }; 7114 }; 7115 7116 function context() { 7117 this._isStrokeTransparent = false;
7118 this._strokeOpacity = 1; 7119 this.strokeStyle = '#000000'; 7120 this.fillStyle = '#000000'; 7121 this._isFillTransparent = false; 7122 this._fillOpacity = 1; 7123 this.font = "12pt times"; 7124 this.textBaseline = 'alphabetic'; // top,bottom,middle,ideographic,alphabetic,hanging 7125 this.textAlign = 'start'; 7126 this.lineWidth = 1; 7127 this.lineJoin = 'miter'; // round, bevel, miter 7128 this.lineCap = 'butt'; // butt, round, square 7129 this._transform = [1, 0, 0, 1, 0, 0]; // sx, shy, shx, sy, tx, ty 7130 this.globalCompositeOperation = 'normal'; 7131 this.globalAlpha = 1.0; 7132 this._clip_path = []; 7133 // TODO miter limit //default 10 7134 7135 // Not HTML API 7136 this.ignoreClearRect = false; 7137 7138 this.copy = function (ctx) { 7139 this._isStrokeTransparent = ctx._isStrokeTransparent; 7140 this._strokeOpacity = ctx._strokeOpacity; 7141 this.strokeStyle = ctx.strokeStyle; 7142 this._isFillTransparent = ctx._isFillTransparent; 7143 this._fillOpacity = ctx._fillOpacity; 7144 this.fillStyle = ctx.fillStyle; 7145 this.font = ctx.font; 7146 this.lineWidth = ctx.lineWidth; 7147 this.lineJoin = ctx.lineJoin; 7148 this.lineCap = ctx.lineCap; 7149 this.textBaseline = ctx.textBaseline; 7150 this.textAlign = ctx.textAlign; 7151 this._fontSize = ctx._fontSize; 7152 this._transform = ctx._transform.slice(0); 7153 this.globalCompositeOperation = ctx.globalCompositeOperation; 7154 this.globalAlpha = ctx.globalAlpha; 7155 this._clip_path = ctx._clip_path.slice(0); //TODO deep copy? 7156 7157 // Not HTML API 7158 this.ignoreClearRect = ctx.ignoreClearRect; 7159 }; 7160 } 7161 7162 return this; 7163})(jsPDF.API); 7164 7165/** @preserve 7166 * jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser 7167 * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com 7168 * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria 7169 * 2014 Diego Casorran, https://github.com/diegocr 7170 * 2014 Daniel Husar, https://github.com/danielhusar 7171 * 2014 Wolfgang Gassler, https://github.com/woolfg 7172 * 2014 Steven Spungin, https://github.com/flamenco 7173 * 7174 * 7175 * ==================================================================== 7176 */ 7177 7178(function (jsPDFAPI) { 7179 var clone, _DrillForContent, FontNameDB, FontStyleMap, TextAlignMap, FontWeightMap, FloatMap, ClearMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, checkForFooter, process, tableToJson; 7180 clone = function () { 7181 return function (obj) { 7182 Clone.prototype = obj; 7183 return new Clone(); 7184 }; 7185 function Clone() {} 7186 }(); 7187 PurgeWhiteSpace = function PurgeWhiteSpace(array) { 7188 var fragment, i, l, lTrimmed, r, rTrimmed, trailingSpace; 7189 i = 0; 7190 l = array.length; 7191 fragment = void 0; 7192 lTrimmed = false; 7193 rTrimmed = false; 7194 while (!lTrimmed && i !== l) { 7195 fragment = array[i] = array[i].trimLeft(); 7196 if (fragment) { 7197 lTrimmed = true; 7198 } 7199 i++; 7200 } 7201 i = l - 1; 7202 while (l && !rTrimmed && i !== -1) { 7203 fragment = array[i] = array[i].trimRight(); 7204 if (fragment) { 7205 rTrimmed = true; 7206 } 7207 i--; 7208 } 7209 r = /\s+$/g; 7210 trailingSpace = true; 7211 i = 0; 7212 while (i !== l) { 7213 // Leave the line breaks intact 7214 if (array[i] != "\u2028") { 7215 fragment = array[i].replace(/\s+/g, " "); 7216 if (trailingSpace) { 7217 fragment = fragment.trimLeft(); 7218 } 7219 if (fragment) { 7220 trailingSpace = r.test(fragment); 7221 } 7222 array[i] = fragment; 7223 } 7224 i++; 7225 } 7226 return array; 7227 }; 7228 Renderer = function Renderer(pdf, x, y, settings) { 7229 this.pdf = pdf; 7230 this.x = x; 7231 this.y = y; 7232 this.settings = settings; 7233 //list of functions which are called after each element-rendering process 7234 this.watchFunctions = []; 7235 this.init(); 7236 return this; 7237 }; 7238 ResolveFont = function ResolveFont(css_font_family_string) { 7239 var name, part, parts; 7240 name = void 0; 7241 parts = css_font_family_string.split(","); 7242 part = parts.shift(); 7243 while (!name && part) { 7244 name = FontNameDB[part.trim().toLowerCase()]; 7245 part = parts.shift(); 7246 } 7247 return name; 7248 }; 7249 ResolveUnitedNumber = function ResolveUnitedNumber(css_line_height_string) { 7250 7251 //IE8 issues 7252 css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string; 7253 if (css_line_height_string.indexOf("em") > -1 && !isNaN(Number(css_line_height_string.replace("em", "")))) { 7254 css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px"; 7255 } 7256 if (css_line_height_string.indexOf("pt") > -1 && !isNaN(Number(css_line_height_string.replace("pt", "")))) { 7257 css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px"; 7258 } 7259 7260 var normal, undef, value; 7261 undef = void 0; 7262 normal = 16.00; 7263 value = UnitedNumberMap[css_line_height_string]; 7264 if (value) { 7265 return value; 7266 } 7267 value = { 7268 "xx-small": 9, 7269 "x-small": 11, 7270 small: 13, 7271 medium: 16, 7272 large: 19, 7273 "x-large": 23, 7274 "xx-large": 28, 7275 auto: 0 7276 }[{ css_line_height_string: css_line_height_string }]; 7277 7278 if (value !== undef) { 7279 return UnitedNumberMap[css_line_height_string] = value / normal; 7280 } 7281 if (value = parseFloat(css_line_height_string)) { 7282 return UnitedNumberMap[css_line_height_string] = value / normal; 7283 } 7284 value = css_line_height_string.match(/([\d\.]+)(px)/); 7285 if (value.length === 3) { 7286 return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal; 7287 } 7288 return UnitedNumberMap[css_line_height_string] = 1; 7289 }; 7290 GetCSS = function GetCSS(element) { 7291 var css, tmp, computedCSSElement; 7292 computedCSSElement = function (el) { 7293 var compCSS; 7294 compCSS = function (el) { 7295 if (document.defaultView && document.defaultView.getComputedStyle) { 7296 return document.defaultView.getComputedStyle(el, null); 7297 } else if (el.currentStyle) { 7298 return el.currentStyle; 7299 } else { 7300 return el.style; 7301 } 7302 }(el); 7303 return function (prop) { 7304 prop = prop.replace(/-\D/g, function (match) { 7305 return match.charAt(1).toUpperCase(); 7306 }); 7307 return compCSS[prop]; 7308 }; 7309 }(element); 7310 css = {}; 7311 tmp = void 0; 7312 css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times"; 7313 css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal"; 7314 css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left"; 7315 tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal"; 7316 if (tmp === "bold") { 7317 if (css["font-style"] === "normal") { 7318 css["font-style"] = tmp; 7319 } else { 7320 css["font-style"] = tmp + css["font-style"]; 7321 } 7322 } 7323 css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1; 7324 css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1; 7325 css["display"] = computedCSSElement("display") === "inline" ? "inline" : "block"; 7326 7327 tmp = css["display"] === "block"; 7328 css["margin-top"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-top")) || 0; 7329 css["margin-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0; 7330 css["padding-top"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-top")) || 0; 7331 css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0; 7332 css["margin-left"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-left")) || 0; 7333 css["margin-right"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-right")) || 0; 7334 css["padding-left"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-left")) || 0; 7335 css["padding-right"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-right")) || 0; 7336 7337 css["page-break-before"] = computedCSSElement("page-break-before") || "auto"; 7338 7339 //float and clearing of floats 7340 css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none"; 7341 css["clear"] = ClearMap[computedCSSElement("clear")] || "none"; 7342 7343 css["color"] = computedCSSElement("color"); 7344 7345 return css; 7346 }; 7347 elementHandledElsewhere = function elementHandledElsewhere(element, renderer, elementHandlers) { 7348 var handlers, i, isHandledElsewhere, l, t; 7349 isHandledElsewhere = false;
7350 i = void 0; 7351 l = void 0; 7352 t = void 0; 7353 handlers = elementHandlers["#" + element.id]; 7354 if (handlers) { 7355 if (typeof handlers === "function") { 7356 isHandledElsewhere = handlers(element, renderer); 7357 } else { 7358 i = 0; 7359 l = handlers.length; 7360 while (!isHandledElsewhere && i !== l) { 7361 isHandledElsewhere = handlers[i](element, renderer); 7362 i++; 7363 } 7364 } 7365 } 7366 handlers = elementHandlers[element.nodeName]; 7367 if (!isHandledElsewhere && handlers) { 7368 if (typeof handlers === "function") { 7369 isHandledElsewhere = handlers(element, renderer); 7370 } else { 7371 i = 0; 7372 l = handlers.length; 7373 while (!isHandledElsewhere && i !== l) { 7374 isHandledElsewhere = handlers[i](element, renderer); 7375 i++; 7376 } 7377 } 7378 } 7379 return isHandledElsewhere; 7380 }; 7381 tableToJson = function tableToJson(table, renderer) { 7382 var data, headers, i, j, rowData, tableRow, table_obj, table_with, cell, l; 7383 data = []; 7384 headers = []; 7385 i = 0; 7386 l = table.rows[0].cells.length; 7387 table_with = table.clientWidth; 7388 while (i < l) { 7389 cell = table.rows[0].cells[i]; 7390 headers[i] = { 7391 name: cell.textContent.toLowerCase().replace(/\s+/g, ''), 7392 prompt: cell.textContent.replace(/\r?\n/g, ''), 7393 width: cell.clientWidth / table_with * renderer.pdf.internal.pageSize.width 7394 }; 7395 i++; 7396 } 7397 i = 1; 7398 while (i < table.rows.length) { 7399 tableRow = table.rows[i]; 7400 rowData = {}; 7401 j = 0; 7402 while (j < tableRow.cells.length) { 7403 rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, ''); 7404 j++; 7405 } 7406 data.push(rowData); 7407 i++; 7408 } 7409 return table_obj = { 7410 rows: data, 7411 headers: headers 7412 }; 7413 }; 7414 var SkipNode = { 7415 SCRIPT: 1, 7416 STYLE: 1, 7417 NOSCRIPT: 1, 7418 OBJECT: 1, 7419 EMBED: 1, 7420 SELECT: 1 7421 }; 7422 var listCount = 1; 7423 _DrillForContent = function DrillForContent(element, renderer, elementHandlers) { 7424 var cn, cns, fragmentCSS, i, isBlock, l, px2pt, table2json, cb; 7425 cns = element.childNodes; 7426 cn = void 0; 7427 fragmentCSS = GetCSS(element); 7428 isBlock = fragmentCSS.display === "block"; 7429 if (isBlock) { 7430 renderer.setBlockBoundary(); 7431 renderer.setBlockStyle(fragmentCSS); 7432 } 7433 px2pt = 0.264583 * 72 / 25.4; 7434 i = 0; 7435 l = cns.length; 7436 while (i < l) { 7437 cn = cns[i]; 7438 if ((typeof cn === "undefined" ? "undefined" : _typeof(cn)) === "object") { 7439 7440 //execute all watcher functions to e.g. reset floating 7441 renderer.executeWatchFunctions(cn); 7442 7443 /*** HEADER rendering **/ 7444 if (cn.nodeType === 1 && cn.nodeName === 'HEADER') { 7445 var header = cn; 7446 //store old top margin 7447 var oldMarginTop = renderer.pdf.margins_doc.top; 7448 //subscribe for new page event and render header first on every page 7449 renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) { 7450 //set current y position to old margin 7451 renderer.y = oldMarginTop; 7452 //render all child nodes of the header element 7453 _DrillForContent(header, renderer, elementHandlers); 7454 //set margin to old margin + rendered header + 10 space to prevent overlapping 7455 //important for other plugins (e.g. table) to start rendering at correct position after header 7456 renderer.pdf.margins_doc.top = renderer.y + 10; 7457 renderer.y += 10; 7458 }, false); 7459 } 7460 7461 if (cn.nodeType === 8 && cn.nodeName === "#comment") { 7462 if (~cn.textContent.indexOf("ADD_PAGE")) { 7463 renderer.pdf.addPage(); 7464 renderer.y = renderer.pdf.margins_doc.top; 7465 } 7466 } else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) { 7467 /*** IMAGE RENDERING ***/ 7468 var cached_image; 7469 if (cn.nodeName === "IMG") { 7470 var url = cn.getAttribute("src"); 7471 cached_image = images[renderer.pdf.sHashCode(url) || url]; 7472 } 7473 if (cached_image) { 7474 if (renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height && renderer.y > renderer.pdf.margins_doc.top) { 7475 renderer.pdf.addPage(); 7476 renderer.y = renderer.pdf.margins_doc.top; 7477 //check if we have to set back some values due to e.g. header rendering for new page 7478 renderer.executeWatchFunctions(cn); 7479 } 7480 7481 var imagesCSS = GetCSS(cn); 7482 var imageX = renderer.x; 7483 var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor; 7484 7485 //define additional paddings, margins which have to be taken into account for margin calculations 7486 var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"]) * fontToUnitRatio; 7487 var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"]) * fontToUnitRatio; 7488 var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"]) * fontToUnitRatio; 7489 var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"]) * fontToUnitRatio; 7490 7491 //if float is set to right, move the image to the right border 7492 //add space if margin is set 7493 if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') { 7494 imageX += renderer.settings.width - cn.width - additionalSpaceRight; 7495 } else { 7496 imageX += additionalSpaceLeft; 7497 } 7498 7499 renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height); 7500 cached_image = undefined; 7501 //if the float prop is specified we have to float the text around the image 7502 if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') { 7503 //add functiont to set back coordinates after image rendering 7504 renderer.watchFunctions.push(function (diffX, thresholdY, diffWidth, el) { 7505 //undo drawing box adaptions which were set by floating 7506 if (renderer.y >= thresholdY) { 7507 renderer.x += diffX; 7508 renderer.settings.width += diffWidth; 7509 return true; 7510 } else if (el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x + el.width > renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width) { 7511 renderer.x += diffX; 7512 renderer.y = thresholdY; 7513 renderer.settings.width += diffWidth; 7514 return true; 7515 } else { 7516 return false;
7517 } 7518 }.bind(this, imagesCSS['float'] === 'left' ? -cn.width - additionalSpaceLeft - additionalSpaceRight : 0, renderer.y + cn.height + additionalSpaceTop + additionalSpaceBottom, cn.width)); 7519 //reset floating by clear:both divs 7520 //just set cursorY after the floating element 7521 renderer.watchFunctions.push(function (yPositionAfterFloating, pages, el) { 7522 if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) { 7523 if (el.nodeType === 1 && GetCSS(el).clear === 'both') { 7524 renderer.y = yPositionAfterFloating; 7525 return true; 7526 } else { 7527 return false; 7528 } 7529 } else { 7530 return true; 7531 } 7532 }.bind(this, renderer.y + cn.height, renderer.pdf.internal.getNumberOfPages())); 7533 7534 //if floating is set we decrease the available width by the image width 7535 renderer.settings.width -= cn.width + additionalSpaceLeft + additionalSpaceRight; 7536 //if left just add the image width to the X coordinate 7537 if (imagesCSS['float'] === 'left') { 7538 renderer.x += cn.width + additionalSpaceLeft + additionalSpaceRight; 7539 } 7540 } else { 7541 //if no floating is set, move the rendering cursor after the image height 7542 renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom; 7543 } 7544 7545 /*** TABLE RENDERING ***/ 7546 } else if (cn.nodeName === "TABLE") { 7547 table2json = tableToJson(cn, renderer); 7548 renderer.y += 10; 7549 renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, { 7550 autoSize: false, 7551 printHeaders: elementHandlers.printHeaders, 7552 margins: renderer.pdf.margins_doc, 7553 css: GetCSS(cn) 7554 }); 7555 renderer.y = renderer.pdf.lastCellPos.y + renderer.pdf.lastCellPos.h + 20; 7556 } else if (cn.nodeName === "OL" || cn.nodeName === "UL") { 7557 listCount = 1; 7558 if (!elementHandledElsewhere(cn, renderer, elementHandlers)) { 7559 _DrillForContent(cn, renderer, elementHandlers); 7560 } 7561 renderer.y += 10; 7562 } else if (cn.nodeName === "LI") { 7563 var temp = renderer.x; 7564 renderer.x += 20 / renderer.pdf.internal.scaleFactor; 7565 renderer.y += 3; 7566 if (!elementHandledElsewhere(cn, renderer, elementHandlers)) { 7567 _DrillForContent(cn, renderer, elementHandlers); 7568 } 7569 renderer.x = temp; 7570 } else if (cn.nodeName === "BR") { 7571 renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor; 7572 renderer.addText("\u2028", clone(fragmentCSS)); 7573 } else { 7574 if (!elementHandledElsewhere(cn, renderer, elementHandlers)) { 7575 _DrillForContent(cn, renderer, elementHandlers); 7576 } 7577 } 7578 } else if (cn.nodeType === 3) { 7579 var value = cn.nodeValue; 7580 if (cn.nodeValue && cn.parentNode.nodeName === "LI") { 7581 if (cn.parentNode.parentNode.nodeName === "OL") { 7582 value = listCount++ + '. ' + value; 7583 } else { 7584 var fontSize = fragmentCSS["font-size"]; 7585 var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor; 7586 var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor; 7587 var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor; 7588 cb = function cb(x, y) { 7589 this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD'); 7590 }; 7591 } 7592 } 7593 // Only add the text if the text node is in the body element 7594 // Add compatibility with IE11 7595 if (!!(cn.ownerDocument.body.compareDocumentPosition(cn) & 16)) { 7596 renderer.addText(value, fragmentCSS); 7597 } 7598 } else if (typeof cn === "string") { 7599 renderer.addText(cn, fragmentCSS); 7600 } 7601 } 7602 i++; 7603 } 7604 elementHandlers.outY = renderer.y; 7605 7606 if (isBlock) { 7607 return renderer.setBlockBoundary(cb); 7608 } 7609 }; 7610 images = {}; 7611 loadImgs = function loadImgs(element, renderer, elementHandlers, cb) { 7612 var imgs = element.getElementsByTagName('img'), 7613 l = imgs.length, 7614 found_images, 7615 x = 0; 7616 function done() { 7617 renderer.pdf.internal.events.publish('imagesLoaded'); 7618 cb(found_images); 7619 } 7620 function loadImage(url, width, height) { 7621 if (!url) return; 7622 var img = new Image(); 7623 found_images = ++x; 7624 img.crossOrigin = ''; 7625 img.onerror = img.onload = function () { 7626 if (img.complete) { 7627 //to support data urls in images, set width and height 7628 //as those values are not recognized automatically 7629 if (img.src.indexOf('data:image/') === 0) { 7630 img.width = width || img.width || 0; 7631 img.height = height || img.height || 0; 7632 } 7633 //if valid image add to known images array
7634 if (img.width + img.height) { 7635 var hash = renderer.pdf.sHashCode(url) || url; 7636 images[hash] = images[hash] || img; 7637 } 7638 } 7639 if (! --x) { 7640 done(); 7641 } 7642 }; 7643 img.src = url; 7644 } 7645 while (l--) { 7646 loadImage(imgs[l].getAttribute("src"), imgs[l].width, imgs[l].height); 7647 }return x || done(); 7648 }; 7649 checkForFooter = function checkForFooter(elem, renderer, elementHandlers) { 7650 //check if we can found a <footer> element 7651 var footer = elem.getElementsByTagName("footer"); 7652 if (footer.length > 0) { 7653 7654 footer = footer[0]; 7655 7656 //bad hack to get height of footer 7657 //creat dummy out and check new y after fake rendering 7658 var oldOut = renderer.pdf.internal.write; 7659 var oldY = renderer.y; 7660 renderer.pdf.internal.write = function () {}; 7661 _DrillForContent(footer, renderer, elementHandlers); 7662 var footerHeight = Math.ceil(renderer.y - oldY) + 5; 7663 renderer.y = oldY; 7664 renderer.pdf.internal.write = oldOut; 7665 7666 //add 20% to prevent overlapping 7667 renderer.pdf.margins_doc.bottom += footerHeight; 7668 7669 //Create function render header on every page 7670 var renderFooter = function renderFooter(pageInfo) { 7671 var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1; 7672 //set current y position to old margin 7673 var oldPosition = renderer.y; 7674 //render all child nodes of the header element 7675 renderer.y = renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom; 7676 renderer.pdf.margins_doc.bottom -= footerHeight; 7677 7678 //check if we have to add page numbers 7679 var spans = footer.getElementsByTagName('span'); 7680 for (var i = 0; i < spans.length; ++i) { 7681 //if we find some span element with class pageCounter, set the page 7682 if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) { 7683 spans[i].innerHTML = pageNumber; 7684 } 7685 //if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages 7686 if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) { 7687 spans[i].innerHTML = '###jsPDFVarTotalPages###'; 7688 } 7689 } 7690 7691 //render footer content 7692 _DrillForContent(footer, renderer, elementHandlers); 7693 //set bottom margin to previous height including the footer height 7694 renderer.pdf.margins_doc.bottom += footerHeight; 7695 //important for other plugins (e.g. table) to start rendering at correct position after header 7696 renderer.y = oldPosition; 7697 }; 7698 7699 //check if footer contains totalPages which should be replace at the disoposal of the document 7700 var spans = footer.getElementsByTagName('span'); 7701 for (var i = 0; i < spans.length; ++i) { 7702 if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) { 7703 renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true); 7704 } 7705 } 7706 7707 //register event to render footer on every new page 7708 renderer.pdf.internal.events.subscribe('addPage', renderFooter, false); 7709 //render footer on first page 7710 renderFooter(); 7711 7712 //prevent footer rendering 7713 SkipNode['FOOTER'] = 1; 7714 } 7715 }; 7716 process = function process(pdf, element, x, y, settings, callback) { 7717 if (!element) return false; 7718 if (typeof element !== "string" && !element.parentNode) element = '' + element.innerHTML; 7719 if (typeof element === "string") { 7720 element = function (element) { 7721 var $frame, $hiddendiv, framename, visuallyhidden; 7722 framename = "jsPDFhtmlText" + Date.now().toString() + (Math.random() * 1000).toFixed(0); 7723 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;"; 7724 $hiddendiv = document.createElement('div'); 7725 $hiddendiv.style.cssText = visuallyhidden; 7726 $hiddendiv.innerHTML = "<iframe style=\"height:1px;width:1px\" name=\"" + framename + "\" />"; 7727 document.body.appendChild($hiddendiv); 7728 $frame = window.frames[framename]; 7729 $frame.document.open(); 7730 $frame.document.writeln(element); 7731 $frame.document.close(); 7732 return $frame.document.body; 7733 }(element.replace(/<\/?script[^>]*?>/gi, '')); 7734 } 7735 var r = new Renderer(pdf, x, y, settings), 7736 out; 7737 7738 // 1. load images 7739 // 2. prepare optional footer elements 7740 // 3. render content 7741 loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) { 7742 checkForFooter(element, r, settings.elementHandlers); 7743 _DrillForContent(element, r, settings.elementHandlers); 7744 //send event dispose for final taks (e.g. footer totalpage replacement) 7745 r.pdf.internal.events.publish('htmlRenderingFinished'); 7746 out = r.dispose(); 7747 if (typeof callback === 'function') callback(out);else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!'); 7748 }); 7749 return out || { x: r.x, y: r.y }; 7750 }; 7751 Renderer.prototype.init = function () { 7752 this.paragraph = { 7753 text: [], 7754 style: [] 7755 }; 7756 return this.pdf.internal.write("q"); 7757 }; 7758 Renderer.prototype.dispose = function () { 7759 this.pdf.internal.write("Q"); 7760 return { 7761 x: this.x, 7762 y: this.y, 7763 ready: true 7764 }; 7765 }; 7766 7767 //Checks if we have to execute some watcher functions 7768 //e.g. to end text floating around an image 7769 Renderer.prototype.executeWatchFunctions = function (el) { 7770 var ret = false;
7771 var narray = []; 7772 if (this.watchFunctions.length > 0) { 7773 for (var i = 0; i < this.watchFunctions.length; ++i) { 7774 if (this.watchFunctions[i](el) === true) { 7775 ret = true; 7776 } else { 7777 narray.push(this.watchFunctions[i]); 7778 } 7779 } 7780 this.watchFunctions = narray; 7781 } 7782 return ret; 7783 }; 7784 7785 Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) { 7786 var currentLineLength, defaultFontSize, ff, fontMetrics, fontMetricsCache, fragment, fragmentChopped, fragmentLength, fragmentSpecificMetrics, fs, k, line, lines, maxLineLength, style; 7787 defaultFontSize = 12; 7788 k = this.pdf.internal.scaleFactor; 7789 fontMetricsCache = {}; 7790 ff = void 0; 7791 fs = void 0; 7792 fontMetrics = void 0; 7793 fragment = void 0; 7794 style = void 0; 7795 fragmentSpecificMetrics = void 0; 7796 fragmentLength = void 0; 7797 fragmentChopped = void 0; 7798 line = []; 7799 lines = [line]; 7800 currentLineLength = 0; 7801 maxLineLength = this.settings.width; 7802 while (fragments.length) { 7803 fragment = fragments.shift(); 7804 style = styles.shift(); 7805 if (fragment) { 7806 ff = style["font-family"]; 7807 fs = style["font-style"]; 7808 fontMetrics = fontMetricsCache[ff + fs]; 7809 if (!fontMetrics) { 7810 fontMetrics = this.pdf.internal.getFont(ff, fs).metadata.Unicode; 7811 fontMetricsCache[ff + fs] = fontMetrics; 7812 } 7813 fragmentSpecificMetrics = { 7814 widths: fontMetrics.widths, 7815 kerning: fontMetrics.kerning, 7816 fontSize: style["font-size"] * defaultFontSize, 7817 textIndent: currentLineLength 7818 }; 7819 fragmentLength = this.pdf.getStringUnitWidth(fragment, fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; 7820 if (fragment == "\u2028") { 7821 line = []; 7822 lines.push(line); 7823 } else if (currentLineLength + fragmentLength > maxLineLength) { 7824 fragmentChopped = this.pdf.splitTextToSize(fragment, maxLineLength, fragmentSpecificMetrics); 7825 line.push([fragmentChopped.shift(), style]); 7826 while (fragmentChopped.length) { 7827 line = [[fragmentChopped.shift(), style]]; 7828 lines.push(line); 7829 } 7830 currentLineLength = this.pdf.getStringUnitWidth(line[0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; 7831 } else { 7832 line.push([fragment, style]); 7833 currentLineLength += fragmentLength; 7834 } 7835 } 7836 } 7837 7838 //if text alignment was set, set margin/indent of each line 7839 if (style['text-align'] !== undefined && (style['text-align'] === 'center' || style['text-align'] === 'right' || style['text-align'] === 'justify')) { 7840 for (var i = 0; i < lines.length; ++i) { 7841 var length = this.pdf.getStringUnitWidth(lines[i][0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; 7842 //if there is more than on line we have to clone the style object as all lines hold a reference on this object 7843 if (i > 0) { 7844 lines[i][0][1] = clone(lines[i][0][1]); 7845 } 7846 var space = maxLineLength - length; 7847 7848 if (style['text-align'] === 'right') { 7849 lines[i][0][1]['margin-left'] = space; 7850 //if alignment is not right, it has to be center so split the space to the left and the right 7851 } else if (style['text-align'] === 'center') { 7852 lines[i][0][1]['margin-left'] = space / 2; 7853 //if justify was set, calculate the word spacing and define in by using the css property 7854 } else if (style['text-align'] === 'justify') { 7855 var countSpaces = lines[i][0][0].split(' ').length - 1;
7856 lines[i][0][1]['word-spacing'] = space / countSpaces; 7857 //ignore the last line in justify mode 7858 if (i === lines.length - 1) { 7859 lines[i][0][1]['word-spacing'] = 0; 7860 } 7861 } 7862 } 7863 } 7864 7865 return lines; 7866 }; 7867 Renderer.prototype.RenderTextFragment = function (text, style) { 7868 var defaultFontSize, font, maxLineHeight; 7869 7870 maxLineHeight = 0; 7871 defaultFontSize = 12; 7872 7873 if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) { 7874 this.pdf.internal.write("ET", "Q"); 7875 this.pdf.addPage(); 7876 this.y = this.pdf.margins_doc.top; 7877 this.pdf.internal.write("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), style.color, "Td"); 7878 //move cursor by one line on new page 7879 maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]); 7880 this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td"); 7881 } 7882 7883 font = this.pdf.internal.getFont(style["font-family"], style["font-style"]); 7884 7885 // text color 7886 var pdfTextColor = this.getPdfColor(style["color"]); 7887 if (pdfTextColor !== this.lastTextColor) { 7888 this.pdf.internal.write(pdfTextColor); 7889 this.lastTextColor = pdfTextColor; 7890 } 7891 7892 //set the word spacing for e.g. justify style 7893 if (style['word-spacing'] !== undefined && style['word-spacing'] > 0) { 7894 this.pdf.internal.write(style['word-spacing'].toFixed(2), "Tw"); 7895 } 7896 7897 this.pdf.internal.write("/" + font.id, (defaultFontSize * style["font-size"]).toFixed(2), "Tf", "(" + this.pdf.internal.pdfEscape(text) + ") Tj"); 7898 7899 //set the word spacing back to neutral => 0 7900 if (style['word-spacing'] !== undefined) { 7901 this.pdf.internal.write(0, "Tw"); 7902 } 7903 }; 7904 7905 // Accepts #FFFFFF, rgb(int,int,int), or CSS Color Name 7906 Renderer.prototype.getPdfColor = function (style) { 7907 var textColor; 7908 var r, g, b; 7909 7910 var rx = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/; 7911 var m = rx.exec(style); 7912 if (m != null) { 7913 r = parseInt(m[1]); 7914 g = parseInt(m[2]); 7915 b = parseInt(m[3]); 7916 } else { 7917 if (style.charAt(0) != '#') { 7918 style = CssColors.colorNameToHex(style); 7919 if (!style) { 7920 style = '#000000'; 7921 } 7922 } 7923 r = style.substring(1, 3); 7924 r = parseInt(r, 16); 7925 g = style.substring(3, 5); 7926 g = parseInt(g, 16); 7927 b = style.substring(5, 7); 7928 b = parseInt(b, 16); 7929 } 7930 7931 if (typeof r === 'string' && /^#[0-9A-Fa-f]{6}$/.test(r)) { 7932 var hex = parseInt(r.substr(1), 16); 7933 r = hex >> 16 & 255; 7934 g = hex >> 8 & 255; 7935 b = hex & 255; 7936 } 7937 7938 var f3 = this.f3; 7939 if (r === 0 && g === 0 && b === 0 || typeof g === 'undefined') { 7940 textColor = f3(r / 255) + ' g'; 7941 } else { 7942 textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' '); 7943 } 7944 return textColor; 7945 }; 7946 7947 Renderer.prototype.f3 = function (number) { 7948 return number.toFixed(3); // Ie, %.3f 7949 }, Renderer.prototype.renderParagraph = function (cb) { 7950 var blockstyle, defaultFontSize, fontToUnitRatio, fragments, i, l, line, lines, maxLineHeight, out, paragraphspacing_after, paragraphspacing_before, priorblockstyle, styles, fontSize; 7951 fragments = PurgeWhiteSpace(this.paragraph.text); 7952 styles = this.paragraph.style; 7953 blockstyle = this.paragraph.blockstyle; 7954 priorblockstyle = this.paragraph.priorblockstyle || {}; 7955 this.paragraph = { 7956 text: [], 7957 style: [], 7958 blockstyle: {}, 7959 priorblockstyle: blockstyle 7960 }; 7961 if (!fragments.join("").trim()) { 7962 return; 7963 } 7964 lines = this.splitFragmentsIntoLines(fragments, styles); 7965 line = void 0; 7966 maxLineHeight = void 0; 7967 defaultFontSize = 12; 7968 fontToUnitRatio = defaultFontSize / this.pdf.internal.scaleFactor; 7969 this.priorMarginBottom = this.priorMarginBottom || 0; 7970 paragraphspacing_before = (Math.max((blockstyle["margin-top"] || 0) - this.priorMarginBottom, 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio; 7971 paragraphspacing_after = ((blockstyle["margin-bottom"] || 0) + (blockstyle["padding-bottom"] || 0)) * fontToUnitRatio; 7972 this.priorMarginBottom = blockstyle["margin-bottom"] || 0; 7973 7974 if (blockstyle['page-break-before'] === 'always') { 7975 this.pdf.addPage(); 7976 this.y = 0; 7977 paragraphspacing_before = ((blockstyle["margin-top"] || 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio; 7978 } 7979 7980 out = this.pdf.internal.write; 7981 i = void 0; 7982 l = void 0; 7983 this.y += paragraphspacing_before; 7984 out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); 7985 7986 //stores the current indent of cursor position 7987 var currentIndent = 0; 7988 7989 while (lines.length) { 7990 line = lines.shift(); 7991 maxLineHeight = 0; 7992 i = 0; 7993 l = line.length; 7994 while (i !== l) { 7995 if (line[i][0].trim()) { 7996 maxLineHeight = Math.max(maxLineHeight, line[i][1]["line-height"], line[i][1]["font-size"]); 7997 fontSize = line[i][1]["font-size"] * 7; 7998 } 7999 i++; 8000 } 8001 //if we have to move the cursor to adapt the indent 8002 var indentMove = 0; 8003 var wantedIndent = 0; 8004 //if a margin was added (by e.g. a text-alignment), move the cursor 8005 if (line[0][1]["margin-left"] !== undefined && line[0][1]["margin-left"] > 0) { 8006 wantedIndent = this.pdf.internal.getCoordinateString(line[0][1]["margin-left"]); 8007 indentMove = wantedIndent - currentIndent; 8008 currentIndent = wantedIndent; 8009 } 8010 var indentMore = Math.max(blockstyle["margin-left"] || 0, 0) * fontToUnitRatio; 8011 //move the cursor 8012 out(indentMove + indentMore, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td"); 8013 i = 0; 8014 l = line.length; 8015 while (i !== l) { 8016 if (line[i][0]) { 8017 this.RenderTextFragment(line[i][0], line[i][1]); 8018 } 8019 i++; 8020 } 8021 this.y += maxLineHeight * fontToUnitRatio; 8022 8023 //if some watcher function was executed successful, so e.g. margin and widths were changed, 8024 //reset line drawing and calculate position and lines again 8025 //e.g. to stop text floating around an image
8026 if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) { 8027 var localFragments = []; 8028 var localStyles = []; 8029 //create fragment array of 8030 lines.forEach(function (localLine) { 8031 var i = 0; 8032 var l = localLine.length; 8033 while (i !== l) { 8034 if (localLine[i][0]) { 8035 localFragments.push(localLine[i][0] + ' '); 8036 localStyles.push(localLine[i][1]); 8037 } 8038 ++i; 8039 } 8040 }); 8041 //split lines again due to possible coordinate changes 8042 lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles); 8043 //reposition the current cursor 8044 out("ET", "Q"); 8045 out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); 8046 } 8047 } 8048 if (cb && typeof cb === "function") { 8049 cb.call(this, this.x - 9, this.y - fontSize / 2); 8050 } 8051 out("ET", "Q"); 8052 return this.y += paragraphspacing_after; 8053 }; 8054 Renderer.prototype.setBlockBoundary = function (cb) { 8055 return this.renderParagraph(cb); 8056 }; 8057 Renderer.prototype.setBlockStyle = function (css) { 8058 return this.paragraph.blockstyle = css; 8059 }; 8060 Renderer.prototype.addText = function (text, css) { 8061 this.paragraph.text.push(text); 8062 return this.paragraph.style.push(css); 8063 }; 8064 FontNameDB = { 8065 helvetica: "helvetica", 8066 "sans-serif": "helvetica", 8067 "times new roman": "times", 8068 serif: "times", 8069 times: "times", 8070 monospace: "courier", 8071 courier: "courier" 8072 }; 8073 FontWeightMap = { 8074 100: "normal", 8075 200: "normal", 8076 300: "normal", 8077 400: "normal", 8078 500: "bold", 8079 600: "bold", 8080 700: "bold", 8081 800: "bold", 8082 900: "bold", 8083 normal: "normal", 8084 bold: "bold", 8085 bolder: "bold", 8086 lighter: "normal" 8087 }; 8088 FontStyleMap = { 8089 normal: "normal", 8090 italic: "italic", 8091 oblique: "italic" 8092 }; 8093 TextAlignMap = { 8094 left: "left", 8095 right: "right", 8096 center: "center", 8097 justify: "justify" 8098 }; 8099 FloatMap = { 8100 none: 'none', 8101 right: 'right', 8102 left: 'left' 8103 }; 8104 ClearMap = { 8105 none: 'none', 8106 both: 'both' 8107 }; 8108 UnitedNumberMap = { 8109 normal: 1 8110 }; 8111 /** 8112 * Converts HTML-formatted text into formatted PDF text. 8113 * 8114 * Notes: 8115 * 2012-07-18 8116 * Plugin relies on having browser, DOM around. The HTML is pushed into dom and traversed. 8117 * Plugin relies on jQuery for CSS extraction. 8118 * Targeting HTML output from Markdown templating, which is a very simple 8119 * markup - div, span, em, strong, p. No br-based paragraph separation supported explicitly (but still may work.) 8120 * Images, tables are NOT supported. 8121 * 8122 * @public 8123 * @function 8124 * @param HTML {String or DOM Element} HTML-formatted text, or pointer to DOM element that is to be rendered into PDF. 8125 * @param x {Number} starting X coordinate in jsPDF instance's declared units. 8126 * @param y {Number} starting Y coordinate in jsPDF instance's declared units. 8127 * @param settings {Object} Additional / optional variables controlling parsing, rendering. 8128 * @returns {Object} jsPDF instance 8129 */ 8130 jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins) { 8131 "use strict"; 8132 8133 this.margins_doc = margins || { 8134 top: 0, 8135 bottom: 0 8136 }; 8137 if (!settings) settings = {}; 8138 if (!settings.elementHandlers) settings.elementHandlers = {}; 8139 8140 return process(this, HTML, isNaN(x) ? 4 : x, isNaN(y) ? 4 : y, settings, callback); 8141 }; 8142})(jsPDF.API); 8143 8144/** ==================================================================== 8145 * jsPDF JavaScript plugin 8146 * Copyright (c) 2013 Youssef Beddad, [email protected] 8147 * 8148 * 8149 * ==================================================================== 8150 */ 8151 8152/*global jsPDF */ 8153 8154(function (jsPDFAPI) { 8155 'use strict'; 8156 8157 var jsNamesObj, jsJsObj, text; 8158 jsPDFAPI.addJS = function (txt) { 8159 text = txt; 8160 this.internal.events.subscribe('postPutResources', function (txt) { 8161 jsNamesObj = this.internal.newObject(); 8162 this.internal.write('<< /Names [(EmbeddedJS) ' + (jsNamesObj + 1) + ' 0 R] >>', 'endobj'); 8163 jsJsObj = this.internal.newObject(); 8164 this.internal.write('<< /S /JavaScript /JS (', text, ') >>', 'endobj'); 8165 }); 8166 this.internal.events.subscribe('putCatalog', function () { 8167 if (jsNamesObj !== undefined && jsJsObj !== undefined) { 8168 this.internal.write('/Names <</JavaScript ' + jsNamesObj + ' 0 R>>'); 8169 } 8170 }); 8171 return this; 8172 }; 8173})(jsPDF.API); 8174 8175/** 8176 * jsPDF Outline PlugIn 8177 * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) [email protected] 8178 * 8179 * Licensed under the MIT License. 8180 * http://opensource.org/licenses/mit-license 8181 */ 8182 8183/** 8184 * Generates a PDF Outline 8185 */ 8186 8187(function (jsPDFAPI) { 8188 'use strict'; 8189 8190 jsPDFAPI.events.push(['postPutResources', function () { 8191 var pdf = this; 8192 var rx = /^(\d+) 0 obj$/; 8193 8194 // Write action goto objects for each page 8195 // this.outline.destsGoto = []; 8196 // for (var i = 0; i < totalPages; i++) { 8197 // var id = pdf.internal.newObject(); 8198 // this.outline.destsGoto.push(id); 8199 // pdf.internal.write("<</D[" + (i * 2 + 3) + " 0 R /XYZ null 8200 // null null]/S/GoTo>> endobj"); 8201 // } 8202 // 8203 // for (var i = 0; i < dests.length; i++) { 8204 // pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 8205 // R"); 8206 // } 8207 // 8208 if (this.outline.root.children.length > 0) { 8209 var lines = pdf.outline.render().split(/\r\n/); 8210 for (var i = 0; i < lines.length; i++) { 8211 var line = lines[i]; 8212 var m = rx.exec(line); 8213 if (m != null) { 8214 var oid = m[1]; 8215 pdf.internal.newObjectDeferredBegin(oid); 8216 } 8217 pdf.internal.write(line); 8218 } 8219 } 8220 8221 // This code will write named destination for each page reference 8222 // (page_1, etc) 8223 if (this.outline.createNamedDestinations) { 8224 var totalPages = this.internal.pages.length; 8225 // WARNING: this assumes jsPDF starts on page 3 and pageIDs 8226 // follow 5, 7, 9, etc 8227 // Write destination objects for each page 8228 var dests = []; 8229 for (var i = 0; i < totalPages; i++) { 8230 var id = pdf.internal.newObject(); 8231 dests.push(id); 8232 var info = pdf.internal.getPageInfo(i + 1); 8233 pdf.internal.write("<< /D[" + info.objId + " 0 R /XYZ null null null]>> endobj"); 8234 } 8235 8236 // assign a name for each destination 8237 var names2Oid = pdf.internal.newObject();
vendor: 5,061 bytes, lines 8238-8427
8238 pdf.internal.write('<< /Names [ '); 8239 for (var i = 0; i < dests.length; i++) { 8240 pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 R"); 8241 } 8242 pdf.internal.write(' ] >>', 'endobj'); 8243 8244 // var kids = pdf.internal.newObject(); 8245 // pdf.internal.write('<< /Kids [ ' + names2Oid + ' 0 R'); 8246 // pdf.internal.write(' ] >>', 'endobj'); 8247 8248 var namesOid = pdf.internal.newObject(); 8249 pdf.internal.write('<< /Dests ' + names2Oid + " 0 R"); 8250 pdf.internal.write('>>', 'endobj'); 8251 } 8252 }]); 8253 8254 jsPDFAPI.events.push(['putCatalog', function () { 8255 var pdf = this; 8256 if (pdf.outline.root.children.length > 0) { 8257 pdf.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); 8258 if (this.outline.createNamedDestinations) { 8259 pdf.internal.write("/Names " + namesOid + " 0 R"); 8260 } 8261 // Open with Bookmarks showing 8262 // pdf.internal.write("/PageMode /UseOutlines"); 8263 } 8264 }]); 8265 8266 jsPDFAPI.events.push(['initialized', function () { 8267 var pdf = this; 8268 8269 pdf.outline = { 8270 createNamedDestinations: false, 8271 root: { 8272 children: [] 8273 } 8274 }; 8275 8276 var namesOid; 8277 var destsGoto = []; 8278 8279 /** 8280 * Options: pageNumber 8281 */ 8282 pdf.outline.add = function (parent, title, options) { 8283 var item = { 8284 title: title, 8285 options: options, 8286 children: [] 8287 }; 8288 if (parent == null) { 8289 parent = this.root; 8290 } 8291 parent.children.push(item); 8292 return item; 8293 }; 8294 8295 pdf.outline.render = function () { 8296 this.ctx = {}; 8297 this.ctx.val = ''; 8298 this.ctx.pdf = pdf; 8299 8300 this.genIds_r(this.root); 8301 this.renderRoot(this.root); 8302 this.renderItems(this.root); 8303 8304 return this.ctx.val; 8305 }; 8306 8307 pdf.outline.genIds_r = function (node) { 8308 node.id = pdf.internal.newObjectDeferred(); 8309 for (var i = 0; i < node.children.length; i++) { 8310 this.genIds_r(node.children[i]); 8311 } 8312 }; 8313 8314 pdf.outline.renderRoot = function (node) { 8315 this.objStart(node); 8316 this.line('/Type /Outlines'); 8317 if (node.children.length > 0) { 8318 this.line('/First ' + this.makeRef(node.children[0])); 8319 this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); 8320 } 8321 this.line('/Count ' + this.count_r({ 8322 count: 0 8323 }, node)); 8324 this.objEnd(); 8325 }; 8326 8327 pdf.outline.renderItems = function (node) { 8328 for (var i = 0; i < node.children.length; i++) { 8329 var item = node.children[i]; 8330 this.objStart(item); 8331 8332 this.line('/Title ' + this.makeString(item.title)); 8333 8334 this.line('/Parent ' + this.makeRef(node)); 8335 if (i > 0) { 8336 this.line('/Prev ' + this.makeRef(node.children[i - 1])); 8337 } 8338 if (i < node.children.length - 1) { 8339 this.line('/Next ' + this.makeRef(node.children[i + 1])); 8340 } 8341 if (item.children.length > 0) { 8342 this.line('/First ' + this.makeRef(item.children[0])); 8343 this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); 8344 } 8345 8346 var count = this.count = this.count_r({ 8347 count: 0 8348 }, item); 8349 if (count > 0) { 8350 this.line('/Count ' + count); 8351 } 8352 8353 if (item.options) { 8354 if (item.options.pageNumber) { 8355 // Explicit Destination 8356 //WARNING this assumes page ids are 3,5,7, etc. 8357 var info = pdf.internal.getPageInfo(item.options.pageNumber); 8358 this.line('/Dest ' + '[' + info.objId + ' 0 R /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); 8359 // this line does not work on all clients (pageNumber instead of page ref) 8360 //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); 8361 8362 // Named Destination 8363 // this.line('/Dest (page_' + (item.options.pageNumber) + ')'); 8364 8365 // Action Destination 8366 // var id = pdf.internal.newObject(); 8367 // pdf.internal.write('<</D[' + (item.options.pageNumber - 1) + ' /XYZ null null null]/S/GoTo>> endobj'); 8368 // this.line('/A ' + id + ' 0 R' ); 8369 } 8370 } 8371 this.objEnd(); 8372 } 8373 for (var i = 0; i < node.children.length; i++) { 8374 var item = node.children[i]; 8375 this.renderItems(item); 8376 } 8377 }; 8378 8379 pdf.outline.line = function (text) { 8380 this.ctx.val += text + '\r\n'; 8381 }; 8382 8383 pdf.outline.makeRef = function (node) { 8384 return node.id + ' 0 R'; 8385 }; 8386 8387 pdf.outline.makeString = function (val) { 8388 return '(' + pdf.internal.pdfEscape(val) + ')'; 8389 }; 8390 8391 pdf.outline.objStart = function (node) { 8392 this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; 8393 }; 8394 8395 pdf.outline.objEnd = function (node) { 8396 this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; 8397 }; 8398 8399 pdf.outline.count_r = function (ctx, node) { 8400 for (var i = 0; i < node.children.length; i++) { 8401 ctx.count++; 8402 this.count_r(ctx, node.children[i]); 8403 } 8404 return ctx.count; 8405 }; 8406 }]); 8407 8408 return this; 8409})(jsPDF.API); 8410 8411/**@preserve 8412 * ==================================================================== 8413 * jsPDF PNG PlugIn 8414 * Copyright (c) 2014 James Robb, https://github.com/jamesbrobb 8415 * 8416 * 8417 * ==================================================================== 8418 */ 8419 8420(function (jsPDFAPI) { 8421 'use strict'; 8422 8423 /* 8424 * @see http://www.w3.org/TR/PNG-Chunks.html 8425 * 8426 Color Allowed Interpretation 8427 Type Bit Depths
8428 0 1,2,4,8,16 Each pixel is a grayscale sample. 8429 2 8,16 Each pixel is an R,G,B triple. 8430 3 1,2,4,8 Each pixel is a palette index; 8431 a PLTE chunk must appear. 8432 4 8,16 Each pixel is a grayscale sample, 8433 followed by an alpha sample. 8434 6 8,16 Each pixel is an R,G,B triple, 8435 followed by an alpha sample. 8436 */ 8437 8438 /* 8439 * PNG filter method types 8440 * 8441 * @see http://www.w3.org/TR/PNG-Filters.html 8442 * @see http://www.libpng.org/pub/png/book/chapter09.html 8443 * 8444 * This is what the value 'Predictor' in decode params relates to 8445 * 8446 * 15 is "optimal prediction", which means the prediction algorithm can change from line to line. 8447 * 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. 8448 * 8449 0 None 8450 1 Sub 8451 2 Up 8452 3 Average 8453 4 Paeth 8454 */ 8455 8456 var doesNotHavePngJS = function doesNotHavePngJS() {
8457 return typeof PNG !== 'function' || typeof FlateStream !== 'function'; 8458 }, 8459 canCompress = function canCompress(value) { 8460 return value !== jsPDFAPI.image_compression.NONE && hasCompressionJS(); 8461 }, 8462 hasCompressionJS = function hasCompressionJS() { 8463 var inst = typeof Deflater === 'function'; 8464 if (!inst) throw new Error("requires deflate.js for compression"); 8465 return inst; 8466 }, 8467 compressBytes = function compressBytes(bytes, lineLength, colorsPerPixel, compression) { 8468 8469 var level = 5, 8470 filter_method = filterUp; 8471 8472 switch (compression) { 8473 8474 case jsPDFAPI.image_compression.FAST: 8475 8476 level = 3; 8477 filter_method = filterSub; 8478 break; 8479 8480 case jsPDFAPI.image_compression.MEDIUM: 8481 8482 level = 6; 8483 filter_method = filterAverage; 8484 break; 8485 8486 case jsPDFAPI.image_compression.SLOW: 8487 8488 level = 9; 8489 filter_method = filterPaeth; //uses to sum to choose best filter for each line 8490 break; 8491 } 8492 8493 bytes = applyPngFilterMethod(bytes, lineLength, colorsPerPixel, filter_method); 8494 8495 var header = new Uint8Array(createZlibHeader(level)); 8496 var checksum = adler32(bytes); 8497 8498 var deflate = new Deflater(level); 8499 var a = deflate.append(bytes); 8500 var cBytes = deflate.flush(); 8501 8502 var len = header.length + a.length + cBytes.length; 8503 8504 var cmpd = new Uint8Array(len + 4); 8505 cmpd.set(header); 8506 cmpd.set(a, header.length); 8507 cmpd.set(cBytes, header.length + a.length); 8508 8509 cmpd[len++] = checksum >>> 24 & 0xff; 8510 cmpd[len++] = checksum >>> 16 & 0xff; 8511 cmpd[len++] = checksum >>> 8 & 0xff; 8512 cmpd[len++] = checksum & 0xff; 8513 8514 return jsPDFAPI.arrayBufferToBinaryString(cmpd); 8515 }, 8516 createZlibHeader = function createZlibHeader(bytes, level) { 8517 /* 8518 * @see http://www.ietf.org/rfc/rfc1950.txt for zlib header 8519 */ 8520 var cm = 8; 8521 var cinfo = Math.LOG2E * Math.log(0x8000) - 8; 8522 var cmf = cinfo << 4 | cm; 8523 8524 var hdr = cmf << 8; 8525 var flevel = Math.min(3, (level - 1 & 0xff) >> 1); 8526 8527 hdr |= flevel << 6; 8528 hdr |= 0; //FDICT 8529 hdr += 31 - hdr % 31; 8530 8531 return [cmf, hdr & 0xff & 0xff]; 8532 }, 8533 adler32 = function adler32(array, param) { 8534 var adler = 1; 8535 var s1 = adler & 0xffff, 8536 s2 = adler >>> 16 & 0xffff; 8537 var len = array.length; 8538 var tlen; 8539 var i = 0; 8540 8541 while (len > 0) { 8542 tlen = len > param ? param : len; 8543 len -= tlen; 8544 do { 8545 s1 += array[i++]; 8546 s2 += s1; 8547 } while (--tlen); 8548 8549 s1 %= 65521; 8550 s2 %= 65521; 8551 } 8552 8553 return (s2 << 16 | s1) >>> 0; 8554 }, 8555 applyPngFilterMethod = function applyPngFilterMethod(bytes, lineLength, colorsPerPixel, filter_method) { 8556 var lines = bytes.length / lineLength, 8557 result = new Uint8Array(bytes.length + lines), 8558 filter_methods = getFilterMethods(), 8559 i = 0, 8560 line, 8561 prevLine, 8562 offset; 8563 8564 for (; i < lines; i++) { 8565 offset = i * lineLength; 8566 line = bytes.subarray(offset, offset + lineLength); 8567 8568 if (filter_method) { 8569 result.set(filter_method(line, colorsPerPixel, prevLine), offset + i); 8570 } else { 8571 8572 var j = 0, 8573 len = filter_methods.length, 8574 results = []; 8575 8576 for (; j < len; j++) { 8577 results[j] = filter_methods[j](line, colorsPerPixel, prevLine); 8578 }var ind = getIndexOfSmallestSum(results.concat()); 8579 8580 result.set(results[ind], offset + i); 8581 } 8582 8583 prevLine = line; 8584 } 8585 8586 return result; 8587 }, 8588 filterNone = function filterNone(line, colorsPerPixel, prevLine) { 8589 /*var result = new Uint8Array(line.length + 1); 8590 result[0] = 0; 8591 result.set(line, 1);*/ 8592 8593 var result = Array.apply([], line); 8594 result.unshift(0); 8595 8596 return result; 8597 }, 8598 filterSub = function filterSub(line, colorsPerPixel, prevLine) { 8599 var result = [], 8600 i = 0, 8601 len = line.length, 8602 left; 8603 8604 result[0] = 1; 8605 8606 for (; i < len; i++) { 8607 left = line[i - colorsPerPixel] || 0; 8608 result[i + 1] = line[i] - left + 0x0100 & 0xff;
8609 } 8610 8611 return result; 8612 }, 8613 filterUp = function filterUp(line, colorsPerPixel, prevLine) { 8614 var result = [], 8615 i = 0, 8616 len = line.length, 8617 up; 8618 8619 result[0] = 2; 8620 8621 for (; i < len; i++) { 8622 up = prevLine && prevLine[i] || 0; 8623 result[i + 1] = line[i] - up + 0x0100 & 0xff; 8624 } 8625 8626 return result; 8627 }, 8628 filterAverage = function filterAverage(line, colorsPerPixel, prevLine) { 8629 var result = [], 8630 i = 0, 8631 len = line.length, 8632 left, 8633 up; 8634 8635 result[0] = 3; 8636 8637 for (; i < len; i++) { 8638 left = line[i - colorsPerPixel] || 0; 8639 up = prevLine && prevLine[i] || 0; 8640 result[i + 1] = line[i] + 0x0100 - (left + up >>> 1) & 0xff; 8641 } 8642 8643 return result; 8644 }, 8645 filterPaeth = function filterPaeth(line, colorsPerPixel, prevLine) { 8646 var result = [], 8647 i = 0, 8648 len = line.length, 8649 left, 8650 up, 8651 upLeft, 8652 paeth; 8653 8654 result[0] = 4; 8655 8656 for (; i < len; i++) { 8657 left = line[i - colorsPerPixel] || 0; 8658 up = prevLine && prevLine[i] || 0; 8659 upLeft = prevLine && prevLine[i - colorsPerPixel] || 0; 8660 paeth = paethPredictor(left, up, upLeft); 8661 result[i + 1] = line[i] - paeth + 0x0100 & 0xff;
8662 } 8663 8664 return result; 8665 }, 8666 paethPredictor = function paethPredictor(left, up, upLeft) { 8667 8668 var p = left + up - upLeft, 8669 pLeft = Math.abs(p - left), 8670 pUp = Math.abs(p - up), 8671 pUpLeft = Math.abs(p - upLeft); 8672 8673 return pLeft <= pUp && pLeft <= pUpLeft ? left : pUp <= pUpLeft ? up : upLeft; 8674 }, 8675 getFilterMethods = function getFilterMethods() { 8676 return [filterNone, filterSub, filterUp, filterAverage, filterPaeth]; 8677 }, 8678 getIndexOfSmallestSum = function getIndexOfSmallestSum(arrays) { 8679 var i = 0, 8680 len = arrays.length, 8681 sum, 8682 min, 8683 ind; 8684 8685 while (i < len) { 8686 sum = absSum(arrays[i].slice(1)); 8687 8688 if (sum < min || !min) { 8689 min = sum; 8690 ind = i; 8691 } 8692 8693 i++; 8694 } 8695 8696 return ind; 8697 }, 8698 absSum = function absSum(array) { 8699 var i = 0, 8700 len = array.length, 8701 sum = 0; 8702 8703 while (i < len) { 8704 sum += Math.abs(array[i++]); 8705 }return sum; 8706 }, 8707 getPredictorFromCompression = function getPredictorFromCompression(compression) { 8708 var predictor; 8709 switch (compression) { 8710 case jsPDFAPI.image_compression.FAST: 8711 predictor = 11; 8712 break; 8713 8714 case jsPDFAPI.image_compression.MEDIUM: 8715 predictor = 13; 8716 break; 8717 8718 case jsPDFAPI.image_compression.SLOW: 8719 predictor = 14; 8720 break; 8721 8722 default: 8723 predictor = 12; 8724 break; 8725 } 8726 return predictor; 8727 }, 8728 logImg = function logImg(img) { 8729 console.log("width: " + img.width);
8730 console.log("height: " + img.height); 8731 console.log("bits: " + img.bits); 8732 console.log("colorType: " + img.colorType); 8733 console.log("transparency:"); 8734 console.log(img.transparency); 8735 console.log("text:"); 8736 console.log(img.text); 8737 console.log("compressionMethod: " + img.compressionMethod); 8738 console.log("filterMethod: " + img.filterMethod); 8739 console.log("interlaceMethod: " + img.interlaceMethod); 8740 console.log("imgData:"); 8741 console.log(img.imgData); 8742 console.log("palette:"); 8743 console.log(img.palette); 8744 console.log("colors: " + img.colors); 8745 console.log("colorSpace: " + img.colorSpace); 8746 console.log("pixelBitlength: " + img.pixelBitlength); 8747 console.log("hasAlphaChannel: " + img.hasAlphaChannel); 8748 }; 8749 8750 jsPDFAPI.processPNG = function (imageData, imageIndex, alias, compression, dataAsBinaryString) { 8751 'use strict'; 8752 8753 var colorSpace = this.color_spaces.DEVICE_RGB, 8754 decode = this.decode.FLATE_DECODE, 8755 bpc = 8, 8756 img, 8757 dp, 8758 trns, 8759 colors, 8760 pal, 8761 smask; 8762 8763 /* if(this.isString(imageData)) { 8764 }*/ 8765 8766 if (this.isArrayBuffer(imageData)) imageData = new Uint8Array(imageData); 8767 8768 if (this.isArrayBufferView(imageData)) { 8769 8770 if (doesNotHavePngJS()) throw new Error("PNG support requires png.js and zlib.js"); 8771 8772 img = new PNG(imageData); 8773 imageData = img.imgData; 8774 bpc = img.bits; 8775 colorSpace = img.colorSpace;
8776 colors = img.colors; 8777 8778 //logImg(img); 8779 8780 /* 8781 * colorType 6 - Each pixel is an R,G,B triple, followed by an alpha sample. 8782 * 8783 * colorType 4 - Each pixel is a grayscale sample, followed by an alpha sample. 8784 * 8785 * Extract alpha to create two separate images, using the alpha as a sMask 8786 */ 8787 if ([4, 6].indexOf(img.colorType) !== -1) { 8788 8789 /* 8790 * processes 8 bit RGBA and grayscale + alpha images 8791 */ 8792 if (img.bits === 8) { 8793 8794 var pixels = img.pixelBitlength == 32 ? new Uint32Array(img.decodePixels().buffer) : img.pixelBitlength == 16 ? new Uint16Array(img.decodePixels().buffer) : new Uint8Array(img.decodePixels().buffer), 8795 len = pixels.length, 8796 imgData = new Uint8Array(len * img.colors), 8797 alphaData = new Uint8Array(len), 8798 pDiff = img.pixelBitlength - img.bits, 8799 i = 0, 8800 n = 0, 8801 pixel, 8802 pbl; 8803 8804 for (; i < len; i++) { 8805 pixel = pixels[i]; 8806 pbl = 0; 8807 8808 while (pbl < pDiff) { 8809 8810 imgData[n++] = pixel >>> pbl & 0xff; 8811 pbl = pbl + img.bits; 8812 } 8813 8814 alphaData[i] = pixel >>> pbl & 0xff; 8815 } 8816 } 8817 8818 /* 8819 * processes 16 bit RGBA and grayscale + alpha images 8820 */ 8821 if (img.bits === 16) { 8822 8823 var pixels = new Uint32Array(img.decodePixels().buffer), 8824 len = pixels.length, 8825 imgData = new Uint8Array(len * (32 / img.pixelBitlength) * img.colors), 8826 alphaData = new Uint8Array(len * (32 / img.pixelBitlength)), 8827 hasColors = img.colors > 1, 8828 i = 0, 8829 n = 0, 8830 a = 0, 8831 pixel; 8832 8833 while (i < len) { 8834 pixel = pixels[i++]; 8835 8836 imgData[n++] = pixel >>> 0 & 0xFF; 8837 8838 if (hasColors) { 8839 imgData[n++] = pixel >>> 16 & 0xFF; 8840 8841 pixel = pixels[i++]; 8842 imgData[n++] = pixel >>> 0 & 0xFF; 8843 } 8844 8845 alphaData[a++] = pixel >>> 16 & 0xFF; 8846 } 8847 8848 bpc = 8; 8849 } 8850 8851 if (canCompress(compression)) { 8852 8853 imageData = compressBytes(imgData, img.width * img.colors, img.colors, compression); 8854 smask = compressBytes(alphaData, img.width, 1, compression); 8855 } else { 8856 8857 imageData = imgData; 8858 smask = alphaData; 8859 decode = null; 8860 } 8861 } 8862 8863 /* 8864 * Indexed png. Each pixel is a palette index. 8865 */ 8866 if (img.colorType === 3) { 8867 8868 colorSpace = this.color_spaces.INDEXED; 8869 pal = img.palette; 8870 8871 if (img.transparency.indexed) { 8872 8873 var trans = img.transparency.indexed; 8874 8875 var total = 0, 8876 i = 0, 8877 len = trans.length; 8878 8879 for (; i < len; ++i) { 8880 total += trans[i]; 8881 }total = total / 255; 8882 8883 /* 8884 * a single color is specified as 100% transparent (0), 8885 * so we set trns to use a /Mask with that index 8886 */ 8887 if (total === len - 1 && trans.indexOf(0) !== -1) { 8888 trns = [trans.indexOf(0)]; 8889 8890 /* 8891 * there's more than one colour within the palette that specifies 8892 * a transparency value less than 255, so we unroll the pixels to create an image sMask 8893 */ 8894 } else if (total !== len) { 8895 8896 var pixels = img.decodePixels(), 8897 alphaData = new Uint8Array(pixels.length), 8898 i = 0, 8899 len = pixels.length; 8900 8901 for (; i < len; i++) { 8902 alphaData[i] = trans[pixels[i]]; 8903 }smask = compressBytes(alphaData, img.width, 1); 8904 } 8905 } 8906 } 8907 8908 var predictor = getPredictorFromCompression(compression); 8909 8910 if (decode === this.decode.FLATE_DECODE) dp = '/Predictor ' + predictor + ' /Colors ' + colors + ' /BitsPerComponent ' + bpc + ' /Columns ' + img.width;else 8911 //remove 'Predictor' as it applies to the type of png filter applied to its IDAT - we only apply with compression 8912 dp = '/Colors ' + colors + ' /BitsPerComponent ' + bpc + ' /Columns ' + img.width; 8913 8914 if (this.isArrayBuffer(imageData) || this.isArrayBufferView(imageData)) imageData = this.arrayBufferToBinaryString(imageData); 8915 8916 if (smask && this.isArrayBuffer(smask) || this.isArrayBufferView(smask)) smask = this.arrayBufferToBinaryString(smask); 8917 8918 return this.createImageInfo(imageData, img.width, img.height, colorSpace, bpc, decode, imageIndex, alias, dp, trns, pal, smask, predictor); 8919 } 8920 8921 throw new Error("Unsupported PNG image data, try using JPEG instead."); 8922 }; 8923})(jsPDF.API); 8924 8925/** 8926 * jsPDF Autoprint Plugin 8927 * 8928 * Licensed under the MIT License. 8929 * http://opensource.org/licenses/mit-license 8930 */ 8931 8932(function (jsPDFAPI) { 8933 'use strict'; 8934 8935 jsPDFAPI.autoPrint = function () { 8936 'use strict'; 8937 8938 var refAutoPrintTag; 8939 8940 this.internal.events.subscribe('postPutResources', function () { 8941 refAutoPrintTag = this.internal.newObject(); 8942 this.internal.write("<< /S/Named /Type/Action /N/Print >>", "endobj"); 8943 }); 8944 8945 this.internal.events.subscribe("putCatalog", function () { 8946 this.internal.write("/OpenAction " + refAutoPrintTag + " 0" + " R"); 8947 }); 8948 return this; 8949 }; 8950})(jsPDF.API); 8951 8952/** @preserve 8953 * jsPDF split_text_to_size plugin - MIT license. 8954 * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com 8955 * 2014 Diego Casorran, https://github.com/diegocr 8956 */ 8957/** 8958 * 8959 * ==================================================================== 8960 */ 8961 8962(function (API) { 8963 'use strict'; 8964 8965 /** 8966 Returns an array of length matching length of the 'word' string, with each 8967 cell ocupied by the width of the char in that position. 8968 8969 @function 8970 @param word {String} 8971 @param widths {Object} 8972 @param kerning {Object} 8973 @returns {Array} 8974 */ 8975 8976 var getCharWidthsArray = API.getCharWidthsArray = function (text, options) { 8977 8978 if (!options) { 8979 options = {}; 8980 } 8981 8982 var widths = options.widths ? options.widths : this.internal.getFont().metadata.Unicode.widths, 8983 widthsFractionOf = widths.fof ? widths.fof : 1, 8984 kerning = options.kerning ? options.kerning : this.internal.getFont().metadata.Unicode.kerning, 8985 kerningFractionOf = kerning.fof ? kerning.fof : 1; 8986 8987 // console.log("widths, kergnings", widths, kerning) 8988 8989 var i, 8990 l, 8991 char_code, 8992 prior_char_code = 0 // for kerning 8993 , 8994 default_char_width = widths[0] || widthsFractionOf, 8995 output = []; 8996 8997 for (i = 0, l = text.length; i < l; i++) { 8998 char_code = text.charCodeAt(i); 8999 output.push((widths[char_code] || default_char_width) / widthsFractionOf + (kerning[char_code] && kerning[char_code][prior_char_code] || 0) / kerningFractionOf); 9000 prior_char_code = char_code; 9001 } 9002 9003 return output; 9004 }; 9005 var getArraySum = function getArraySum(array) { 9006 var i = array.length, 9007 output = 0; 9008 while (i) { 9009 i--; 9010 output += array[i]; 9011 } 9012 return output; 9013 }; 9014 /**
9015 Returns a widths of string in a given font, if the font size is set as 1 point. 9016 9017 In other words, this is "proportional" value. For 1 unit of font size, the length 9018 of the string will be that much. 9019 9020 Multiply by font size to get actual width in *points* 9021 Then divide by 72 to get inches or divide by (72/25.6) to get 'mm' etc. 9022 9023 @public 9024 @function 9025 @param 9026 @returns {Type} 9027 */ 9028 var getStringUnitWidth = API.getStringUnitWidth = function (text, options) { 9029 return getArraySum(getCharWidthsArray.call(this, text, options)); 9030 }; 9031 9032 /** 9033 returns array of lines 9034 */ 9035 var splitLongWord = function splitLongWord(word, widths_array, firstLineMaxLen, maxLen) { 9036 var answer = []; 9037 9038 // 1st, chop off the piece that can fit on the hanging line. 9039 var i = 0, 9040 l = word.length, 9041 workingLen = 0; 9042 while (i !== l && workingLen + widths_array[i] < firstLineMaxLen) { 9043 workingLen += widths_array[i];i++; 9044 } 9045 // this is first line. 9046 answer.push(word.slice(0, i)); 9047 9048 // 2nd. Split the rest into maxLen pieces. 9049 var startOfLine = i; 9050 workingLen = 0; 9051 while (i !== l) { 9052 if (workingLen + widths_array[i] > maxLen) { 9053 answer.push(word.slice(startOfLine, i)); 9054 workingLen = 0; 9055 startOfLine = i; 9056 } 9057 workingLen += widths_array[i];i++; 9058 } 9059 if (startOfLine !== i) { 9060 answer.push(word.slice(startOfLine, i)); 9061 } 9062 9063 return answer; 9064 }; 9065 9066 // Note, all sizing inputs for this function must be in "font measurement units" 9067 // By default, for PDF, it's "point". 9068 var splitParagraphIntoLines = function splitParagraphIntoLines(text, maxlen, options) { 9069 // at this time works only on Western scripts, ones with space char 9070 // separating the words. Feel free to expand. 9071 9072 if (!options) { 9073 options = {}; 9074 } 9075 9076 var line = [], 9077 lines = [line], 9078 line_length = options.textIndent || 0, 9079 separator_length = 0, 9080 current_word_length = 0, 9081 word, 9082 widths_array, 9083 words = text.split(' '), 9084 spaceCharWidth = getCharWidthsArray(' ', options)[0], 9085 i, 9086 l, 9087 tmp, 9088 lineIndent; 9089 9090 if (options.lineIndent === -1) { 9091 lineIndent = words[0].length + 2; 9092 } else { 9093 lineIndent = options.lineIndent || 0; 9094 } 9095 if (lineIndent) { 9096 var pad = Array(lineIndent).join(" "), 9097 wrds = []; 9098 words.map(function (wrd) { 9099 wrd = wrd.split(/\s*\n/); 9100 if (wrd.length > 1) { 9101 wrds = wrds.concat(wrd.map(function (wrd, idx) { 9102 return (idx && wrd.length ? "\n" : "") + wrd; 9103 })); 9104 } else { 9105 wrds.push(wrd[0]); 9106 } 9107 }); 9108 words = wrds; 9109 lineIndent = getStringUnitWidth(pad, options); 9110 } 9111 9112 for (i = 0, l = words.length; i < l; i++) { 9113 var force = 0; 9114 9115 word = words[i]; 9116 if (lineIndent && word[0] == "\n") { 9117 word = word.substr(1); 9118 force = 1; 9119 } 9120 widths_array = getCharWidthsArray(word, options); 9121 current_word_length = getArraySum(widths_array); 9122 9123 if (line_length + separator_length + current_word_length > maxlen || force) { 9124 if (current_word_length > maxlen) { 9125 // this happens when you have space-less long URLs for example. 9126 // we just chop these to size. We do NOT insert hiphens 9127 tmp = splitLongWord(word, widths_array, maxlen - (line_length + separator_length), maxlen); 9128 // first line we add to existing line object 9129 line.push(tmp.shift()); // it's ok to have extra space indicator there 9130 // last line we make into new line object 9131 line = [tmp.pop()]; 9132 // lines in the middle we apped to lines object as whole lines 9133 while (tmp.length) { 9134 lines.push([tmp.shift()]); // single fragment occupies whole line 9135 } 9136 current_word_length = getArraySum(widths_array.slice(word.length - line[0].length)); 9137 } else { 9138 // just put it on a new line 9139 line = [word]; 9140 } 9141 9142 // now we attach new line to lines 9143 lines.push(line); 9144 line_length = current_word_length + lineIndent; 9145 separator_length = spaceCharWidth; 9146 } else { 9147 line.push(word); 9148 9149 line_length += separator_length + current_word_length; 9150 separator_length = spaceCharWidth; 9151 } 9152 } 9153 9154 if (lineIndent) { 9155 var postProcess = function postProcess(ln, idx) { 9156 return (idx ? pad : '') + ln.join(" "); 9157 }; 9158 } else { 9159 var postProcess = function postProcess(ln) { 9160 return ln.join(" "); 9161 }; 9162 } 9163 9164 return lines.map(postProcess); 9165 }; 9166 9167 /**
9168 Splits a given string into an array of strings. Uses 'size' value 9169 (in measurement units declared as default for the jsPDF instance) 9170 and the font's "widths" and "Kerning" tables, where available, to 9171 determine display length of a given string for a given font. 9172 9173 We use character's 100% of unit size (height) as width when Width 9174 table or other default width is not available. 9175 9176 @public 9177 @function 9178 @param text {String} Unencoded, regular JavaScript (Unicode, UTF-16 / UCS-2) string. 9179 @param size {Number} Nominal number, measured in units default to this instance of jsPDF. 9180 @param options {Object} Optional flags needed for chopper to do the right thing. 9181 @returns {Array} with strings chopped to size. 9182 */ 9183 API.splitTextToSize = function (text, maxlen, options) { 9184 'use strict'; 9185 9186 if (!options) { 9187 options = {}; 9188 } 9189 9190 var fsize = options.fontSize || this.internal.getFontSize(), 9191 newOptions = function (options) { 9192 var widths = { 0: 1 }, 9193 kerning = {}; 9194 9195 if (!options.widths || !options.kerning) { 9196 var f = this.internal.getFont(options.fontName, options.fontStyle), 9197 encoding = 'Unicode'; 9198 // NOT UTF8, NOT UTF16BE/LE, NOT UCS2BE/LE 9199 // Actual JavaScript-native String's 16bit char codes used. 9200 // no multi-byte logic here 9201 9202 if (f.metadata[encoding]) { 9203 return { 9204 widths: f.metadata[encoding].widths || widths, 9205 kerning: f.metadata[encoding].kerning || kerning 9206 }; 9207 } 9208 } else { 9209 return { 9210 widths: options.widths, 9211 kerning: options.kerning 9212 }; 9213 } 9214 9215 // then use default values 9216 return { 9217 widths: widths, 9218 kerning: kerning 9219 }; 9220 }.call(this, options); 9221 9222 // first we split on end-of-line chars 9223 var paragraphs; 9224 if (Array.isArray(text)) { 9225 paragraphs = text; 9226 } else { 9227 paragraphs = text.split(/\r?\n/); 9228 } 9229 9230 // now we convert size (max length of line) into "font size units" 9231 // at present time, the "font size unit" is always 'point' 9232 // 'proportional' means, "in proportion to font size" 9233 var fontUnit_maxLen = 1.0 * this.internal.scaleFactor * maxlen / fsize; 9234 // at this time, fsize is always in "points" regardless of the default measurement unit of the doc. 9235 // this may change in the future? 9236 // until then, proportional_maxlen is likely to be in 'points' 9237 9238 // If first line is to be indented (shorter or longer) than maxLen 9239 // we indicate that by using CSS-style "text-indent" option. 9240 // here it's in font units too (which is likely 'points') 9241 // it can be negative (which makes the first line longer than maxLen) 9242 newOptions.textIndent = options.textIndent ? options.textIndent * 1.0 * this.internal.scaleFactor / fsize : 0; 9243 newOptions.lineIndent = options.lineIndent; 9244 9245 var i, 9246 l, 9247 output = []; 9248 for (i = 0, l = paragraphs.length; i < l; i++) { 9249 output = output.concat(splitParagraphIntoLines(paragraphs[i], fontUnit_maxLen, newOptions)); 9250 } 9251 9252 return output; 9253 }; 9254})(jsPDF.API); 9255 9256/** @preserve 9257jsPDF standard_fonts_metrics plugin 9258Copyright (c) 2012 Willow Systems Corporation, willow-systems.com 9259MIT license. 9260*/ 9261/** 9262 * 9263 * ==================================================================== 9264 */ 9265 9266(function (API) { 9267 'use strict'; 9268 9269 /* 9270 # reference (Python) versions of 'compress' and 'uncompress' 9271 # only 'uncompress' function is featured lower as JavaScript 9272 # if you want to unit test "roundtrip", just transcribe the reference 9273 # 'compress' function from Python into JavaScript 9274 9275 def compress(data): 9276 9277 keys = '0123456789abcdef' 9278 values = 'klmnopqrstuvwxyz' 9279 mapping = dict(zip(keys, values)) 9280 vals = [] 9281 for key in data.keys(): 9282 value = data[key] 9283 try: 9284 keystring = hex(key)[2:] 9285 keystring = keystring[:-1] + mapping[keystring[-1:]] 9286 except: 9287 keystring = key.join(["'","'"]) 9288 #print('Keystring is %s' % keystring) 9289 9290 try: 9291 if value < 0: 9292 valuestring = hex(value)[3:] 9293 numberprefix = '-' 9294 else: 9295 valuestring = hex(value)[2:] 9296 numberprefix = '' 9297 valuestring = numberprefix + valuestring[:-1] + mapping[valuestring[-1:]] 9298 except: 9299 if type(value) == dict: 9300 valuestring = compress(value) 9301 else:
9302 raise Exception("Don't know what to do with value type %s" % type(value)) 9303 9304 vals.append(keystring+valuestring) 9305 9306 return '{' + ''.join(vals) + '}' 9307 9308 def uncompress(data): 9309 9310 decoded = '0123456789abcdef' 9311 encoded = 'klmnopqrstuvwxyz' 9312 mapping = dict(zip(encoded, decoded)) 9313 9314 sign = +1 9315 stringmode = False 9316 stringparts = [] 9317 9318 output = {} 9319 9320 activeobject = output 9321 parentchain = [] 9322 9323 keyparts = '' 9324 valueparts = '' 9325 9326 key = None 9327 9328 ending = set(encoded) 9329 9330 i = 1 9331 l = len(data) - 1 # stripping starting, ending {} 9332 while i != l: # stripping {} 9333 # -, {, }, ' are special. 9334 9335 ch = data[i] 9336 i += 1 9337 9338 if ch == "'": 9339 if stringmode: 9340 # end of string mode 9341 stringmode = False 9342 key = ''.join(stringparts) 9343 else: 9344 # start of string mode 9345 stringmode = True 9346 stringparts = [] 9347 elif stringmode == True: 9348 #print("Adding %s to stringpart" % ch) 9349 stringparts.append(ch) 9350 9351 elif ch == '{': 9352 # start of object 9353 parentchain.append( [activeobject, key] ) 9354 activeobject = {} 9355 key = None 9356 #DEBUG = True 9357 elif ch == '}': 9358 # end of object 9359 parent, key = parentchain.pop() 9360 parent[key] = activeobject 9361 key = None 9362 activeobject = parent 9363 #DEBUG = False 9364 9365 elif ch == '-': 9366 sign = -1 9367 else: 9368 # must be number 9369 if key == None: 9370 #debug("In Key. It is '%s', ch is '%s'" % (keyparts, ch)) 9371 if ch in ending: 9372 #debug("End of key") 9373 keyparts += mapping[ch] 9374 key = int(keyparts, 16) * sign 9375 sign = +1 9376 keyparts = '' 9377 else: 9378 keyparts += ch 9379 else: 9380 #debug("In value. It is '%s', ch is '%s'" % (valueparts, ch)) 9381 if ch in ending: 9382 #debug("End of value") 9383 valueparts += mapping[ch] 9384 activeobject[key] = int(valueparts, 16) * sign 9385 sign = +1 9386 key = None 9387 valueparts = '' 9388 else: 9389 valueparts += ch 9390 9391 #debug(activeobject) 9392 9393 return output 9394 9395 */ 9396 9397 /** 9398 Uncompresses data compressed into custom, base16-like format. 9399 @public 9400 @function 9401 @param 9402 @returns {Type} 9403 */ 9404 9405 var uncompress = function uncompress(data) { 9406 9407 var decoded = '0123456789abcdef', 9408 encoded = 'klmnopqrstuvwxyz', 9409 mapping = {}; 9410 9411 for (var i = 0; i < encoded.length; i++) { 9412 mapping[encoded[i]] = decoded[i]; 9413 } 9414 9415 var undef, 9416 output = {}, 9417 sign = 1, 9418 stringparts // undef. will be [] in string mode 9419 9420 , 9421 activeobject = output, 9422 parentchain = [], 9423 parent_key_pair, 9424 keyparts = '', 9425 valueparts = '', 9426 key // undef. will be Truthy when Key is resolved. 9427 , 9428 datalen = data.length - 1 // stripping ending } 9429 , 9430 ch; 9431 9432 i = 1; // stripping starting { 9433 9434 while (i != datalen) { 9435 // - { } ' are special. 9436 9437 ch = data[i]; 9438 i += 1; 9439 9440 if (ch == "'") { 9441 if (stringparts) { 9442 // end of string mode 9443 key = stringparts.join(''); 9444 stringparts = undef; 9445 } else { 9446 // start of string mode 9447 stringparts = []; 9448 } 9449 } else if (stringparts) { 9450 stringparts.push(ch); 9451 } else if (ch == '{') { 9452 // start of object 9453 parentchain.push([activeobject, key]); 9454 activeobject = {}; 9455 key = undef; 9456 } else if (ch == '}') { 9457 // end of object 9458 parent_key_pair = parentchain.pop(); 9459 parent_key_pair[0][parent_key_pair[1]] = activeobject; 9460 key = undef; 9461 activeobject = parent_key_pair[0]; 9462 } else if (ch == '-') { 9463 sign = -1; 9464 } else { 9465 // must be number 9466 if (key === undef) { 9467 if (mapping.hasOwnProperty(ch)) { 9468 keyparts += mapping[ch]; 9469 key = parseInt(keyparts, 16) * sign; 9470 sign = +1; 9471 keyparts = ''; 9472 } else { 9473 keyparts += ch; 9474 } 9475 } else { 9476 if (mapping.hasOwnProperty(ch)) { 9477 valueparts += mapping[ch]; 9478 activeobject[key] = parseInt(valueparts, 16) * sign; 9479 sign = +1; 9480 key = undef; 9481 valueparts = ''; 9482 } else { 9483 valueparts += ch; 9484 } 9485 } 9486 } 9487 } // end while 9488 9489 return output; 9490 }; 9491 9492 // encoding = 'Unicode' 9493 // NOT UTF8, NOT UTF16BE/LE, NOT UCS2BE/LE. NO clever BOM behavior 9494 // Actual 16bit char codes used. 9495 // no multi-byte logic here 9496 9497 // Unicode characters to WinAnsiEncoding: 9498 // {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} 9499 // as you can see, all Unicode chars are outside of 0-255 range. No char code conflicts. 9500 // this means that you can give Win cp1252 encoded strings to jsPDF for rendering directly 9501 // as well as give strings with some (supported by these fonts) Unicode characters and 9502 // these will be mapped to win cp1252
9503 // for example, you can send char code (cp1252) 0x80 or (unicode) 0x20AC, getting "Euro" glyph displayed in both cases. 9504 9505 var encodingBlock = { 9506 'codePages': ['WinAnsiEncoding'], 9507 'WinAnsiEncoding': uncompress("{19m8n201n9q201o9r201s9l201t9m201u8m201w9n201x9o201y8o202k8q202l8r202m9p202q8p20aw8k203k8t203t8v203u9v2cq8s212m9t15m8w15n9w2dw9s16k8u16l9u17s9z17x8y17y9y}") 9508 }, 9509 encodings = { 'Unicode': { 9510 'Courier': encodingBlock, 9511 'Courier-Bold': encodingBlock, 9512 'Courier-BoldOblique': encodingBlock, 9513 'Courier-Oblique': encodingBlock, 9514 'Helvetica': encodingBlock, 9515 'Helvetica-Bold': encodingBlock, 9516 'Helvetica-BoldOblique': encodingBlock, 9517 'Helvetica-Oblique': encodingBlock, 9518 'Times-Roman': encodingBlock, 9519 'Times-Bold': encodingBlock, 9520 'Times-BoldItalic': encodingBlock, 9521 'Times-Italic': encodingBlock 9522 // , 'Symbol' 9523 // , 'ZapfDingbats' 9524 } } 9525 /** 9526 Resources: 9527 Font metrics data is reprocessed derivative of contents of 9528 "Font Metrics for PDF Core 14 Fonts" package, which exhibits the following copyright and license: 9529 9530 Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. 9531 9532 This file and the 14 PostScript(R) AFM files it accompanies may be used, 9533 copied, and distributed for any purpose and without charge, with or without
9534 modification, provided that all copyright notices are retained; that the AFM 9535 files are not distributed without this file; that all modifications to this 9536 file or any of the AFM files are prominently noted in the modified file(s); 9537 and that this paragraph is not modified. Adobe Systems has no responsibility 9538 or obligation to support the use of the AFM files. 9539 9540 */ 9541 , 9542 fontMetrics = { 'Unicode': { 9543 // all sizing numbers are n/fontMetricsFractionOf = one font size unit 9544 // this means that if fontMetricsFractionOf = 1000, and letter A's width is 476, it's 9545 // width is 476/1000 or 47.6% of its height (regardless of font size) 9546 // At this time this value applies to "widths" and "kerning" numbers. 9547 9548 // char code 0 represents "default" (average) width - use it for chars missing in this table. 9549 // key 'fof' represents the "fontMetricsFractionOf" value 9550 9551 'Courier-Oblique': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), 9552 '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}}}"), 9553 'Helvetica-Bold': uncompress("{'widths'{k3s2q4scx1w201n3r201o6o201s1w201t1w201u1w201w3m201x3m201y3m2k1w2l2l202m2n2n3r2o3r2p5t202q6o2r1s2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v2l3w3u3x3u3y3u3z3x4k6l4l4s4m4s4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3r4v4s4w3x4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v2l5w1w5x2l5y3u5z3r6k2l6l3r6m3x6n3r6o3x6p3r6q2l6r3x6s3x6t1w6u1w6v3r6w1w6x5t6y3x6z3x7k3x7l3x7m2r7n3r7o2l7p3x7q3r7r4y7s3r7t3r7u3m7v2r7w1w7x2r7y3u202l3rcl4sal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3xbq3rbr1wbs2lbu2obv3rbz3xck4s202k3rcm4scn4sco4scp4scq6ocr4scs4mct4mcu4mcv4mcw1w2m2zcy1wcz1wdl4sdm4y
9553dn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3res3ret3reu3rev3rew1wex1wey1wez1wfl3xfm3xfn3xfo3xfp3xfq3xfr3ufs3xft3xfu3xfv3xfw3xfz3r203k6o212m6o2dw2l2cq2l3t3r3u2l17s4m19m3r}'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}}}"), 9554 'Courier': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), 9555 'Courier-BoldOblique': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), 9556 '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}}}") 9557 //, 'Symbol': uncompress("{'widths'{k3uaw4r19m3m2k1t2l2l202m2y2n3m2p5n202q6o3k3m2s2l2t2l2v3r2w1t3m3m2y1t2z1wbk2sbl3r'fof'6o3n3m3o3m3p3m3q3m3r3m3s3m3t3m3u1w3v1w3w3r3x3r3y3r3z2wbp3t3l3m5v2l5x2l5z3m2q4yfr3r7v3k7w1o7x3k}'kerning'{'fof'-6o}}") 9558 , 'Helvetica': uncompress("{'widths'{k3p2q4mcx1w201n3r201o6o201s1q201t1q201u1q201w2l201x2l201y2l2k1w2l1w202m2n2n3r2o3r2p5t202q6o2r1n2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v1w3w3u3x3u3y3u3z3r4k6p4l4m4m4m4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3m4v4m4w3r4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v1w5w1w5x1w5y2z5z3r6k2l6l3r6m3r6n3m6o3r6p3r6q1w6r3r6s3r6t1q6u1q6v3m6w1q6x5n6y3r6z3r7k3r7l3r7m2l7n3m7o1w7p3r7q3m7r4s7s3m7t3m7u3m7v2l7w1u7x2l7y3u202l3rcl4mal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3rbr1wbs2lbu2obv3rbz3xck4m202k3rcm4mcn4mco4mcp4mcq6ocr4scs4mct4mcu4mcv4mcw1w2m2ncy1wcz1wdl4sdm4y
9558dn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3mes3ret3reu3rev3rew1wex1wey1wez1wfl3rfm3rfn3rfo3rfp3rfq3rfr3ufs3xft3rfu3rfv3rfw3rfz3m203k6o212m6o2dw2l2cq2l3t3r3u1w17s4m19m3r}'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}}}"), 9559 'Helvetica-BoldOblique': uncompress("{'widths'{k3s2q4scx1w201n3r201o6o201s1w201t1w201u1w201w3m201x3m201y3m2k1w2l2l202m2n2n3r2o3r2p5t202q6o2r1s2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v2l3w3u3x3u3y3u3z3x4k6l4l4s4m4s4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3r4v4s4w3x4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v2l5w1w5x2l5y3u5z3r6k2l6l3r6m3x6n3r6o3x6p3r6q2l6r3x6s3x6t1w6u1w6v3r6w1w6x5t6y3x6z3x7k3x7l3x7m2r7n3r7o2l7p3x7q3r7r4y7s3r7t3r7u3m7v2r7w1w7x2r7y3u202l3rcl4sal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3xbq3rbr1wbs2lbu2obv3rbz3xck4s202k3rcm4scn4sco4scp4scq6ocr4scs4mct4mcu4mcv4mcw1w2m2zcy1wcz1wdl4sdm4y
9559dn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3res3ret3reu3rev3rew1wex1wey1wez1wfl3xfm3xfn3xfo3xfp3xfq3xfr3ufs3xft3xfu3xfv3xfw3xfz3r203k6o212m6o2dw2l2cq2l3t3r3u2l17s4m19m3r}'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}}}") 9560 //, 'ZapfDingbats': uncompress("{'widths'{k4u2k1w'fof'6o}'kerning'{'fof'-6o}}") 9561 , 'Courier-Bold': uncompress("{'widths'{k3w'fof'6o}'kerning'{'fof'-6o}}"), 9562 '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}
9562fv{17su5tu5qs}fw{17su5tu5qs}}}"), 9563 '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}}}"), 9564 'Helvetica-Oblique': uncompress("{'widths'{k3p2q4mcx1w201n3r201o6o201s1q201t1q201u1q201w2l201x2l201y2l2k1w2l1w202m2n2n3r2o3r2p5t202q6o2r1n2s2l2t2l2u2r2v3u2w1w2x2l2y1w2z1w3k3r3l3r3m3r3n3r3o3r3p3r3q3r3r3r3s3r203t2l203u2l3v1w3w3u3x3u3y3u3z3r4k6p4l4m4m4m4n4s4o4s4p4m4q3x4r4y4s4s4t1w4u3m4v4m4w3r4x5n4y4s4z4y5k4m5l4y5m4s5n4m5o3x5p4s5q4m5r5y5s4m5t4m5u3x5v1w5w1w5x1w5y2z5z3r6k2l6l3r6m3r6n3m6o3r6p3r6q1w6r3r6s3r6t1q6u1q6v3m6w1q6x5n6y3r6z3r7k3r7l3r7m2l7n3m7o1w7p3r7q3m7r4s7s3m7t3m7u3m7v2l7w1u7x2l7y3u202l3rcl4mal2lam3ran3rao3rap3rar3ras2lat4tau2pav3raw3uay4taz2lbk2sbl3u'fof'6obo2lbp3rbr1wbs2lbu2obv3rbz3xck4m202k3rcm4mcn4mco4mcp4mcq6ocr4scs4mct4mcu4mcv4mcw1w2m2ncy1wcz1wdl4sdm4y
9564dn4ydo4ydp4ydq4yds4ydt4sdu4sdv4sdw4sdz3xek3rel3rem3ren3reo3rep3req5ter3mes3ret3reu3rev3rew1wex1wey1wez1wfl3rfm3rfn3rfo3rfp3rfq3rfr3ufs3xft3rfu3rfv3rfw3rfz3m203k6o212m6o2dw2l2cq2l3t3r3u1w17s4m19m3r}'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}}}") 9565 } }; 9566 9567 /* 9568 This event handler is fired when a new jsPDF object is initialized 9569 This event handler appends metrics data to standard fonts within 9570 that jsPDF instance. The metrics are mapped over Unicode character 9571 codes, NOT CIDs or other codes matching the StandardEncoding table of the 9572 standard PDF fonts. 9573 Future: 9574 Also included is the encoding maping table, converting Unicode (UCS-2, UTF-16) 9575 char codes to StandardEncoding character codes. The encoding table is to be used 9576 somewhere around "pdfEscape" call. 9577 */ 9578 9579 API.events.push(['addFont', function (font) { 9580 var metrics, 9581 unicode_section, 9582 encoding = 'Unicode', 9583 encodingBlock; 9584 9585 metrics = fontMetrics[encoding][font.PostScriptName]; 9586 if (metrics) { 9587 if (font.metadata[encoding]) { 9588 unicode_section = font.metadata[encoding]; 9589 } else { 9590 unicode_section = font.metadata[encoding] = {}; 9591 } 9592 9593 unicode_section.widths = metrics.widths; 9594 unicode_section.kerning = metrics.kerning; 9595 } 9596 9597 encodingBlock = encodings[encoding][font.PostScriptName]; 9598 if (encodingBlock) { 9599 if (font.metadata[encoding]) { 9600 unicode_section = font.metadata[encoding]; 9601 } else { 9602 unicode_section = font.metadata[encoding] = {}; 9603 } 9604 9605 unicode_section.encoding = encodingBlock; 9606 if (encodingBlock.codePages && encodingBlock.codePages.length) { 9607 font.encoding = encodingBlock.codePages[0]; 9608 } 9609 } 9610 }]); // end of adding event handler 9611})(jsPDF.API); 9612 9613/** @preserve 9614jsPDF SVG plugin 9615Copyright (c) 2012 Willow Systems Corporation, willow-systems.com 9616*/ 9617/** 9618 * 9619 * ==================================================================== 9620 */ 9621 9622(function (jsPDFAPI) { 9623 'use strict'; 9624 9625 /** 9626 Parses SVG XML and converts only some of the SVG elements into 9627 PDF elements. 9628 9629 Supports: 9630 paths 9631 9632 @public 9633 @function 9634 @param 9635 @returns {Type} 9636 */ 9637 9638 jsPDFAPI.addSVG = function (svgtext, x, y, w, h) { 9639 // 'this' is _jsPDF object returned when jsPDF is inited (new jsPDF()) 9640 9641 var undef; 9642 9643 if (x === undef || y === undef) { 9644 throw new Error("addSVG needs values for 'x' and 'y'"); 9645 } 9646 9647 function InjectCSS(cssbody, document) { 9648 var styletag = document.createElement('style'); 9649 styletag.type = 'text/css'; 9650 if (styletag.styleSheet) { 9651 // ie 9652 styletag.styleSheet.cssText = cssbody; 9653 } else { 9654 // others 9655 styletag.appendChild(document.createTextNode(cssbody)); 9656 } 9657 document.getElementsByTagName("head")[0].appendChild(styletag); 9658 } 9659 9660 function createWorkerNode(document) { 9661 9662 var frameID = 'childframe' // Date.now().toString() + '_' + (Math.random() * 100).toString() 9663 , 9664 frame = document.createElement('iframe'); 9665 9666 InjectCSS('.jsPDF_sillysvg_iframe {display:none;position:absolute;}', document); 9667 9668 frame.name = frameID; 9669 frame.setAttribute("width", 0); 9670 frame.setAttribute("height", 0); 9671 frame.setAttribute("frameborder", "0"); 9672 frame.setAttribute("scrolling", "no"); 9673 frame.setAttribute("seamless", "seamless"); 9674 frame.setAttribute("class", "jsPDF_sillysvg_iframe"); 9675 9676 document.body.appendChild(frame); 9677 9678 return frame; 9679 } 9680 9681 function attachSVGToWorkerNode(svgtext, frame) { 9682 var framedoc = (frame.contentWindow || frame.contentDocument).document; 9683 framedoc.write(svgtext); 9684 framedoc.close(); 9685 return framedoc.getElementsByTagName('svg')[0]; 9686 } 9687 9688 function convertPathToPDFLinesArgs(path) { 9689 'use strict'; 9690 // we will use 'lines' method call. it needs: 9691 // - starting coordinate pair 9692 // - array of arrays of vector shifts (2-len for line, 6 len for bezier) 9693 // - scale array [horizontal, vertical] ratios 9694 // - style (stroke, fill, both) 9695 9696 var x = parseFloat(path[1]), 9697 y = parseFloat(path[2]), 9698 vectors = [], 9699 position = 3, 9700 len = path.length; 9701 9702 while (position < len) { 9703 if (path[position] === 'c') { 9704 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])]); 9705 position += 7; 9706 } else if (path[position] === 'l') { 9707 vectors.push([parseFloat(path[position + 1]), parseFloat(path[position + 2])]); 9708 position += 3; 9709 } else { 9710 position += 1; 9711 } 9712 } 9713 return [x, y, vectors]; 9714 } 9715
9716 var workernode = createWorkerNode(document), 9717 svgnode = attachSVGToWorkerNode(svgtext, workernode), 9718 scale = [1, 1], 9719 svgw = parseFloat(svgnode.getAttribute('width')), 9720 svgh = parseFloat(svgnode.getAttribute('height')); 9721 9722 if (svgw && svgh) { 9723 // setting both w and h makes image stretch to size. 9724 // this may distort the image, but fits your demanded size 9725 if (w && h) { 9726 scale = [w / svgw, h / svgh]; 9727 } 9728 // if only one is set, that value is set as max and SVG 9729 // is scaled proportionately. 9730 else if (w) { 9731 scale = [w / svgw, w / svgw]; 9732 } else if (h) { 9733 scale = [h / svgh, h / svgh]; 9734 } 9735 } 9736 9737 var i, 9738 l, 9739 tmp, 9740 linesargs, 9741 items = svgnode.childNodes; 9742 for (i = 0, l = items.length; i < l; i++) { 9743 tmp = items[i]; 9744 if (tmp.tagName && tmp.tagName.toUpperCase() === 'PATH') { 9745 linesargs = convertPathToPDFLinesArgs(tmp.getAttribute("d").split(' ')); 9746 // path start x coordinate 9747 linesargs[0] = linesargs[0] * scale[0] + x; // where x is upper left X of image 9748 // path start y coordinate 9749 linesargs[1] = linesargs[1] * scale[1] + y; // where y is upper left Y of image 9750 // the rest of lines are vectors. these will adjust with scale value auto. 9751 this.lines.call(this, linesargs[2] // lines 9752 , linesargs[0] // starting x 9753 , linesargs[1] // starting y 9754 , scale); 9755 } 9756 } 9757 9758 // clean up 9759 // workernode.parentNode.removeChild(workernode) 9760 9761 return this; 9762 }; 9763})(jsPDF.API); 9764 9765/** ==================================================================== 9766 * jsPDF total_pages plugin 9767 * Copyright (c) 2013 Eduardo Menezes de Morais, [email protected] 9768 * 9769 * 9770 * ==================================================================== 9771 */ 9772 9773(function (jsPDFAPI) { 9774 'use strict'; 9775 9776 jsPDFAPI.putTotalPages = function (pageExpression) { 9777 'use strict'; 9778 9779 var replaceExpression = new RegExp(pageExpression, 'g'); 9780 for (var n = 1; n <= this.internal.getNumberOfPages(); n++) { 9781 for (var i = 0; i < this.internal.pages[n].length; i++) { 9782 this.internal.pages[n][i] = this.internal.pages[n][i].replace(replaceExpression, this.internal.getNumberOfPages()); 9783 } 9784 } 9785 return this; 9786 }; 9787})(jsPDF.API); 9788 9789/** ==================================================================== 9790 * jsPDF XMP metadata plugin 9791 * Copyright (c) 2016 Jussi Utunen, [email protected] 9792 * 9793 * 9794 * ==================================================================== 9795 */ 9796 9797/*global jsPDF */ 9798 9799/** 9800* Adds XMP formatted metadata to PDF 9801* 9802* @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. 9803* @param {String} namespaceuri Sets the namespace URI for the metadata. Last character should be slash or hash. 9804* @function 9805* @returns {jsPDF} 9806* @methodOf jsPDF# 9807* @name addMetadata 9808*/ 9809 9810(function (jsPDFAPI) { 9811 'use strict'; 9812 9813 var xmpmetadata = ""; 9814 var xmpnamespaceuri = ""; 9815 var metadata_object_number = ""; 9816 9817 jsPDFAPI.addMetadata = function (metadata, namespaceuri) { 9818 xmpnamespaceuri = namespaceuri || "http://jspdf.default.namespaceuri/"; //The namespace URI for an XMP name shall not be empty 9819 xmpmetadata = metadata; 9820 this.internal.events.subscribe('postPutResources', function () { 9821 if (!xmpmetadata) { 9822 metadata_object_number = ""; 9823 } else { 9824 var xmpmeta_beginning = '<x:xmpmeta xmlns:x="adobe:ns:meta/">'; 9825 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>'; 9826 var rdf_ending = '</jspdf:metadata></rdf:Description></rdf:RDF>'; 9827 var xmpmeta_ending = '</x:xmpmeta>'; 9828 var utf8_xmpmeta_beginning = unescape(encodeURIComponent(xmpmeta_beginning)); 9829 var utf8_rdf_beginning = unescape(encodeURIComponent(rdf_beginning)); 9830 var utf8_metadata = unescape(encodeURIComponent(xmpmetadata)); 9831 var utf8_rdf_ending = unescape(encodeURIComponent(rdf_ending)); 9832 var utf8_xmpmeta_ending = unescape(encodeURIComponent(xmpmeta_ending)); 9833 9834 var total_len = utf8_rdf_beginning.length + utf8_metadata.length + utf8_rdf_ending.length + utf8_xmpmeta_beginning.length + utf8_xmpmeta_ending.length; 9835 9836 metadata_object_number = this.internal.newObject();
vendor: 7,132 bytes, lines 9837-10065
9837 this.internal.write('<< /Type /Metadata /Subtype /XML /Length ' + total_len + ' >>'); 9838 this.internal.write('stream'); 9839 this.internal.write(utf8_xmpmeta_beginning + utf8_rdf_beginning + utf8_metadata + utf8_rdf_ending + utf8_xmpmeta_ending); 9840 this.internal.write('endstream'); 9841 this.internal.write('endobj'); 9842 } 9843 }); 9844 this.internal.events.subscribe('putCatalog', function () { 9845 if (metadata_object_number) { 9846 this.internal.write('/Metadata ' + metadata_object_number + ' 0 R'); 9847 } 9848 }); 9849 return this; 9850 }; 9851})(jsPDF.API); 9852 9853/* Blob.js 9854 * A Blob implementation. 9855 * 2014-07-24 9856 * 9857 * By Eli Grey, http://eligrey.com 9858 * By Devin Samarin, https://github.com/dsamarin 9859 * License: X11/MIT 9860 * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md 9861 */ 9862 9863/*global self, unescape */ 9864/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 9865 plusplus: true */ 9866 9867/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ 9868 9869(function (view) { 9870 "use strict"; 9871 9872 view.URL = view.URL || view.webkitURL; 9873 9874 if (view.Blob && view.URL) { 9875 try { 9876 new Blob; 9877 return; 9878 } catch (e) {} 9879 } 9880 9881 // Internally we use a BlobBuilder implementation to base Blob off of 9882 // in order to support older browsers that only have BlobBuilder 9883 var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { 9884 var 9885 get_class = function(object) { 9886 return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; 9887 } 9888 , FakeBlobBuilder = function BlobBuilder() { 9889 this.data = []; 9890 } 9891 , FakeBlob = function Blob(data, type, encoding) { 9892 this.data = data; 9893 this.size = data.length; 9894 this.type = type; 9895 this.encoding = encoding; 9896 } 9897 , FBB_proto = FakeBlobBuilder.prototype 9898 , FB_proto = FakeBlob.prototype 9899 , FileReaderSync = view.FileReaderSync 9900 , FileException = function(type) { 9901 this.code = this[this.name = type]; 9902 } 9903 , file_ex_codes = ( 9904 "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " 9905 + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" 9906 ).split(" ") 9907 , file_ex_code = file_ex_codes.length 9908 , real_URL = view.URL || view.webkitURL || view 9909 , real_create_object_URL = real_URL.createObjectURL 9910 , real_revoke_object_URL = real_URL.revokeObjectURL 9911 , URL = real_URL 9912 , btoa = view.btoa 9913 , atob = view.atob 9914 9915 , ArrayBuffer = view.ArrayBuffer 9916 , Uint8Array = view.Uint8Array 9917 9918 , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/; 9919 FakeBlob.fake = FB_proto.fake = true; 9920 while (file_ex_code--) { 9921 FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; 9922 } 9923 // Polyfill URL 9924 if (!real_URL.createObjectURL) { 9925 URL = view.URL = function(uri) { 9926 var 9927 uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") 9928 , uri_origin; 9929 uri_info.href = uri; 9930 if (!("origin" in uri_info)) { 9931 if (uri_info.protocol.toLowerCase() === "data:") { 9932 uri_info.origin = null; 9933 } else { 9934 uri_origin = uri.match(origin); 9935 uri_info.origin = uri_origin && uri_origin[1]; 9936 } 9937 } 9938 return uri_info; 9939 }; 9940 } 9941 URL.createObjectURL = function(blob) { 9942 var 9943 type = blob.type 9944 , data_URI_header; 9945 if (type === null) { 9946 type = "application/octet-stream"; 9947 } 9948 if (blob instanceof FakeBlob) { 9949 data_URI_header = "data:" + type; 9950 if (blob.encoding === "base64") { 9951 return data_URI_header + ";base64," + blob.data; 9952 } else if (blob.encoding === "URI") { 9953 return data_URI_header + "," + decodeURIComponent(blob.data); 9954 } if (btoa) { 9955 return data_URI_header + ";base64," + btoa(blob.data); 9956 } else { 9957 return data_URI_header + "," + encodeURIComponent(blob.data); 9958 } 9959 } else if (real_create_object_URL) { 9960 return real_create_object_URL.call(real_URL, blob); 9961 } 9962 }; 9963 URL.revokeObjectURL = function(object_URL) { 9964 if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { 9965 real_revoke_object_URL.call(real_URL, object_URL); 9966 } 9967 }; 9968 FBB_proto.append = function(data/*, endings*/) { 9969 var bb = this.data; 9970 // decode data to a binary string 9971 if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { 9972 var 9973 str = "" 9974 , buf = new Uint8Array(data) 9975 , i = 0 9976 , buf_len = buf.length; 9977 for (; i < buf_len; i++) { 9978 str += String.fromCharCode(buf[i]); 9979 } 9980 bb.push(str); 9981 } else if (get_class(data) === "Blob" || get_class(data) === "File") { 9982 if (FileReaderSync) { 9983 var fr = new FileReaderSync; 9984 bb.push(fr.readAsBinaryString(data)); 9985 } else { 9986 // async FileReader won't work as BlobBuilder is sync 9987 throw new FileException("NOT_READABLE_ERR"); 9988 } 9989 } else if (data instanceof FakeBlob) { 9990 if (data.encoding === "base64" && atob) { 9991 bb.push(atob(data.data)); 9992 } else if (data.encoding === "URI") { 9993 bb.push(decodeURIComponent(data.data)); 9994 } else if (data.encoding === "raw") { 9995 bb.push(data.data); 9996 } 9997 } else { 9998 if (typeof data !== "string") { 9999 data += ""; // convert unsupported types to strings 10000 } 10001 // decode UTF-16 to binary string 10002 bb.push(unescape(encodeURIComponent(data))); 10003 } 10004 }; 10005 FBB_proto.getBlob = function(type) { 10006 if (!arguments.length) { 10007 type = null; 10008 } 10009 return new FakeBlob(this.data.join(""), type, "raw"); 10010 }; 10011 FBB_proto.toString = function() { 10012 return "[object BlobBuilder]"; 10013 }; 10014 FB_proto.slice = function(start, end, type) { 10015 var args = arguments.length; 10016 if (args < 3) { 10017 type = null; 10018 } 10019 return new FakeBlob( 10020 this.data.slice(start, args > 1 ? end : this.data.length) 10021 , type 10022 , this.encoding 10023 ); 10024 }; 10025 FB_proto.toString = function() { 10026 return "[object Blob]"; 10027 }; 10028 FB_proto.close = function() { 10029 this.size = 0; 10030 delete this.data; 10031 }; 10032 return FakeBlobBuilder; 10033 }(view)); 10034 10035 view.Blob = function(blobParts, options) { 10036 var type = options ? (options.type || "") : ""; 10037 var builder = new BlobBuilder(); 10038 if (blobParts) { 10039 for (var i = 0, len = blobParts.length; i < len; i++) { 10040 if (Uint8Array && blobParts[i] instanceof Uint8Array) { 10041 builder.append(blobParts[i].buffer); 10042 } 10043 else { 10044 builder.append(blobParts[i]); 10045 } 10046 } 10047 } 10048 var blob = builder.getBlob(type); 10049 if (!blob.slice && blob.webkitSlice) { 10050 blob.slice = blob.webkitSlice; 10051 } 10052 return blob; 10053 }; 10054 10055 var getPrototypeOf = Object.getPrototypeOf || function(object) { 10056 return object.__proto__; 10057 }; 10058 view.Blob.prototype = getPrototypeOf(new view.Blob()); 10059}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || undefined.content || undefined)); 10060 10061/* FileSaver.js 10062 * A saveAs() FileSaver implementation. 10063 * 1.1.20151003 10064 * 10065 * By Eli Grey, http://eligrey.com
10066 * License: MIT 10067 * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 10068 */ 10069 10070/*global self */ 10071/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 10072 10073/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 10074 10075var saveAs = saveAs || (function(view) { 10076 "use strict"; 10077 // IE <10 is explicitly unsupported 10078 if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 10079 return; 10080 } 10081 var 10082 doc = view.document 10083 // only get URL when necessary in case Blob.js hasn't overridden it yet 10084 , get_URL = function() { 10085 return view.URL || view.webkitURL || view; 10086 } 10087 , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 10088 , can_use_save_link = "download" in save_link 10089 , click = function(node) { 10090 var event = new MouseEvent("click"); 10091 node.dispatchEvent(event); 10092 } 10093 , is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent) 10094 , webkit_req_fs = view.webkitRequestFileSystem 10095 , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 10096 , throw_outside = function(ex) { 10097 (view.setImmediate || view.setTimeout)(function() { 10098 throw ex; 10099 }, 0); 10100 } 10101 , force_saveable_type = "application/octet-stream" 10102 , fs_min_size = 0 10103 // See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and 10104 // https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047 10105 // for the reasoning behind the timeout and revocation flow 10106 , arbitrary_revoke_timeout = 500 // in ms 10107 , revoke = function(file) { 10108 var revoker = function() { 10109 if (typeof file === "string") { // file is an object URL 10110 get_URL().revokeObjectURL(file); 10111 } else { // file is a File 10112 file.remove(); 10113 } 10114 }; 10115 if (view.chrome) { 10116 revoker(); 10117 } else { 10118 setTimeout(revoker, arbitrary_revoke_timeout); 10119 } 10120 } 10121 , dispatch = function(filesaver, event_types, event) { 10122 event_types = [].concat(event_types); 10123 var i = event_types.length; 10124 while (i--) { 10125 var listener = filesaver["on" + event_types[i]]; 10126 if (typeof listener === "function") { 10127 try { 10128 listener.call(filesaver, event || filesaver); 10129 } catch (ex) { 10130 throw_outside(ex); 10131 } 10132 } 10133 } 10134 } 10135 , auto_bom = function(blob) { 10136 // prepend BOM for UTF-8 XML and text/* types (including HTML) 10137 if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 10138 return new Blob(["\ufeff", blob], {type: blob.type}); 10139 } 10140 return blob; 10141 } 10142 , FileSaver = function(blob, name, no_auto_bom) { 10143 if (!no_auto_bom) { 10144 blob = auto_bom(blob); 10145 } 10146 // First try a.download, then web filesystem, then object URLs 10147 var 10148 filesaver = this 10149 , type = blob.type 10150 , blob_changed = false 10151 , object_url 10152 , target_view 10153 , dispatch_all = function() { 10154 dispatch(filesaver, "writestart progress write writeend".split(" ")); 10155 } 10156 // on any filesys errors revert to saving with object URLs 10157 , fs_error = function() { 10158 if (target_view && is_safari && typeof FileReader !== "undefined") { 10159 // Safari doesn't allow downloading of blob urls 10160 var reader = new FileReader(); 10161 reader.onloadend = function() { 10162 var base64Data = reader.result; 10163 target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/)); 10164 filesaver.readyState = filesaver.DONE; 10165 dispatch_all(); 10166 }; 10167 reader.readAsDataURL(blob); 10168 filesaver.readyState = filesaver.INIT; 10169 return; 10170 } 10171 // don't create more object URLs than needed 10172 if (blob_changed || !object_url) { 10173 object_url = get_URL().createObjectURL(blob); 10174 } 10175 if (target_view) { 10176 target_view.location.href = object_url; 10177 } else { 10178 var new_tab = view.open(object_url, "_blank"); 10179 if (new_tab == undefined && is_safari) { 10180 //Apple do not allow window.open, see http://bit.ly/1kZffRI 10181 view.location.href = object_url; 10182 } 10183 } 10184 filesaver.readyState = filesaver.DONE; 10185 dispatch_all(); 10186 revoke(object_url); 10187 } 10188 , abortable = function(func) { 10189 return function() { 10190 if (filesaver.readyState !== filesaver.DONE) { 10191 return func.apply(this, arguments); 10192 } 10193 }; 10194 } 10195 , create_if_not_found = {create: true, exclusive: false} 10196 , slice; 10197 filesaver.readyState = filesaver.INIT; 10198 if (!name) { 10199 name = "download"; 10200 } 10201 if (can_use_save_link) { 10202 object_url = get_URL().createObjectURL(blob); 10203 setTimeout(function() { 10204 save_link.href = object_url;
10205 save_link.download = name; 10206 click(save_link); 10207 dispatch_all(); 10208 revoke(object_url); 10209 filesaver.readyState = filesaver.DONE; 10210 }); 10211 return; 10212 } 10213 // Object and web filesystem URLs have a problem saving in Google Chrome when 10214 // viewed in a tab, so I force save with application/octet-stream 10215 // http://code.google.com/p/chromium/issues/detail?id=91158 10216 // Update: Google errantly closed 91158, I submitted it again: 10217 // https://code.google.com/p/chromium/issues/detail?id=389642 10218 if (view.chrome && type && type !== force_saveable_type) { 10219 slice = blob.slice || blob.webkitSlice; 10220 blob = slice.call(blob, 0, blob.size, force_saveable_type); 10221 blob_changed = true; 10222 } 10223 // Since I can't be sure that the guessed media type will trigger a download 10224 // in WebKit, I append .download to the filename. 10225 // https://bugs.webkit.org/show_bug.cgi?id=65440 10226 if (webkit_req_fs && name !== "download") { 10227 name += ".download"; 10228 } 10229 if (type === force_saveable_type || webkit_req_fs) { 10230 target_view = view; 10231 } 10232 if (!req_fs) { 10233 fs_error(); 10234 return; 10235 } 10236 fs_min_size += blob.size; 10237 req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 10238 fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 10239 var save = function() { 10240 dir.getFile(name, create_if_not_found, abortable(function(file) { 10241 file.createWriter(abortable(function(writer) { 10242 writer.onwriteend = function(event) { 10243 target_view.location.href = file.toURL(); 10244 filesaver.readyState = filesaver.DONE; 10245 dispatch(filesaver, "writeend", event); 10246 revoke(file); 10247 }; 10248 writer.onerror = function() { 10249 var error = writer.error; 10250 if (error.code !== error.ABORT_ERR) { 10251 fs_error(); 10252 } 10253 }; 10254 "writestart progress write abort".split(" ").forEach(function(event) { 10255 writer["on" + event] = filesaver["on" + event]; 10256 }); 10257 writer.write(blob); 10258 filesaver.abort = function() { 10259 writer.abort(); 10260 filesaver.readyState = filesaver.DONE; 10261 }; 10262 filesaver.readyState = filesaver.WRITING; 10263 }), fs_error); 10264 }), fs_error); 10265 }; 10266 dir.getFile(name, {create: false}, abortable(function(file) { 10267 // delete file if it already exists 10268 file.remove(); 10269 save(); 10270 }), abortable(function(ex) { 10271 if (ex.code === ex.NOT_FOUND_ERR) { 10272 save(); 10273 } else { 10274 fs_error(); 10275 } 10276 })); 10277 }), fs_error); 10278 }), fs_error); 10279 } 10280 , FS_proto = FileSaver.prototype 10281 , saveAs = function(blob, name, no_auto_bom) { 10282 return new FileSaver(blob, name, no_auto_bom); 10283 }; 10284 // IE 10+ (native saveAs) 10285 if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 10286 return function(blob, name, no_auto_bom) { 10287 if (!no_auto_bom) { 10288 blob = auto_bom(blob); 10289 } 10290 return navigator.msSaveOrOpenBlob(blob, name || "download"); 10291 }; 10292 } 10293 10294 FS_proto.abort = function() { 10295 var filesaver = this; 10296 filesaver.readyState = filesaver.DONE; 10297 dispatch(filesaver, "abort"); 10298 }; 10299 FS_proto.readyState = FS_proto.INIT = 0; 10300 FS_proto.WRITING = 1; 10301 FS_proto.DONE = 2; 10302 10303 FS_proto.error = 10304 FS_proto.onwritestart = 10305 FS_proto.onprogress = 10306 FS_proto.onwrite = 10307 FS_proto.onabort = 10308 FS_proto.onerror = 10309 FS_proto.onwriteend = 10310 null; 10311 10312 return saveAs; 10313}( 10314 typeof self !== "undefined" && self 10315 || typeof window !== "undefined" && window 10316 || undefined.content 10317)); 10318// `self` is undefined in Firefox for Android content script context 10319// while `this` is nsIContentFrameMessageManager 10320// with an attribute `content` that corresponds to the window 10321 10322if (typeof module !== "undefined" && module.exports) { 10323 module.exports.saveAs = saveAs; 10324} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { 10325 define([], function() { 10326 return saveAs; 10327 }); 10328} 10329 10330/* 10331 * Copyright (c) 2012 chick307 <[email protected]> 10332 * 10333 * Licensed under the MIT License. 10334 * http://opensource.org/licenses/mit-license 10335 */ 10336 10337void function(global, callback) { 10338 if (typeof module === 'object') { 10339 module.exports = callback(); 10340 } else if (typeof define === 'function') { 10341 define(callback); 10342 } else { 10343 global.adler32cs = callback(); 10344 } 10345}(jsPDF, function() { 10346 var _hasArrayBuffer = typeof ArrayBuffer === 'function' && 10347 typeof Uint8Array === 'function'; 10348 10349 var _Buffer = null, _isBuffer = (function() { 10350 if (!_hasArrayBuffer) 10351 return function _isBuffer() { return false }; 10352 10353 try { 10354 var buffer = {}; 10355 if (typeof buffer.Buffer === 'function') 10356 _Buffer = buffer.Buffer; 10357 } catch (error) {} 10358 10359 return function _isBuffer(value) { 10360 return value instanceof ArrayBuffer || 10361 _Buffer !== null && value instanceof _Buffer; 10362 }; 10363 }()); 10364 10365 var _utf8ToBinary = (function() { 10366 if (_Buffer !== null) { 10367 return function _utf8ToBinary(utf8String) {
10368 return new _Buffer(utf8String, 'utf8').toString('binary'); 10369 }; 10370 } else { 10371 return function _utf8ToBinary(utf8String) { 10372 return unescape(encodeURIComponent(utf8String)); 10373 }; 10374 } 10375 }()); 10376 10377 var MOD = 65521; 10378 10379 var _update = function _update(checksum, binaryString) { 10380 var a = checksum & 0xFFFF, b = checksum >>> 16; 10381 for (var i = 0, length = binaryString.length; i < length; i++) { 10382 a = (a + (binaryString.charCodeAt(i) & 0xFF)) % MOD; 10383 b = (b + a) % MOD; 10384 } 10385 return (b << 16 | a) >>> 0; 10386 }; 10387 10388 var _updateUint8Array = function _updateUint8Array(checksum, uint8Array) { 10389 var a = checksum & 0xFFFF, b = checksum >>> 16; 10390 for (var i = 0, length = uint8Array.length, x; i < length; i++) { 10391 a = (a + uint8Array[i]) % MOD; 10392 b = (b + a) % MOD; 10393 } 10394 return (b << 16 | a) >>> 0 10395 }; 10396 10397 var exports = {}; 10398 10399 var Adler32 = exports.Adler32 = (function() { 10400 var ctor = function Adler32(checksum) { 10401 if (!(this instanceof ctor)) { 10402 throw new TypeError( 10403 'Constructor cannot called be as a function.'); 10404 } 10405 if (!isFinite(checksum = checksum == null ? 1 : +checksum)) { 10406 throw new Error( 10407 'First arguments needs to be a finite number.'); 10408 } 10409 this.checksum = checksum >>> 0; 10410 }; 10411 10412 var proto = ctor.prototype = {}; 10413 proto.constructor = ctor; 10414 10415 ctor.from = function(from) { 10416 from.prototype = proto; 10417 return from; 10418 }(function from(binaryString) { 10419 if (!(this instanceof ctor)) { 10420 throw new TypeError( 10421 'Constructor cannot called be as a function.'); 10422 } 10423 if (binaryString == null) 10424 throw new Error('First argument needs to be a string.'); 10425 this.checksum = _update(1, binaryString.toString()); 10426 }); 10427 10428 ctor.fromUtf8 = function(fromUtf8) { 10429 fromUtf8.prototype = proto; 10430 return fromUtf8; 10431 }(function fromUtf8(utf8String) { 10432 if (!(this instanceof ctor)) { 10433 throw new TypeError( 10434 'Constructor cannot called be as a function.'); 10435 } 10436 if (utf8String == null) 10437 throw new Error('First argument needs to be a string.'); 10438 var binaryString = _utf8ToBinary(utf8String.toString()); 10439 this.checksum = _update(1, binaryString); 10440 }); 10441 10442 if (_hasArrayBuffer) { 10443 ctor.fromBuffer = function(fromBuffer) { 10444 fromBuffer.prototype = proto; 10445 return fromBuffer; 10446 }(function fromBuffer(buffer) { 10447 if (!(this instanceof ctor)) { 10448 throw new TypeError( 10449 'Constructor cannot called be as a function.'); 10450 } 10451 if (!_isBuffer(buffer)) 10452 throw new Error('First argument needs to be ArrayBuffer.'); 10453 var array = new Uint8Array(buffer); 10454 return this.checksum = _updateUint8Array(1, array); 10455 }); 10456 } 10457 10458 proto.update = function update(binaryString) { 10459 if (binaryString == null) 10460 throw new Error('First argument needs to be a string.'); 10461 binaryString = binaryString.toString(); 10462 return this.checksum = _update(this.checksum, binaryString); 10463 }; 10464 10465 proto.updateUtf8 = function updateUtf8(utf8String) { 10466 if (utf8String == null) 10467 throw new Error('First argument needs to be a string.'); 10468 var binaryString = _utf8ToBinary(utf8String.toString()); 10469 return this.checksum = _update(this.checksum, binaryString); 10470 }; 10471 10472 if (_hasArrayBuffer) { 10473 proto.updateBuffer = function updateBuffer(buffer) { 10474 if (!_isBuffer(buffer)) 10475 throw new Error('First argument needs to be ArrayBuffer.'); 10476 var array = new Uint8Array(buffer); 10477 return this.checksum = _updateUint8Array(this.checksum, array); 10478 }; 10479 } 10480 10481 proto.clone = function clone() { 10482 return new Adler32(this.checksum); 10483 }; 10484 10485 return ctor; 10486 }()); 10487 10488 exports.from = function from(binaryString) { 10489 if (binaryString == null) 10490 throw new Error('First argument needs to be a string.'); 10491 return _update(1, binaryString.toString()); 10492 }; 10493 10494 exports.fromUtf8 = function fromUtf8(utf8String) { 10495 if (utf8String == null) 10496 throw new Error('First argument needs to be a string.'); 10497 var binaryString = _utf8ToBinary(utf8String.toString()); 10498 return _update(1, binaryString); 10499 }; 10500 10501 if (_hasArrayBuffer) { 10502 exports.fromBuffer = function fromBuffer(buffer) { 10503 if (!_isBuffer(buffer)) 10504 throw new Error('First argument need to be ArrayBuffer.'); 10505 var array = new Uint8Array(buffer); 10506 return _updateUint8Array(1, array); 10507 }; 10508 } 10509 10510 return exports; 10511}); 10512 10513/** 10514 * CssColors 10515 * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) [email protected] 10516 * 10517 * Licensed under the MIT License. 10518 * http://opensource.org/licenses/mit-license 10519 */ 10520 10521/** 10522 * Usage CssColors('red'); 10523 * Returns RGB hex color with '#' prefix 10524 */ 10525 10526var CssColors = {}; 10527CssColors._colorsTable = { 10528 "aliceblue" : "#f0f8ff", 10529 "antiquewhite" : "#faebd7", 10530 "aqua" : "#00ffff", 10531 "aquamarine" : "#7fffd4", 10532 "azure" : "#f0ffff", 10533 "beige" : "#f5f5dc", 10534 "bisque" : "#ffe4c4", 10535 "black" : "#000000", 10536 "blanchedalmond" : "#ffebcd", 10537 "blue" : "#0000ff", 10538 "blueviolet" : "#8a2be2", 10539 "brown" : "#a52a2a", 10540 "burlywood" : "#deb887", 10541 "cadetblue" : "#5f9ea0", 10542 "chartreuse" : "#7fff00", 10543 "chocolate" : "#d2691e", 10544 "coral" : "#ff7f50", 10545 "cornflowerblue" : "#6495ed", 10546 "cornsilk" : "#fff8dc", 10547 "crimson" : "#dc143c", 10548 "cyan" : "#00ffff", 10549 "darkblue" : "#00008b", 10550 "darkcyan" : "#008b8b", 10551 "darkgoldenrod" : "#b8860b", 10552 "darkgray" : "#a9a9a9", 10553 "darkgreen" : "#006400", 10554 "darkkhaki" : "#bdb76b", 10555 "darkmagenta" : "#8b008b", 10556 "darkolivegreen" : "#556b2f", 10557 "darkorange" : "#ff8c00", 10558 "darkorchid" : "#9932cc", 10559 "darkred" : "#8b0000", 10560 "darksalmon" : "#e9967a", 10561 "darkseagreen" : "#8fbc8f", 10562 "darkslateblue" : "#483d8b", 10563 "darkslategray" : "#2f4f4f", 10564 "darkturquoise" : "#00ced1", 10565 "darkviolet" : "#9400d3", 10566 "deeppink" : "#ff1493", 10567 "deepskyblue" : "#00bfff", 10568 "dimgray" : "#696969", 10569 "dodgerblue" : "#1e90ff", 10570 "firebrick" : "#b22222", 10571 "floralwhite" : "#fffaf0", 10572 "forestgreen" : "#228b22", 10573 "fuchsia" : "#ff00ff", 10574 "gainsboro" : "#dcdcdc", 10575 "ghostwhite" : "#f8f8ff",
10576 "gold" : "#ffd700", 10577 "goldenrod" : "#daa520", 10578 "gray" : "#808080", 10579 "green" : "#008000", 10580 "greenyellow" : "#adff2f", 10581 "honeydew" : "#f0fff0", 10582 "hotpink" : "#ff69b4", 10583 "indianred " : "#cd5c5c", 10584 "indigo" : "#4b0082", 10585 "ivory" : "#fffff0", 10586 "khaki" : "#f0e68c", 10587 "lavender" : "#e6e6fa", 10588 "lavenderblush" : "#fff0f5", 10589 "lawngreen" : "#7cfc00", 10590 "lemonchiffon" : "#fffacd", 10591 "lightblue" : "#add8e6", 10592 "lightcoral" : "#f08080", 10593 "lightcyan" : "#e0ffff", 10594 "lightgoldenrodyellow" : "#fafad2", 10595 "lightgrey" : "#d3d3d3", 10596 "lightgreen" : "#90ee90", 10597 "lightpink" : "#ffb6c1", 10598 "lightsalmon" : "#ffa07a", 10599 "lightseagreen" : "#20b2aa", 10600 "lightskyblue" : "#87cefa", 10601 "lightslategray" : "#778899", 10602 "lightsteelblue" : "#b0c4de", 10603 "lightyellow" : "#ffffe0", 10604 "lime" : "#00ff00", 10605 "limegreen" : "#32cd32", 10606 "linen" : "#faf0e6", 10607 "magenta" : "#ff00ff", 10608 "maroon" : "#800000", 10609 "mediumaquamarine" : "#66cdaa", 10610 "mediumblue" : "#0000cd", 10611 "mediumorchid" : "#ba55d3", 10612 "mediumpurple" : "#9370d8", 10613 "mediumseagreen" : "#3cb371", 10614 "mediumslateblue" : "#7b68ee", 10615 "mediumspringgreen" : "#00fa9a", 10616 "mediumturquoise" : "#48d1cc", 10617 "mediumvioletred" : "#c71585", 10618 "midnightblue" : "#191970", 10619 "mintcream" : "#f5fffa", 10620 "mistyrose" : "#ffe4e1", 10621 "moccasin" : "#ffe4b5", 10622 "navajowhite" : "#ffdead", 10623 "navy" : "#000080", 10624 "oldlace" : "#fdf5e6", 10625 "olive" : "#808000", 10626 "olivedrab" : "#6b8e23", 10627 "orange" : "#ffa500", 10628 "orangered" : "#ff4500", 10629 "orchid" : "#da70d6", 10630 "palegoldenrod" : "#eee8aa", 10631 "palegreen" : "#98fb98", 10632 "paleturquoise" : "#afeeee", 10633 "palevioletred" : "#d87093", 10634 "papayawhip" : "#ffefd5", 10635 "peachpuff" : "#ffdab9", 10636 "peru" : "#cd853f", 10637 "pink" : "#ffc0cb", 10638 "plum" : "#dda0dd", 10639 "powderblue" : "#b0e0e6", 10640 "purple" : "#800080", 10641 "red" : "#ff0000", 10642 "rosybrown" : "#bc8f8f", 10643 "royalblue" : "#4169e1", 10644 "saddlebrown" : "#8b4513", 10645 "salmon" : "#fa8072", 10646 "sandybrown" : "#f4a460", 10647 "seagreen" : "#2e8b57", 10648 "seashell" : "#fff5ee", 10649 "sienna" : "#a0522d", 10650 "silver" : "#c0c0c0", 10651 "skyblue" : "#87ceeb", 10652 "slateblue" : "#6a5acd", 10653 "slategray" : "#708090", 10654 "snow" : "#fffafa", 10655 "springgreen" : "#00ff7f", 10656 "steelblue" : "#4682b4", 10657 "tan" : "#d2b48c", 10658 "teal" : "#008080", 10659 "thistle" : "#d8bfd8", 10660 "tomato" : "#ff6347", 10661 "turquoise" : "#40e0d0", 10662 "violet" : "#ee82ee", 10663 "wheat" : "#f5deb3", 10664 "white" : "#ffffff", 10665 "whitesmoke" : "#f5f5f5", 10666 "yellow" : "#ffff00", 10667 "yellowgreen" : "#9acd32" 10668}; 10669 10670CssColors.colorNameToHex = function(color) { 10671 color = color.toLowerCase(); 10672 if (typeof this._colorsTable[color] != 'undefined') 10673 return this._colorsTable[color]; 10674 10675 return false; 10676}; 10677 10678/* 10679 Deflate.js - https://github.com/gildas-lormeau/zip.js 10680 Copyright (c) 2013 Gildas Lormeau. All rights reserved. 10681 10682 Redistribution and use in source and binary forms, with or without 10683 modification, are permitted provided that the following conditions are met: 10684 10685 1. Redistributions of source code must retain the above copyright notice, 10686 this list of conditions and the following disclaimer. 10687 10688 2. Redistributions in binary form must reproduce the above copyright 10689 notice, this list of conditions and the following disclaimer in 10690 the documentation and/or other materials provided with the distribution. 10691 10692 3. The names of the authors may not be used to endorse or promote products 10693 derived from this software without specific prior written permission. 10694 10695 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, 10696 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 10697 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, 10698 INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, 10699 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 10700 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 10701 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 10702 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 10703 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 10704 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10705 */ 10706 10707/* 10708 * This program is based on JZlib 1.0.2 ymnk, JCraft,Inc. 10709 * JZlib is based on zlib-1.1.3, so all credit should go authors 10710 * Jean-loup Gailly([email protected]) and Mark Adler([email protected]) 10711 * and contributors of zlib. 10712 */ 10713 10714var Deflater = (function(obj) { 10715 10716 // Global 10717 10718 var MAX_BITS = 15; 10719 var D_CODES = 30; 10720 var BL_CODES = 19; 10721 10722 var LENGTH_CODES = 29; 10723 var LITERALS = 256; 10724 var L_CODES = (LITERALS + 1 + LENGTH_CODES); 10725 var HEAP_SIZE = (2 * L_CODES + 1); 10726 10727 var END_BLOCK = 256; 10728 10729 // Bit length codes must not exceed MAX_BL_BITS bits 10730 var MAX_BL_BITS = 7; 10731 10732 // repeat previous bit length 3-6 times (2 bits of repeat count) 10733 var REP_3_6 = 16; 10734
10735 // repeat a zero length 3-10 times (3 bits of repeat count) 10736 var REPZ_3_10 = 17; 10737 10738 // repeat a zero length 11-138 times (7 bits of repeat count) 10739 var REPZ_11_138 = 18; 10740 10741 // The lengths of the bit length codes are sent in order of decreasing 10742 // probability, to avoid transmitting the lengths for unused bit 10743 // length codes. 10744 10745 var Buf_size = 8 * 2; 10746 10747 // JZlib version : "1.0.2" 10748 var Z_DEFAULT_COMPRESSION = -1; 10749 10750 // compression strategy 10751 var Z_FILTERED = 1; 10752 var Z_HUFFMAN_ONLY = 2; 10753 var Z_DEFAULT_STRATEGY = 0; 10754 10755 var Z_NO_FLUSH = 0; 10756 var Z_PARTIAL_FLUSH = 1; 10757 var Z_FULL_FLUSH = 3; 10758 var Z_FINISH = 4; 10759 10760 var Z_OK = 0; 10761 var Z_STREAM_END = 1; 10762 var Z_NEED_DICT = 2; 10763 var Z_STREAM_ERROR = -2; 10764 var Z_DATA_ERROR = -3; 10765 var Z_BUF_ERROR = -5; 10766 10767 // Tree 10768 10769 // see definition of array dist_code below 10770 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, 10771 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, 10772 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, 10773 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, 10774 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, 10775 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, 10776 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, 10777 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, 10778 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, 10779 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, 10780 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, 10781 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, 10782 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, 10783 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 ]; 10784 10785 function Tree() { 10786 var that = this; 10787 10788 // dyn_tree; // the dynamic tree 10789 // max_code; // largest code with non zero frequency 10790 // stat_desc; // the corresponding static tree 10791 10792 // Compute the optimal bit lengths for a tree and update the total bit 10793 // length 10794 // for the current block. 10795 // IN assertion: the fields freq and dad are set, heap[heap_max] and 10796 // above are the tree nodes sorted by increasing frequency. 10797 // OUT assertions: the field len is set to the optimal bit length, the 10798 // array bl_count contains the frequencies for each bit length. 10799 // The length opt_len is updated; static_len is also updated if stree is 10800 // not null. 10801 function gen_bitlen(s) { 10802 var tree = that.dyn_tree; 10803 var stree = that.stat_desc.static_tree; 10804 var extra = that.stat_desc.extra_bits; 10805 var base = that.stat_desc.extra_base; 10806 var max_length = that.stat_desc.max_length; 10807 var h; // heap index 10808 var n, m; // iterate over the tree elements 10809 var bits; // bit length 10810 var xbits; // extra bits 10811 var f; // frequency 10812 var overflow = 0; // number of elements with bit length too large 10813 10814 for (bits = 0; bits <= MAX_BITS; bits++) 10815 s.bl_count[bits] = 0; 10816 10817 // In a first pass, compute the optimal bit lengths (which may 10818 // overflow in the case of the bit length tree). 10819 tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap 10820 10821 for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { 10822 n = s.heap[h]; 10823 bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; 10824 if (bits > max_length) { 10825 bits = max_length; 10826 overflow++; 10827 } 10828 tree[n * 2 + 1] = bits;
10829 // We overwrite tree[n*2+1] which is no longer needed 10830 10831 if (n > that.max_code) 10832 continue; // not a leaf node 10833 10834 s.bl_count[bits]++; 10835 xbits = 0; 10836 if (n >= base) 10837 xbits = extra[n - base]; 10838 f = tree[n * 2]; 10839 s.opt_len += f * (bits + xbits); 10840 if (stree) 10841 s.static_len += f * (stree[n * 2 + 1] + xbits); 10842 } 10843 if (overflow === 0) 10844 return; 10845 10846 // This happens for example on obj2 and pic of the Calgary corpus 10847 // Find the first bit length which could increase: 10848 do { 10849 bits = max_length - 1; 10850 while (s.bl_count[bits] === 0) 10851 bits--; 10852 s.bl_count[bits]--; // move one leaf down the tree 10853 s.bl_count[bits + 1] += 2; // move one overflow item as its brother 10854 s.bl_count[max_length]--; 10855 // The brother of the overflow item also moves one step up, 10856 // but this does not affect bl_count[max_length] 10857 overflow -= 2; 10858 } while (overflow > 0); 10859 10860 for (bits = max_length; bits !== 0; bits--) { 10861 n = s.bl_count[bits]; 10862 while (n !== 0) { 10863 m = s.heap[--h]; 10864 if (m > that.max_code) 10865 continue; 10866 if (tree[m * 2 + 1] != bits) { 10867 s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2]; 10868 tree[m * 2 + 1] = bits; 10869 } 10870 n--; 10871 } 10872 } 10873 } 10874 10875 // Reverse the first len bits of a code, using straightforward code (a 10876 // faster 10877 // method would use a table) 10878 // IN assertion: 1 <= len <= 15 10879 function bi_reverse(code, // the value to invert 10880 len // its bit length 10881 ) { 10882 var res = 0; 10883 do { 10884 res |= code & 1; 10885 code >>>= 1; 10886 res <<= 1; 10887 } while (--len > 0); 10888 return res >>> 1; 10889 } 10890 10891 // Generate the codes for a given tree and bit counts (which need not be 10892 // optimal). 10893 // IN assertion: the array bl_count contains the bit length statistics for 10894 // the given tree and the field len is set for all tree elements. 10895 // OUT assertion: the field code is set for all tree elements of non 10896 // zero code length. 10897 function gen_codes(tree, // the tree to decorate 10898 max_code, // largest code with non zero frequency 10899 bl_count // number of codes at each bit length 10900 ) { 10901 var next_code = []; // next code value for each 10902 // bit length 10903 var code = 0; // running code value 10904 var bits; // bit index 10905 var n; // code index 10906 var len; 10907 10908 // The distribution counts are first used to generate the code values 10909 // without bit reversal. 10910 for (bits = 1; bits <= MAX_BITS; bits++) { 10911 next_code[bits] = code = ((code + bl_count[bits - 1]) << 1); 10912 } 10913 10914 // Check that the bit counts in bl_count are consistent. The last code 10915 // must be all ones. 10916 // Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, 10917 // "inconsistent bit counts"); 10918 // Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); 10919 10920 for (n = 0; n <= max_code; n++) { 10921 len = tree[n * 2 + 1]; 10922 if (len === 0) 10923 continue; 10924 // Now reverse the bits 10925 tree[n * 2] = bi_reverse(next_code[len]++, len); 10926 } 10927 } 10928 10929 // Construct one Huffman tree and assigns the code bit strings and lengths. 10930 // Update the total bit length for the current block. 10931 // IN assertion: the field freq is set for all tree elements. 10932 // OUT assertions: the fields len and code are set to the optimal bit length 10933 // and corresponding code. The length opt_len is updated; static_len is 10934 // also updated if stree is not null. The field max_code is set. 10935 that.build_tree = function(s) { 10936 var tree = that.dyn_tree; 10937 var stree = that.stat_desc.static_tree; 10938 var elems = that.stat_desc.elems; 10939 var n, m; // iterate over heap elements 10940 var max_code = -1; // largest code with non zero frequency 10941 var node; // new node being created 10942 10943 // Construct the initial heap, with least frequent element in 10944 // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. 10945 // heap[0] is not used. 10946 s.heap_len = 0; 10947 s.heap_max = HEAP_SIZE; 10948 10949 for (n = 0; n < elems; n++) { 10950 if (tree[n * 2] !== 0) { 10951 s.heap[++s.heap_len] = max_code = n; 10952 s.depth[n] = 0; 10953 } else { 10954 tree[n * 2 + 1] = 0; 10955 } 10956 } 10957 10958 // The pkzip format requires that at least one distance code exists, 10959 // and that at least one bit should be sent even if there is only one 10960 // possible code. So to avoid special checks later on we force at least 10961 // two codes of non zero frequency. 10962 while (s.heap_len < 2) { 10963 node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0; 10964 tree[node * 2] = 1; 10965 s.depth[node] = 0; 10966 s.opt_len--; 10967 if (stree) 10968 s.static_len -= stree[node * 2 + 1]; 10969 // node is 0 or 1 so it does not have extra bits 10970 } 10971 that.max_code = max_code; 10972 10973 // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, 10974 // establish sub-heaps of increasing lengths: 10975 10976 for (n = Math.floor(s.heap_len / 2); n >= 1; n--) 10977 s.pqdownheap(tree, n); 10978 10979 // Construct the Huffman tree by repeatedly combining the least two 10980 // frequent nodes. 10981 10982 node = elems; // next internal node of the tree 10983 do { 10984 // n = node of least frequency 10985 n = s.heap[1]; 10986 s.heap[1] = s.heap[s.heap_len--]; 10987 s.pqdownheap(tree, 1); 10988 m = s.heap[1]; // m = node of next least frequency 10989 10990 s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency 10991 s.heap[--s.heap_max] = m; 10992 10993 // Create a new node father of n and m 10994 tree[node * 2] = (tree[n * 2] + tree[m * 2]); 10995 s.depth[node] = Math.max(s.depth[n], s.depth[m]) + 1; 10996 tree[n * 2 + 1] = tree[m * 2 + 1] = node; 10997 10998 // and insert the new node in the heap 10999 s.heap[1] = node++; 11000 s.pqdownheap(tree, 1); 11001 } while (s.heap_len >= 2); 11002 11003 s.heap[--s.heap_max] = s.heap[1]; 11004 11005 // At this point, the fields freq and dad are set. We can now 11006 // generate the bit lengths. 11007 11008 gen_bitlen(s); 11009 11010 // The field len is now set, we can generate the bit codes 11011 gen_codes(tree, that.max_code, s.bl_count); 11012 }; 11013 11014 } 11015 11016 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, 11017 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, 11018 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, 11019 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, 11020 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, 11021 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, 11022 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 ]; 11023 11024 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 ]; 11025 11026 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, 11027 24576 ]; 11028 11029 // Mapping from a distance to a distance code. dist is the distance - 1 and 11030 // must not have side effects. _dist_code[256] and _dist_code[257] are never 11031 // used. 11032 Tree.d_code = function(dist) { 11033 return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >>> 7)]); 11034 }; 11035 11036 // extra bits for each length code 11037 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 ]; 11038 11039 // extra bits for each distance code 11040 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 ]; 11041 11042 // extra bits for each bit length code 11043 Tree.extra_blbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 ]; 11044 11045 Tree.bl_order = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; 11046 11047 // StaticTree 11048 11049 function StaticTree(static_tree, extra_bits, extra_base, elems, max_length) { 11050 var that = this; 11051 that.static_tree = static_tree; 11052 that.extra_bits = extra_bits; 11053 that.extra_base = extra_base; 11054 that.elems = elems; 11055 that.max_length = max_length; 11056 } 11057 11058 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, 11059 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, 11060 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, 11061 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, 11062 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, 11063 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, 11064 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, 11065 173, 8, 109, 8, 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, 27
110655, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9, 11066 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, 11067 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, 11068 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, 11069 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, 11070 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, 11071 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, 11072 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, 11073 99, 8, 227, 8 ]; 11074 11075 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, 11076 25, 5, 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 ]; 11077 11078 StaticTree.static_l_desc = new StaticTree(StaticTree.static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); 11079 11080 StaticTree.static_d_desc = new StaticTree(StaticTree.static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS); 11081 11082 StaticTree.static_bl_desc = new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS); 11083 11084 // Deflate 11085 11086 var MAX_MEM_LEVEL = 9; 11087 var DEF_MEM_LEVEL = 8; 11088 11089 function Config(good_length, max_lazy, nice_length, max_chain, func) { 11090 var that = this; 11091 that.good_length = good_length; 11092 that.max_lazy = max_lazy; 11093 that.nice_length = nice_length; 11094 that.max_chain = max_chain; 11095 that.func = func; 11096 } 11097 11098 var STORED = 0; 11099 var FAST = 1; 11100 var SLOW = 2; 11101 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), 11102 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), 11103 new Config(32, 128, 258, 1024, SLOW), new Config(32, 258, 258, 4096, SLOW) ]; 11104 11105 var z_errmsg = [ "need dictionary", // Z_NEED_DICT 11106 // 2 11107 "stream end", // Z_STREAM_END 1 11108 "", // Z_OK 0 11109 "", // Z_ERRNO (-1) 11110 "stream error", // Z_STREAM_ERROR (-2) 11111 "data error", // Z_DATA_ERROR (-3) 11112 "", // Z_MEM_ERROR (-4) 11113 "buffer error", // Z_BUF_ERROR (-5) 11114 "",// Z_VERSION_ERROR (-6) 11115 "" ]; 11116 11117 // block not completed, need more input or more output 11118 var NeedMore = 0; 11119 11120 // block flush performed 11121 var BlockDone = 1; 11122 11123 // finish started, need only more output at next deflate 11124 var FinishStarted = 2; 11125 11126 // finish done, accept no more input or output 11127 var FinishDone = 3; 11128 11129 // preset dictionary flag in zlib header 11130 var PRESET_DICT = 0x20; 11131 11132 var INIT_STATE = 42; 11133 var BUSY_STATE = 113; 11134 var FINISH_STATE = 666; 11135 11136 // The deflate compression method 11137 var Z_DEFLATED = 8; 11138 11139 var STORED_BLOCK = 0; 11140 var STATIC_TREES = 1; 11141 var DYN_TREES = 2; 11142 11143 var MIN_MATCH = 3; 11144 var MAX_MATCH = 258; 11145 var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); 11146 11147 function smaller(tree, n, m, depth) { 11148 var tn2 = tree[n * 2]; 11149 var tm2 = tree[m * 2]; 11150 return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); 11151 } 11152 11153 function Deflate() { 11154 11155 var that = this; 11156 var strm; // pointer back to this zlib stream 11157 var status; // as the name implies 11158 // pending_buf; // output still pending 11159 var pending_buf_size; // size of pending_buf 11160 // pending_out; // next pending byte to output to the stream 11161 // pending; // nb of bytes in the pending buffer 11162 var method; // STORED (for zip only) or DEFLATED 11163 var last_flush; // value of flush param for previous deflate call 11164 11165 var w_size; // LZ77 window size (32K by default) 11166 var w_bits; // log2(w_size) (8..16) 11167 var w_mask; // w_size - 1 11168 11169 var window; 11170 // Sliding window. Input bytes are read into the second half of the window, 11171 // and move to the first half later to keep a dictionary of at least wSize 11172 // bytes. With this organization, matches are limited to a distance of 11173 // wSize-MAX_MATCH bytes, but this ensures that IO is always 11174 // performed with a length multiple of the block size. Also, it limits 11175 // the window size to 64K, which is quite useful on MSDOS. 11176 // To do: use the user input buffer as sliding window. 11177 11178 var window_size; 11179 // Actual size of window: 2*wSize, except when the user input buffer 11180 // is directly used as sliding window. 11181 11182 var prev; 11183 // Link to older string with same hash index. To limit the size of this 11184 // array to 64K, this link is maintained only for the last 32K strings. 11185 // An index in this array is thus a window index modulo 32K. 11186 11187 var head; // Heads of the hash chains or NIL. 11188 11189 var ins_h;
11189 // hash index of string to be inserted 11190 var hash_size; // number of elements in hash table 11191 var hash_bits; // log2(hash_size) 11192 var hash_mask; // hash_size-1 11193 11194 // Number of bits by which ins_h must be shifted at each input 11195 // step. It must be such that after MIN_MATCH steps, the oldest 11196 // byte no longer takes part in the hash key, that is: 11197 // hash_shift * MIN_MATCH >= hash_bits 11198 var hash_shift; 11199 11200 // Window position at the beginning of the current output block. Gets 11201 // negative when the window is moved backwards. 11202 11203 var block_start; 11204 11205 var match_length; // length of best match 11206 var prev_match; // previous match 11207 var match_available; // set if previous match exists 11208 var strstart; // start of string to insert 11209 var match_start; // start of matching string 11210 var lookahead; // number of valid bytes ahead in window 11211 11212 // Length of the best match at previous step. Matches not greater than this 11213 // are discarded. This is used in the lazy match evaluation. 11214 var prev_length; 11215 11216 // To speed up deflation, hash chains are never searched beyond this 11217 // length. A higher limit improves compression ratio but degrades the speed. 11218 var max_chain_length; 11219 11220 // Attempt to find a better match only when the current match is strictly 11221 // smaller than this value. This mechanism is used only for compression 11222 // levels >= 4. 11223 var max_lazy_match; 11224 11225 // Insert new strings in the hash table only if the match length is not 11226 // greater than this length. This saves time but degrades compression. 11227 // max_insert_length is used only for compression levels <= 3. 11228 11229 var level; // compression level (1..9) 11230 var strategy; // favor or force Huffman coding 11231 11232 // Use a faster search when the previous match is longer than this 11233 var good_match; 11234 11235 // Stop searching when current match exceeds this 11236 var nice_match; 11237 11238 var dyn_ltree; // literal and length tree 11239 var dyn_dtree; // distance tree 11240 var bl_tree; // Huffman tree for bit lengths 11241 11242 var l_desc = new Tree(); // desc for literal tree 11243 var d_desc = new Tree(); // desc for distance tree 11244 var bl_desc = new Tree(); // desc for bit length tree 11245 11246 // that.heap_len; // number of elements in the heap 11247 // that.heap_max; // element of largest frequency 11248 // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. 11249 // The same heap array is used to build all trees. 11250 11251 // Depth of each subtree used as tie breaker for trees of equal frequency 11252 that.depth = []; 11253 11254 var l_buf; // index for literals or lengths */ 11255 11256 // Size of match buffer for literals/lengths. There are 4 reasons for 11257 // limiting lit_bufsize to 64K: 11258 // - frequencies can be kept in 16 bit counters 11259 // - if compression is not successful for the first block, all input 11260 // data is still in the window so we can still emit a stored block even 11261 // when input comes from standard input. (This can also be done for 11262 // all blocks if lit_bufsize is not greater than 32K.) 11263 // - if compression is not successful for a file smaller than 64K, we can 11264 // even emit a stored file instead of a stored block (saving 5 bytes). 11265 // This is applicable only for zip (not gzip or zlib). 11266 // - creating new Huffman trees less frequently may not provide fast 11267 // adaptation to changes in the input data statistics. (Take for 11268 // example a binary file with poorly compressible code followed by 11269 // a highly compressible string table.) Smaller buffer sizes give 11270 // fast adaptation but have of course the overhead of transmitting 11271 // trees more frequently. 11272 // - I can't count above 4 11273 var lit_bufsize; 11274 11275 var last_lit; // running index in l_buf 11276 11277 // Buffer for distances. To simplify the code, d_buf and l_buf have 11278 // the same number of elements. To use different lengths, an extra flag 11279 // array would be necessary. 11280 11281 var d_buf; // index of pendig_buf 11282 11283 // that.opt_len; // bit length of current block with optimal trees 11284 // that.static_len; // bit length of current block with static trees 11285 var matches; // number of string matches in current block 11286 var last_eob_len; // bit length of EOB code for last block 11287 11288 // Output buffer. bits are inserted starting at the bottom (least 11289 // significant bits). 11290 var bi_buf; 11291 11292 // Number of valid bits in bi_buf. All bits above the last valid bit 11293 // are always zero. 11294 var bi_valid; 11295 11296 // number of codes at each bit length for an optimal tree 11297 that.bl_count = []; 11298 11299 // heap used to build the Huffman trees
11300 that.heap = []; 11301 11302 dyn_ltree = []; 11303 dyn_dtree = []; 11304 bl_tree = []; 11305 11306 function lm_init() { 11307 var i; 11308 window_size = 2 * w_size; 11309 11310 head[hash_size - 1] = 0; 11311 for (i = 0; i < hash_size - 1; i++) { 11312 head[i] = 0; 11313 } 11314 11315 // Set the default configuration parameters: 11316 max_lazy_match = config_table[level].max_lazy; 11317 good_match = config_table[level].good_length; 11318 nice_match = config_table[level].nice_length; 11319 max_chain_length = config_table[level].max_chain; 11320 11321 strstart = 0; 11322 block_start = 0; 11323 lookahead = 0; 11324 match_length = prev_length = MIN_MATCH - 1; 11325 match_available = 0; 11326 ins_h = 0; 11327 } 11328 11329 function init_block() { 11330 var i; 11331 // Initialize the trees. 11332 for (i = 0; i < L_CODES; i++) 11333 dyn_ltree[i * 2] = 0; 11334 for (i = 0; i < D_CODES; i++) 11335 dyn_dtree[i * 2] = 0; 11336 for (i = 0; i < BL_CODES; i++) 11337 bl_tree[i * 2] = 0; 11338 11339 dyn_ltree[END_BLOCK * 2] = 1; 11340 that.opt_len = that.static_len = 0; 11341 last_lit = matches = 0; 11342 } 11343 11344 // Initialize the tree data structures for a new zlib stream. 11345 function tr_init() { 11346 11347 l_desc.dyn_tree = dyn_ltree; 11348 l_desc.stat_desc = StaticTree.static_l_desc; 11349 11350 d_desc.dyn_tree = dyn_dtree; 11351 d_desc.stat_desc = StaticTree.static_d_desc; 11352 11353 bl_desc.dyn_tree = bl_tree; 11354 bl_desc.stat_desc = StaticTree.static_bl_desc; 11355 11356 bi_buf = 0; 11357 bi_valid = 0; 11358 last_eob_len = 8; // enough lookahead for inflate 11359 11360 // Initialize the first block of the first file: 11361 init_block(); 11362 } 11363 11364 // Restore the heap property by moving down the tree starting at node k, 11365 // exchanging a node with the smallest of its two sons if necessary, 11366 // stopping 11367 // when the heap property is re-established (each father smaller than its 11368 // two sons). 11369 that.pqdownheap = function(tree, // the tree to restore 11370 k // node to move down 11371 ) { 11372 var heap = that.heap; 11373 var v = heap[k]; 11374 var j = k << 1; // left son of k 11375 while (j <= that.heap_len) { 11376 // Set j to the smallest of the two sons: 11377 if (j < that.heap_len && smaller(tree, heap[j + 1], heap[j], that.depth)) { 11378 j++; 11379 } 11380 // Exit if v is smaller than both sons 11381 if (smaller(tree, v, heap[j], that.depth)) 11382 break; 11383 11384 // Exchange v with the smallest son 11385 heap[k] = heap[j]; 11386 k = j; 11387 // And continue down the tree, setting j to the left son of k 11388 j <<= 1; 11389 } 11390 heap[k] = v; 11391 }; 11392 11393 // Scan a literal or distance tree to determine the frequencies of the codes 11394 // in the bit length tree. 11395 function scan_tree(tree,// the tree to be scanned 11396 max_code // and its largest code of non zero frequency 11397 ) { 11398 var n; // iterates over all tree elements 11399 var prevlen = -1; // last emitted length 11400 var curlen; // length of current code 11401 var nextlen = tree[0 * 2 + 1]; // length of next code 11402 var count = 0; // repeat count of the current code 11403 var max_count = 7; // max repeat count 11404 var min_count = 4; // min repeat count 11405 11406 if (nextlen === 0) { 11407 max_count = 138; 11408 min_count = 3; 11409 } 11410 tree[(max_code + 1) * 2 + 1] = 0xffff; // guard 11411 11412 for (n = 0; n <= max_code; n++) { 11413 curlen = nextlen; 11414 nextlen = tree[(n + 1) * 2 + 1]; 11415 if (++count < max_count && curlen == nextlen) { 11416 continue; 11417 } else if (count < min_count) { 11418 bl_tree[curlen * 2] += count; 11419 } else if (curlen !== 0) { 11420 if (curlen != prevlen) 11421 bl_tree[curlen * 2]++; 11422 bl_tree[REP_3_6 * 2]++; 11423 } else if (count <= 10) { 11424 bl_tree[REPZ_3_10 * 2]++; 11425 } else { 11426 bl_tree[REPZ_11_138 * 2]++; 11427 } 11428 count = 0; 11429 prevlen = curlen; 11430 if (nextlen === 0) { 11431 max_count = 138; 11432 min_count = 3; 11433 } else if (curlen == nextlen) { 11434 max_count = 6; 11435 min_count = 3; 11436 } else { 11437 max_count = 7; 11438 min_count = 4; 11439 } 11440 } 11441 } 11442 11443 // Construct the Huffman tree for the bit lengths and return the index in 11444 // bl_order of the last bit length code to send. 11445 function build_bl_tree() { 11446 var max_blindex; // index of last bit length code of non zero freq 11447 11448 // Determine the bit length frequencies for literal and distance trees 11449 scan_tree(dyn_ltree, l_desc.max_code); 11450 scan_tree(dyn_dtree, d_desc.max_code); 11451 11452 // Build the bit length tree: 11453 bl_desc.build_tree(that); 11454 // opt_len now includes the length of the tree representations, except 11455 // the lengths of the bit lengths codes and the 5+5+4 bits for the 11456 // counts. 11457 11458 // Determine the number of bit length codes to send. The pkzip format 11459 // requires that at least 4 bit length codes be sent. (appnote.txt says 11460 // 3 but the actual value used is 4.) 11461 for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { 11462 if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] !== 0) 11463 break; 11464 } 11465 // Update opt_len to include the bit length tree and counts 11466 that.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; 11467 11468 return max_blindex; 11469 } 11470 11471 // Output a byte on the stream. 11472 // IN assertion: there is enough room in pending_buf. 11473 function put_byte(p) { 11474 that.pending_buf[that.pending++] = p; 11475 } 11476 11477 function put_short(w) { 11478 put_byte(w & 0xff); 11479 put_byte((w >>> 8) & 0xff); 11480 } 11481 11482 function putShortMSB(b) { 11483 put_byte((b >> 8) & 0xff); 11484 put_byte((b & 0xff) & 0xff); 11485 } 11486 11487 function send_bits(value, length) { 11488 var val, len = length; 11489 if (bi_valid > Buf_size - len) { 11490 val = value; 11491 // bi_buf |= (val << bi_valid); 11492 bi_buf |= ((val << bi_valid) & 0xffff); 11493 put_short(bi_buf); 11494 bi_buf = val >>> (Buf_size - bi_valid); 11495 bi_valid += len - Buf_size; 11496 } else { 11497 // bi_buf |= (value) << bi_valid; 11498 bi_buf |= (((value) << bi_valid) & 0xffff); 11499 bi_valid += len; 11500 } 11501 } 11502 11503 function send_code(c, tree) { 11504 var c2 = c * 2; 11505 send_bits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff); 11506 } 11507 11508 // Send a literal or distance tree in compressed form, using the codes in 11509 // bl_tree. 11510 function send_tree(tree,// the tree to be sent 11511 max_code // and its largest code of non zero frequency 11512 ) { 11513 var n; // iterates over all tree elements 11514 var prevlen = -1; // last emitted length 11515 var curlen; // length of current code 11516 var nextlen = tree[0 * 2 + 1]; // length of next code 11517 var count = 0; // repeat count of the current code 11518 var max_count = 7; // max repeat count 11519 var min_count = 4; // min repeat count 11520 11521 if (nextlen === 0) { 11522 max_count = 138; 11523 min_count = 3; 11524 } 11525 11526 for (n = 0; n <= max_code; n++) { 11527 curlen = nextlen; 11528 nextlen = tree[(n + 1) * 2 + 1]; 11529 if (++count < max_count && curlen == nextlen) { 11530 continue; 11531 } else if (count < min_count) { 11532 do { 11533 send_code(curlen, bl_tree); 11534 } while (--count !== 0); 11535 } else if (curlen !== 0) { 11536 if (curlen != prevlen) { 11537 send_code(curlen, bl_tree); 11538 count--; 11539 } 11540 send_code(REP_3_6, bl_tree); 11541 send_bits(count - 3, 2); 11542 } else if (count <= 10) { 11543 send_code(REPZ_3_10, bl_tree); 11544 send_bits(count - 3, 3); 11545 } else { 11546 send_code(REPZ_11_138, bl_tree); 11547 send_bits(count - 11, 7); 11548 } 11549 count = 0; 11550 prevlen = curlen; 11551 if (nextlen === 0) { 11552 max_count = 138; 11553 min_count = 3; 11554 } else if (curlen == nextlen) { 11555 max_count = 6; 11556 min_count = 3; 11557 } else { 11558 max_count = 7; 11559 min_count = 4; 11560 } 11561 } 11562 } 11563 11564 // Send the header for a block using dynamic Huffman trees: the counts, the 11565 // lengths of the bit length codes, the literal tree and the distance tree. 11566 // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. 11567 function send_all_trees(lcodes, dcodes, blcodes) { 11568 var rank; // index in bl_order 11569 11570 send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt 11571 send_bits(dcodes - 1, 5); 11572 send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt 11573 for (rank = 0; rank < blcodes; rank++) { 11574 send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); 11575 } 11576 send_tree(dyn_ltree, lcodes - 1); // literal tree 11577 send_tree(dyn_dtree, dcodes - 1); // distance tree 11578 } 11579 11580 // Flush the bit buffer, keeping at most 7 bits in it. 11581 function bi_flush() { 11582 if (bi_valid == 16) { 11583 put_short(bi_buf); 11584 bi_buf = 0; 11585 bi_valid = 0; 11586 } else if (bi_valid >= 8) { 11587 put_byte(bi_buf & 0xff); 11588 bi_buf >>>= 8; 11589 bi_valid -= 8; 11590 } 11591 } 11592 11593 // Send one empty static block to give enough lookahead for inflate. 11594 // This takes 10 bits, of which 7 may remain in the bit buffer. 11595 // The current inflate code requires 9 bits of lookahead. If the 11596 // last two codes for the previous block (real code plus EOB) were coded 11597 // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode 11598 // the last real code. In this case we send two empty static blocks instead
11599 // of one. (There are no problems if the previous block is stored or fixed.) 11600 // To simplify the code, we assume the worst case of last real code encoded 11601 // on one bit only. 11602 function _tr_align() { 11603 send_bits(STATIC_TREES << 1, 3); 11604 send_code(END_BLOCK, StaticTree.static_ltree); 11605 11606 bi_flush(); 11607 11608 // Of the 10 bits for the empty block, we have already sent 11609 // (10 - bi_valid) bits. The lookahead for the last real code (before 11610 // the EOB of the previous block) was thus at least one plus the length 11611 // of the EOB plus what we have just sent of the empty static block. 11612 if (1 + last_eob_len + 10 - bi_valid < 9) { 11613 send_bits(STATIC_TREES << 1, 3); 11614 send_code(END_BLOCK, StaticTree.static_ltree); 11615 bi_flush(); 11616 } 11617 last_eob_len = 7; 11618 } 11619 11620 // Save the match info and tally the frequency counts. Return true if 11621 // the current block must be flushed. 11622 function _tr_tally(dist, // distance of matched string 11623 lc // match length-MIN_MATCH or unmatched char (if dist==0) 11624 ) { 11625 var out_length, in_length, dcode; 11626 that.pending_buf[d_buf + last_lit * 2] = (dist >>> 8) & 0xff;
11627 that.pending_buf[d_buf + last_lit * 2 + 1] = dist & 0xff; 11628 11629 that.pending_buf[l_buf + last_lit] = lc & 0xff; 11630 last_lit++; 11631 11632 if (dist === 0) { 11633 // lc is the unmatched char 11634 dyn_ltree[lc * 2]++; 11635 } else { 11636 matches++; 11637 // Here, lc is the match length - MIN_MATCH 11638 dist--; // dist = match distance - 1 11639 dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++; 11640 dyn_dtree[Tree.d_code(dist) * 2]++; 11641 } 11642 11643 if ((last_lit & 0x1fff) === 0 && level > 2) { 11644 // Compute an upper bound for the compressed length 11645 out_length = last_lit * 8; 11646 in_length = strstart - block_start; 11647 for (dcode = 0; dcode < D_CODES; dcode++) { 11648 out_length += dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]); 11649 } 11650 out_length >>>= 3; 11651 if ((matches < Math.floor(last_lit / 2)) && out_length < Math.floor(in_length / 2)) 11652 return true; 11653 } 11654 11655 return (last_lit == lit_bufsize - 1); 11656 // We avoid equality with lit_bufsize because of wraparound at 64K 11657 // on 16 bit machines and because stored blocks are restricted to 11658 // 64K-1 bytes. 11659 } 11660 11661 // Send the block data compressed using the given Huffman trees 11662 function compress_block(ltree, dtree) { 11663 var dist; // distance of matched string 11664 var lc; // match length or unmatched char (if dist === 0) 11665 var lx = 0; // running index in l_buf 11666 var code; // the code to send 11667 var extra; // number of extra bits to send 11668 11669 if (last_lit !== 0) { 11670 do { 11671 dist = ((that.pending_buf[d_buf + lx * 2] << 8) & 0xff00) | (that.pending_buf[d_buf + lx * 2 + 1] & 0xff); 11672 lc = (that.pending_buf[l_buf + lx]) & 0xff; 11673 lx++; 11674 11675 if (dist === 0) { 11676 send_code(lc, ltree); // send a literal byte 11677 } else { 11678 // Here, lc is the match length - MIN_MATCH 11679 code = Tree._length_code[lc]; 11680 11681 send_code(code + LITERALS + 1, ltree); // send the length 11682 // code 11683 extra = Tree.extra_lbits[code]; 11684 if (extra !== 0) { 11685 lc -= Tree.base_length[code]; 11686 send_bits(lc, extra); // send the extra length bits 11687 } 11688 dist--; // dist is now the match distance - 1 11689 code = Tree.d_code(dist); 11690 11691 send_code(code, dtree); // send the distance code 11692 extra = Tree.extra_dbits[code]; 11693 if (extra !== 0) { 11694 dist -= Tree.base_dist[code]; 11695 send_bits(dist, extra); // send the extra distance bits 11696 } 11697 } // literal or match pair ? 11698 11699 // Check that the overlay between pending_buf and d_buf+l_buf is 11700 // ok: 11701 } while (lx < last_lit); 11702 } 11703 11704 send_code(END_BLOCK, ltree); 11705 last_eob_len = ltree[END_BLOCK * 2 + 1]; 11706 } 11707 11708 // Flush the bit buffer and align the output on a byte boundary 11709 function bi_windup() { 11710 if (bi_valid > 8) { 11711 put_short(bi_buf); 11712 } else if (bi_valid > 0) { 11713 put_byte(bi_buf & 0xff); 11714 } 11715 bi_buf = 0; 11716 bi_valid = 0; 11717 } 11718 11719 // Copy a stored block, storing first the length and its 11720 // one's complement if requested. 11721 function copy_block(buf, // the input data 11722 len, // its length 11723 header // true if block header must be written 11724 ) { 11725 bi_windup(); // align on byte boundary 11726 last_eob_len = 8; // enough lookahead for inflate 11727 11728 if (header) { 11729 put_short(len); 11730 put_short(~len); 11731 } 11732 11733 that.pending_buf.set(window.subarray(buf, buf + len), that.pending); 11734 that.pending += len; 11735 } 11736 11737 // Send a stored block 11738 function _tr_stored_block(buf, // input block 11739 stored_len, // length of input block 11740 eof // true if this is the last block for a file 11741 ) { 11742 send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type 11743 copy_block(buf, stored_len, true); // with header 11744 } 11745 11746 // Determine the best encoding for the current block: dynamic trees, static 11747 // trees or store, and output the encoded block to the zip file. 11748 function _tr_flush_block(buf, // input block, or NULL if too old 11749 stored_len, // length of input block 11750 eof // true if this is the last block for a file 11751 ) { 11752 var opt_lenb, static_lenb;// opt_len and static_len in bytes 11753 var max_blindex = 0; // index of last bit length code of non zero freq 11754 11755 // Build the Huffman trees unless a stored block is forced 11756 if (level > 0) { 11757 // Construct the literal and distance trees 11758 l_desc.build_tree(that); 11759 11760 d_desc.build_tree(that); 11761 11762 // At this point, opt_len and static_len are the total bit lengths 11763 // of 11764 // the compressed block data, excluding the tree representations. 11765 11766 // Build the bit length tree for the above two trees, and get the 11767 // index 11768 // in bl_order of the last bit length code to send. 11769 max_blindex = build_bl_tree(); 11770 11771 // Determine the best encoding. Compute first the block length in 11772 // bytes 11773 opt_lenb = (that.opt_len + 3 + 7) >>> 3; 11774 static_lenb = (that.static_len + 3 + 7) >>> 3; 11775 11776 if (static_lenb <= opt_lenb) 11777 opt_lenb = static_lenb; 11778 } else { 11779 opt_lenb = static_lenb = stored_len + 5; // force a stored block 11780 } 11781
11782 if ((stored_len + 4 <= opt_lenb) && buf != -1) { 11783 // 4: two words for the lengths 11784 // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. 11785 // Otherwise we can't have processed more than WSIZE input bytes 11786 // since 11787 // the last block flush, because compression would have been 11788 // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to 11789 // transform a block into a stored block. 11790 _tr_stored_block(buf, stored_len, eof); 11791 } else if (static_lenb == opt_lenb) { 11792 send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); 11793 compress_block(StaticTree.static_ltree, StaticTree.static_dtree); 11794 } else { 11795 send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); 11796 send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1); 11797 compress_block(dyn_ltree, dyn_dtree); 11798 } 11799 11800 // The above check is made mod 2^32, for files larger than 512 MB 11801 // and uLong implemented on 32 bits. 11802 11803 init_block(); 11804 11805 if (eof) { 11806 bi_windup(); 11807 } 11808 } 11809 11810 function flush_block_only(eof) { 11811 _tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof); 11812 block_start = strstart; 11813 strm.flush_pending(); 11814 } 11815 11816 // Fill the window when the lookahead becomes insufficient. 11817 // Updates strstart and lookahead. 11818 // 11819 // IN assertion: lookahead < MIN_LOOKAHEAD 11820 // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD 11821 // At least one byte has been read, or avail_in === 0; reads are 11822 // performed for at least two bytes (required for the zip translate_eol 11823 // option -- not supported here). 11824 function fill_window() { 11825 var n, m; 11826 var p; 11827 var more; // Amount of free space at the end of the window. 11828 11829 do { 11830 more = (window_size - lookahead - strstart); 11831 11832 // Deal with !@#$% 64K limit: 11833 if (more === 0 && strstart === 0 && lookahead === 0) { 11834 more = w_size; 11835 } else if (more == -1) { 11836 // Very unlikely, but possible on 16 bit machine if strstart == 11837 // 0 11838 // and lookahead == 1 (input done one byte at time) 11839 more--; 11840 11841 // If the window is almost full and there is insufficient 11842 // lookahead, 11843 // move the upper half to the lower one to make room in the 11844 // upper half. 11845 } else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) { 11846 window.set(window.subarray(w_size, w_size + w_size), 0); 11847 11848 match_start -= w_size; 11849 strstart -= w_size; // we now have strstart >= MAX_DIST 11850 block_start -= w_size; 11851 11852 // Slide the hash table (could be avoided with 32 bit values 11853 // at the expense of memory usage). We slide even when level == 11854 // 0 11855 // to keep the hash table consistent if we switch back to level 11856 // > 0 11857 // later. (Using level 0 permanently is not an optimal usage of 11858 // zlib, so we don't care about this pathological case.) 11859 11860 n = hash_size; 11861 p = n; 11862 do { 11863 m = (head[--p] & 0xffff); 11864 head[p] = (m >= w_size ? m - w_size : 0); 11865 } while (--n !== 0); 11866 11867 n = w_size; 11868 p = n; 11869 do { 11870 m = (prev[--p] & 0xffff); 11871 prev[p] = (m >= w_size ? m - w_size : 0); 11872 // If n is not on any hash chain, prev[n] is garbage but 11873 // its value will never be used. 11874 } while (--n !== 0); 11875 more += w_size; 11876 } 11877 11878 if (strm.avail_in === 0) 11879 return; 11880 11881 // If there was no sliding: 11882 // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && 11883 // more == window_size - lookahead - strstart 11884 // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) 11885 // => more >= window_size - 2*WSIZE + 2 11886 // In the BIG_MEM or MMAP case (not yet supported), 11887 // window_size == input_size + MIN_LOOKAHEAD && 11888 // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. 11889 // Otherwise, window_size == 2*WSIZE so more >= 2. 11890 // If there was sliding, more >= WSIZE. So in all cases, more >= 2. 11891 11892 n = strm.read_buf(window, strstart + lookahead, more); 11893 lookahead += n; 11894 11895 // Initialize the hash value now that we have some input: 11896 if (lookahead >= MIN_MATCH) { 11897 ins_h = window[strstart] & 0xff; 11898 ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; 11899 } 11900 // If the whole input has less than MIN_MATCH bytes, ins_h is 11901 // garbage, 11902 // but this is not important since only literal bytes will be 11903 // emitted. 11904 } while (lookahead < MIN_LOOKAHEAD && strm.avail_in !== 0); 11905 } 11906 11907 // Copy without compression as much as possible from the input stream, 11908 // return 11909 // the current block state. 11910 // This function does not insert new strings in the dictionary since 11911 // uncompressible data is probably not useful. This function is used
11912 // only for the level=0 compression option. 11913 // NOTE: this function should be optimized to avoid extra copying from 11914 // window to pending_buf. 11915 function deflate_stored(flush) { 11916 // Stored blocks are limited to 0xffff bytes, pending_buf is limited 11917 // to pending_buf_size, and each stored block has a 5 byte header: 11918 11919 var max_block_size = 0xffff; 11920 var max_start; 11921 11922 if (max_block_size > pending_buf_size - 5) { 11923 max_block_size = pending_buf_size - 5; 11924 } 11925 11926 // Copy as much as possible from input to output: 11927 while (true) { 11928 // Fill the window as much as possible: 11929 if (lookahead <= 1) { 11930 fill_window(); 11931 if (lookahead === 0 && flush == Z_NO_FLUSH) 11932 return NeedMore; 11933 if (lookahead === 0) 11934 break; // flush the current block 11935 } 11936 11937 strstart += lookahead; 11938 lookahead = 0; 11939 11940 // Emit a stored block if pending_buf will be full: 11941 max_start = block_start + max_block_size; 11942 if (strstart === 0 || strstart >= max_start) { 11943 // strstart === 0 is possible when wraparound on 16-bit machine 11944 lookahead = (strstart - max_start); 11945 strstart = max_start; 11946 11947 flush_block_only(false); 11948 if (strm.avail_out === 0) 11949 return NeedMore; 11950 11951 } 11952 11953 // Flush if we may have to slide, otherwise block_start may become 11954 // negative and the data will be gone: 11955 if (strstart - block_start >= w_size - MIN_LOOKAHEAD) { 11956 flush_block_only(false); 11957 if (strm.avail_out === 0) 11958 return NeedMore; 11959 } 11960 } 11961 11962 flush_block_only(flush == Z_FINISH); 11963 if (strm.avail_out === 0) 11964 return (flush == Z_FINISH) ? FinishStarted : NeedMore; 11965 11966 return flush == Z_FINISH ? FinishDone : BlockDone; 11967 } 11968 11969 function longest_match(cur_match) { 11970 var chain_length = max_chain_length; // max hash chain length 11971 var scan = strstart; // current string 11972 var match; // matched string 11973 var len; // length of current match 11974 var best_len = prev_length; // best match length so far 11975 var limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; 11976 var _nice_match = nice_match; 11977 11978 // Stop when cur_match becomes <= limit. To simplify the code, 11979 // we prevent matches with the string of window index 0. 11980 11981 var wmask = w_mask; 11982 11983 var strend = strstart + MAX_MATCH; 11984 var scan_end1 = window[scan + best_len - 1]; 11985 var scan_end = window[scan + best_len]; 11986 11987 // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 11988 // 16. 11989 // It is easy to get rid of this optimization if necessary. 11990
11991 // Do not waste too much time if we already have a good match: 11992 if (prev_length >= good_match) { 11993 chain_length >>= 2; 11994 } 11995 11996 // Do not look for matches beyond the end of the input. This is 11997 // necessary 11998 // to make deflate deterministic. 11999 if (_nice_match > lookahead) 12000 _nice_match = lookahead; 12001 12002 do { 12003 match = cur_match; 12004 12005 // Skip to next match if the match length cannot increase 12006 // or if the match length is less than 2: 12007 if (window[match + best_len] != scan_end || window[match + best_len - 1] != scan_end1 || window[match] != window[scan] 12008 || window[++match] != window[scan + 1]) 12009 continue; 12010 12011 // The check at best_len-1 can be removed because it will be made 12012 // again later. (This heuristic is not always a win.) 12013 // It is not necessary to compare scan[2] and match[2] since they 12014 // are always equal when the other bytes match, given that 12015 // the hash keys are equal and that HASH_BITS >= 8. 12016 scan += 2; 12017 match++; 12018 12019 // We check for insufficient lookahead only every 8th comparison; 12020 // the 256th check will be made at strstart+258. 12021 do { 12022 } while (window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] 12023 && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] 12024 && window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend); 12025 12026 len = MAX_MATCH - (strend - scan); 12027 scan = strend - MAX_MATCH; 12028 12029 if (len > best_len) { 12030 match_start = cur_match; 12031 best_len = len; 12032 if (len >= _nice_match) 12033 break; 12034 scan_end1 = window[scan + best_len - 1]; 12035 scan_end = window[scan + best_len]; 12036 } 12037 12038 } while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length !== 0); 12039 12040 if (best_len <= lookahead) 12041 return best_len; 12042 return lookahead; 12043 } 12044 12045 // Compress as much as possible from the input stream, return the current 12046 // block state. 12047 // This function does not perform lazy evaluation of matches and inserts 12048 // new strings in the dictionary only for unmatched strings or for short 12049 // matches. It is used only for the fast compression options. 12050 function deflate_fast(flush) { 12051 // short hash_head = 0; // head of the hash chain 12052 var hash_head = 0; // head of the hash chain 12053 var bflush; // set if current block must be flushed 12054 12055 while (true) { 12056 // Make sure that we always have enough lookahead, except 12057 // at the end of the input file. We need MAX_MATCH bytes 12058 // for the next match, plus MIN_MATCH bytes to insert the 12059 // string following the next match. 12060 if (lookahead < MIN_LOOKAHEAD) { 12061 fill_window(); 12062 if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { 12063 return NeedMore; 12064 } 12065 if (lookahead === 0) 12066 break; // flush the current block 12067 } 12068 12069 // Insert the string window[strstart .. strstart+2] in the 12070 // dictionary, and set hash_head to the head of the hash chain: 12071 if (lookahead >= MIN_MATCH) { 12072 ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; 12073 12074 // prev[strstart&w_mask]=hash_head=head[ins_h]; 12075 hash_head = (head[ins_h] & 0xffff); 12076 prev[strstart & w_mask] = head[ins_h]; 12077 head[ins_h] = strstart; 12078 } 12079 12080 // Find the longest match, discarding those <= prev_length. 12081 // At this point we have always match_length < MIN_MATCH 12082 12083 if (hash_head !== 0 && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { 12084 // To simplify the code, we prevent matches with the string 12085 // of window index 0 (in particular we have to avoid a match 12086 // of the string with itself at the start of the input file). 12087 if (strategy != Z_HUFFMAN_ONLY) { 12088 match_length = longest_match(hash_head); 12089 } 12090 // longest_match() sets match_start 12091 } 12092 if (match_length >= MIN_MATCH) { 12093 // check_match(strstart, match_start, match_length); 12094 12095 bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); 12096 12097 lookahead -= match_length; 12098 12099 // Insert new strings in the hash table only if the match length 12100 // is not too large. This saves time but degrades compression. 12101 if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) { 12102 match_length--; // string at strstart already in hash table 12103 do { 12104 strstart++; 12105 12106 ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
12107 // prev[strstart&w_mask]=hash_head=head[ins_h]; 12108 hash_head = (head[ins_h] & 0xffff); 12109 prev[strstart & w_mask] = head[ins_h]; 12110 head[ins_h] = strstart; 12111 12112 // strstart never exceeds WSIZE-MAX_MATCH, so there are 12113 // always MIN_MATCH bytes ahead. 12114 } while (--match_length !== 0); 12115 strstart++; 12116 } else { 12117 strstart += match_length; 12118 match_length = 0; 12119 ins_h = window[strstart] & 0xff; 12120 12121 ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; 12122 // If lookahead < MIN_MATCH, ins_h is garbage, but it does 12123 // not 12124 // matter since it will be recomputed at next deflate call. 12125 } 12126 } else { 12127 // No match, output a literal byte 12128 12129 bflush = _tr_tally(0, window[strstart] & 0xff); 12130 lookahead--; 12131 strstart++; 12132 } 12133 if (bflush) { 12134 12135 flush_block_only(false); 12136 if (strm.avail_out === 0) 12137 return NeedMore; 12138 } 12139 } 12140 12141 flush_block_only(flush == Z_FINISH); 12142 if (strm.avail_out === 0) { 12143 if (flush == Z_FINISH) 12144 return FinishStarted; 12145 else 12146 return NeedMore; 12147 } 12148 return flush == Z_FINISH ? FinishDone : BlockDone; 12149 } 12150 12151 // Same as above, but achieves better compression. We use a lazy 12152 // evaluation for matches: a match is finally adopted only if there is 12153 // no better match at the next window position. 12154 function deflate_slow(flush) { 12155 // short hash_head = 0; // head of hash chain 12156 var hash_head = 0; // head of hash chain 12157 var bflush; // set if current block must be flushed 12158 var max_insert; 12159 12160 // Process the input block. 12161 while (true) { 12162 // Make sure that we always have enough lookahead, except 12163 // at the end of the input file. We need MAX_MATCH bytes 12164 // for the next match, plus MIN_MATCH bytes to insert the 12165 // string following the next match. 12166 12167 if (lookahead < MIN_LOOKAHEAD) { 12168 fill_window(); 12169 if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { 12170 return NeedMore; 12171 } 12172 if (lookahead === 0) 12173 break; // flush the current block 12174 } 12175 12176 // Insert the string window[strstart .. strstart+2] in the 12177 // dictionary, and set hash_head to the head of the hash chain: 12178 12179 if (lookahead >= MIN_MATCH) { 12180 ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; 12181 // prev[strstart&w_mask]=hash_head=head[ins_h]; 12182 hash_head = (head[ins_h] & 0xffff); 12183 prev[strstart & w_mask] = head[ins_h]; 12184 head[ins_h] = strstart; 12185 } 12186 12187 // Find the longest match, discarding those <= prev_length. 12188 prev_length = match_length; 12189 prev_match = match_start; 12190 match_length = MIN_MATCH - 1; 12191 12192 if (hash_head !== 0 && prev_length < max_lazy_match && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { 12193 // To simplify the code, we prevent matches with the string 12194 // of window index 0 (in particular we have to avoid a match 12195 // of the string with itself at the start of the input file). 12196 12197 if (strategy != Z_HUFFMAN_ONLY) { 12198 match_length = longest_match(hash_head); 12199 } 12200 // longest_match() sets match_start 12201 12202 if (match_length <= 5 && (strategy == Z_FILTERED || (match_length == MIN_MATCH && strstart - match_start > 4096))) { 12203 12204 // If prev_match is also MIN_MATCH, match_start is garbage 12205 // but we will ignore the current match anyway. 12206 match_length = MIN_MATCH - 1; 12207 } 12208 } 12209 12210 // If there was a match at the previous step and the current 12211 // match is not better, output the previous match: 12212 if (prev_length >= MIN_MATCH && match_length <= prev_length) { 12213 max_insert = strstart + lookahead - MIN_MATCH; 12214 // Do not insert strings in hash table beyond this. 12215 12216 // check_match(strstart-1, prev_match, prev_length); 12217 12218 bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); 12219 12220 // Insert in hash table all strings up to the end of the match. 12221 // strstart-1 and strstart are already inserted. If there is not 12222 // enough lookahead, the last two strings are not inserted in 12223 // the hash table. 12224 lookahead -= prev_length - 1; 12225 prev_length -= 2; 12226 do { 12227 if (++strstart <= max_insert) { 12228 ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
12229 // prev[strstart&w_mask]=hash_head=head[ins_h]; 12230 hash_head = (head[ins_h] & 0xffff); 12231 prev[strstart & w_mask] = head[ins_h]; 12232 head[ins_h] = strstart; 12233 } 12234 } while (--prev_length !== 0); 12235 match_available = 0; 12236 match_length = MIN_MATCH - 1; 12237 strstart++; 12238 12239 if (bflush) { 12240 flush_block_only(false); 12241 if (strm.avail_out === 0) 12242 return NeedMore; 12243 } 12244 } else if (match_available !== 0) { 12245 12246 // If there was no match at the previous position, output a 12247 // single literal. If there was a match but the current match 12248 // is longer, truncate the previous match to a single literal. 12249 12250 bflush = _tr_tally(0, window[strstart - 1] & 0xff); 12251 12252 if (bflush) { 12253 flush_block_only(false); 12254 } 12255 strstart++; 12256 lookahead--; 12257 if (strm.avail_out === 0) 12258 return NeedMore; 12259 } else { 12260 // There is no previous match to compare with, wait for 12261 // the next step to decide. 12262 12263 match_available = 1; 12264 strstart++; 12265 lookahead--; 12266 } 12267 } 12268 12269 if (match_available !== 0) { 12270 bflush = _tr_tally(0, window[strstart - 1] & 0xff); 12271 match_available = 0; 12272 } 12273 flush_block_only(flush == Z_FINISH); 12274 12275 if (strm.avail_out === 0) { 12276 if (flush == Z_FINISH) 12277 return FinishStarted; 12278 else 12279 return NeedMore; 12280 } 12281 12282 return flush == Z_FINISH ? FinishDone : BlockDone; 12283 } 12284 12285 function deflateReset(strm) { 12286 strm.total_in = strm.total_out = 0; 12287 strm.msg = null; // 12288 12289 that.pending = 0; 12290 that.pending_out = 0; 12291 12292 status = BUSY_STATE; 12293 12294 last_flush = Z_NO_FLUSH; 12295 12296 tr_init(); 12297 lm_init(); 12298 return Z_OK; 12299 } 12300 12301 that.deflateInit = function(strm, _level, bits, _method, memLevel, _strategy) { 12302 if (!_method) 12303 _method = Z_DEFLATED; 12304 if (!memLevel) 12305 memLevel = DEF_MEM_LEVEL; 12306 if (!_strategy) 12307 _strategy = Z_DEFAULT_STRATEGY; 12308 12309 // byte[] my_version=ZLIB_VERSION; 12310 12311 // 12312 // if (!version || version[0] != my_version[0] 12313 // || stream_size != sizeof(z_stream)) { 12314 // return Z_VERSION_ERROR; 12315 // } 12316 12317 strm.msg = null; 12318 12319 if (_level == Z_DEFAULT_COMPRESSION) 12320 _level = 6; 12321 12322 if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || _method != Z_DEFLATED || bits < 9 || bits > 15 || _level < 0 || _level > 9 || _strategy < 0 12323 || _strategy > Z_HUFFMAN_ONLY) { 12324 return Z_STREAM_ERROR; 12325 } 12326 12327 strm.dstate = that; 12328 12329 w_bits = bits; 12330 w_size = 1 << w_bits; 12331 w_mask = w_size - 1; 12332 12333 hash_bits = memLevel + 7; 12334 hash_size = 1 << hash_bits; 12335 hash_mask = hash_size - 1; 12336 hash_shift = Math.floor((hash_bits + MIN_MATCH - 1) / MIN_MATCH); 12337 12338 window = new Uint8Array(w_size * 2); 12339 prev = []; 12340 head = []; 12341 12342 lit_bufsize = 1 << (memLevel + 6); // 16K elements by default 12343 12344 // We overlay pending_buf and d_buf+l_buf. This works since the average 12345 // output size for (length,distance) codes is <= 24 bits. 12346 that.pending_buf = new Uint8Array(lit_bufsize * 4); 12347 pending_buf_size = lit_bufsize * 4; 12348 12349 d_buf = Math.floor(lit_bufsize / 2); 12350 l_buf = (1 + 2) * lit_bufsize; 12351 12352 level = _level; 12353 12354 strategy = _strategy; 12355 method = _method & 0xff; 12356 12357 return deflateReset(strm); 12358 }; 12359 12360 that.deflateEnd = function() { 12361 if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) { 12362 return Z_STREAM_ERROR; 12363 } 12364 // Deallocate in reverse order of allocations: 12365 that.pending_buf = null; 12366 head = null; 12367 prev = null; 12368 window = null; 12369 // free 12370 that.dstate = null; 12371 return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; 12372 }; 12373 12374 that.deflateParams = function(strm, _level, _strategy) { 12375 var err = Z_OK; 12376 12377 if (_level == Z_DEFAULT_COMPRESSION) { 12378 _level = 6; 12379 } 12380 if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { 12381 return Z_STREAM_ERROR; 12382 } 12383 12384 if (config_table[level].func != config_table[_level].func && strm.total_in !== 0) { 12385 // Flush the last buffer: 12386 err = strm.deflate(Z_PARTIAL_FLUSH); 12387 } 12388 12389 if (level != _level) { 12390 level = _level; 12391 max_lazy_match = config_table[level].max_lazy; 12392 good_match = config_table[level].good_length; 12393 nice_match = config_table[level].nice_length; 12394 max_chain_length = config_table[level].max_chain; 12395 } 12396 strategy = _strategy; 12397 return err; 12398 }; 12399 12400 that.deflateSetDictionary = function(strm, dictionary, dictLength) { 12401 var length = dictLength; 12402 var n, index = 0; 12403 12404 if (!dictionary || status != INIT_STATE) 12405 return Z_STREAM_ERROR; 12406 12407 if (length < MIN_MATCH) 12408 return Z_OK; 12409 if (length > w_size - MIN_LOOKAHEAD) { 12410 length = w_size - MIN_LOOKAHEAD; 12411 index = dictLength - length; // use the tail of the dictionary 12412 } 12413 window.set(dictionary.subarray(index, index + length), 0); 12414 12415 strstart = length; 12416 block_start = length; 12417 12418 // Insert all strings in the hash table (except for the last two bytes). 12419 // s->lookahead stays null, so s->ins_h will be recomputed at the next 12420 // call of fill_window. 12421 12422 ins_h = window[0] & 0xff;
12423 ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; 12424 12425 for (n = 0; n <= length - MIN_MATCH; n++) { 12426 ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; 12427 prev[n & w_mask] = head[ins_h]; 12428 head[ins_h] = n; 12429 } 12430 return Z_OK; 12431 }; 12432 12433 that.deflate = function(_strm, flush) { 12434 var i, header, level_flags, old_flush, bstate; 12435 12436 if (flush > Z_FINISH || flush < 0) { 12437 return Z_STREAM_ERROR; 12438 } 12439 12440 if (!_strm.next_out || (!_strm.next_in && _strm.avail_in !== 0) || (status == FINISH_STATE && flush != Z_FINISH)) { 12441 _strm.msg = z_errmsg[Z_NEED_DICT - (Z_STREAM_ERROR)]; 12442 return Z_STREAM_ERROR; 12443 } 12444 if (_strm.avail_out === 0) { 12445 _strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; 12446 return Z_BUF_ERROR; 12447 } 12448 12449 strm = _strm; // just in case 12450 old_flush = last_flush; 12451 last_flush = flush; 12452 12453 // Write the zlib header 12454 if (status == INIT_STATE) { 12455 header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; 12456 level_flags = ((level - 1) & 0xff) >> 1; 12457 12458 if (level_flags > 3) 12459 level_flags = 3; 12460 header |= (level_flags << 6); 12461 if (strstart !== 0) 12462 header |= PRESET_DICT; 12463 header += 31 - (header % 31); 12464 12465 status = BUSY_STATE; 12466 putShortMSB(header); 12467 } 12468 12469 // Flush as much pending output as possible 12470 if (that.pending !== 0) { 12471 strm.flush_pending(); 12472 if (strm.avail_out === 0) { 12473 // console.log(" avail_out==0"); 12474 // Since avail_out is 0, deflate will be called again with 12475 // more output space, but possibly with both pending and 12476 // avail_in equal to zero. There won't be anything to do, 12477 // but this is not an error situation so make sure we 12478 // return OK instead of BUF_ERROR at next call of deflate: 12479 last_flush = -1; 12480 return Z_OK; 12481 } 12482 12483 // Make sure there is something to do and avoid duplicate 12484 // consecutive 12485 // flushes. For repeated and useless calls with Z_FINISH, we keep 12486 // returning Z_STREAM_END instead of Z_BUFF_ERROR. 12487 } else if (strm.avail_in === 0 && flush <= old_flush && flush != Z_FINISH) { 12488 strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; 12489 return Z_BUF_ERROR; 12490 } 12491 12492 // User must not provide more input after the first FINISH: 12493 if (status == FINISH_STATE && strm.avail_in !== 0) { 12494 _strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; 12495 return Z_BUF_ERROR; 12496 } 12497 12498 // Start a new block or continue the current one. 12499 if (strm.avail_in !== 0 || lookahead !== 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) { 12500 bstate = -1; 12501 switch (config_table[level].func) { 12502 case STORED: 12503 bstate = deflate_stored(flush); 12504 break; 12505 case FAST: 12506 bstate = deflate_fast(flush); 12507 break; 12508 case SLOW: 12509 bstate = deflate_slow(flush); 12510 break; 12511 default: 12512 } 12513 12514 if (bstate == FinishStarted || bstate == FinishDone) { 12515 status = FINISH_STATE; 12516 } 12517 if (bstate == NeedMore || bstate == FinishStarted) { 12518 if (strm.avail_out === 0) { 12519 last_flush = -1; // avoid BUF_ERROR next call, see above 12520 } 12521 return Z_OK; 12522 // If flush != Z_NO_FLUSH && avail_out === 0, the next call 12523 // of deflate should use the same flush parameter to make sure 12524 // that the flush is complete. So we don't have to output an 12525 // empty block here, this will be done at next call. This also 12526 // ensures that for a very small output buffer, we emit at most 12527 // one empty block. 12528 } 12529 12530 if (bstate == BlockDone) { 12531 if (flush == Z_PARTIAL_FLUSH) { 12532 _tr_align(); 12533 } else { // FULL_FLUSH or SYNC_FLUSH 12534 _tr_stored_block(0, 0, false); 12535 // For a full flush, this empty block will be recognized 12536 // as a special marker by inflate_sync(). 12537 if (flush == Z_FULL_FLUSH) { 12538 // state.head[s.hash_size-1]=0; 12539 for (i = 0; i < hash_size/*-1*/; i++) 12540 // forget history 12541 head[i] = 0; 12542 } 12543 } 12544 strm.flush_pending(); 12545 if (strm.avail_out === 0) { 12546 last_flush = -1; // avoid BUF_ERROR at next call, see above 12547 return Z_OK; 12548 } 12549 } 12550 } 12551 12552 if (flush != Z_FINISH) 12553 return Z_OK; 12554 return Z_STREAM_END; 12555 }; 12556 } 12557 12558 // ZStream 12559 12560 function ZStream() { 12561 var that = this; 12562 that.next_in_index = 0; 12563 that.next_out_index = 0; 12564 // that.next_in; // next input byte 12565 that.avail_in = 0; // number of bytes available at next_in 12566 that.total_in = 0; // total nb of input bytes read so far 12567 // that.next_out; // next output byte should be put there 12568 that.avail_out = 0; // remaining free space at next_out 12569 that.total_out = 0; // total nb of bytes output so far 12570 // that.msg; 12571 // that.dstate; 12572 } 12573 12574 ZStream.prototype = { 12575 deflateInit : function(level, bits) { 12576 var that = this; 12577 that.dstate = new Deflate(); 12578 if (!bits) 12579 bits = MAX_BITS; 12580 return that.dstate.deflateInit(that, level, bits); 12581 }, 12582 12583 deflate : function(flush) { 12584 var that = this; 12585 if (!that.dstate) { 12586 return Z_STREAM_ERROR; 12587 } 12588 return that.dstate.deflate(that, flush); 12589 }, 12590 12591 deflateEnd : function() { 12592 var that = this; 12593 if (!that.dstate) 12594 return Z_STREAM_ERROR; 12595 var ret = that.dstate.deflateEnd(); 12596 that.dstate = null; 12597 return ret; 12598 }, 12599 12600 deflateParams : function(level, strategy) { 12601 var that = this; 12602 if (!that.dstate) 12603 return Z_STREAM_ERROR; 12604 return that.dstate.deflateParams(that, level, strategy); 12605 }, 12606 12607 deflateSetDictionary : function(dictionary, dictLength) { 12608 var that = this; 12609 if (!that.dstate) 12610 return Z_STREAM_ERROR; 12611 return that.dstate.deflateSetDictionary(that, dictionary, dictLength); 12612 }, 12613 12614 // Read a new buffer from the current input stream, update the 12615 // total number of bytes read. All deflate() input goes through 12616 // this function so some applications may wish to modify it to avoid 12617 // allocating a large strm->next_in buffer and copying from it. 12618 // (See also flush_pending()). 12619 read_buf : function(buf, start, size) { 12620 var that = this; 12621 var len = that.avail_in; 12622 if (len > size) 12623 len = size; 12624 if (len === 0) 12625 return 0; 12626 that.avail_in -= len; 12627 buf.set(that.next_in.subarray(that.next_in_index, that.next_in_index + len), start); 12628 that.next_in_index += len; 12629 that.total_in += len; 12630 return len; 12631 }, 12632 12633 // Flush as much pending output as possible. All deflate() output goes 12634 // through this function so some applications may wish to modify it 12635 // to avoid allocating a large strm->next_out buffer and copying into it. 12636 // (See also read_buf()). 12637 flush_pending : function() { 12638 var that = this; 12639 var len = that.dstate.pending; 12640 12641 if (len > that.avail_out) 12642 len = that.avail_out; 12643 if (len === 0) 12644 return; 12645 12646 // if (that.dstate.pending_buf.length <= that.dstate.pending_out || that.next_out.length <= that.next_out_index 12647 // || that.dstate.pending_buf.length < (that.dstate.pending_out + len) || that.next_out.length < (that.next_out_index + 12648 // len)) { 12649 // console.log(that.dstate.pending_buf.length + ", " + that.dstate.pending_out + ", " + that.next_out.length + ", " + 12650 // that.next_out_index + ", " + len); 12651 // console.log("avail_out=" + that.avail_out); 12652 // } 12653 12654 that.next_out.set(that.dstate.pending_buf.subarray(that.dstate.pending_out, that.dstate.pending_out + len), that.next_out_index); 12655 12656 that.next_out_index += len; 12657 that.dstate.pending_out += len; 12658 that.total_out += len; 12659 that.avail_out -= len; 12660 that.dstate.pending -= len; 12661 if (that.dstate.pending === 0) { 12662 that.dstate.pending_out = 0; 12663 } 12664 } 12665 }; 12666 12667 // Deflater 12668 12669 return function Deflater(level) { 12670 var that = this; 12671 var z = new ZStream(); 12672 var bufsize = 512; 12673 var flush = Z_NO_FLUSH; 12674 var buf = new Uint8Array(bufsize); 12675 12676 if (typeof level == "undefined") 12677 level = Z_DEFAULT_COMPRESSION; 12678 z.deflateInit(level); 12679 z.next_out = buf; 12680 12681 that.append = function(data, onprogress) { 12682 var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array; 12683 if (!data.length) 12684 return; 12685 z.next_in_index = 0; 12686 z.next_in = data; 12687 z.avail_in = data.length; 12688 do { 12689 z.next_out_index = 0; 12690 z.avail_out = bufsize; 12691 err = z.deflate(flush); 12692 if (err != Z_OK) 12693 throw "deflating: " + z.msg; 12694 if (z.next_out_index) 12695 if (z.next_out_index == bufsize) 12696 buffers.push(new Uint8Array(buf)); 12697 else 12698 buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index))); 12699 bufferSize += z.next_out_index; 12700 if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) { 12701 onprogress(z.next_in_index); 12702 lastIndex = z.next_in_index; 12703 } 12704 } while (z.avail_in > 0 || z.avail_out === 0); 12705 array = new Uint8Array(bufferSize);
12706 buffers.forEach(function(chunk) { 12707 array.set(chunk, bufferIndex); 12708 bufferIndex += chunk.length; 12709 }); 12710 return array; 12711 }; 12712 that.flush = function() { 12713 var err, buffers = [], bufferIndex = 0, bufferSize = 0, array; 12714 do { 12715 z.next_out_index = 0; 12716 z.avail_out = bufsize; 12717 err = z.deflate(Z_FINISH); 12718 if (err != Z_STREAM_END && err != Z_OK) 12719 throw "deflating: " + z.msg; 12720 if (bufsize - z.avail_out > 0) 12721 buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index))); 12722 bufferSize += z.next_out_index; 12723 } while (z.avail_in > 0 || z.avail_out === 0); 12724 z.deflateEnd(); 12725 array = new Uint8Array(bufferSize); 12726 buffers.forEach(function(chunk) { 12727 array.set(chunk, bufferIndex); 12728 bufferIndex += chunk.length; 12729 }); 12730 return array; 12731 }; 12732 }; 12733})(undefined); 12734 12735 12736/* 12737 html2canvas 0.5.0-beta3 <http://html2canvas.hertzen.com> 12738 Copyright (c) 2016 Niklas von Hertzen 12739 12740 Released under License 12741*/ 12742 12743!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,module,exports;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){ 12744(function (global){ 12745/*! http://mths.be/punycode v1.2.4 by @mathias */ 12746(function(root) { 12747 12748 /** Detect free variables */ 12749 var freeExports = typeof exports == 'object' && exports; 12750 var freeModule = typeof module == 'object' && module && 12751 module.exports == freeExports && module; 12752 var freeGlobal = typeof global == 'object' && global; 12753 if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { 12754 root = freeGlobal; 12755 } 12756 12757 /** 12758 * The `punycode` object. 12759 * @name punycode 12760 * @type Object 12761 */ 12762 var punycode, 12763 12764 /** Highest positive signed 32-bit float value */ 12765 maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 12766 12767 /** Bootstring parameters */ 12768 base = 36, 12769 tMin = 1, 12770 tMax = 26, 12771 skew = 38, 12772 damp = 700, 12773 initialBias = 72, 12774 initialN = 128, // 0x80 12775 delimiter = '-', // '\x2D' 12776 12777 /** Regular expressions */ 12778 regexPunycode = /^xn--/, 12779 regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars 12780 regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators 12781 12782 /** Error messages */ 12783 errors = { 12784 'overflow': 'Overflow: input needs wider integers to process', 12785 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', 12786 'invalid-input': 'Invalid input' 12787 }, 12788 12789 /** Convenience shortcuts */ 12790 baseMinusTMin = base - tMin, 12791 floor = Math.floor, 12792 stringFromCharCode = String.fromCharCode, 12793 12794 /** Temporary variable */ 12795 key; 12796 12797 /*--------------------------------------------------------------------------*/ 12798 12799 /** 12800 * A generic error utility function. 12801 * @private 12802 * @param {String} type The error type. 12803 * @returns {Error} Throws a `RangeError` with the applicable error message. 12804 */ 12805 function error(type) { 12806 throw RangeError(errors[type]); 12807 } 12808 12809 /** 12810 * A generic `Array#map` utility function. 12811 * @private 12812 * @param {Array} array The array to iterate over. 12813 * @param {Function} callback The function that gets called for every array 12814 * item. 12815 * @returns {Array} A new array of values returned by the callback function. 12816 */ 12817 function map(array, fn) { 12818 var length = array.length; 12819 while (length--) { 12820 array[length] = fn(array[length]); 12821 } 12822 return array; 12823 } 12824 12825 /** 12826 * A simple `Array#map`-like wrapper to work with domain name strings. 12827 * @private 12828 * @param {String} domain The domain name. 12829 * @param {Function} callback The function that gets called for every 12830 * character. 12831 * @returns {Array} A new string of characters returned by the callback 12832 * function. 12833 */ 12834 function mapDomain(string, fn) { 12835 return map(string.split(regexSeparators), fn).join('.'); 12836 } 12837 12838 /** 12839 * Creates an array containing the numeric code points of each Unicode 12840 * character in the string. While JavaScript uses UCS-2 internally, 12841 * this function will convert a pair of surrogate halves (each of which 12842 * UCS-2 exposes as separate characters) into a single code point, 12843 * matching UTF-16. 12844 * @see `punycode.ucs2.encode` 12845 * @see <http://mathiasbynens.be/notes/javascript-encoding> 12846 * @memberOf punycode.ucs2 12847 * @name decode 12848 * @param {String} string The Unicode input string (UCS-2). 12849 * @returns {Array} The new array of code points. 12850 */ 12851 function ucs2decode(string) { 12852 var output = [], 12853 counter = 0, 12854 length = string.length, 12855 value, 12856 extra; 12857 while (counter < length) { 12858 value = string.charCodeAt(counter++); 12859 if (value >= 0xD800 && value <= 0xDBFF && counter < length) { 12860 // high surrogate, and there is a next character 12861 extra = string.charCodeAt(counter++); 12862 if ((extra & 0xFC00) == 0xDC00) { // low surrogate
vendor: 9,396 bytes, lines 12863-13207
12863 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); 12864 } else { 12865 // unmatched surrogate; only append this code unit, in case the next 12866 // code unit is the high surrogate of a surrogate pair 12867 output.push(value); 12868 counter--; 12869 } 12870 } else { 12871 output.push(value); 12872 } 12873 } 12874 return output; 12875 } 12876 12877 /** 12878 * Creates a string based on an array of numeric code points. 12879 * @see `punycode.ucs2.decode` 12880 * @memberOf punycode.ucs2 12881 * @name encode 12882 * @param {Array} codePoints The array of numeric code points. 12883 * @returns {String} The new Unicode string (UCS-2). 12884 */ 12885 function ucs2encode(array) { 12886 return map(array, function(value) { 12887 var output = ''; 12888 if (value > 0xFFFF) { 12889 value -= 0x10000; 12890 output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); 12891 value = 0xDC00 | value & 0x3FF; 12892 } 12893 output += stringFromCharCode(value); 12894 return output; 12895 }).join(''); 12896 } 12897 12898 /** 12899 * Converts a basic code point into a digit/integer. 12900 * @see `digitToBasic()` 12901 * @private 12902 * @param {Number} codePoint The basic numeric code point value. 12903 * @returns {Number} The numeric value of a basic code point (for use in 12904 * representing integers) in the range `0` to `base - 1`, or `base` if 12905 * the code point does not represent a value. 12906 */ 12907 function basicToDigit(codePoint) { 12908 if (codePoint - 48 < 10) { 12909 return codePoint - 22; 12910 } 12911 if (codePoint - 65 < 26) { 12912 return codePoint - 65; 12913 } 12914 if (codePoint - 97 < 26) { 12915 return codePoint - 97; 12916 } 12917 return base; 12918 } 12919 12920 /** 12921 * Converts a digit/integer into a basic code point. 12922 * @see `basicToDigit()` 12923 * @private 12924 * @param {Number} digit The numeric value of a basic code point. 12925 * @returns {Number} The basic code point whose value (when used for 12926 * representing integers) is `digit`, which needs to be in the range 12927 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is 12928 * used; else, the lowercase form is used. The behavior is undefined 12929 * if `flag` is non-zero and `digit` has no uppercase form. 12930 */ 12931 function digitToBasic(digit, flag) { 12932 // 0..25 map to ASCII a..z or A..Z 12933 // 26..35 map to ASCII 0..9 12934 return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); 12935 } 12936 12937 /** 12938 * Bias adaptation function as per section 3.4 of RFC 3492. 12939 * http://tools.ietf.org/html/rfc3492#section-3.4 12940 * @private 12941 */ 12942 function adapt(delta, numPoints, firstTime) { 12943 var k = 0; 12944 delta = firstTime ? floor(delta / damp) : delta >> 1; 12945 delta += floor(delta / numPoints); 12946 for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { 12947 delta = floor(delta / baseMinusTMin); 12948 } 12949 return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); 12950 } 12951 12952 /** 12953 * Converts a Punycode string of ASCII-only symbols to a string of Unicode 12954 * symbols. 12955 * @memberOf punycode 12956 * @param {String} input The Punycode string of ASCII-only symbols. 12957 * @returns {String} The resulting string of Unicode symbols. 12958 */ 12959 function decode(input) { 12960 // Don't use UCS-2 12961 var output = [], 12962 inputLength = input.length, 12963 out, 12964 i = 0, 12965 n = initialN, 12966 bias = initialBias, 12967 basic, 12968 j, 12969 index, 12970 oldi, 12971 w, 12972 k, 12973 digit, 12974 t, 12975 /** Cached calculation results */ 12976 baseMinusT; 12977 12978 // Handle the basic code points: let `basic` be the number of input code 12979 // points before the last delimiter, or `0` if there is none, then copy 12980 // the first basic code points to the output. 12981 12982 basic = input.lastIndexOf(delimiter); 12983 if (basic < 0) { 12984 basic = 0; 12985 } 12986 12987 for (j = 0; j < basic; ++j) { 12988 // if it's not a basic code point 12989 if (input.charCodeAt(j) >= 0x80) { 12990 error('not-basic'); 12991 } 12992 output.push(input.charCodeAt(j)); 12993 } 12994 12995 // Main decoding loop: start just after the last delimiter if any basic code 12996 // points were copied; start at the beginning otherwise. 12997 12998 for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { 12999 13000 // `index` is the index of the next character to be consumed. 13001 // Decode a generalized variable-length integer into `delta`, 13002 // which gets added to `i`. The overflow checking is easier 13003 // if we increase `i` as we go, then subtract off its starting 13004 // value at the end to obtain `delta`. 13005 for (oldi = i, w = 1, k = base; /* no condition */; k += base) { 13006 13007 if (index >= inputLength) { 13008 error('invalid-input'); 13009 } 13010 13011 digit = basicToDigit(input.charCodeAt(index++)); 13012 13013 if (digit >= base || digit > floor((maxInt - i) / w)) { 13014 error('overflow'); 13015 } 13016 13017 i += digit * w; 13018 t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); 13019 13020 if (digit < t) { 13021 break; 13022 } 13023 13024 baseMinusT = base - t; 13025 if (w > floor(maxInt / baseMinusT)) { 13026 error('overflow'); 13027 } 13028 13029 w *= baseMinusT; 13030 13031 } 13032 13033 out = output.length + 1; 13034 bias = adapt(i - oldi, out, oldi == 0); 13035 13036 // `i` was supposed to wrap around from `out` to `0`, 13037 // incrementing `n` each time, so we'll fix that now: 13038 if (floor(i / out) > maxInt - n) { 13039 error('overflow'); 13040 } 13041 13042 n += floor(i / out); 13043 i %= out; 13044 13045 // Insert `n` at position `i` of the output 13046 output.splice(i++, 0, n); 13047 13048 } 13049 13050 return ucs2encode(output); 13051 } 13052 13053 /** 13054 * Converts a string of Unicode symbols to a Punycode string of ASCII-only 13055 * symbols. 13056 * @memberOf punycode 13057 * @param {String} input The string of Unicode symbols. 13058 * @returns {String} The resulting Punycode string of ASCII-only symbols. 13059 */ 13060 function encode(input) { 13061 var n, 13062 delta, 13063 handledCPCount, 13064 basicLength, 13065 bias, 13066 j, 13067 m, 13068 q, 13069 k, 13070 t, 13071 currentValue, 13072 output = [], 13073 /** `inputLength` will hold the number of code points in `input`. */ 13074 inputLength, 13075 /** Cached calculation results */ 13076 handledCPCountPlusOne, 13077 baseMinusT, 13078 qMinusT; 13079 13080 // Convert the input in UCS-2 to Unicode 13081 input = ucs2decode(input); 13082 13083 // Cache the length 13084 inputLength = input.length; 13085 13086 // Initialize the state 13087 n = initialN; 13088 delta = 0; 13089 bias = initialBias; 13090 13091 // Handle the basic code points 13092 for (j = 0; j < inputLength; ++j) { 13093 currentValue = input[j]; 13094 if (currentValue < 0x80) { 13095 output.push(stringFromCharCode(currentValue)); 13096 } 13097 } 13098 13099 handledCPCount = basicLength = output.length; 13100 13101 // `handledCPCount` is the number of code points that have been handled; 13102 // `basicLength` is the number of basic code points. 13103 13104 // Finish the basic string - if it is not empty - with a delimiter 13105 if (basicLength) { 13106 output.push(delimiter); 13107 } 13108 13109 // Main encoding loop: 13110 while (handledCPCount < inputLength) { 13111 13112 // All non-basic code points < n have been handled already. Find the next 13113 // larger one: 13114 for (m = maxInt, j = 0; j < inputLength; ++j) { 13115 currentValue = input[j]; 13116 if (currentValue >= n && currentValue < m) { 13117 m = currentValue; 13118 } 13119 } 13120 13121 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, 13122 // but guard against overflow 13123 handledCPCountPlusOne = handledCPCount + 1; 13124 if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { 13125 error('overflow'); 13126 } 13127 13128 delta += (m - n) * handledCPCountPlusOne; 13129 n = m; 13130 13131 for (j = 0; j < inputLength; ++j) { 13132 currentValue = input[j]; 13133 13134 if (currentValue < n && ++delta > maxInt) { 13135 error('overflow'); 13136 } 13137 13138 if (currentValue == n) { 13139 // Represent delta as a generalized variable-length integer 13140 for (q = delta, k = base; /* no condition */; k += base) { 13141 t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); 13142 if (q < t) { 13143 break; 13144 } 13145 qMinusT = q - t; 13146 baseMinusT = base - t; 13147 output.push( 13148 stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) 13149 ); 13150 q = floor(qMinusT / baseMinusT); 13151 } 13152 13153 output.push(stringFromCharCode(digitToBasic(q, 0))); 13154 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); 13155 delta = 0; 13156 ++handledCPCount; 13157 } 13158 } 13159 13160 ++delta; 13161 ++n; 13162 13163 } 13164 return output.join(''); 13165 } 13166 13167 /** 13168 * Converts a Punycode string representing a domain name to Unicode. Only the 13169 * Punycoded parts of the domain name will be converted, i.e. it doesn't 13170 * matter if you call it on a string that has already been converted to 13171 * Unicode. 13172 * @memberOf punycode 13173 * @param {String} domain The Punycode domain name to convert to Unicode. 13174 * @returns {String} The Unicode representation of the given Punycode 13175 * string. 13176 */ 13177 function toUnicode(domain) { 13178 return mapDomain(domain, function(string) { 13179 return regexPunycode.test(string) 13180 ? decode(string.slice(4).toLowerCase()) 13181 : string; 13182 }); 13183 } 13184 13185 /** 13186 * Converts a Unicode string representing a domain name to Punycode. Only the 13187 * non-ASCII parts of the domain name will be converted, i.e. it doesn't 13188 * matter if you call it with a domain that's already in ASCII. 13189 * @memberOf punycode 13190 * @param {String} domain The domain name to convert, as a Unicode string. 13191 * @returns {String} The Punycode representation of the given domain name. 13192 */ 13193 function toASCII(domain) { 13194 return mapDomain(domain, function(string) { 13195 return regexNonASCII.test(string) 13196 ? 'xn--' + encode(string) 13197 : string; 13198 }); 13199 } 13200 13201 /*--------------------------------------------------------------------------*/ 13202 13203 /** Define the public API */ 13204 punycode = { 13205 /** 13206 * A string representing the current Punycode.js version number. 13207 * @memberOf punycode
13208 * @type String 13209 */ 13210 'version': '1.2.4', 13211 /** 13212 * An object of methods to convert from JavaScript's internal character 13213 * representation (UCS-2) to Unicode code points, and back. 13214 * @see <http://mathiasbynens.be/notes/javascript-encoding> 13215 * @memberOf punycode 13216 * @type Object 13217 */ 13218 'ucs2': { 13219 'decode': ucs2decode, 13220 'encode': ucs2encode 13221 }, 13222 'decode': decode, 13223 'encode': encode, 13224 'toASCII': toASCII, 13225 'toUnicode': toUnicode 13226 }; 13227 13228 /** Expose `punycode` */ 13229 // Some AMD build optimizers, like r.js, check for specific condition patterns 13230 // like the following: 13231 if ( 13232 typeof define == 'function' && 13233 typeof define.amd == 'object' && 13234 define.amd 13235 ) { 13236 define('punycode', function() { 13237 return punycode; 13238 }); 13239 } else if (freeExports && !freeExports.nodeType) { 13240 if (freeModule) { // in Node.js or RingoJS v0.8.0+ 13241 freeModule.exports = punycode; 13242 } else { // in Narwhal or RingoJS v0.7.0- 13243 for (key in punycode) { 13244 punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); 13245 } 13246 } 13247 } else { // in Rhino or a web browser 13248 root.punycode = punycode; 13249 } 13250 13251}(this)); 13252 13253}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}); 13254},{}],2:[function(_dereq_,module,exports){ 13255var log = _dereq_('./log'); 13256 13257function restoreOwnerScroll(ownerDocument, x, y) { 13258 if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) { 13259 ownerDocument.defaultView.scrollTo(x, y); 13260 } 13261} 13262 13263function cloneCanvasContents(canvas, clonedCanvas) { 13264 try { 13265 if (clonedCanvas) { 13266 clonedCanvas.width = canvas.width; 13267 clonedCanvas.height = canvas.height; 13268 clonedCanvas.getContext("2d").putImageData(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height), 0, 0); 13269 } 13270 } catch(e) { 13271 log("Unable to copy canvas content from", canvas, e); 13272 } 13273} 13274 13275function cloneNode(node, javascriptEnabled) { 13276 var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false); 13277 13278 var child = node.firstChild; 13279 while(child) { 13280 if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') { 13281 clone.appendChild(cloneNode(child, javascriptEnabled)); 13282 } 13283 child = child.nextSibling; 13284 } 13285 13286 if (node.nodeType === 1) { 13287 clone._scrollTop = node.scrollTop; 13288 clone._scrollLeft = node.scrollLeft; 13289 if (node.nodeName === "CANVAS") { 13290 cloneCanvasContents(node, clone); 13291 } else if (node.nodeName === "TEXTAREA" || node.nodeName === "SELECT") { 13292 clone.value = node.value; 13293 } 13294 } 13295 13296 return clone; 13297} 13298 13299function initNode(node) { 13300 if (node.nodeType === 1) { 13301 node.scrollTop = node._scrollTop; 13302 node.scrollLeft = node._scrollLeft; 13303 13304 var child = node.firstChild; 13305 while(child) { 13306 initNode(child); 13307 child = child.nextSibling; 13308 } 13309 } 13310} 13311 13312module.exports = function(ownerDocument, containerDocument, width, height, options, x ,y) { 13313 var documentElement = cloneNode(ownerDocument.documentElement, options.javascriptEnabled); 13314 var container = containerDocument.createElement("iframe"); 13315 13316 container.className = "html2canvas-container"; 13317 container.style.visibility = "hidden"; 13318 container.style.position = "fixed"; 13319 container.style.left = "-10000px"; 13320 container.style.top = "0px"; 13321 container.style.border = "0"; 13322 container.width = width; 13323 container.height = height; 13324 container.scrolling = "no"; // ios won't scroll without it 13325 containerDocument.body.appendChild(container); 13326 13327 return new Promise(function(resolve) { 13328 var documentClone = container.contentWindow.document; 13329 13330 /* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle 13331 if window url is about:blank, we can assign the url to current by writing onto the document 13332 */ 13333 container.contentWindow.onload = container.onload = function() { 13334 var interval = setInterval(function() { 13335 if (documentClone.body.childNodes.length > 0) { 13336 initNode(documentClone.documentElement); 13337 clearInterval(interval); 13338 if (options.type === "view") { 13339 container.contentWindow.scrollTo(x, y); 13340 if ((/(iPad|iPhone|iPod)/g).test(navigator.userAgent) && (container.contentWindow.scrollY !== y || container.contentWindow.scrollX !== x)) { 13341 documentClone.documentElement.style.top = (-y) + "px"; 13342 documentClone.documentElement.style.left = (-x) + "px"; 13343 documentClone.documentElement.style.position = 'absolute'; 13344 } 13345 } 13346 resolve(container); 13347 } 13348 }, 50); 13349 }; 13350 13351 documentClone.open(); 13352 documentClone.write("<!DOCTYPE html><html></html>"); 13353 // Chrome scrolls the parent document for some reason after the write to the cloned window??? 13354 restoreOwnerScroll(ownerDocument, x, y); 13355 documentClone.replaceChild(documentClone.adoptNode(documentElement), documentClone.documentElement); 13356 documentClone.close(); 13357 }); 13358}; 13359 13360},{"./log":13}],3:[function(_dereq_,module,exports){ 13361// http://dev.w3.org/csswg/css-color/ 13362 13363function Color(value) { 13364 this.r = 0; 13365 this.g = 0; 13366 this.b = 0; 13367 this.a = null; 13368 var result = this.fromArray(value) || 13369 this.namedColor(value) || 13370 this.rgb(value) || 13371 this.rgba(value) || 13372 this.hex6(value) || 13373 this.hex3(value); 13374} 13375 13376Color.prototype.darken = function(amount) { 13377 var a = 1 - amount; 13378 return new Color([ 13379 Math.round(this.r * a), 13380 Math.round(this.g * a), 13381 Math.round(this.b * a), 13382 this.a 13383 ]); 13384}; 13385 13386Color.prototype.isTransparent = function() { 13387 return this.a === 0; 13388}; 13389 13390Color.prototype.isBlack = function() { 13391 return this.r === 0 && this.g === 0 && this.b === 0; 13392}; 13393 13394Color.prototype.fromArray = function(array) { 13395 if (Array.isArray(array)) { 13396 this.r = Math.min(array[0], 255); 13397 this.g = Math.min(array[1], 255); 13398 this.b = Math.min(array[2], 255); 13399 if (array.length > 3) { 13400 this.a = array[3]; 13401 } 13402 } 13403 13404 return (Array.isArray(array)); 13405}; 13406 13407var _hex3 = /^#([a-f0-9]{3})$/i; 13408 13409Color.prototype.hex3 = function(value) { 13410 var match = null; 13411 if ((match = value.match(_hex3)) !== null) { 13412 this.r = parseInt(match[1][0] + match[1][0], 16); 13413 this.g = parseInt(match[1][1] + match[1][1], 16); 13414 this.b = parseInt(match[1][2] + match[1][2], 16); 13415 } 13416 return match !== null; 13417}; 13418 13419var _hex6 = /^#([a-f0-9]{6})$/i; 13420 13421Color.prototype.hex6 = function(value) { 13422 var match = null; 13423 if ((match = value.match(_hex6)) !== null) { 13424 this.r = parseInt(match[1].substring(0, 2), 16); 13425 this.g = parseInt(match[1].substring(2, 4), 16); 13426 this.b = parseInt(match[1].substring(4, 6), 16); 13427 } 13428 return match !== null; 13429}; 13430 13431 13432var _rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/; 13433 13434Color.prototype.rgb = function(value) { 13435 var match = null; 13436 if ((match = value.match(_rgb)) !== null) { 13437 this.r = Number(match[1]); 13438 this.g = Number(match[2]); 13439 this.b = Number(match[3]); 13440 } 13441 return match !== null; 13442}; 13443 13444var _rgba = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?\.?\d+)\s*\)$/; 13445 13446Color.prototype.rgba = function(value) { 13447 var match = null; 13448 if ((match = value.match(_rgba)) !== null) { 13449 this.r = Number(match[1]); 13450 this.g = Number(match[2]); 13451 this.b = Number(match[3]); 13452 this.a = Number(match[4]); 13453 } 13454 return match !== null; 13455}; 13456 13457Color.prototype.toString = function() { 13458 return this.a !== null && this.a !== 1 ? 13459 "rgba(" + [this.r, this.g, this.b, this.a].join(",") + ")" : 13460 "rgb(" + [this.r, this.g, this.b].join(",") + ")"; 13461}; 13462 13463Color.prototype.namedColor = function(value) { 13464 value = value.toLowerCase(); 13465 var color = colors[value]; 13466 if (color) { 13467 this.r = color[0]; 13468 this.g = color[1]; 13469 this.b = color[2]; 13470 } else if (value === "transparent") { 13471 this.r = this.g = this.b = this.a = 0; 13472 return true; 13473 } 13474 13475 return !!color; 13476}; 13477 13478Color.prototype.isColor = true; 13479 13480// 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}, {})) 13481var colors = { 13482 "aliceblue": [240, 248, 255], 13483 "antiquewhite": [250, 235, 215], 13484 "aqua": [0, 255, 255], 13485 "aquamarine": [127, 255, 212], 13486 "azure": [240, 255, 255], 13487 "beige": [245, 245, 220], 13488 "bisque": [255, 228, 196], 13489 "black": [0, 0, 0], 13490 "blanchedalmond": [255, 235, 205], 13491 "blue": [0, 0, 255], 13492 "blueviolet": [138, 43, 226], 13493 "brown": [165, 42, 42], 13494 "burlywood": [222, 184, 135], 13495 "cadetblue": [95, 158, 160], 13496 "chartreuse": [127, 255, 0], 13497 "chocolate": [210, 105, 30], 13498 "coral": [255, 127, 80], 13499 "cornflowerblue": [100, 149, 237], 13500 "cornsilk": [255, 248, 220], 13501 "crimson": [220, 20, 60], 13502 "cyan": [0, 255, 255], 13503 "darkblue": [0, 0, 139], 13504 "darkcyan": [0, 139, 139], 13505 "darkgoldenrod": [184, 134, 11], 13506 "darkgray": [169, 169, 169], 13507 "darkgreen": [0, 100, 0], 13508 "darkgrey": [169, 169, 169], 13509 "darkkhaki": [189, 183, 107], 13510 "darkmagenta": [139, 0, 139], 13511 "darkolivegreen": [85, 107, 47], 13512 "darkorange": [255, 140, 0], 13513 "darkorchid": [153, 50, 204], 13514 "darkred": [139, 0, 0], 13515 "darksalmon": [233, 150, 122], 13516 "darkseagreen": [143, 188, 143], 13517 "darkslateblue": [72, 61, 139], 13518 "darkslategray": [47, 79, 79], 13519 "darkslategrey": [47, 79, 79],
13520 "darkturquoise": [0, 206, 209], 13521 "darkviolet": [148, 0, 211], 13522 "deeppink": [255, 20, 147], 13523 "deepskyblue": [0, 191, 255], 13524 "dimgray": [105, 105, 105], 13525 "dimgrey": [105, 105, 105], 13526 "dodgerblue": [30, 144, 255], 13527 "firebrick": [178, 34, 34], 13528 "floralwhite": [255, 250, 240], 13529 "forestgreen": [34, 139, 34], 13530 "fuchsia": [255, 0, 255], 13531 "gainsboro": [220, 220, 220], 13532 "ghostwhite": [248, 248, 255], 13533 "gold": [255, 215, 0], 13534 "goldenrod": [218, 165, 32], 13535 "gray": [128, 128, 128], 13536 "green": [0, 128, 0], 13537 "greenyellow": [173, 255, 47], 13538 "grey": [128, 128, 128], 13539 "honeydew": [240, 255, 240], 13540 "hotpink": [255, 105, 180], 13541 "indianred": [205, 92, 92], 13542 "indigo": [75, 0, 130], 13543 "ivory": [255, 255, 240], 13544 "khaki": [240, 230, 140], 13545 "lavender": [230, 230, 250], 13546 "lavenderblush": [255, 240, 245], 13547 "lawngreen": [124, 252, 0], 13548 "lemonchiffon": [255, 250, 205], 13549 "lightblue": [173, 216, 230], 13550 "lightcoral": [240, 128, 128], 13551 "lightcyan": [224, 255, 255], 13552 "lightgoldenrodyellow": [250, 250, 210], 13553 "lightgray": [211, 211, 211], 13554 "lightgreen": [144, 238, 144], 13555 "lightgrey": [211, 211, 211], 13556 "lightpink": [255, 182, 193], 13557 "lightsalmon": [255, 160, 122], 13558 "lightseagreen": [32, 178, 170], 13559 "lightskyblue": [135, 206, 250], 13560 "lightslategray": [119, 136, 153], 13561 "lightslategrey": [119, 136, 153], 13562 "lightsteelblue": [176, 196, 222], 13563 "lightyellow": [255, 255, 224], 13564 "lime": [0, 255, 0], 13565 "limegreen": [50, 205, 50], 13566 "linen": [250, 240, 230], 13567 "magenta": [255, 0, 255], 13568 "maroon": [128, 0, 0], 13569 "mediumaquamarine": [102, 205, 170], 13570 "mediumblue": [0, 0, 205], 13571 "mediumorchid": [186, 85, 211], 13572 "mediumpurple": [147, 112, 219], 13573 "mediumseagreen": [60, 179, 113], 13574 "mediumslateblue": [123, 104, 238], 13575 "mediumspringgreen": [0, 250, 154], 13576 "mediumturquoise": [72, 209, 204], 13577 "mediumvioletred": [199, 21, 133], 13578 "midnightblue": [25, 25, 112], 13579 "mintcream": [245, 255, 250], 13580 "mistyrose": [255, 228, 225], 13581 "moccasin": [255, 228, 181], 13582 "navajowhite": [255, 222, 173], 13583 "navy": [0, 0, 128], 13584 "oldlace": [253, 245, 230], 13585 "olive": [128, 128, 0], 13586 "olivedrab": [107, 142, 35], 13587 "orange": [255, 165, 0], 13588 "orangered": [255, 69, 0], 13589 "orchid": [218, 112, 214], 13590 "palegoldenrod": [238, 232, 170], 13591 "palegreen": [152, 251, 152], 13592 "paleturquoise": [175, 238, 238], 13593 "palevioletred": [219, 112, 147], 13594 "papayawhip": [255, 239, 213], 13595 "peachpuff": [255, 218, 185], 13596 "peru": [205, 133, 63], 13597 "pink": [255, 192, 203], 13598 "plum": [221, 160, 221], 13599 "powderblue": [176, 224, 230], 13600 "purple": [128, 0, 128], 13601 "rebeccapurple": [102, 51, 153], 13602 "red": [255, 0, 0], 13603 "rosybrown": [188, 143, 143], 13604 "royalblue": [65, 105, 225], 13605 "saddlebrown": [139, 69, 19], 13606 "salmon": [250, 128, 114], 13607 "sandybrown": [244, 164, 96], 13608 "seagreen": [46, 139, 87], 13609 "seashell": [255, 245, 238], 13610 "sienna": [160, 82, 45], 13611 "silver": [192, 192, 192], 13612 "skyblue": [135, 206, 235], 13613 "slateblue": [106, 90, 205], 13614 "slategray": [112, 128, 144], 13615 "slategrey": [112, 128, 144], 13616 "snow": [255, 250, 250], 13617 "springgreen": [0, 255, 127], 13618 "steelblue": [70, 130, 180], 13619 "tan": [210, 180, 140], 13620 "teal": [0, 128, 128], 13621 "thistle": [216, 191, 216], 13622 "tomato": [255, 99, 71], 13623 "turquoise": [64, 224, 208], 13624 "violet": [238, 130, 238], 13625 "wheat": [245, 222, 179], 13626 "white": [255, 255, 255], 13627 "whitesmoke": [245, 245, 245], 13628 "yellow": [255, 255, 0], 13629 "yellowgreen": [154, 205, 50] 13630}; 13631 13632module.exports = Color; 13633 13634},{}],4:[function(_dereq_,module,exports){ 13635var Support = _dereq_('./support'); 13636var CanvasRenderer = _dereq_('./renderers/canvas'); 13637var ImageLoader = _dereq_('./imageloader'); 13638var NodeParser = _dereq_('./nodeparser'); 13639var NodeContainer = _dereq_('./nodecontainer'); 13640var log = _dereq_('./log'); 13641var utils = _dereq_('./utils'); 13642var createWindowClone = _dereq_('./clone'); 13643var loadUrlDocument = _dereq_('./proxy').loadUrlDocument; 13644var getBounds = utils.getBounds; 13645 13646var html2canvasNodeAttribute = "data-html2canvas-node"; 13647var html2canvasCloneIndex = 0; 13648 13649function html2canvas(nodeList, options) { 13650 var index = html2canvasCloneIndex++; 13651 options = options || {}; 13652 if (options.logging) { 13653 log.options.logging = true; 13654 log.options.start = Date.now(); 13655 } 13656 13657 options.async = typeof(options.async) === "undefined" ? true : options.async; 13658 options.allowTaint = typeof(options.allowTaint) === "undefined" ? false : options.allowTaint; 13659 options.removeContainer = typeof(options.removeContainer) === "undefined" ? true : options.removeContainer; 13660 options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled; 13661 options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout; 13662 options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer; 13663 options.strict = !!options.strict; 13664 13665 if (typeof(nodeList) === "string") { 13666 if (typeof(options.proxy) !== "string") { 13667 return Promise.reject("Proxy must be used when rendering url"); 13668 } 13669 var width = options.width != null ? options.width : window.innerWidth; 13670 var height = options.height != null ? options.height : window.innerHeight; 13671 return loadUrlDocument(absoluteUrl(nodeList), options.proxy, document, width, height, options).then(function(container) { 13672 return renderWindow(container.contentWindow.document.documentElement, container, options, width, height); 13673 }); 13674 } 13675 13676 var node = ((nodeList === undefined) ? [document.documentElement] : ((nodeList.length) ? nodeList : [nodeList]))[0]; 13677 node.setAttribute(html2canvasNodeAttribute + index, index); 13678 return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) { 13679 if (typeof(options.onrendered) === "function") { 13680 log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"); 13681 options.onrendered(canvas); 13682 } 13683 return canvas; 13684 }); 13685} 13686 13687html2canvas.CanvasRenderer = CanvasRenderer; 13688html2canvas.NodeContainer = NodeContainer; 13689html2canvas.log = log; 13690html2canvas.utils = utils; 13691 13692var html2canvasExport = (typeof(document) === "undefined" || typeof(Object.create) !== "function" || typeof(document.createElement("canvas").getContext) !== "function") ? function() { 13693 return Promise.reject("No canvas support"); 13694} : html2canvas; 13695 13696module.exports = html2canvasExport; 13697 13698if (typeof(define) === 'function' && define.amd) { 13699 define('html2canvas', [], function() { 13700 return html2canvasExport; 13701 }); 13702} 13703 13704function renderDocument(document, options, windowWidth, windowHeight, html2canvasIndex) { 13705 return createWindowClone(document, document, windowWidth, windowHeight, options, document.defaultView.pageXOffset, document.defaultView.pageYOffset).then(function(container) { 13706 log("Document cloned"); 13707 var attributeName = html2canvasNodeAttribute + html2canvasIndex; 13708 var selector = "[" + attributeName + "='" + html2canvasIndex + "']"; 13709 document.querySelector(selector).removeAttribute(attributeName); 13710 var clonedWindow = container.contentWindow; 13711 var node = clonedWindow.document.querySelector(selector);
13712 var oncloneHandler = (typeof(options.onclone) === "function") ? Promise.resolve(options.onclone(clonedWindow.document)) : Promise.resolve(true); 13713 return oncloneHandler.then(function() { 13714 return renderWindow(node, container, options, windowWidth, windowHeight); 13715 }); 13716 }); 13717} 13718 13719function renderWindow(node, container, options, windowWidth, windowHeight) { 13720 var clonedWindow = container.contentWindow; 13721 var support = new Support(clonedWindow.document); 13722 var imageLoader = new ImageLoader(options, support); 13723 var bounds = getBounds(node); 13724 var width = options.type === "view" ? windowWidth : documentWidth(clonedWindow.document); 13725 var height = options.type === "view" ? windowHeight : documentHeight(clonedWindow.document); 13726 var renderer = new options.renderer(width, height, imageLoader, options, document); 13727 var parser = new NodeParser(node, renderer, support, imageLoader, options); 13728 return parser.ready.then(function() { 13729 log("Finished rendering"); 13730 var canvas; 13731 13732 if (options.type === "view") { 13733 canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0}); 13734 } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) { 13735 canvas = renderer.canvas; 13736 } else { 13737 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}); 13738 } 13739 13740 cleanupContainer(container, options); 13741 return canvas; 13742 }); 13743} 13744 13745function cleanupContainer(container, options) { 13746 if (options.removeContainer) { 13747 container.parentNode.removeChild(container); 13748 log("Cleaned up container"); 13749 } 13750} 13751 13752function crop(canvas, bounds) { 13753 var croppedCanvas = document.createElement("canvas"); 13754 var x1 = Math.min(canvas.width - 1, Math.max(0, bounds.left)); 13755 var x2 = Math.min(canvas.width, Math.max(1, bounds.left + bounds.width)); 13756 var y1 = Math.min(canvas.height - 1, Math.max(0, bounds.top)); 13757 var y2 = Math.min(canvas.height, Math.max(1, bounds.top + bounds.height)); 13758 croppedCanvas.width = bounds.width; 13759 croppedCanvas.height = bounds.height; 13760 var width = x2-x1; 13761 var height = y2-y1; 13762 log("Cropping canvas at:", "left:", bounds.left, "top:", bounds.top, "width:", width, "height:", height); 13763 log("Resulting crop with width", bounds.width, "and height", bounds.height, "with x", x1, "and y", y1); 13764 croppedCanvas.getContext("2d").drawImage(canvas, x1, y1, width, height, bounds.x, bounds.y, width, height); 13765 return croppedCanvas; 13766} 13767 13768function documentWidth (doc) { 13769 return Math.max( 13770 Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), 13771 Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), 13772 Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) 13773 ); 13774} 13775 13776function documentHeight (doc) { 13777 return Math.max( 13778 Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), 13779 Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), 13780 Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) 13781 ); 13782} 13783 13784function absoluteUrl(url) { 13785 var link = document.createElement("a"); 13786 link.href = url; 13787 link.href = link.href; 13788 return link; 13789} 13790 13791},{"./clone":2,"./imageloader":11,"./log":13,"./nodecontainer":14,"./nodeparser":15,"./proxy":16,"./renderers/canvas":20,"./support":22,"./utils":26}],5:[function(_dereq_,module,exports){ 13792var log = _dereq_('./log'); 13793var smallImage = _dereq_('./utils').smallImage; 13794 13795function DummyImageContainer(src) { 13796 this.src = src; 13797 log("DummyImageContainer for", src); 13798 if (!this.promise || !this.image) { 13799 log("Initiating DummyImageContainer"); 13800 DummyImageContainer.prototype.image = new Image(); 13801 var image = this.image; 13802 DummyImageContainer.prototype.promise = new Promise(function(resolve, reject) { 13803 image.onload = resolve; 13804 image.onerror = reject; 13805 image.src = smallImage(); 13806 if (image.complete === true) { 13807 resolve(image); 13808 } 13809 }); 13810 } 13811} 13812 13813module.exports = DummyImageContainer; 13814 13815}
13815,{"./log":13,"./utils":26}],6:[function(_dereq_,module,exports){ 13816var smallImage = _dereq_('./utils').smallImage; 13817 13818function Font(family, size) { 13819 var container = document.createElement('div'), 13820 img = document.createElement('img'), 13821 span = document.createElement('span'), 13822 sampleText = 'Hidden Text', 13823 baseline, 13824 middle; 13825 13826 container.style.visibility = "hidden"; 13827 container.style.fontFamily = family; 13828 container.style.fontSize = size; 13829 container.style.margin = 0; 13830 container.style.padding = 0; 13831 13832 document.body.appendChild(container); 13833 13834 img.src = smallImage(); 13835 img.width = 1; 13836 img.height = 1; 13837 13838 img.style.margin = 0; 13839 img.style.padding = 0; 13840 img.style.verticalAlign = "baseline"; 13841 13842 span.style.fontFamily = family; 13843 span.style.fontSize = size; 13844 span.style.margin = 0; 13845 span.style.padding = 0; 13846 13847 span.appendChild(document.createTextNode(sampleText)); 13848 container.appendChild(span); 13849 container.appendChild(img); 13850 baseline = (img.offsetTop - span.offsetTop) + 1; 13851 13852 container.removeChild(span); 13853 container.appendChild(document.createTextNode(sampleText)); 13854 13855 container.style.lineHeight = "normal"; 13856 img.style.verticalAlign = "super"; 13857 13858 middle = (img.offsetTop-container.offsetTop) + 1; 13859 13860 document.body.removeChild(container); 13861 13862 this.baseline = baseline; 13863 this.lineWidth = 1; 13864 this.middle = middle; 13865} 13866 13867module.exports = Font; 13868 13869},{"./utils":26}],7:[function(_dereq_,module,exports){ 13870var Font = _dereq_('./font'); 13871 13872function FontMetrics() { 13873 this.data = {}; 13874} 13875 13876FontMetrics.prototype.getMetrics = function(family, size) { 13877 if (this.data[family + "-" + size] === undefined) { 13878 this.data[family + "-" + size] = new Font(family, size); 13879 } 13880 return this.data[family + "-" + size]; 13881}; 13882 13883module.exports = FontMetrics; 13884 13885},{"./font":6}],8:[function(_dereq_,module,exports){ 13886var utils = _dereq_('./utils'); 13887var getBounds = utils.getBounds; 13888var loadUrlDocument = _dereq_('./proxy').loadUrlDocument; 13889 13890function FrameContainer(container, sameOrigin, options) { 13891 this.image = null; 13892 this.src = container; 13893 var self = this; 13894 var bounds = getBounds(container); 13895 this.promise = (!sameOrigin ? this.proxyLoad(options.proxy, bounds, options) : new Promise(function(resolve) { 13896 if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) { 13897 container.contentWindow.onload = container.onload = function() { 13898 resolve(container); 13899 }; 13900 } else { 13901 resolve(container); 13902 } 13903 })).then(function(container) { 13904 var html2canvas = _dereq_('./core'); 13905 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}); 13906 }).then(function(canvas) { 13907 return self.image = canvas; 13908 }); 13909} 13910 13911FrameContainer.prototype.proxyLoad = function(proxy, bounds, options) { 13912 var container = this.src; 13913 return loadUrlDocument(container.src, proxy, container.ownerDocument, bounds.width, bounds.height, options); 13914}; 13915 13916module.exports = FrameContainer; 13917 13918},{"./core":4,"./proxy":16,"./utils":26}],9:[function(_dereq_,module,exports){ 13919function GradientContainer(imageData) { 13920 this.src = imageData.value; 13921 this.colorStops = []; 13922 this.type = null; 13923 this.x0 = 0.5; 13924 this.y0 = 0.5; 13925 this.x1 = 0.5; 13926 this.y1 = 0.5; 13927 this.promise = Promise.resolve(true); 13928} 13929 13930GradientContainer.TYPES = { 13931 LINEAR: 1, 13932 RADIAL: 2 13933}; 13934 13935// TODO: support hsl[a], negative %/length values 13936// TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle ) 13937GradientContainer.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; 13938 13939module.exports = GradientContainer; 13940 13941},{}],10:[function(_dereq_,module,exports){ 13942function ImageContainer(src, cors) { 13943 this.src = src; 13944 this.image = new Image(); 13945 var self = this; 13946 this.tainted = null; 13947 this.promise = new Promise(function(resolve, reject) { 13948 self.image.onload = resolve; 13949 self.image.onerror = reject; 13950 if (cors) { 13951 self.image.crossOrigin = "anonymous"; 13952 } 13953 self.image.src = src; 13954 if (self.image.complete === true) { 13955 resolve(self.image); 13956 } 13957 }); 13958} 13959 13960module.exports = ImageContainer; 13961 13962},{}],11:[function(_dereq_,module,exports){ 13963var log = _dereq_('./log'); 13964var ImageContainer = _dereq_('./imagecontainer'); 13965var DummyImageContainer = _dereq_('./dummyimagecontainer'); 13966var ProxyImageContainer = _dereq_('./proxyimagecontainer'); 13967var FrameContainer = _dereq_('./framecontainer'); 13968var SVGContainer = _dereq_('./svgcontainer'); 13969var SVGNodeContainer = _dereq_('./svgnodecontainer'); 13970var LinearGradientContainer = _dereq_('./lineargradientcontainer'); 13971var WebkitGradientContainer = _dereq_('./webkitgradientcontainer'); 13972var bind = _dereq_('./utils').bind; 13973 13974function ImageLoader(options, support) { 13975 this.link = null; 13976 this.options = options; 13977 this.support = support; 13978 this.origin = this.getOrigin(window.location.href); 13979} 13980 13981ImageLoader.prototype.findImages = function(nodes) { 13982 var images = []; 13983 nodes.reduce(function(imageNodes, container) { 13984 switch(container.node.nodeName) { 13985 case "IMG": 13986 return imageNodes.concat([{ 13987 args: [container.node.src], 13988 method: "url" 13989 }]); 13990 case "svg": 13991 case "IFRAME": 13992 return imageNodes.concat([{ 13993 args: [container.node], 13994 method: container.node.nodeName 13995 }]); 13996 } 13997 return imageNodes; 13998 }
13998, []).forEach(this.addImage(images, this.loadImage), this); 13999 return images; 14000}; 14001 14002ImageLoader.prototype.findBackgroundImage = function(images, container) { 14003 container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this); 14004 return images; 14005}; 14006 14007ImageLoader.prototype.addImage = function(images, callback) { 14008 return function(newImage) { 14009 newImage.args.forEach(function(image) { 14010 if (!this.imageExists(images, image)) { 14011 images.splice(0, 0, callback.call(this, newImage)); 14012 log('Added image #' + (images.length), typeof(image) === "string" ? image.substring(0, 100) : image); 14013 } 14014 }, this); 14015 }; 14016}; 14017 14018ImageLoader.prototype.hasImageBackground = function(imageData) { 14019 return imageData.method !== "none"; 14020}; 14021 14022ImageLoader.prototype.loadImage = function(imageData) { 14023 if (imageData.method === "url") { 14024 var src = imageData.args[0]; 14025 if (this.isSVG(src) && !this.support.svg && !this.options.allowTaint) { 14026 return new SVGContainer(src); 14027 } else if (src.match(/data:image\/.*;base64,/i)) { 14028 return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false); 14029 } else if (this.isSameOrigin(src) || this.options.allowTaint === true || this.isSVG(src)) { 14030 return new ImageContainer(src, false); 14031 } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) { 14032 return new ImageContainer(src, true); 14033 } else if (this.options.proxy) { 14034 return new ProxyImageContainer(src, this.options.proxy); 14035 } else { 14036 return new DummyImageContainer(src); 14037 } 14038 } else if (imageData.method === "linear-gradient") { 14039 return new LinearGradientContainer(imageData); 14040 } else if (imageData.method === "gradient") { 14041 return new WebkitGradientContainer(imageData); 14042 } else if (imageData.method === "svg") { 14043 return new SVGNodeContainer(imageData.args[0], this.support.svg); 14044 } else if (imageData.method === "IFRAME") { 14045 return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options); 14046 } else { 14047 return new DummyImageContainer(imageData); 14048 } 14049}; 14050 14051ImageLoader.prototype.isSVG = function(src) { 14052 return src.substring(src.length - 3).toLowerCase() === "svg" || SVGContainer.prototype.isInline(src); 14053}; 14054 14055ImageLoader.prototype.imageExists = function(images, src) { 14056 return images.some(function(image) { 14057 return image.src === src; 14058 }); 14059}; 14060 14061ImageLoader.prototype.isSameOrigin = function(url) { 14062 return (this.getOrigin(url) === this.origin); 14063}; 14064 14065ImageLoader.prototype.getOrigin = function(url) { 14066 var link = this.link || (this.link = document.createElement("a")); 14067 link.href = url; 14068 link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/ 14069 return link.protocol + link.hostname + link.port; 14070}; 14071 14072ImageLoader.prototype.getPromise = function(container) { 14073 return this.timeout(container, this.options.imageTimeout)['catch'](function() { 14074 var dummy = new DummyImageContainer(container.src); 14075 return dummy.promise.then(function(image) { 14076 container.image = image; 14077 }); 14078 }); 14079}; 14080 14081ImageLoader.prototype.get = function(src) { 14082 var found = null; 14083 return this.images.some(function(img) { 14084 return (found = img).src === src; 14085 }) ? found : null; 14086}; 14087 14088ImageLoader.prototype.fetch = function(nodes) { 14089 this.images = nodes.reduce(bind(this.findBackgroundImage, this), this.findImages(nodes)); 14090 this.images.forEach(function(image, index) { 14091 image.promise.then(function() { 14092 log("Succesfully loaded image #"+ (index+1), image); 14093 }, function(e) { 14094 log("Failed loading image #"+ (index+1), image, e); 14095 }); 14096 }); 14097 this.ready = Promise.all(this.images.map(this.getPromise, this)); 14098 log("Finished searching images"); 14099 return this; 14100}; 14101 14102ImageLoader.prototype.timeout = function(container, timeout) { 14103 var timer; 14104 var promise = Promise.race([container.promise, new Promise(function(res, reject) { 14105 timer = setTimeout(function() { 14106 log("Timed out loading image", container); 14107 reject(container); 14108 }, timeout); 14109 })]).then(function(container) { 14110 clearTimeout(timer); 14111 return container; 14112 }); 14113 promise['catch'](function() { 14114 clearTimeout(timer); 14115 }); 14116 return promise; 14117}; 14118 14119module.exports = ImageLoader; 14120 14121},{"./dummyimagecontainer":5,"./framecontainer":8,"./imagecontainer":10,"./lineargradientcontainer":12,"./log":13,"./proxyimagecontainer":17,"./svgcontainer":23,"./svgnodecontainer":24,"./ut
14121ils":26,"./webkitgradientcontainer":27}],12:[function(_dereq_,module,exports){ 14122var GradientContainer = _dereq_('./gradientcontainer'); 14123var Color = _dereq_('./color'); 14124 14125function LinearGradientContainer(imageData) { 14126 GradientContainer.apply(this, arguments); 14127 this.type = GradientContainer.TYPES.LINEAR; 14128 14129 var hasDirection = LinearGradientContainer.REGEXP_DIRECTION.test( imageData.args[0] ) || 14130 !GradientContainer.REGEXP_COLORSTOP.test( imageData.args[0] ); 14131 14132 if (hasDirection) { 14133 imageData.args[0].split(/\s+/).reverse().forEach(function(position, index) { 14134 switch(position) { 14135 case "left": 14136 this.x0 = 0; 14137 this.x1 = 1; 14138 break; 14139 case "top": 14140 this.y0 = 0; 14141 this.y1 = 1; 14142 break; 14143 case "right": 14144 this.x0 = 1; 14145 this.x1 = 0; 14146 break; 14147 case "bottom": 14148 this.y0 = 1; 14149 this.y1 = 0; 14150 break; 14151 case "to": 14152 var y0 = this.y0; 14153 var x0 = this.x0; 14154 this.y0 = this.y1; 14155 this.x0 = this.x1; 14156 this.x1 = x0; 14157 this.y1 = y0; 14158 break; 14159 case "center": 14160 break; // centered by default 14161 // Firefox internally converts position keywords to percentages: 14162 // http://www.w3.org/TR/2010/WD-CSS2-20101207/colors.html#propdef-background-position 14163 default: // percentage or absolute length 14164 // TODO: support absolute start point positions (e.g., use bounds to convert px to a ratio) 14165 var ratio = parseFloat(position, 10) * 1e-2; 14166 if (isNaN(ratio)) { // invalid or unhandled value 14167 break; 14168 } 14169 if (index === 0) { 14170 this.y0 = ratio; 14171 this.y1 = 1 - this.y0; 14172 } else { 14173 this.x0 = ratio; 14174 this.x1 = 1 - this.x0; 14175 } 14176 break; 14177 } 14178 }, this); 14179 } else { 14180 this.y0 = 0; 14181 this.y1 = 1; 14182 } 14183 14184 this.colorStops = imageData.args.slice(hasDirection ? 1 : 0).map(function(colorStop) { 14185 var colorStopMatch = colorStop.match(GradientContainer.REGEXP_COLORSTOP); 14186 var value = +colorStopMatch[2]; 14187 var unit = value === 0 ? "%" : colorStopMatch[3]; // treat "0" as "0%" 14188 return { 14189 color: new Color(colorStopMatch[1]), 14190 // TODO: support absolute stop positions (e.g., compute gradient line length & convert px to ratio) 14191 stop: unit === "%" ? value / 100 : null 14192 }; 14193 }); 14194 14195 if (this.colorStops[0].stop === null) { 14196 this.colorStops[0].stop = 0; 14197 } 14198 14199 if (this.colorStops[this.colorStops.length - 1].stop === null) { 14200 this.colorStops[this.colorStops.length - 1].stop = 1; 14201 } 14202 14203 // calculates and fills-in explicit stop positions when omitted from rule 14204 this.colorStops.forEach(function(colorStop, index) { 14205 if (colorStop.stop === null) { 14206 this.colorStops.slice(index).some(function(find, count) { 14207 if (find.stop !== null) { 14208 colorStop.stop = ((find.stop - this.colorStops[index - 1].stop) / (count + 1)) + this.colorStops[index - 1].stop; 14209 return true; 14210 } else { 14211 return false; 14212 } 14213 }, this); 14214 } 14215 }, this); 14216} 14217 14218LinearGradientContainer.prototype = Object.create(GradientContainer.prototype); 14219 14220// TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle ) 14221LinearGradientContainer.REGEXP_DIRECTION = /^\s*(?:to|left|right|top|bottom|center|\d{1,3}(?:\.\d+)?%?)(?:\s|$)/i; 14222 14223module.exports = LinearGradientContainer; 14224 14225},{"./color":3,"./gradientcontainer":9}],13:[function(_dereq_,module,exports){ 14226var logger = function() { 14227 if (logger.options.logging && window.console && window.console.log) { 14228 Function.prototype.bind.call(window.console.log, (window.console)).apply(window.console, [(Date.now() - logger.options.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0))); 14229 } 14230}; 14231 14232logger.options = {logging: false}; 14233module.exports = logger; 14234 14235},{}],14:[function(_dereq_,module,exports){ 14236var Color = _dereq_('./color'); 14237var utils = _dereq_('./utils'); 14238var getBounds = utils.getBounds;
14239var parseBackgrounds = utils.parseBackgrounds; 14240var offsetBounds = utils.offsetBounds; 14241 14242function NodeContainer(node, parent) { 14243 this.node = node; 14244 this.parent = parent; 14245 this.stack = null; 14246 this.bounds = null; 14247 this.borders = null; 14248 this.clip = []; 14249 this.backgroundClip = []; 14250 this.offsetBounds = null; 14251 this.visible = null; 14252 this.computedStyles = null; 14253 this.colors = {}; 14254 this.styles = {}; 14255 this.backgroundImages = null; 14256 this.transformData = null; 14257 this.transformMatrix = null; 14258 this.isPseudoElement = false; 14259 this.opacity = null; 14260} 14261 14262NodeContainer.prototype.cloneTo = function(stack) { 14263 stack.visible = this.visible; 14264 stack.borders = this.borders; 14265 stack.bounds = this.bounds; 14266 stack.clip = this.clip; 14267 stack.backgroundClip = this.backgroundClip; 14268 stack.computedStyles = this.computedStyles; 14269 stack.styles = this.styles; 14270 stack.backgroundImages = this.backgroundImages; 14271 stack.opacity = this.opacity; 14272}; 14273 14274NodeContainer.prototype.getOpacity = function() { 14275 return this.opacity === null ? (this.opacity = this.cssFloat('opacity')) : this.opacity; 14276}; 14277 14278NodeContainer.prototype.assignStack = function(stack) { 14279 this.stack = stack; 14280 stack.children.push(this); 14281}; 14282 14283NodeContainer.prototype.isElementVisible = function() { 14284 return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : ( 14285 this.css('display') !== "none" && 14286 this.css('visibility') !== "hidden" && 14287 !this.node.hasAttribute("data-html2canvas-ignore") && 14288 (this.node.nodeName !== "INPUT" || this.node.getAttribute("type") !== "hidden") 14289 ); 14290}; 14291 14292NodeContainer.prototype.css = function(attribute) { 14293 if (!this.computedStyles) { 14294 this.computedStyles = this.isPseudoElement ? this.parent.computedStyle(this.before ? ":before" : ":after") : this.computedStyle(null); 14295 } 14296 14297 return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]); 14298}; 14299 14300NodeContainer.prototype.prefixedCss = function(attribute) { 14301 var prefixes = ["webkit", "moz", "ms", "o"]; 14302 var value = this.css(attribute); 14303 if (value === undefined) { 14304 prefixes.some(function(prefix) { 14305 value = this.css(prefix + attribute.substr(0, 1).toUpperCase() + attribute.substr(1)); 14306 return value !== undefined; 14307 }, this); 14308 } 14309 return value === undefined ? null : value; 14310}; 14311 14312NodeContainer.prototype.computedStyle = function(type) { 14313 return this.node.ownerDocument.defaultView.getComputedStyle(this.node, type); 14314}; 14315 14316NodeContainer.prototype.cssInt = function(attribute) { 14317 var value = parseInt(this.css(attribute), 10); 14318 return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html 14319}; 14320 14321NodeContainer.prototype.color = function(attribute) { 14322 return this.colors[attribute] || (this.colors[attribute] = new Color(this.css(attribute))); 14323}; 14324 14325NodeContainer.prototype.cssFloat = function(attribute) { 14326 var value = parseFloat(this.css(attribute)); 14327 return (isNaN(value)) ? 0 : value; 14328}; 14329 14330NodeContainer.prototype.fontWeight = function() { 14331 var weight = this.css("fontWeight"); 14332 switch(parseInt(weight, 10)){ 14333 case 401: 14334 weight = "bold"; 14335 break; 14336 case 400: 14337 weight = "normal"; 14338 break; 14339 } 14340 return weight; 14341}; 14342 14343NodeContainer.prototype.parseClip = function() { 14344 var matches = this.css('clip').match(this.CLIP); 14345 if (matches) { 14346 return { 14347 top: parseInt(matches[1], 10), 14348 right: parseInt(matches[2], 10), 14349 bottom: parseInt(matches[3], 10), 14350 left: parseInt(matches[4], 10) 14351 }; 14352 } 14353 return null; 14354}; 14355 14356NodeContainer.prototype.parseBackgroundImages = function() { 14357 return this.backgroundImages || (this.backgroundImages = parseBackgrounds(this.css("backgroundImage"))); 14358}; 14359 14360NodeContainer.prototype.cssList = function(property, index) { 14361 var value = (this.css(property) || '').split(','); 14362 value = value[index || 0] || value[0] || 'auto'; 14363 value = value.trim().split(' '); 14364 if (value.length === 1) { 14365 value = [value[0], isPercentage(value[0]) ? 'auto' : value[0]]; 14366 } 14367 return value; 14368}; 14369
14370NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) { 14371 var size = this.cssList("backgroundSize", index); 14372 var width, height; 14373 14374 if (isPercentage(size[0])) { 14375 width = bounds.width * parseFloat(size[0]) / 100; 14376 } else if (/contain|cover/.test(size[0])) { 14377 var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height; 14378 return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio}; 14379 } else { 14380 width = parseInt(size[0], 10); 14381 } 14382 14383 if (size[0] === 'auto' && size[1] === 'auto') { 14384 height = image.height; 14385 } else if (size[1] === 'auto') { 14386 height = width / image.width * image.height; 14387 } else if (isPercentage(size[1])) { 14388 height = bounds.height * parseFloat(size[1]) / 100; 14389 } else { 14390 height = parseInt(size[1], 10); 14391 } 14392 14393 if (size[0] === 'auto') { 14394 width = height / image.height * image.width; 14395 } 14396 14397 return {width: width, height: height}; 14398}; 14399 14400NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) { 14401 var position = this.cssList('backgroundPosition', index); 14402 var left, top; 14403 14404 if (isPercentage(position[0])){ 14405 left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100); 14406 } else { 14407 left = parseInt(position[0], 10); 14408 } 14409 14410 if (position[1] === 'auto') { 14411 top = left / image.width * image.height; 14412 } else if (isPercentage(position[1])){ 14413 top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100; 14414 } else { 14415 top = parseInt(position[1], 10); 14416 } 14417 14418 if (position[0] === 'auto') { 14419 left = top / image.height * image.width; 14420 } 14421 14422 return {left: left, top: top}; 14423}; 14424 14425NodeContainer.prototype.parseBackgroundRepeat = function(index) { 14426 return this.cssList("backgroundRepeat", index)[0]; 14427}; 14428 14429NodeContainer.prototype.parseTextShadows = function() { 14430 var textShadow = this.css("textShadow"); 14431 var results = []; 14432 14433 if (textShadow && textShadow !== 'none') { 14434 var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY); 14435 for (var i = 0; shadows && (i < shadows.length); i++) { 14436 var s = shadows[i].match(this.TEXT_SHADOW_VALUES); 14437 results.push({ 14438 color: new Color(s[0]), 14439 offsetX: s[1] ? parseFloat(s[1].replace('px', '')) : 0, 14440 offsetY: s[2] ? parseFloat(s[2].replace('px', '')) : 0, 14441 blur: s[3] ? s[3].replace('px', '') : 0 14442 }); 14443 } 14444 } 14445 return results; 14446}; 14447 14448NodeContainer.prototype.parseTransform = function() { 14449 if (!this.transformData) { 14450 if (this.hasTransform()) { 14451 var offset = this.parseBounds(); 14452 var origin = this.prefixedCss("transformOrigin").split(" ").map(removePx).map(asFloat); 14453 origin[0] += offset.left; 14454 origin[1] += offset.top; 14455 this.transformData = { 14456 origin: origin, 14457 matrix: this.parseTransformMatrix() 14458 }; 14459 } else { 14460 this.transformData = { 14461 origin: [0, 0], 14462 matrix: [1, 0, 0, 1, 0, 0] 14463 }; 14464 } 14465 } 14466 return this.transformData; 14467}; 14468 14469NodeContainer.prototype.parseTransformMatrix = function() { 14470 if (!this.transformMatrix) { 14471 var transform = this.prefixedCss("transform"); 14472 var matrix = transform ? parseMatrix(transform.match(this.MATRIX_PROPERTY)) : null; 14473 this.transformMatrix = matrix ? matrix : [1, 0, 0, 1, 0, 0]; 14474 } 14475 return this.transformMatrix; 14476}; 14477 14478NodeContainer.prototype.parseBounds = function() { 14479 return this.bounds || (this.bounds = this.hasTransform() ? offsetBounds(this.node) : getBounds(this.node)); 14480}; 14481 14482NodeContainer.prototype.hasTransform = function() { 14483 return this.parseTransformMatrix().join(",") !== "1,0,0,1,0,0" || (this.parent && this.parent.hasTransform()); 14484}; 14485 14486NodeContainer.prototype.getValue = function() { 14487 var value = this.node.value || ""; 14488 if (this.node.tagName === "SELECT") { 14489 value = selectionValue(this.node); 14490 } else if (this.node.type === "password") { 14491 value = Array(value.length + 1).join('\u2022'); // jshint ignore:line 14492 } 14493 return value.length === 0 ? (this.node.placeholder || "") : value; 14494}; 14495 14496NodeContainer.prototype.MATRIX_PROPERTY = /(matrix|matrix3d)\((.+)\)/; 14497NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g; 14498NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g; 14499NodeContainer.prototype.CLIP = /^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/; 14500 14501function selectionValue(node) { 14502 var option = node.options[node.selectedIndex || 0]; 14503 return option ? (option.text || "") : ""; 14504} 14505 14506function parseMatrix(match) { 14507 if (match && match[1] === "matrix") { 14508 return match[2].split(",").map(function(s) { 14509 return parseFloat(s.trim()); 14510 }); 14511 } else if (match && match[1] === "matrix3d") { 14512 var matrix3d = match[2].split(",").map(function(s) { 14513 return parseFloat(s.trim()); 14514 }); 14515 return [matrix3d[0], matrix3d[1], matrix3d[4], matrix3d[5], matrix3d[12], matrix3d[13]]; 14516 } 14517} 14518 14519function isPercentage(value) { 14520 return value.toString().indexOf("%") !== -1; 14521} 14522 14523function removePx(str) { 14524 return str.replace("px", ""); 14525} 14526 14527function asFloat(str) { 14528 return parseFloat(str); 14529} 14530 14531module.exports = NodeContainer; 14532 14533},{"./color":3,"./utils":26}],15:[function(_dereq_,module,exports){ 14534var log = _dereq_('./log'); 14535var punycode = _dereq_('punycode'); 14536var NodeContainer = _dereq_('./nodecontainer'); 14537var TextContainer = _dereq_('./textcontainer'); 14538var PseudoElementContainer = _dereq_('./pseudoelementcontainer'); 14539var FontMetrics = _dereq_('./fontmetrics'); 14540var Color = _dereq_('./color'); 14541var StackingContext = _dereq_('./stackingcontext'); 14542var utils = _dereq_('./utils'); 14543var bind = utils.bind; 14544var getBounds = utils.getBounds;
14545var parseBackgrounds = utils.parseBackgrounds; 14546var offsetBounds = utils.offsetBounds; 14547 14548function NodeParser(element, renderer, support, imageLoader, options) { 14549 log("Starting NodeParser"); 14550 this.renderer = renderer; 14551 this.options = options; 14552 this.range = null; 14553 this.support = support; 14554 this.renderQueue = []; 14555 this.stack = new StackingContext(true, 1, element.ownerDocument, null); 14556 var parent = new NodeContainer(element, null); 14557 if (options.background) { 14558 renderer.rectangle(0, 0, renderer.width, renderer.height, new Color(options.background)); 14559 } 14560 if (element === element.ownerDocument.documentElement) { 14561 // http://www.w3.org/TR/css3-background/#special-backgrounds 14562 var canvasBackground = new NodeContainer(parent.color('backgroundColor').isTransparent() ? element.ownerDocument.body : element.ownerDocument.documentElement, null); 14563 renderer.rectangle(0, 0, renderer.width, renderer.height, canvasBackground.color('backgroundColor')); 14564 } 14565 parent.visibile = parent.isElementVisible(); 14566 this.createPseudoHideStyles(element.ownerDocument); 14567 this.disableAnimations(element.ownerDocument); 14568 this.nodes = flatten([parent].concat(this.getChildren(parent)).filter(function(container) { 14569 return container.visible = container.isElementVisible(); 14570 }).map(this.getPseudoElements, this)); 14571 this.fontMetrics = new FontMetrics(); 14572 log("Fetched nodes, total:", this.nodes.length); 14573 log("Calculate overflow clips"); 14574 this.calculateOverflowClips(); 14575 log("Start fetching images"); 14576 this.images = imageLoader.fetch(this.nodes.filter(isElement)); 14577 this.ready = this.images.ready.then(bind(function() { 14578 log("Images loaded, starting parsing"); 14579 log("Creating stacking contexts"); 14580 this.createStackingContexts(); 14581 log("Sorting stacking contexts"); 14582 this.sortStackingContexts(this.stack); 14583 this.parse(this.stack); 14584 log("Render queue created with " + this.renderQueue.length + " items"); 14585 return new Promise(bind(function(resolve) { 14586 if (!options.async) { 14587 this.renderQueue.forEach(this.paint, this); 14588 resolve(); 14589 } else if (typeof(options.async) === "function") { 14590 options.async.call(this, this.renderQueue, resolve); 14591 } else if (this.renderQueue.length > 0){ 14592 this.renderIndex = 0; 14593 this.asyncRenderer(this.renderQueue, resolve); 14594 } else { 14595 resolve(); 14596 } 14597 }, this)); 14598 }, this)); 14599} 14600 14601NodeParser.prototype.calculateOverflowClips = function() { 14602 this.nodes.forEach(function(container) { 14603 if (isElement(container)) { 14604 if (isPseudoElement(container)) { 14605 container.appendToDOM(); 14606 } 14607 container.borders = this.parseBorders(container); 14608 var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : []; 14609 var cssClip = container.parseClip(); 14610 if (cssClip && ["absolute", "fixed"].indexOf(container.css('position')) !== -1) { 14611 clip.push([["rect", 14612 container.bounds.left + cssClip.left, 14613 container.bounds.top + cssClip.top, 14614 cssClip.right - cssClip.left, 14615 cssClip.bottom - cssClip.top 14616 ]]); 14617 } 14618 container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip; 14619 container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip; 14620 if (isPseudoElement(container)) { 14621 container.cleanDOM(); 14622 } 14623 } else if (isTextNode(container)) { 14624 container.clip = hasParentClip(container) ? container.parent.clip : []; 14625 } 14626 if (!isPseudoElement(container)) { 14627 container.bounds = null; 14628 } 14629 }, this); 14630}; 14631 14632function hasParentClip(container) { 14633 return container.parent && container.parent.clip.length; 14634} 14635 14636NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) { 14637 asyncTimer = asyncTimer || Date.now(); 14638 this.paint(queue[this.renderIndex++]); 14639 if (queue.length === this.renderIndex) { 14640 resolve(); 14641 } else if (asyncTimer + 20 > Date.now()) { 14642 this.asyncRenderer(queue, resolve, asyncTimer); 14643 } else { 14644 setTimeout(bind(function() { 14645 this.asyncRenderer(queue, resolve); 14646 }, this), 0); 14647 } 14648}; 14649 14650NodeParser.prototype.createPseudoHideStyles = function(document) { 14651 this.createStyles(document, '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + ':before { content: "" !important; display: none !important; }' + 14652 '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER + ':after { content: "" !important; display: none !important; }'); 14653}; 14654 14655NodeParser.prototype.disableAnimations = function(document) { 14656 this.createStyles(document, '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' +
14657 '-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}'); 14658}; 14659 14660NodeParser.prototype.createStyles = function(document, styles) { 14661 var hidePseudoElements = document.createElement('style'); 14662 hidePseudoElements.innerHTML = styles; 14663 document.body.appendChild(hidePseudoElements); 14664}; 14665 14666NodeParser.prototype.getPseudoElements = function(container) { 14667 var nodes = [[container]]; 14668 if (container.node.nodeType === Node.ELEMENT_NODE) { 14669 var before = this.getPseudoElement(container, ":before"); 14670 var after = this.getPseudoElement(container, ":after"); 14671 14672 if (before) { 14673 nodes.push(before); 14674 } 14675 14676 if (after) { 14677 nodes.push(after); 14678 } 14679 } 14680 return flatten(nodes); 14681}; 14682 14683function toCamelCase(str) { 14684 return str.replace(/(\-[a-z])/g, function(match){ 14685 return match.toUpperCase().replace('-',''); 14686 }); 14687} 14688 14689NodeParser.prototype.getPseudoElement = function(container, type) { 14690 var style = container.computedStyle(type); 14691 if(!style || !style.content || style.content === "none" || style.content === "-moz-alt-content" || style.display === "none") { 14692 return null; 14693 } 14694 14695 var content = stripQuotes(style.content); 14696 var isImage = content.substr(0, 3) === 'url'; 14697 var pseudoNode = document.createElement(isImage ? 'img' : 'html2canvaspseudoelement'); 14698 var pseudoContainer = new PseudoElementContainer(pseudoNode, container, type); 14699 14700 for (var i = style.length-1; i >= 0; i--) { 14701 var property = toCamelCase(style.item(i)); 14702 pseudoNode.style[property] = style[property]; 14703 } 14704 14705 pseudoNode.className = PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + " " + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER; 14706 14707 if (isImage) { 14708 pseudoNode.src = parseBackgrounds(content)[0].args[0]; 14709 return [pseudoContainer]; 14710 } else { 14711 var text = document.createTextNode(content); 14712 pseudoNode.appendChild(text); 14713 return [pseudoContainer, new TextContainer(text, pseudoContainer)]; 14714 } 14715}; 14716 14717 14718NodeParser.prototype.getChildren = function(parentContainer) { 14719 return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) { 14720 var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement); 14721 return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container; 14722 }, this)); 14723}; 14724 14725NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) { 14726 var stack = new StackingContext(hasOwnStacking, container.getOpacity(), container.node, container.parent); 14727 container.cloneTo(stack); 14728 var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack; 14729 parentStack.contexts.push(stack); 14730 container.stack = stack; 14731}; 14732 14733NodeParser.prototype.createStackingContexts = function() { 14734 this.nodes.forEach(function(container) { 14735 if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container) || container.hasTransform())) { 14736 this.newStackingContext(container, true); 14737 } else if (isElement(container) && ((isPositioned(container) && zIndex0(container)) || isInlineBlock(container) || isFloating(container))) { 14738 this.newStackingContext(container, false); 14739 } else { 14740 container.assignStack(container.parent.stack); 14741 } 14742 }, this); 14743}; 14744 14745NodeParser.prototype.isBodyWithTransparentRoot = function(container) { 14746 return container.node.nodeName === "BODY" && container.parent.color('backgroundColor').isTransparent(); 14747}; 14748 14749NodeParser.prototype.isRootElement = function(container) { 14750 return container.parent === null; 14751}; 14752 14753NodeParser.prototype.sortStackingContexts = function(stack) { 14754 stack.contexts.sort(zIndexSort(stack.contexts.slice(0)));
14755 stack.contexts.forEach(this.sortStackingContexts, this); 14756}; 14757 14758NodeParser.prototype.parseTextBounds = function(container) { 14759 return function(text, index, textList) { 14760 if (container.parent.css("textDecoration").substr(0, 4) !== "none" || text.trim().length !== 0) { 14761 if (this.support.rangeBounds && !container.parent.hasTransform()) { 14762 var offset = textList.slice(0, index).join("").length; 14763 return this.getRangeBounds(container.node, offset, text.length); 14764 } else if (container.node && typeof(container.node.data) === "string") { 14765 var replacementNode = container.node.splitText(text.length); 14766 var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform()); 14767 container.node = replacementNode; 14768 return bounds; 14769 } 14770 } else if(!this.support.rangeBounds || container.parent.hasTransform()){ 14771 container.node = container.node.splitText(text.length); 14772 } 14773 return {}; 14774 }; 14775}; 14776 14777NodeParser.prototype.getWrapperBounds = function(node, transform) { 14778 var wrapper = node.ownerDocument.createElement('html2canvaswrapper'); 14779 var parent = node.parentNode, 14780 backupText = node.cloneNode(true); 14781 14782 wrapper.appendChild(node.cloneNode(true)); 14783 parent.replaceChild(wrapper, node); 14784 var bounds = transform ? offsetBounds(wrapper) : getBounds(wrapper); 14785 parent.replaceChild(backupText, wrapper); 14786 return bounds; 14787}; 14788 14789NodeParser.prototype.getRangeBounds = function(node, offset, length) { 14790 var range = this.range || (this.range = node.ownerDocument.createRange()); 14791 range.setStart(node, offset); 14792 range.setEnd(node, offset + length); 14793 return range.getBoundingClientRect(); 14794}; 14795 14796function ClearTransform() {} 14797 14798NodeParser.prototype.parse = function(stack) { 14799 // http://www.w3.org/TR/CSS21/visuren.html#z-index 14800 var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first). 14801 var descendantElements = stack.children.filter(isElement); 14802 var descendantNonFloats = descendantElements.filter(not(isFloating)); 14803 var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants. 14804 var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats. 14805 var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. 14806 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. 14807 var text = stack.children.filter(isTextNode).filter(hasText); 14808 var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first). 14809 negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats) 14810 .concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) { 14811 this.renderQueue.push(container); 14812 if (isStackingContext(container)) { 14813 this.parse(container); 14814 this.renderQueue.push(new ClearTransform()); 14815 } 14816 }, this); 14817}; 14818 14819NodeParser.prototype.paint = function(container) { 14820 try { 14821 if (container instanceof ClearTransform) { 14822 this.renderer.ctx.restore(); 14823 } else if (isTextNode(container)) { 14824 if (isPseudoElement(container.parent)) { 14825 container.parent.appendToDOM(); 14826 } 14827 this.paintText(container); 14828 if (isPseudoElement(container.parent)) { 14829 container.parent.cleanDOM(); 14830 } 14831 } else { 14832 this.paintNode(container); 14833 } 14834 } catch(e) { 14835 log(e); 14836 if (this.options.strict) { 14837 throw e; 14838 } 14839 } 14840}; 14841 14842NodeParser.prototype.paintNode = function(container) { 14843 if (isStackingContext(container)) { 14844 this.renderer.setOpacity(container.opacity); 14845 this.renderer.ctx.save(); 14846 if (container.hasTransform()) { 14847 this.renderer.setTransform(container.parseTransform()); 14848 } 14849 } 14850 14851 if (container.node.nodeName === "INPUT" && container.node.type === "checkbox") { 14852 this.paintCheckbox(container); 14853 } else if (container.node.nodeName === "INPUT" && container.node.type === "radio") { 14854 this.paintRadio(container); 14855 } else { 14856 this.paintElement(container); 14857 } 14858}; 14859 14860NodeParser.prototype.paintElement = function(container) { 14861 var bounds = container.parseBounds(); 14862 this.renderer.clip(container.backgroundClip, function() {
14863 this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth)); 14864 }, this); 14865 14866 this.renderer.clip(container.clip, function() { 14867 this.renderer.renderBorders(container.borders.borders); 14868 }, this); 14869 14870 this.renderer.clip(container.backgroundClip, function() { 14871 switch (container.node.nodeName) { 14872 case "svg": 14873 case "IFRAME": 14874 var imgContainer = this.images.get(container.node); 14875 if (imgContainer) { 14876 this.renderer.renderImage(container, bounds, container.borders, imgContainer); 14877 } else { 14878 log("Error loading <" + container.node.nodeName + ">", container.node); 14879 } 14880 break; 14881 case "IMG": 14882 var imageContainer = this.images.get(container.node.src); 14883 if (imageContainer) { 14884 this.renderer.renderImage(container, bounds, container.borders, imageContainer); 14885 } else { 14886 log("Error loading <img>", container.node.src); 14887 } 14888 break; 14889 case "CANVAS": 14890 this.renderer.renderImage(container, bounds, container.borders, {image: container.node}); 14891 break; 14892 case "SELECT": 14893 case "INPUT": 14894 case "TEXTAREA": 14895 this.paintFormValue(container); 14896 break; 14897 } 14898 }, this); 14899}; 14900 14901NodeParser.prototype.paintCheckbox = function(container) { 14902 var b = container.parseBounds(); 14903 14904 var size = Math.min(b.width, b.height); 14905 var bounds = {width: size - 1, height: size - 1, top: b.top, left: b.left}; 14906 var r = [3, 3]; 14907 var radius = [r, r, r, r]; 14908 var borders = [1,1,1,1].map(function(w) { 14909 return {color: new Color('#A5A5A5'), width: w}; 14910 }); 14911 14912 var borderPoints = calculateCurvePoints(bounds, radius, borders); 14913 14914 this.renderer.clip(container.backgroundClip, function() { 14915 this.renderer.rectangle(bounds.left + 1, bounds.top + 1, bounds.width - 2, bounds.height - 2, new Color("#DEDEDE")); 14916 this.renderer.renderBorders(calculateBorders(borders, bounds, borderPoints, radius)); 14917 if (container.node.checked) { 14918 this.renderer.font(new Color('#424242'), 'normal', 'normal', 'bold', (size - 3) + "px", 'arial'); 14919 this.renderer.text("\u2714", bounds.left + size / 6, bounds.top + size - 1); 14920 } 14921 }, this); 14922}; 14923 14924NodeParser.prototype.paintRadio = function(container) { 14925 var bounds = container.parseBounds(); 14926 14927 var size = Math.min(bounds.width, bounds.height) - 2; 14928 14929 this.renderer.clip(container.backgroundClip, function() { 14930 this.renderer.circleStroke(bounds.left + 1, bounds.top + 1, size, new Color('#DEDEDE'), 1, new Color('#A5A5A5')); 14931 if (container.node.checked) { 14932 this.renderer.circle(Math.ceil(bounds.left + size / 4) + 1, Math.ceil(bounds.top + size / 4) + 1, Math.floor(size / 2), new Color('#424242')); 14933 } 14934 }, this); 14935}; 14936 14937NodeParser.prototype.paintFormValue = function(container) { 14938 var value = container.getValue(); 14939 if (value.length > 0) { 14940 var document = container.node.ownerDocument; 14941 var wrapper = document.createElement('html2canvaswrapper'); 14942 var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color', 14943 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 14944 'width', 'height', 'borderLeftStyle', 'borderTopStyle', 'borderLeftWidth', 'borderTopWidth', 14945 'boxSizing', 'whiteSpace', 'wordWrap']; 14946 14947 properties.forEach(function(property) { 14948 try { 14949 wrapper.style[property] = container.css(property); 14950 } catch(e) { 14951 // Older IE has issues with "border" 14952 log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); 14953 } 14954 }); 14955 var bounds = container.parseBounds(); 14956 wrapper.style.position = "fixed"; 14957 wrapper.style.left = bounds.left + "px"; 14958 wrapper.style.top = bounds.top + "px"; 14959 wrapper.textContent = value; 14960 document.body.appendChild(wrapper); 14961 this.paintText(new TextContainer(wrapper.firstChild, container)); 14962 document.body.removeChild(wrapper); 14963 } 14964}; 14965 14966NodeParser.prototype.paintText = function(container) { 14967 container.applyTextTransform(); 14968 var characters = punycode.ucs2.decode(container.node.data); 14969 var textList = (!this.options.letterRendering || noLetterSpacing(container)) && !hasUnicode(container.node.data) ? getWords(characters) : characters.map(function(ch
14969aracter) { 14970 return punycode.ucs2.encode([character]); 14971 }); 14972 14973 var weight = container.parent.fontWeight(); 14974 var size = container.parent.css('fontSize'); 14975 var family = container.parent.css('fontFamily'); 14976 var shadows = container.parent.parseTextShadows(); 14977 14978 this.renderer.font(container.parent.color('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family); 14979 if (shadows.length) { 14980 // TODO: support multiple text shadows 14981 this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur); 14982 } else { 14983 this.renderer.clearShadow(); 14984 } 14985 14986 this.renderer.clip(container.parent.clip, function() { 14987 textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) { 14988 if (bounds) { 14989 this.renderer.text(textList[index], bounds.left, bounds.bottom); 14990 this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size)); 14991 } 14992 }, this); 14993 }, this); 14994}; 14995 14996NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) { 14997 switch(container.css("textDecoration").split(" ")[0]) { 14998 case "underline": 14999 // Draws a line at the baseline of the font 15000 // 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 15001 this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.color("color")); 15002 break; 15003 case "overline": 15004 this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.color("color")); 15005 break; 15006 case "line-through": 15007 // TODO try and find exact position for line-through 15008 this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.color("color")); 15009 break; 15010 } 15011}; 15012 15013var borderColorTransforms = { 15014 inset: [ 15015 ["darken", 0.60], 15016 ["darken", 0.10], 15017 ["darken", 0.10], 15018 ["darken", 0.60] 15019 ] 15020}; 15021 15022NodeParser.prototype.parseBorders = function(container) { 15023 var nodeBounds = container.parseBounds(); 15024 var radius = getBorderRadiusData(container); 15025 var borders = ["Top", "Right", "Bottom", "Left"].map(function(side, index) { 15026 var style = container.css('border' + side + 'Style'); 15027 var color = container.color('border' + side + 'Color'); 15028 if (style === "inset" && color.isBlack()) { 15029 color = new Color([255, 255, 255, color.a]); // this is wrong, but 15030 } 15031 var colorTransform = borderColorTransforms[style] ? borderColorTransforms[style][index] : null; 15032 return { 15033 width: container.cssInt('border' + side + 'Width'), 15034 color: colorTransform ? color[colorTransform[0]](colorTransform[1]) : color, 15035 args: null 15036 }; 15037 }); 15038 var borderPoints = calculateCurvePoints(nodeBounds, radius, borders); 15039 15040 return { 15041 clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds), 15042 borders: calculateBorders(borders, nodeBounds, borderPoints, radius) 15043 }; 15044}; 15045 15046function calculateBorders(borders, nodeBounds, borderPoints, radius) { 15047 return borders.map(function(border, borderSide) { 15048 if (border.width > 0) { 15049 var bx = nodeBounds.left; 15050 var by = nodeBounds.top; 15051 var bw = nodeBounds.width; 15052 var bh = nodeBounds.height - (borders[2].width); 15053 15054 switch(borderSide) { 15055 case 0: 15056 // top border 15057 bh = borders[0].width; 15058 border.args = drawSide({ 15059 c1: [bx, by], 15060 c2: [bx + bw, by], 15061 c3: [bx + bw - borders[1].width, by + bh], 15062 c4: [bx + borders[3].width, by + bh] 15063 }, radius[0], radius[1], 15064 borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner); 15065 break; 15066 case 1: 15067 // right border 15068 bx = nodeBounds.left + nodeBounds.width - (borders[1].width); 15069 bw = borders[1].width; 15070 15071 border.args = drawSide({
15072 c1: [bx + bw, by], 15073 c2: [bx + bw, by + bh + borders[2].width], 15074 c3: [bx, by + bh], 15075 c4: [bx, by + borders[0].width] 15076 }, radius[1], radius[2], 15077 borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner); 15078 break; 15079 case 2: 15080 // bottom border 15081 by = (by + nodeBounds.height) - (borders[2].width); 15082 bh = borders[2].width; 15083 border.args = drawSide({ 15084 c1: [bx + bw, by + bh], 15085 c2: [bx, by + bh], 15086 c3: [bx + borders[3].width, by], 15087 c4: [bx + bw - borders[3].width, by] 15088 }, radius[2], radius[3], 15089 borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner); 15090 break; 15091 case 3: 15092 // left border 15093 bw = borders[3].width; 15094 border.args = drawSide({ 15095 c1: [bx, by + bh + borders[2].width], 15096 c2: [bx, by], 15097 c3: [bx + bw, by + borders[0].width], 15098 c4: [bx + bw, by + bh] 15099 }, radius[3], radius[0], 15100 borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner); 15101 break; 15102 } 15103 } 15104 return border; 15105 }); 15106} 15107 15108NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) { 15109 var backgroundClip = container.css('backgroundClip'), 15110 borderArgs = []; 15111 15112 switch(backgroundClip) { 15113 case "content-box": 15114 case "padding-box": 15115 parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width); 15116 parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width); 15117 parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width); 15118 parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width); 15119 break; 15120 15121 default: 15122 parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top); 15123 parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top); 15124 parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height); 15125 parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height); 15126 break; 15127 } 15128 15129 return borderArgs; 15130}; 15131 15132function getCurvePoints(x, y, r1, r2) { 15133 var kappa = 4 * ((Math.sqrt(2) - 1) / 3); 15134 var ox = (r1) * kappa, // control point offset horizontal 15135 oy = (r2) * kappa, // control point offset vertical 15136 xm = x + r1, // x-middle 15137 ym = y + r2; // y-middle 15138 return { 15139 topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}), 15140 topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}), 15141 bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}), 15142 bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y}) 15143 }; 15144} 15145 15146function calculateCurvePoints(bounds, borderRadius, borders) { 15147 var x = bounds.left, 15148 y = bounds.top, 15149 width = bounds.width, 15150 height = bounds.height, 15151 15152 tlh = borderRadius[0][0] < width / 2 ? borderRadius[0][0] : width / 2, 15153 tlv = borderRadius[0][1] < height / 2 ? borderRadius[0][1] : height / 2, 15154 trh = borderRadius[1][0] < width / 2 ? borderRadius[1][0] : width / 2, 15155 trv = borderRadius[1][1] < height / 2 ? borderRadius[1][1] : height / 2, 15156 brh = borderRadius[2][0] < width / 2 ? borderRadius[2][0] : width / 2, 15157 brv = borderRadius[2][1] < height / 2 ? borderRadius[2][1] : height / 2, 15158 blh = borderRadius[3][0] < width / 2 ? borderRadius[3][0] : width / 2, 15159 blv = borderRadius[3][1] < height / 2 ? borderRadius[3][1] : height / 2; 15160 15161 var topWidth = width - trh, 15162 rightHeight = height - brv, 15163 bottomWidth = width - brh, 15164 leftHeight = height - blv; 15165 15166 return { 15167 topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5), 15168 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), 15169 topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5), 15170 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), 15171 bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5), 15172 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), 15173 bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5), 15174 bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), blv - borders[2].width).bottomLeft.subdivide(0.5) 15175 }; 15176} 15177 15178function bezierCurve(start, startControl, endControl, end) { 15179 var lerp = function (a, b, t) { 15180 return { 15181 x: a.x + (b.x - a.x) * t, 15182 y: a.y + (b.y - a.y) * t 15183 }; 15184 }; 15185 15186 return { 15187 start: start, 15188 startControl: startControl, 15189 endControl: endControl, 15190 end: end, 15191 subdivide: function(t) { 15192 var ab = lerp(start, startControl, t), 15193 bc = lerp(startControl, endControl, t), 15194 cd = lerp(endControl, end, t), 15195 abbc = lerp(ab, bc, t), 15196 bccd = lerp(bc, cd, t), 15197 dest = lerp(abbc, bccd, t); 15198 return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)]; 15199 }, 15200 curveTo: function(borderArgs) { 15201 borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]); 15202 }, 15203 curveToReversed: function(borderArgs) { 15204 borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]); 15205 } 15206 }; 15207} 15208 15209function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { 15210 var borderArgs = []; 15211 15212 if (radius1[0] > 0 || radius1[1] > 0) { 15213 borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]); 15214 outer1[1].curveTo(borderArgs); 15215 } else { 15216 borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]); 15217 } 15218 15219 if (radius2[0] > 0 || radius2[1] > 0) { 15220 borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]); 15221 outer2[0].curveTo(borderArgs); 15222 borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]); 15223 inner2[0].curveToReversed(borderArgs); 15224 } else { 15225 borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]); 15226 borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]); 15227 } 15228 15229 if (radius1[0] > 0 || radius1[1] > 0) { 15230 borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]); 15231 inner1[1].curveToReversed(borderArgs); 15232 } else { 15233 borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]); 15234 } 15235 15236 return borderArgs; 15237} 15238 15239function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) { 15240 if (radius1[0] > 0 || radius1[1] > 0) {
15241 borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]); 15242 corner1[0].curveTo(borderArgs); 15243 corner1[1].curveTo(borderArgs); 15244 } else { 15245 borderArgs.push(["line", x, y]); 15246 } 15247 15248 if (radius2[0] > 0 || radius2[1] > 0) { 15249 borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]); 15250 } 15251} 15252 15253function negativeZIndex(container) { 15254 return container.cssInt("zIndex") < 0; 15255} 15256 15257function positiveZIndex(container) { 15258 return container.cssInt("zIndex") > 0; 15259} 15260 15261function zIndex0(container) { 15262 return container.cssInt("zIndex") === 0; 15263} 15264 15265function inlineLevel(container) { 15266 return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1; 15267} 15268 15269function isStackingContext(container) { 15270 return (container instanceof StackingContext); 15271} 15272 15273function hasText(container) { 15274 return container.node.data.trim().length > 0; 15275} 15276 15277function noLetterSpacing(container) { 15278 return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing"))); 15279} 15280 15281function getBorderRadiusData(container) { 15282 return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) { 15283 var value = container.css('border' + side + 'Radius'); 15284 var arr = value.split(" "); 15285 if (arr.length <= 1) { 15286 arr[1] = arr[0]; 15287 } 15288 return arr.map(asInt); 15289 }); 15290} 15291 15292function renderableNode(node) { 15293 return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE); 15294} 15295 15296function isPositionedForStacking(container) { 15297 var position = container.css("position"); 15298 var zIndex = (["absolute", "relative", "fixed"].indexOf(position) !== -1) ? container.css("zIndex") : "auto"; 15299 return zIndex !== "auto"; 15300} 15301 15302function isPositioned(container) { 15303 return container.css("position") !== "static"; 15304} 15305 15306function isFloating(container) { 15307 return container.css("float") !== "none"; 15308} 15309 15310function isInlineBlock(container) { 15311 return ["inline-block", "inline-table"].indexOf(container.css("display")) !== -1; 15312} 15313 15314function not(callback) { 15315 var context = this; 15316 return function() { 15317 return !callback.apply(context, arguments); 15318 }; 15319} 15320 15321function isElement(container) { 15322 return container.node.nodeType === Node.ELEMENT_NODE; 15323} 15324 15325function isPseudoElement(container) { 15326 return container.isPseudoElement === true; 15327} 15328 15329function isTextNode(container) { 15330 return container.node.nodeType === Node.TEXT_NODE; 15331} 15332 15333function zIndexSort(contexts) { 15334 return function(a, b) { 15335 return (a.cssInt("zIndex") + (contexts.indexOf(a) / contexts.length)) - (b.cssInt("zIndex") + (contexts.indexOf(b) / contexts.length)); 15336 }; 15337} 15338 15339function hasOpacity(container) { 15340 return container.getOpacity() < 1; 15341} 15342 15343function asInt(value) { 15344 return parseInt(value, 10); 15345} 15346 15347function getWidth(border) { 15348 return border.width; 15349} 15350 15351function nonIgnoredElement(nodeContainer) { 15352 return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR", "OPTION"].indexOf(nodeContainer.node.nodeName) === -1); 15353} 15354 15355function flatten(arrays) { 15356 return [].concat.apply([], arrays); 15357} 15358 15359function stripQuotes(content) { 15360 var first = content.substr(0, 1); 15361 return (first === content.substr(content.length - 1) && first.match(/'|"/)) ? content.substr(1, content.length - 2) : content; 15362} 15363 15364function getWords(characters) { 15365 var words = [], i = 0, onWordBoundary = false, word; 15366 while(characters.length) { 15367 if (isWordBoundary(characters[i]) === onWordBoundary) { 15368 word = characters.splice(0, i); 15369 if (word.length) { 15370 words.push(punycode.ucs2.encode(word)); 15371 } 15372 onWordBoundary =! onWordBoundary; 15373 i = 0; 15374 } else { 15375 i++; 15376 } 15377 15378 if (i >= characters.length) { 15379 word = characters.splice(0, i); 15380 if (word.length) { 15381 words.push(punycode.ucs2.encode(word)); 15382 } 15383 } 15384 } 15385 return words; 15386} 15387 15388function isWordBoundary(characterCode) { 15389 return [ 15390 32, // <space> 15391 13, // \r 15392 10, // \n 15393 9, // \t 15394 45 // - 15395 ].indexOf(characterCode) !== -1; 15396} 15397 15398function hasUnicode(string) { 15399 return (/[^\u0000-\u00ff]/).test(string); 15400} 15401 15402module.exports = NodeParser; 15403 15404},{"./color":3,"./fontmetrics":7,"./log":13,"./nodecontainer":14,"./pseudoelementcontainer":18,"./stackingcontext":21,"./textcontainer":25,"./utils":26,"pu
15404nycode":1}],16:[function(_dereq_,module,exports){ 15405var XHR = _dereq_('./xhr'); 15406var utils = _dereq_('./utils'); 15407var log = _dereq_('./log'); 15408var createWindowClone = _dereq_('./clone'); 15409var decode64 = utils.decode64; 15410 15411function Proxy(src, proxyUrl, document) { 15412 var supportsCORS = ('withCredentials' in new XMLHttpRequest()); 15413 if (!proxyUrl) { 15414 return Promise.reject("No proxy configured"); 15415 } 15416 var callback = createCallback(supportsCORS); 15417 var url = createProxyUrl(proxyUrl, src, callback); 15418 15419 return supportsCORS ? XHR(url) : (jsonp(document, url, callback).then(function(response) { 15420 return decode64(response.content); 15421 })); 15422} 15423var proxyCount = 0; 15424 15425function ProxyURL(src, proxyUrl, document) { 15426 var supportsCORSImage = ('crossOrigin' in new Image()); 15427 var callback = createCallback(supportsCORSImage); 15428 var url = createProxyUrl(proxyUrl, src, callback); 15429 return (supportsCORSImage ? Promise.resolve(url) : jsonp(document, url, callback).then(function(response) { 15430 return "data:" + response.type + ";base64," + response.content; 15431 })); 15432} 15433 15434function jsonp(document, url, callback) { 15435 return new Promise(function(resolve, reject) { 15436 var s = document.createElement("script"); 15437 var cleanup = function() { 15438 delete window.html2canvas.proxy[callback]; 15439 document.body.removeChild(s); 15440 }; 15441 window.html2canvas.proxy[callback] = function(response) { 15442 cleanup(); 15443 resolve(response); 15444 }; 15445 s.src = url; 15446 s.onerror = function(e) { 15447 cleanup(); 15448 reject(e); 15449 }; 15450 document.body.appendChild(s); 15451 }); 15452} 15453 15454function createCallback(useCORS) { 15455 return !useCORS ? "html2canvas_" + Date.now() + "_" + (++proxyCount) + "_" + Math.round(Math.random() * 100000) : ""; 15456} 15457 15458function createProxyUrl(proxyUrl, src, callback) { 15459 return proxyUrl + "?url=" + encodeURIComponent(src) + (callback.length ? "&callback=html2canvas.proxy." + callback : ""); 15460} 15461 15462function documentFromHTML(src) { 15463 return function(html) { 15464 var parser = new DOMParser(), doc; 15465 try { 15466 doc = parser.parseFromString(html, "text/html"); 15467 } catch(e) { 15468 log("DOMParser not supported, falling back to createHTMLDocument"); 15469 doc = document.implementation.createHTMLDocument(""); 15470 try { 15471 doc.open(); 15472 doc.write(html); 15473 doc.close(); 15474 } catch(ee) { 15475 log("createHTMLDocument write not supported, falling back to document.body.innerHTML"); 15476 doc.body.innerHTML = html; // ie9 doesnt support writing to documentElement 15477 } 15478 } 15479 15480 var b = doc.querySelector("base"); 15481 if (!b || !b.href.host) { 15482 var base = doc.createElement("base"); 15483 base.href = src; 15484 doc.head.insertBefore(base, doc.head.firstChild); 15485 } 15486 15487 return doc; 15488 }; 15489} 15490 15491function loadUrlDocument(src, proxy, document, width, height, options) { 15492 return new Proxy(src, proxy, window.document).then(documentFromHTML(src)).then(function(doc) { 15493 return createWindowClone(doc, document, width, height, options, 0, 0); 15494 }); 15495} 15496 15497exports.Proxy = Proxy; 15498exports.ProxyURL = ProxyURL; 15499exports.loadUrlDocument = loadUrlDocument; 15500 15501},{"./clone":2,"./log":13,"./utils":26,"./xhr":28}],17:[function(_dereq_,module,exports){ 15502var ProxyURL = _dereq_('./proxy').ProxyURL; 15503 15504function ProxyImageContainer(src, proxy) { 15505 var link = document.createElement("a"); 15506 link.href = src; 15507 src = link.href; 15508 this.src = src; 15509 this.image = new Image(); 15510 var self = this; 15511 this.promise = new Promise(function(resolve, reject) { 15512 self.image.crossOrigin = "Anonymous"; 15513 self.image.onload = resolve; 15514 self.image.onerror = reject; 15515 15516 new ProxyURL(src, proxy, document).then(function(url) { 15517 self.image.src = url; 15518 })['catch'](reject); 15519 }); 15520} 15521 15522module.exports = ProxyImageContainer; 15523 15524},{"./proxy":16}],18:[function(_dereq_,module,exports){ 15525var NodeContainer = _dereq_('./nodecontainer'); 15526 15527function PseudoElementContainer(node, parent, type) { 15528 NodeContainer.call(this, node, parent); 15529 this.isPseudoElement = true; 15530 this.before = type === ":before"; 15531} 15532 15533PseudoElementContainer.prototype.cloneTo = function(stack) { 15534 PseudoElementContainer.prototype.cloneTo.call(this, stack); 15535 stack.isPseudoElement = true; 15536 stack.before = this.before; 15537}; 15538 15539PseudoElementContainer.prototype = Object.create(NodeContainer.prototype); 15540 15541PseudoElementContainer.prototype.appendToDOM = function() { 15542 if (this.before) { 15543 this.parent.node.insertBefore(this.node, this.parent.node.firstChild); 15544 } else { 15545 this.parent.node.appendChild(this.node); 15546 } 15547 this.parent.node.className += " " + this.getHideClass(); 15548}; 15549 15550PseudoElementContainer.prototype.cleanDOM = function() { 15551 this.node.parentNode.removeChild(this.node); 15552 this.parent.node.className = this.parent.node.className.replace(this.getHideClass(), ""); 15553}; 15554 15555PseudoElementContainer.prototype.getHideClass = function() { 15556 return this["PSEUDO_HIDE_ELEMENT_CLASS_" + (this.before ? "BEFORE" : "AFTER")]; 15557}; 15558 15559PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = "___html2canvas___pseudoelement_before"; 15560PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER = "___html2canvas___pseudoelement_after"; 15561 15562module.exports = PseudoElementContainer; 15563 15564},{"./nodecontainer":14}],19:[function(_dereq_,module,exports){ 15565var log = _dereq_('./log'); 15566
15567function Renderer(width, height, images, options, document) { 15568 this.width = width; 15569 this.height = height; 15570 this.images = images; 15571 this.options = options; 15572 this.document = document; 15573} 15574 15575Renderer.prototype.renderImage = function(container, bounds, borderData, imageContainer) { 15576 var paddingLeft = container.cssInt('paddingLeft'), 15577 paddingTop = container.cssInt('paddingTop'), 15578 paddingRight = container.cssInt('paddingRight'), 15579 paddingBottom = container.cssInt('paddingBottom'), 15580 borders = borderData.borders; 15581 15582 var width = bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight); 15583 var height = bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom); 15584 this.drawImage( 15585 imageContainer, 15586 0, 15587 0, 15588 imageContainer.image.width || width, 15589 imageContainer.image.height || height, 15590 bounds.left + paddingLeft + borders[3].width, 15591 bounds.top + paddingTop + borders[0].width, 15592 width, 15593 height 15594 ); 15595}; 15596 15597Renderer.prototype.renderBackground = function(container, bounds, borderData) { 15598 if (bounds.height > 0 && bounds.width > 0) { 15599 this.renderBackgroundColor(container, bounds); 15600 this.renderBackgroundImage(container, bounds, borderData); 15601 } 15602}; 15603 15604Renderer.prototype.renderBackgroundColor = function(container, bounds) { 15605 var color = container.color("backgroundColor"); 15606 if (!color.isTransparent()) { 15607 this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, color); 15608 } 15609}; 15610 15611Renderer.prototype.renderBorders = function(borders) { 15612 borders.forEach(this.renderBorder, this); 15613}; 15614 15615Renderer.prototype.renderBorder = function(data) { 15616 if (!data.color.isTransparent() && data.args !== null) { 15617 this.drawShape(data.args, data.color); 15618 } 15619}; 15620 15621Renderer.prototype.renderBackgroundImage = function(container, bounds, borderData) { 15622 var backgroundImages = container.parseBackgroundImages(); 15623 backgroundImages.reverse().forEach(function(backgroundImage, index, arr) { 15624 switch(backgroundImage.method) { 15625 case "url": 15626 var image = this.images.get(backgroundImage.args[0]); 15627 if (image) { 15628 this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1), borderData); 15629 } else { 15630 log("Error loading background-image", backgroundImage.args[0]); 15631 } 15632 break; 15633 case "linear-gradient": 15634 case "gradient": 15635 var gradientImage = this.images.get(backgroundImage.value); 15636 if (gradientImage) { 15637 this.renderBackgroundGradient(gradientImage, bounds, borderData); 15638 } else { 15639 log("Error loading background-image", backgroundImage.args[0]); 15640 } 15641 break; 15642 case "none": 15643 break; 15644 default: 15645 log("Unknown background-image type", backgroundImage.args[0]); 15646 } 15647 }, this); 15648}; 15649 15650Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index, borderData) { 15651 var size = container.parseBackgroundSize(bounds, imageContainer.image, index); 15652 var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size); 15653 var repeat = container.parseBackgroundRepeat(index); 15654 switch (repeat) { 15655 case "repeat-x": 15656 case "repeat no-repeat": 15657 this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + borderData[3], bounds.top + position.top + borderData[0], 99999, size.height, borderData); 15658 break; 15659 case "repeat-y": 15660 case "no-repeat repeat": 15661 this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + borderData[0], size.width, 99999, borderData); 15662 break; 15663 case "no-repeat": 15664 this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + position.top + borderData[0], size.width, size.height, borderData); 15665 break; 15666 default:
15667 this.renderBackgroundRepeat(imageContainer, position, size, {top: bounds.top, left: bounds.left}, borderData[3], borderData[0]); 15668 break; 15669 } 15670}; 15671 15672module.exports = Renderer; 15673 15674},{"./log":13}],20:[function(_dereq_,module,exports){ 15675var Renderer = _dereq_('../renderer'); 15676var LinearGradientContainer = _dereq_('../lineargradientcontainer'); 15677var log = _dereq_('../log'); 15678 15679function CanvasRenderer(width, height) { 15680 Renderer.apply(this, arguments); 15681 this.canvas = this.options.canvas || this.document.createElement("canvas"); 15682 if (!this.options.canvas) { 15683 this.canvas.width = width; 15684 this.canvas.height = height; 15685 } 15686 this.ctx = this.canvas.getContext("2d"); 15687 this.taintCtx = this.document.createElement("canvas").getContext("2d"); 15688 this.ctx.textBaseline = "bottom"; 15689 this.variables = {}; 15690 log("Initialized CanvasRenderer with size", width, "x", height); 15691} 15692 15693CanvasRenderer.prototype = Object.create(Renderer.prototype); 15694 15695CanvasRenderer.prototype.setFillStyle = function(fillStyle) { 15696 this.ctx.fillStyle = typeof(fillStyle) === "object" && !!fillStyle.isColor ? fillStyle.toString() : fillStyle; 15697 return this.ctx; 15698}; 15699 15700CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) { 15701 this.setFillStyle(color).fillRect(left, top, width, height); 15702}; 15703 15704CanvasRenderer.prototype.circle = function(left, top, size, color) { 15705 this.setFillStyle(color); 15706 this.ctx.beginPath(); 15707 this.ctx.arc(left + size / 2, top + size / 2, size / 2, 0, Math.PI*2, true); 15708 this.ctx.closePath(); 15709 this.ctx.fill(); 15710}; 15711 15712CanvasRenderer.prototype.circleStroke = function(left, top, size, color, stroke, strokeColor) { 15713 this.circle(left, top, size, color); 15714 this.ctx.strokeStyle = strokeColor.toString(); 15715 this.ctx.stroke(); 15716}; 15717 15718CanvasRenderer.prototype.drawShape = function(shape, color) { 15719 this.shape(shape); 15720 this.setFillStyle(color).fill(); 15721}; 15722 15723CanvasRenderer.prototype.taints = function(imageContainer) { 15724 if (imageContainer.tainted === null) { 15725 this.taintCtx.drawImage(imageContainer.image, 0, 0); 15726 try { 15727 this.taintCtx.getImageData(0, 0, 1, 1); 15728 imageContainer.tainted = false; 15729 } catch(e) { 15730 this.taintCtx = document.createElement("canvas").getContext("2d"); 15731 imageContainer.tainted = true; 15732 } 15733 } 15734 15735 return imageContainer.tainted; 15736}; 15737 15738CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx, dy, dw, dh) { 15739 if (!this.taints(imageContainer) || this.options.allowTaint) { 15740 this.ctx.drawImage(imageContainer.image, sx, sy, sw, sh, dx, dy, dw, dh); 15741 } 15742}; 15743 15744CanvasRenderer.prototype.clip = function(shapes, callback, context) { 15745 this.ctx.save(); 15746 shapes.filter(hasEntries).forEach(function(shape) { 15747 this.shape(shape).clip(); 15748 }, this); 15749 callback.call(context); 15750 this.ctx.restore(); 15751}; 15752 15753CanvasRenderer.prototype.shape = function(shape) { 15754 this.ctx.beginPath(); 15755 shape.forEach(function(point, index) { 15756 if (point[0] === "rect") { 15757 this.ctx.rect.apply(this.ctx, point.slice(1)); 15758 } else { 15759 this.ctx[(index === 0) ? "moveTo" : point[0] + "To" ].apply(this.ctx, point.slice(1)); 15760 } 15761 }, this); 15762 this.ctx.closePath(); 15763 return this.ctx; 15764}; 15765 15766CanvasRenderer.prototype.font = function(color, style, variant, weight, size, family) { 15767 this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ").split(",")[0]; 15768}; 15769 15770CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) { 15771 this.setVariable("shadowColor", color.toString()) 15772 .setVariable("shadowOffsetY", offsetX) 15773 .setVariable("shadowOffsetX", offsetY) 15774 .setVariable("shadowBlur", blur); 15775}; 15776 15777CanvasRenderer.prototype.clearShadow = function() { 15778 this.setVariable("shadowColor", "rgba(0,0,0,0)"); 15779}; 15780 15781CanvasRenderer.prototype.setOpacity = function(opacity) { 15782 this.ctx.globalAlpha = opacity; 15783}; 15784 15785CanvasRenderer.prototype.setTransform = function(transform) { 15786 this.ctx.translate(transform.origin[0], transform.origin[1]); 15787 this.ctx.transform.apply(this.ctx, transform.matrix); 15788 this.ctx.translate(-transform.origin[0], -transform.origin[1]); 15789}; 15790 15791CanvasRenderer.prototype.setVariable = function(property, value) { 15792 if (this.variables[property] !== value) { 15793 this.variables[property] = this.ctx[property] = value; 15794 } 15795 15796 return this; 15797}; 15798 15799CanvasRenderer.prototype.text = function(text, left, bottom) { 15800 this.ctx.fillText(text, left, bottom); 15801}; 15802 15803CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, size, bounds, left, top, width, height, borderData) { 15804 var shape = [ 15805 ["line", Math.round(left), Math.round(top)], 15806 ["line", Math.round(left + width), Math.round(top)], 15807 ["line", Math.round(left + width), Math.round(height + top)], 15808 ["line", Math.round(left), Math.round(height + top)] 15809 ]; 15810 this.clip([shape], function() {
15811 this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]); 15812 }, this); 15813}; 15814 15815CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, size, bounds, borderLeft, borderTop) { 15816 var offsetX = Math.round(bounds.left + backgroundPosition.left + borderLeft), offsetY = Math.round(bounds.top + backgroundPosition.top + borderTop); 15817 this.setFillStyle(this.ctx.createPattern(this.resizeImage(imageContainer, size), "repeat")); 15818 this.ctx.translate(offsetX, offsetY); 15819 this.ctx.fill(); 15820 this.ctx.translate(-offsetX, -offsetY); 15821}; 15822 15823CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) { 15824 if (gradientImage instanceof LinearGradientContainer) { 15825 var gradient = this.ctx.createLinearGradient( 15826 bounds.left + bounds.width * gradientImage.x0, 15827 bounds.top + bounds.height * gradientImage.y0, 15828 bounds.left + bounds.width * gradientImage.x1, 15829 bounds.top + bounds.height * gradientImage.y1); 15830 gradientImage.colorStops.forEach(function(colorStop) { 15831 gradient.addColorStop(colorStop.stop, colorStop.color.toString()); 15832 }); 15833 this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, gradient); 15834 } 15835}; 15836 15837CanvasRenderer.prototype.resizeImage = function(imageContainer, size) { 15838 var image = imageContainer.image; 15839 if(image.width === size.width && image.height === size.height) { 15840 return image; 15841 } 15842 15843 var ctx, canvas = document.createElement('canvas'); 15844 canvas.width = size.width; 15845 canvas.height = size.height; 15846 ctx = canvas.getContext("2d"); 15847 ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height ); 15848 return canvas; 15849}; 15850 15851function hasEntries(array) { 15852 return array.length > 0; 15853} 15854 15855module.exports = CanvasRenderer; 15856 15857},{"../lineargradientcontainer":12,"../log":13,"../renderer":19}],21:[function(_dereq_,module,exports){ 15858var NodeContainer = _dereq_('./nodecontainer'); 15859 15860function StackingContext(hasOwnStacking, opacity, element, parent) { 15861 NodeContainer.call(this, element, parent); 15862 this.ownStacking = hasOwnStacking; 15863 this.contexts = []; 15864 this.children = []; 15865 this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity; 15866} 15867 15868StackingContext.prototype = Object.create(NodeContainer.prototype); 15869 15870StackingContext.prototype.getParentStack = function(context) { 15871 var parentStack = (this.parent) ? this.parent.stack : null; 15872 return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack; 15873}; 15874 15875module.exports = StackingContext; 15876 15877},{"./nodecontainer":14}],22:[function(_dereq_,module,exports){ 15878function Support(document) { 15879 this.rangeBounds = this.testRangeBounds(document); 15880 this.cors = this.testCORS(); 15881 this.svg = this.testSVG(); 15882} 15883 15884Support.prototype.testRangeBounds = function(document) { 15885 var range, testElement, rangeBounds, rangeHeight, support = false; 15886 15887 if (document.createRange) { 15888 range = document.createRange(); 15889 if (range.getBoundingClientRect) { 15890 testElement = document.createElement('boundtest'); 15891 testElement.style.height = "123px"; 15892 testElement.style.display = "block"; 15893 document.body.appendChild(testElement); 15894 15895 range.selectNode(testElement); 15896 rangeBounds = range.getBoundingClientRect(); 15897 rangeHeight = rangeBounds.height; 15898 15899 if (rangeHeight === 123) { 15900 support = true; 15901 } 15902 document.body.removeChild(testElement); 15903 } 15904 } 15905 15906 return support; 15907}; 15908 15909Support.prototype.testCORS = function() { 15910 return typeof((new Image()).crossOrigin) !== "undefined"; 15911}; 15912 15913Support.prototype.testSVG = function() { 15914 var img = new Image(); 15915 var canvas = document.createElement("canvas"); 15916 var ctx = canvas.getContext("2d"); 15917 img.src = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>"; 15918 15919 try { 15920 ctx.drawImage(img, 0, 0); 15921 canvas.toDataURL(); 15922 } catch(e) { 15923 return false;
15924 } 15925 return true; 15926}; 15927 15928module.exports = Support; 15929 15930},{}],23:[function(_dereq_,module,exports){ 15931var XHR = _dereq_('./xhr'); 15932var decode64 = _dereq_('./utils').decode64; 15933 15934function SVGContainer(src) { 15935 this.src = src; 15936 this.image = null; 15937 var self = this; 15938 15939 this.promise = this.hasFabric().then(function() { 15940 return (self.isInline(src) ? Promise.resolve(self.inlineFormatting(src)) : XHR(src)); 15941 }).then(function(svg) { 15942 return new Promise(function(resolve) { 15943 window.html2canvas.svg.fabric.loadSVGFromString(svg, self.createCanvas.call(self, resolve)); 15944 }); 15945 }); 15946} 15947 15948SVGContainer.prototype.hasFabric = function() { 15949 return !window.html2canvas.svg || !window.html2canvas.svg.fabric ? Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg")) : Promise.resolve(); 15950}; 15951 15952SVGContainer.prototype.inlineFormatting = function(src) { 15953 return (/^data:image\/svg\+xml;base64,/.test(src)) ? this.decode64(this.removeContentType(src)) : this.removeContentType(src); 15954}; 15955 15956SVGContainer.prototype.removeContentType = function(src) { 15957 return src.replace(/^data:image\/svg\+xml(;base64)?,/,''); 15958}; 15959 15960SVGContainer.prototype.isInline = function(src) { 15961 return (/^data:image\/svg\+xml/i.test(src)); 15962}; 15963 15964SVGContainer.prototype.createCanvas = function(resolve) { 15965 var self = this; 15966 return function (objects, options) { 15967 var canvas = new window.html2canvas.svg.fabric.StaticCanvas('c'); 15968 self.image = canvas.lowerCanvasEl; 15969 canvas 15970 .setWidth(options.width) 15971 .setHeight(options.height) 15972 .add(window.html2canvas.svg.fabric.util.groupSVGElements(objects, options)) 15973 .renderAll(); 15974 resolve(canvas.lowerCanvasEl); 15975 }; 15976}; 15977 15978SVGContainer.prototype.decode64 = function(str) { 15979 return (typeof(window.atob) === "function") ? window.atob(str) : decode64(str); 15980}; 15981 15982module.exports = SVGContainer; 15983 15984},{"./utils":26,"./xhr":28}],24:[function(_dereq_,module,exports){ 15985var SVGContainer = _dereq_('./svgcontainer'); 15986 15987function SVGNodeContainer(node, _native) { 15988 this.src = node; 15989 this.image = null; 15990 var self = this; 15991 15992 this.promise = _native ? new Promise(function(resolve, reject) { 15993 self.image = new Image(); 15994 self.image.onload = resolve; 15995 self.image.onerror = reject; 15996 self.image.src = "data:image/svg+xml," + (new XMLSerializer()).serializeToString(node); 15997 if (self.image.complete === true) { 15998 resolve(self.image); 15999 } 16000 }) : this.hasFabric().then(function() { 16001 return new Promise(function(resolve) { 16002 window.html2canvas.svg.fabric.parseSVGDocument(node, self.createCanvas.call(self, resolve)); 16003 }); 16004 }); 16005} 16006 16007SVGNodeContainer.prototype = Object.create(SVGContainer.prototype); 16008 16009module.exports = SVGNodeContainer; 16010 16011},{"./svgcontainer":23}],25:[function(_dereq_,module,exports){ 16012var NodeContainer = _dereq_('./nodecontainer'); 16013 16014function TextContainer(node, parent) { 16015 NodeContainer.call(this, node, parent); 16016} 16017 16018TextContainer.prototype = Object.create(NodeContainer.prototype); 16019 16020TextContainer.prototype.applyTextTransform = function() { 16021 this.node.data = this.transform(this.parent.css("textTransform")); 16022}; 16023 16024TextContainer.prototype.transform = function(transform) { 16025 var text = this.node.data; 16026 switch(transform){ 16027 case "lowercase": 16028 return text.toLowerCase(); 16029 case "capitalize": 16030 return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize); 16031 case "uppercase": 16032 return text.toUpperCase(); 16033 default: 16034 return text; 16035 } 16036}; 16037 16038function capitalize(m, p1, p2) { 16039 if (m.length > 0) { 16040 return p1 + p2.toUpperCase(); 16041 } 16042} 16043 16044module.exports = TextContainer; 16045 16046},{"./nodecontainer":14}],26:[function(_dereq_,module,exports){ 16047exports.smallImage = function smallImage() { 16048 return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; 16049}; 16050 16051exports.bind = function(callback, context) { 16052 return function() { 16053 return callback.apply(context, arguments); 16054 }; 16055}; 16056 16057/* 16058 * base64-arraybuffer 16059 * https://github.com/niklasvh/base64-arraybuffer 16060 * 16061 * Copyright (c) 2012 Niklas von Hertzen 16062 * Licensed under the MIT license. 16063 */ 16064 16065exports.decode64 = function(base64) { 16066 var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
16066+/"; 16067 var len = base64.length, i, encoded1, encoded2, encoded3, encoded4, byte1, byte2, byte3; 16068 16069 var output = ""; 16070 16071 for (i = 0; i < len; i+=4) { 16072 encoded1 = chars.indexOf(base64[i]); 16073 encoded2 = chars.indexOf(base64[i+1]); 16074 encoded3 = chars.indexOf(base64[i+2]); 16075 encoded4 = chars.indexOf(base64[i+3]); 16076 16077 byte1 = (encoded1 << 2) | (encoded2 >> 4); 16078 byte2 = ((encoded2 & 15) << 4) | (encoded3 >> 2); 16079 byte3 = ((encoded3 & 3) << 6) | encoded4; 16080 if (encoded3 === 64) { 16081 output += String.fromCharCode(byte1); 16082 } else if (encoded4 === 64 || encoded4 === -1) { 16083 output += String.fromCharCode(byte1, byte2); 16084 } else{ 16085 output += String.fromCharCode(byte1, byte2, byte3); 16086 } 16087 } 16088 16089 return output; 16090}; 16091 16092exports.getBounds = function(node) { 16093 if (node.getBoundingClientRect) { 16094 var clientRect = node.getBoundingClientRect(); 16095 var width = node.offsetWidth == null ? clientRect.width : node.offsetWidth; 16096 return { 16097 top: clientRect.top, 16098 bottom: clientRect.bottom || (clientRect.top + clientRect.height), 16099 right: clientRect.left + width, 16100 left: clientRect.left, 16101 width: width, 16102 height: node.offsetHeight == null ? clientRect.height : node.offsetHeight 16103 }; 16104 } 16105 return {}; 16106}; 16107 16108exports.offsetBounds = function(node) { 16109 var parent = node.offsetParent ? exports.offsetBounds(node.offsetParent) : {top: 0, left: 0}; 16110 16111 return { 16112 top: node.offsetTop + parent.top, 16113 bottom: node.offsetTop + node.offsetHeight + parent.top, 16114 right: node.offsetLeft + parent.left + node.offsetWidth, 16115 left: node.offsetLeft + parent.left, 16116 width: node.offsetWidth, 16117 height: node.offsetHeight 16118 }; 16119}; 16120 16121exports.parseBackgrounds = function(backgroundImage) { 16122 var whitespace = ' \r\n\t', 16123 method, definition, prefix, prefix_i, block, results = [], 16124 mode = 0, numParen = 0, quote, args; 16125 var appendResult = function() { 16126 if(method) { 16127 if (definition.substr(0, 1) === '"') { 16128 definition = definition.substr(1, definition.length - 2); 16129 } 16130 if (definition) { 16131 args.push(definition); 16132 } 16133 if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) { 16134 prefix = method.substr(0, prefix_i); 16135 method = method.substr(prefix_i); 16136 } 16137 results.push({ 16138 prefix: prefix, 16139 method: method.toLowerCase(), 16140 value: block, 16141 args: args, 16142 image: null 16143 }); 16144 } 16145 args = []; 16146 method = prefix = definition = block = ''; 16147 }; 16148 args = []; 16149 method = prefix = definition = block = ''; 16150 backgroundImage.split("").forEach(function(c) { 16151 if (mode === 0 && whitespace.indexOf(c) > -1) { 16152 return; 16153 } 16154 switch(c) { 16155 case '"': 16156 if(!quote) { 16157 quote = c; 16158 } else if(quote === c) { 16159 quote = null; 16160 } 16161 break; 16162 case '(': 16163 if(quote) { 16164 break; 16165 } else if(mode === 0) { 16166 mode = 1; 16167 block += c; 16168 return; 16169 } else { 16170 numParen++; 16171 } 16172 break; 16173 case ')': 16174 if (quote) { 16175 break; 16176 } else if(mode === 1) { 16177 if(numParen === 0) { 16178 mode = 0; 16179 block += c; 16180 appendResult(); 16181 return; 16182 } else { 16183 numParen--; 16184 } 16185 } 16186 break; 16187 16188 case ',': 16189 if (quote) { 16190 break; 16191 } else if(mode === 0) { 16192 appendResult(); 16193 return; 16194 } else if (mode === 1) { 16195 if (numParen === 0 && !method.match(/^url$/i)) { 16196 args.push(definition); 16197 definition = ''; 16198 block += c; 16199 return; 16200 } 16201 } 16202 break; 16203 } 16204 16205 block += c; 16206 if (mode === 0) { 16207 method += c; 16208 } else { 16209 definition += c; 16210 } 16211 }); 16212 16213 appendResult(); 16214 return results; 16215}; 16216 16217},{}],27:[function(_dereq_,module,exports){ 16218var GradientContainer = _dereq_('./gradientcontainer'); 16219 16220function WebkitGradientContainer(imageData) { 16221 GradientContainer.apply(this, arguments); 16222 this.type = imageData.args[0] === "linear" ? GradientContainer.TYPES.LINEAR
16222: GradientContainer.TYPES.RADIAL; 16223} 16224 16225WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype); 16226 16227module.exports = WebkitGradientContainer; 16228 16229},{"./gradientcontainer":9}],28:[function(_dereq_,module,exports){ 16230function XHR(url) { 16231 return new Promise(function(resolve, reject) { 16232 var xhr = new XMLHttpRequest(); 16233 xhr.open('GET', url); 16234 16235 xhr.onload = function() { 16236 if (xhr.status === 200) { 16237 resolve(xhr.responseText); 16238 } else { 16239 reject(new Error(xhr.statusText)); 16240 } 16241 }; 16242 16243 xhr.onerror = function() { 16244 reject(new Error("Network Error")); 16245 }; 16246 16247 xhr.send(); 16248 }); 16249} 16250 16251module.exports = XHR; 16252 16253},{}]},{},[4])(4) 16254}); 16255 16256// Generated by CoffeeScript 1.4.0 16257 16258/* 16259# PNG.js 16260# Copyright (c) 2011 Devon Govett 16261# MIT LICENSE 16262# 16263# 16264*/ 16265 16266 16267(function(global) { 16268 var PNG; 16269 16270 PNG = (function() { 16271 var APNG_BLEND_OP_OVER, APNG_BLEND_OP_SOURCE, APNG_DISPOSE_OP_BACKGROUND, APNG_DISPOSE_OP_NONE, APNG_DISPOSE_OP_PREVIOUS, makeImage, scratchCanvas, scratchCtx; 16272 16273 PNG.load = function(url, canvas, callback) { 16274 var xhr, 16275 _this = this; 16276 if (typeof canvas === 'function') { 16277 callback = canvas; 16278 } 16279 xhr = new XMLHttpRequest; 16280 xhr.open("GET", url, true); 16281 xhr.responseType = "arraybuffer"; 16282 xhr.onload = function() { 16283 var data, png; 16284 data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer); 16285 png = new PNG(data); 16286 if (typeof (canvas != null ? canvas.getContext : void 0) === 'function') { 16287 png.render(canvas); 16288 } 16289 return typeof callback === "function" ? callback(png) : void 0; 16290 }; 16291 return xhr.send(null); 16292 }; 16293 16294 APNG_DISPOSE_OP_NONE = 0; 16295 16296 APNG_DISPOSE_OP_BACKGROUND = 1; 16297 16298 APNG_DISPOSE_OP_PREVIOUS = 2; 16299 16300 APNG_BLEND_OP_SOURCE = 0; 16301 16302 APNG_BLEND_OP_OVER = 1; 16303 16304 function PNG(data) { 16305 var chunkSize, colors, palLen, delayDen, delayNum, frame, i, index, key, section, palShort, text, _i, _j, _ref; 16306 this.data = data; 16307 this.pos = 8; 16308 this.palette = []; 16309 this.imgData = []; 16310 this.transparency = {}; 16311 this.animation = null; 16312 this.text = {}; 16313 frame = null; 16314 while (true) { 16315 chunkSize = this.readUInt32(); 16316 section = ((function() { 16317 var _i, _results; 16318 _results = []; 16319 for (i = _i = 0; _i < 4; i = ++_i) { 16320 _results.push(String.fromCharCode(this.data[this.pos++])); 16321 } 16322 return _results; 16323 }).call(this)).join(''); 16324 switch (section) { 16325 case 'IHDR': 16326 this.width = this.readUInt32(); 16327 this.height = this.readUInt32(); 16328 this.bits = this.data[this.pos++]; 16329 this.colorType = this.data[this.pos++]; 16330 this.compressionMethod = this.data[this.pos++]; 16331 this.filterMethod = this.data[this.pos++]; 16332 this.interlaceMethod = this.data[this.pos++]; 16333 break; 16334 case 'acTL': 16335 this.animation = { 16336 numFrames: this.readUInt32(), 16337 numPlays: this.readUInt32() || Infinity, 16338 frames: [] 16339 }; 16340 break; 16341 case 'PLTE': 16342 this.palette = this.read(chunkSize); 16343 break; 16344 case 'fcTL': 16345 if (frame) { 16346 this.animation.frames.push(frame); 16347 } 16348 this.pos += 4; 16349 frame = { 16350 width: this.readUInt32(), 16351 height: this.readUInt32(), 16352 xOffset: this.readUInt32(), 16353 yOffset: this.readUInt32() 16354 }; 16355 delayNum = this.readUInt16(); 16356 delayDen = this.readUInt16() || 100; 16357 frame.delay = 1000 * delayNum / delayDen; 16358 frame.disposeOp = this.data[this.pos++]; 16359 frame.blendOp = this.data[this.pos++]; 16360 frame.data = []; 16361 break; 16362 case 'IDAT': 16363 case 'fdAT': 16364 if (section === 'fdAT') { 16365 this.pos += 4; 16366 chunkSize -= 4; 16367 } 16368 data = (frame != null ? frame.data : void 0) || this.imgData; 16369 for (i = _i = 0; 0 <= chunkSize ? _i < chunkSize : _i > chunkSize; i = 0 <= chunkSize ? ++_i : --_i) { 16370 data.push(this.data[this.pos++]); 16371 } 16372 break; 16373 case 'tRNS': 16374 this.transparency = {}; 16375 switch (this.colorType) { 16376 case 3: 16377 palLen = this.palette.length/3; 16378 this.transparency.indexed = this.read(chunkSize); 16379 if(this.transparency.indexed.length > palLen) 16380 throw new Error('More transparent colors than palette size'); 16381 /* 16382 * According to the PNG spec trns should be increased to the same size as palette if shorter 16383 */ 16384 //palShort = 255 - this.transparency.indexed.length; 16385 palShort = palLen - this.transparency.indexed.length; 16386 if (palShort > 0) { 16387 for (i = _j = 0; 0 <= palShort ? _j < palShort : _j > palShort; i = 0 <= palShort ? ++_j : --_j) { 16388 this.transparency.indexed.push(255); 16389 } 16390 } 16391 break; 16392 case 0: 16393 this.transparency.grayscale = this.read(chunkSize)[0]; 16394 break; 16395 case 2: 16396 this.transparency.rgb = this.read(chunkSize); 16397 } 16398 break; 16399 case 'tEXt': 16400 text = this.read(chunkSize); 16401 index = text.indexOf(0); 16402 key = String.fromCharCode.apply(String, text.slice(0, index)); 16403 this.text[key] = String.fromCharCode.apply(String, text.slice(index + 1)); 16404 break; 16405 case 'IEND': 16406 if (frame) { 16407 this.animation.frames.push(frame); 16408 } 16409 this.colors = (function() { 16410 switch (this.colorType) { 16411 case 0: 16412 case 3: 16413 case 4: 16414 return 1; 16415 case 2: 16416 case 6: 16417 return 3; 16418 } 16419 }).call(this); 16420 this.hasAlphaChannel = (_ref = this.colorType) === 4 || _ref === 6; 16421 colors = this.colors + (this.hasAlphaChannel ? 1 : 0); 16422 this.pixelBitlength = this.bits * colors; 16423 this.colorSpace = (function() { 16424 switch (this.colors) { 16425 case 1: 16426 return 'DeviceGray'; 16427 case 3: 16428 return 'DeviceRGB'; 16429 } 16430 }).call(this); 16431 this.imgData = new Uint8Array(this.imgData); 16432 return; 16433 default: 16434 this.pos += chunkSize; 16435 } 16436 this.pos += 4; 16437 if (this.pos > this.data.length) { 16438 throw new Error("Incomplete or corrupt PNG file"); 16439 } 16440 } 16441 return; 16442 } 16443 16444 PNG.prototype.read = function(bytes) { 16445 var i, _i, _results; 16446 _results = []; 16447 for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { 16448 _results.push(this.data[this.pos++]); 16449 } 16450 return _results; 16451 }; 16452 16453 PNG.prototype.readUInt32 = function() { 16454 var b1, b2, b3, b4; 16455 b1 = this.data[this.pos++] << 24; 16456 b2 = this.data[this.pos++] << 16; 16457 b3 = this.data[this.pos++] << 8; 16458 b4 = this.data[this.pos++]; 16459 return b1 | b2 | b3 | b4; 16460 }; 16461
16462 PNG.prototype.readUInt16 = function() { 16463 var b1, b2; 16464 b1 = this.data[this.pos++] << 8; 16465 b2 = this.data[this.pos++]; 16466 return b1 | b2; 16467 }; 16468 16469 PNG.prototype.decodePixels = function(data) { 16470 var abyte, c, col, i, left, length, p, pa, paeth, pb, pc, pixelBytes, pixels, pos, row, scanlineLength, upper, upperLeft, _i, _j, _k, _l, _m; 16471 if (data == null) { 16472 data = this.imgData; 16473 } 16474 if (data.length === 0) { 16475 return new Uint8Array(0); 16476 } 16477 data = new FlateStream(data); 16478 data = data.getBytes(); 16479 pixelBytes = this.pixelBitlength / 8; 16480 scanlineLength = pixelBytes * this.width; 16481 pixels = new Uint8Array(scanlineLength * this.height); 16482 length = data.length; 16483 row = 0; 16484 pos = 0; 16485 c = 0; 16486 while (pos < length) { 16487 switch (data[pos++]) { 16488 case 0: 16489 for (i = _i = 0; _i < scanlineLength; i = _i += 1) { 16490 pixels[c++] = data[pos++]; 16491 } 16492 break; 16493 case 1: 16494 for (i = _j = 0; _j < scanlineLength; i = _j += 1) { 16495 abyte = data[pos++]; 16496 left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; 16497 pixels[c++] = (abyte + left) % 256; 16498 } 16499 break; 16500 case 2: 16501 for (i = _k = 0; _k < scanlineLength; i = _k += 1) { 16502 abyte = data[pos++]; 16503 col = (i - (i % pixelBytes)) / pixelBytes; 16504 upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; 16505 pixels[c++] = (upper + abyte) % 256; 16506 } 16507 break; 16508 case 3: 16509 for (i = _l = 0; _l < scanlineLength; i = _l += 1) { 16510 abyte = data[pos++]; 16511 col = (i - (i % pixelBytes)) / pixelBytes; 16512 left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; 16513 upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; 16514 pixels[c++] = (abyte + Math.floor((left + upper) / 2)) % 256; 16515 } 16516 break; 16517 case 4: 16518 for (i = _m = 0; _m < scanlineLength; i = _m += 1) { 16519 abyte = data[pos++]; 16520 col = (i - (i % pixelBytes)) / pixelBytes; 16521 left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; 16522 if (row === 0) { 16523 upper = upperLeft = 0; 16524 } else { 16525 upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; 16526 upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i % pixelBytes)]; 16527 } 16528 p = left + upper - upperLeft; 16529 pa = Math.abs(p - left); 16530 pb = Math.abs(p - upper); 16531 pc = Math.abs(p - upperLeft); 16532 if (pa <= pb && pa <= pc) { 16533 paeth = left; 16534 } else if (pb <= pc) { 16535 paeth = upper; 16536 } else { 16537 paeth = upperLeft; 16538 } 16539 pixels[c++] = (abyte + paeth) % 256; 16540 } 16541 break; 16542 default: 16543 throw new Error("Invalid filter algorithm: " + data[pos - 1]); 16544 } 16545 row++; 16546 } 16547 return pixels; 16548 }; 16549 16550 PNG.prototype.decodePalette = function() { 16551 var c, i, length, palette, pos, ret, transparency, _i, _ref, _ref1; 16552 palette = this.palette; 16553 transparency = this.transparency.indexed || []; 16554 ret = new Uint8Array((transparency.length || 0) + palette.length); 16555 pos = 0; 16556 length = palette.length; 16557 c = 0; 16558 for (i = _i = 0, _ref = palette.length; _i < _ref; i = _i += 3) { 16559 ret[pos++] = palette[i]; 16560 ret[pos++] = palette[i + 1]; 16561 ret[pos++] = palette[i + 2]; 16562 ret[pos++] = (_ref1 = transparency[c++]) != null ? _ref1 : 255; 16563 } 16564 return ret; 16565 }; 16566 16567 PNG.prototype.copyToImageData = function(imageData, pixels) { 16568 var alpha, colors, data, i, input, j, k, length, palette, v, _ref; 16569 colors = this.colors; 16570 palette = null; 16571 alpha = this.hasAlphaChannel; 16572 if (this.palette.length) { 16573 palette = (_ref = this._decodedPalette) != null ? _ref : this._decodedPalette = this.decodePalette(); 16574 colors = 4; 16575 alpha = true; 16576 } 16577 data = imageData.data || imageData; 16578 length = data.length; 16579 input = palette || pixels; 16580 i = j = 0; 16581 if (colors === 1) { 16582 while (i < length) { 16583 k = palette ? pixels[i / 4] * 4 : j; 16584 v = input[k++]; 16585 data[i++] = v; 16586 data[i++] = v; 16587 data[i++] = v; 16588 data[i++] = alpha ? input[k++] : 255; 16589 j = k; 16590 } 16591 } else { 16592 while (i < length) { 16593 k = palette ? pixels[i / 4] * 4 : j; 16594 data[i++] = input[k++]; 16595 data[i++] = input[k++]; 16596 data[i++] = input[k++]; 16597 data[i++] = alpha ? input[k++] : 255; 16598 j = k; 16599 } 16600 } 16601 }; 16602 16603 PNG.prototype.decode = function() { 16604 var ret; 16605 ret = new Uint8Array(this.width * this.height * 4); 16606 this.copyToImageData(ret, this.decodePixels()); 16607 return ret; 16608 }; 16609 16610 try { 16611 scratchCanvas = global.document.createElement('canvas'); 16612 scratchCtx = scratchCanvas.getContext('2d'); 16613 } catch(e) { 16614 return -1; 16615 } 16616 16617 makeImage = function(imageData) { 16618 var img; 16619 scratchCtx.width = imageData.width; 16620 scratchCtx.height = imageData.height; 16621 scratchCtx.clearRect(0, 0, imageData.width, imageData.height); 16622 scratchCtx.putImageData(imageData, 0, 0); 16623 img = new Image; 16624 img.src = scratchCanvas.toDataURL(); 16625 return img; 16626 }; 16627 16628 PNG.prototype.decodeFrames = function(ctx) { 16629 var frame, i, imageData, pixels, _i, _len, _ref, _results; 16630 if (!this.animation) { 16631 return; 16632 } 16633 _ref = this.animation.frames; 16634 _results = []; 16635 for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { 16636 frame = _ref[i]; 16637 imageData = ctx.createImageData(frame.width, frame.height); 16638 pixels = this.decodePixels(new Uint8Array(frame.data)); 16639 this.copyToImageData(imageData, pixels); 16640 frame.imageData = imageData; 16641 _results.push(frame.image = makeImage(imageData)); 16642 } 16643 return _results; 16644 }; 16645 16646 PNG.prototype.renderFrame = function(ctx, number) { 16647 var frame, frames, prev; 16648 frames = this.animation.frames; 16649 frame = frames[number]; 16650 prev = frames[number - 1]; 16651 if (number === 0) { 16652 ctx.clearRect(0, 0, this.width, this.height); 16653 } 16654 if ((prev != null ? prev.disposeOp : void 0) === APNG_DISPOSE_OP_BACKGROUND) { 16655 ctx.clearRect(prev.xOffset, prev.yOffset, prev.width, prev.height); 16656 } else if ((prev != null ? prev.disposeOp : void 0) === APNG_DISPOSE_OP_PREVIOUS) { 16657 ctx.putImageData(prev.imageData, prev.xOffset, prev.yOffset); 16658 } 16659 if (frame.blendOp === APNG_BLEND_OP_SOURCE) { 16660 ctx.clearRect(frame.xOffset, frame.yOffset, frame.width, frame.height); 16661 } 16662 return ctx.drawImage(frame.image, frame.xOffset, frame.yOffset); 16663 }; 16664 16665 PNG.prototype.animate = function(ctx) { 16666 var doFrame, frameNumber, frames, numFrames, numPlays, _ref, 16667 _this = this; 16668 frameNumber = 0; 16669 _ref = this.animation, numFrames = _ref.numFrames, frames = _ref.frames, numPlays = _ref.numPlays; 16670 return (doFrame = function() { 16671 var f, frame; 16672 f = frameNumber++ % numFrames; 16673 frame = frames[f]; 16674 _this.renderFrame(ctx, f); 16675 if (numFrames > 1 && frameNumber / numFrames < numPlays) { 16676 return _this.animation._timeout = setTimeout(doFrame, frame.delay); 16677 } 16678 })(); 16679 }; 16680 16681 PNG.prototype.stopAnimation = function() { 16682 var _ref; 16683 return clearTimeout((_ref = this.animation) != null ? _ref._timeout : void 0); 16684 }; 16685 16686 PNG.prototype.render = function(canvas) { 16687 var ctx, data; 16688 if (canvas._png) { 16689 canvas._png.stopAnimation(); 16690 } 16691 canvas._png = this; 16692 canvas.width = this.width; 16693 canvas.height = this.height; 16694 ctx = canvas.getContext("2d"); 16695 if (this.animation) { 16696 this.decodeFrames(ctx); 16697 return this.animate(ctx); 16698 } else { 16699 data = ctx.createImageData(this.width, this.height); 16700 this.copyToImageData(data, this.decodePixels()); 16701 return ctx.putImageData(data, 0, 0); 16702 } 16703 }; 16704 16705 return PNG; 16706 16707 })(); 16708 16709 global.PNG = PNG; 16710 16711})(typeof window !== "undefined" && window || undefined); 16712 16713/* 16714 * Extracted from pdf.js
16715 * https://github.com/andreasgal/pdf.js 16716 * 16717 * Copyright (c) 2011 Mozilla Foundation 16718 * 16719 * Contributors: Andreas Gal <[email protected]> 16720 * Chris G Jones <[email protected]> 16721 * Shaon Barman <[email protected]> 16722 * Vivien Nicolas <[email protected]> 16723 * Justin D'Arcangelo <[email protected]> 16724 * Yury Delendik 16725 * 16726 * 16727 */ 16728 16729var DecodeStream = (function() { 16730 function constructor() { 16731 this.pos = 0; 16732 this.bufferLength = 0; 16733 this.eof = false; 16734 this.buffer = null; 16735 } 16736 16737 constructor.prototype = { 16738 ensureBuffer: function decodestream_ensureBuffer(requested) { 16739 var buffer = this.buffer; 16740 var current = buffer ? buffer.byteLength : 0; 16741 if (requested < current) 16742 return buffer; 16743 var size = 512; 16744 while (size < requested) 16745 size <<= 1; 16746 var buffer2 = new Uint8Array(size); 16747 for (var i = 0; i < current; ++i) 16748 buffer2[i] = buffer[i]; 16749 return this.buffer = buffer2; 16750 }, 16751 getByte: function decodestream_getByte() { 16752 var pos = this.pos; 16753 while (this.bufferLength <= pos) { 16754 if (this.eof) 16755 return null; 16756 this.readBlock(); 16757 } 16758 return this.buffer[this.pos++]; 16759 }, 16760 getBytes: function decodestream_getBytes(length) { 16761 var pos = this.pos; 16762 16763 if (length) { 16764 this.ensureBuffer(pos + length); 16765 var end = pos + length; 16766 16767 while (!this.eof && this.bufferLength < end) 16768 this.readBlock(); 16769 16770 var bufEnd = this.bufferLength; 16771 if (end > bufEnd) 16772 end = bufEnd; 16773 } else { 16774 while (!this.eof) 16775 this.readBlock(); 16776 16777 var end = this.bufferLength; 16778 } 16779 16780 this.pos = end; 16781 return this.buffer.subarray(pos, end); 16782 }, 16783 lookChar: function decodestream_lookChar() { 16784 var pos = this.pos; 16785 while (this.bufferLength <= pos) { 16786 if (this.eof) 16787 return null; 16788 this.readBlock(); 16789 } 16790 return String.fromCharCode(this.buffer[this.pos]); 16791 }, 16792 getChar: function decodestream_getChar() { 16793 var pos = this.pos; 16794 while (this.bufferLength <= pos) { 16795 if (this.eof) 16796 return null; 16797 this.readBlock(); 16798 } 16799 return String.fromCharCode(this.buffer[this.pos++]); 16800 }, 16801 makeSubStream: function decodestream_makeSubstream(start, length, dict) { 16802 var end = start + length; 16803 while (this.bufferLength <= end && !this.eof) 16804 this.readBlock(); 16805 return new Stream(this.buffer, start, length, dict); 16806 }, 16807 skip: function decodestream_skip(n) { 16808 if (!n) 16809 n = 1; 16810 this.pos += n; 16811 }, 16812 reset: function decodestream_reset() { 16813 this.pos = 0; 16814 } 16815 }; 16816 16817 return constructor; 16818})(); 16819 16820var FlateStream = (function() { 16821 if (typeof Uint32Array === 'undefined') { 16822 return undefined; 16823 } 16824 var codeLenCodeMap = new Uint32Array([ 16825 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 16826 ]); 16827 16828 var lengthDecode = new Uint32Array([ 16829 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, 16830 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, 16831 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073, 16832 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102 16833 ]); 16834 16835 var distDecode = new Uint32Array([ 16836 0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, 16837 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, 16838 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01, 16839 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001 16840 ]); 16841 16842 var fixedLitCodeTab = [new Uint32Array([ 16843 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, 16844 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, 16845 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0, 16846 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0, 16847 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8, 16848 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8, 16849 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8, 16850 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8, 16851 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4, 16852 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4,
16853 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4, 16854 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4, 16855 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc, 16856 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec, 16857 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc, 16858 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc, 16859 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2, 16860 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2, 16861 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2, 16862 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2, 16863 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca, 16864 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea, 16865 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da, 16866 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa, 16867 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6, 16868 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6, 16869 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6, 16870 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6, 16871 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce, 16872 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee, 16873 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de, 16874 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe, 16875 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1, 16876 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1, 16877 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1, 16878 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1, 16879 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9, 16880 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9, 16881 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9, 16882 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9, 16883 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5, 16884 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5, 16885 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5, 16886 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5, 16887 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd, 16888 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed, 16889 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd, 16890 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd, 16891 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3, 16892 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3, 16893 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3, 16894 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3, 16895 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb, 16896 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb, 16897 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db, 16898 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb, 16899 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7, 16900 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7, 16901 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7, 16902 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7, 16903 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf, 16904 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef, 16905 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df, 16906 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff 16907 ]), 9]; 16908 16909 var fixedDistCodeTab = [new Uint32Array([ 16910 0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, 16911 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, 16912 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d, 16913 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000 16914 ]), 5]; 16915 16916 function error(e) { 16917 throw new Error(e) 16918 } 16919 16920 function constructor(bytes) { 16921 //var bytes = stream.getBytes(); 16922 var bytesPos = 0; 16923 16924 var cmf = bytes[bytesPos++]; 16925 var flg = bytes[bytesPos++]; 16926 if (cmf == -1 || flg == -1) 16927 error('Invalid header in flate stream'); 16928 if ((cmf & 0x0f) != 0x08) 16929 error('Unknown compression method in flate stream'); 16930 if ((((cmf << 8) + flg) % 31) != 0) 16931 error('Bad FCHECK in flate stream'); 16932 if (flg & 0x20) 16933 error('FDICT bit set in flate stream'); 16934 16935 this.bytes = bytes; 16936 this.bytesPos = bytesPos; 16937 16938 this.codeSize = 0; 16939 this.codeBuf = 0; 16940 16941 DecodeStream.call(this); 16942 } 16943 16944 constructor.prototype = Object.create(DecodeStream.prototype); 16945 16946 constructor.prototype.getBits = function(bits) {
16947 var codeSize = this.codeSize; 16948 var codeBuf = this.codeBuf; 16949 var bytes = this.bytes; 16950 var bytesPos = this.bytesPos; 16951 16952 var b; 16953 while (codeSize < bits) { 16954 if (typeof (b = bytes[bytesPos++]) == 'undefined') 16955 error('Bad encoding in flate stream'); 16956 codeBuf |= b << codeSize; 16957 codeSize += 8; 16958 } 16959 b = codeBuf & ((1 << bits) - 1); 16960 this.codeBuf = codeBuf >> bits; 16961 this.codeSize = codeSize -= bits; 16962 this.bytesPos = bytesPos; 16963 return b; 16964 }; 16965 16966 constructor.prototype.getCode = function(table) { 16967 var codes = table[0]; 16968 var maxLen = table[1]; 16969 var codeSize = this.codeSize; 16970 var codeBuf = this.codeBuf; 16971 var bytes = this.bytes; 16972 var bytesPos = this.bytesPos; 16973 16974 while (codeSize < maxLen) { 16975 var b; 16976 if (typeof (b = bytes[bytesPos++]) == 'undefined') 16977 error('Bad encoding in flate stream'); 16978 codeBuf |= (b << codeSize); 16979 codeSize += 8; 16980 } 16981 var code = codes[codeBuf & ((1 << maxLen) - 1)]; 16982 var codeLen = code >> 16; 16983 var codeVal = code & 0xffff; 16984 if (codeSize == 0 || codeSize < codeLen || codeLen == 0) 16985 error('Bad encoding in flate stream'); 16986 this.codeBuf = (codeBuf >> codeLen); 16987 this.codeSize = (codeSize - codeLen); 16988 this.bytesPos = bytesPos; 16989 return codeVal; 16990 }; 16991 16992 constructor.prototype.generateHuffmanTable = function(lengths) { 16993 var n = lengths.length; 16994 16995 // find max code length 16996 var maxLen = 0; 16997 for (var i = 0; i < n; ++i) { 16998 if (lengths[i] > maxLen) 16999 maxLen = lengths[i]; 17000 } 17001 17002 // build the table 17003 var size = 1 << maxLen; 17004 var codes = new Uint32Array(size); 17005 for (var len = 1, code = 0, skip = 2; 17006 len <= maxLen; 17007 ++len, code <<= 1, skip <<= 1) { 17008 for (var val = 0; val < n; ++val) { 17009 if (lengths[val] == len) { 17010 // bit-reverse the code 17011 var code2 = 0; 17012 var t = code; 17013 for (var i = 0; i < len; ++i) { 17014 code2 = (code2 << 1) | (t & 1); 17015 t >>= 1; 17016 } 17017 17018 // fill the table entries 17019 for (var i = code2; i < size; i += skip) 17020 codes[i] = (len << 16) | val; 17021 17022 ++code; 17023 } 17024 } 17025 } 17026 17027 return [codes, maxLen]; 17028 }; 17029 17030 constructor.prototype.readBlock = function() { 17031 function repeat(stream, array, len, offset, what) { 17032 var repeat = stream.getBits(len) + offset; 17033 while (repeat-- > 0) 17034 array[i++] = what; 17035 } 17036 17037 // read block header 17038 var hdr = this.getBits(3); 17039 if (hdr & 1) 17040 this.eof = true; 17041 hdr >>= 1; 17042 17043 if (hdr == 0) { // uncompressed block 17044 var bytes = this.bytes; 17045 var bytesPos = this.bytesPos; 17046 var b; 17047 17048 if (typeof (b = bytes[bytesPos++]) == 'undefined') 17049 error('Bad block header in flate stream'); 17050 var blockLen = b; 17051 if (typeof (b = bytes[bytesPos++]) == 'undefined') 17052 error('Bad block header in flate stream'); 17053 blockLen |= (b << 8); 17054 if (typeof (b = bytes[bytesPos++]) == 'undefined') 17055 error('Bad block header in flate stream'); 17056 var check = b; 17057 if (typeof (b = bytes[bytesPos++]) == 'undefined') 17058 error('Bad block header in flate stream'); 17059 check |= (b << 8); 17060 if (check != (~blockLen & 0xffff)) 17061 error('Bad uncompressed block length in flate stream'); 17062 17063 this.codeBuf = 0; 17064 this.codeSize = 0; 17065 17066 var bufferLength = this.bufferLength; 17067 var buffer = this.ensureBuffer(bufferLength + blockLen); 17068 var end = bufferLength + blockLen; 17069 this.bufferLength = end; 17070 for (var n = bufferLength; n < end; ++n) { 17071 if (typeof (b = bytes[bytesPos++]) == 'undefined') { 17072 this.eof = true; 17073 break; 17074 } 17075 buffer[n] = b; 17076 } 17077 this.bytesPos = bytesPos; 17078 return; 17079 } 17080 17081 var litCodeTable; 17082 var distCodeTable; 17083 if (hdr == 1) { // compressed block, fixed codes 17084 litCodeTable = fixedLitCodeTab; 17085 distCodeTable = fixedDistCodeTab; 17086 } else if (hdr == 2) { // compressed block, dynamic codes 17087 var numLitCodes = this.getBits(5) + 257; 17088 var numDistCodes = this.getBits(5) + 1; 17089 var numCodeLenCodes = this.getBits(4) + 4; 17090 17091 // build the code lengths code table 17092 var codeLenCodeLengths = Array(codeLenCodeMap.length); 17093 var i = 0; 17094 while (i < numCodeLenCodes) 17095 codeLenCodeLengths[codeLenCodeMap[i++]] = this.getBits(3); 17096 var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths); 17097 17098 // build the literal and distance code tables 17099 var len = 0; 17100 var i = 0; 17101 var codes = numLitCodes + numDistCodes; 17102 var codeLengths = new Array(codes); 17103 while (i < codes) { 17104 var code = this.getCode(codeLenCodeTab); 17105 if (code == 16) { 17106 repeat(this, codeLengths, 2, 3, len); 17107 } else if (code == 17) { 17108 repeat(this, codeLengths, 3, 3, len = 0); 17109 } else if (code == 18) { 17110 repeat(this, codeLengths, 7, 11, len = 0); 17111 } else { 17112 codeLengths[i++] = len = code; 17113 } 17114 } 17115 17116 litCodeTable = 17117 this.generateHuffmanTable(codeLengths.slice(0, numLitCodes)); 17118 distCodeTable = 17119 this.generateHuffmanTable(codeLengths.slice(numLitCodes, codes)); 17120 } else { 17121 error('Unknown block type in flate stream'); 17122 } 17123 17124 var buffer = this.buffer; 17125 var limit = buffer ? buffer.length : 0; 17126 var pos = this.bufferLength; 17127 while (true) { 17128 var code1 = this.getCode(litCodeTable); 17129 if (code1 < 256) { 17130 if (pos + 1 >= limit) { 17131 buffer = this.ensureBuffer(pos + 1); 17132 limit = buffer.length; 17133 } 17134 buffer[pos++] = code1; 17135 continue; 17136 } 17137 if (code1 == 256) { 17138 this.bufferLength = pos; 17139 return; 17140 } 17141 code1 -= 257; 17142 code1 = lengthDecode[code1]; 17143 var code2 = code1 >> 16; 17144 if (code2 > 0) 17145 code2 = this.getBits(code2); 17146 var len = (code1 & 0xffff) + code2;
17147 code1 = this.getCode(distCodeTable); 17148 code1 = distDecode[code1]; 17149 code2 = code1 >> 16; 17150 if (code2 > 0) 17151 code2 = this.getBits(code2); 17152 var dist = (code1 & 0xffff) + code2; 17153 if (pos + len >= limit) { 17154 buffer = this.ensureBuffer(pos + len); 17155 limit = buffer.length; 17156 } 17157 for (var k = 0; k < len; ++k, ++pos) 17158 buffer[pos] = buffer[pos - dist]; 17159 } 17160 }; 17161 17162 return constructor; 17163})(); 17164 17165/** 17166 * JavaScript Polyfill functions for jsPDF 17167 * Collected from public resources by 17168 * https://github.com/diegocr 17169 */ 17170 17171(function (global) { 17172 var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 17173 17174 if (typeof global.btoa === 'undefined') { 17175 global.btoa = function(data) { 17176 // discuss at: http://phpjs.org/functions/base64_encode/ 17177 // original by: Tyler Akins (http://rumkin.com) 17178 // improved by: Bayron Guevara 17179 // improved by: Thunder.m 17180 // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 17181 // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 17182 // improved by: Rafal Kukawski (http://kukawski.pl) 17183 // bugfixed by: Pellentesque Malesuada 17184 // example 1: base64_encode('Kevin van Zonneveld'); 17185 // returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' 17186 17187 var o1,o2,o3,h1,h2,h3,h4,bits,i = 0,ac = 0,enc = '',tmp_arr = []; 17188 17189 if (!data) { 17190 return data; 17191 } 17192 17193 do { // pack three octets into four hexets 17194 o1 = data.charCodeAt(i++); 17195 o2 = data.charCodeAt(i++); 17196 o3 = data.charCodeAt(i++); 17197 17198 bits = o1 << 16 | o2 << 8 | o3; 17199 17200 h1 = bits >> 18 & 0x3f; 17201 h2 = bits >> 12 & 0x3f; 17202 h3 = bits >> 6 & 0x3f; 17203 h4 = bits & 0x3f; 17204 17205 // use hexets to index into b64, and append result to encoded string 17206 tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); 17207 } while (i < data.length); 17208 17209 enc = tmp_arr.join(''); 17210 17211 var r = data.length % 3; 17212 17213 return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); 17214 }; 17215 } 17216 17217 if (typeof global.atob === 'undefined') { 17218 global.atob = function(data) { 17219 // discuss at: http://phpjs.org/functions/base64_decode/ 17220 // original by: Tyler Akins (http://rumkin.com) 17221 // improved by: Thunder.m 17222 // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 17223 // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 17224 // input by: Aman Gupta 17225 // input by: Brett Zamir (http://brett-zamir.me) 17226 // bugfixed by: Onno Marsman 17227 // bugfixed by: Pellentesque Malesuada 17228 // bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 17229 // example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); 17230 // returns 1: 'Kevin van Zonneveld' 17231 17232 var o1,o2,o3,h1,h2,h3,h4,bits,i = 0,ac = 0,dec = '',tmp_arr = []; 17233 17234 if (!data) { 17235 return data; 17236 } 17237 17238 data += ''; 17239 17240 do { // unpack four hexets into three octets using index points in b64 17241 h1 = b64.indexOf(data.charAt(i++)); 17242 h2 = b64.indexOf(data.charAt(i++)); 17243 h3 = b64.indexOf(data.charAt(i++)); 17244 h4 = b64.indexOf(data.charAt(i++)); 17245 17246 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; 17247 17248 o1 = bits >> 16 & 0xff; 17249 o2 = bits >> 8 & 0xff; 17250 o3 = bits & 0xff; 17251 17252 if (h3 == 64) { 17253 tmp_arr[ac++] = String.fromCharCode(o1); 17254 } else if (h4 == 64) { 17255 tmp_arr[ac++] = String.fromCharCode(o1, o2); 17256 } else { 17257 tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); 17258 } 17259 } while (i < data.length); 17260 17261 dec = tmp_arr.join(''); 17262 17263 return dec; 17264 }; 17265 } 17266 17267 if (!Array.prototype.map) { 17268 Array.prototype.map = function(fun /*, thisArg */) { 17269 if (this === void 0 || this === null || typeof fun !== "function") 17270 throw new TypeError(); 17271 17272 var t = Object(this), len = t.length >>> 0, res = new Array(len); 17273 var thisArg = arguments.length > 1 ? arguments[1] : void 0; 17274 for (var i = 0; i < len; i++) { 17275 // NOTE: Absolute correctness would demand Object.defineProperty 17276 // be used. But this method is fairly new, and failure is 17277 // possible only if Object.prototype or Array.prototype 17278 // has a property |i| (very unlikely), so use a less-correct 17279 // but more portable alternative. 17280 if (i in t) 17281 res[i] = fun.call(thisArg, t[i], i, t); 17282 } 17283 17284 return res; 17285 }; 17286 } 17287 17288 17289 if(!Array.isArray) { 17290 Array.isArray = function(arg) { 17291 return Object.prototype.toString.call(arg) === '[object Array]'; 17292 }; 17293 } 17294 17295 if (!Array.prototype.forEach) { 17296 Array.prototype.forEach = function(fun, thisArg) { 17297 "use strict"; 17298 17299 if (this === void 0 || this === null || typeof fun !== "function") 17300 throw new TypeError(); 17301 17302 var t = Object(this), len = t.length >>> 0; 17303 for (var i = 0; i < len; i++) { 17304 if (i in t) 17305 fun.call(thisArg, t[i], i, t); 17306 } 17307 }; 17308 } 17309 17310 if (!Object.keys) { 17311 Object.keys = (function () { 17312 'use strict'; 17313 17314 var hasOwnProperty = Object.prototype.hasOwnProperty, 17315 hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
17316 dontEnums = ['toString','toLocaleString','valueOf','hasOwnProperty', 17317 'isPrototypeOf','propertyIsEnumerable','constructor'], 17318 dontEnumsLength = dontEnums.length; 17319 17320 return function (obj) { 17321 if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 17322 throw new TypeError(); 17323 } 17324 var result = [], prop, i; 17325 17326 for (prop in obj) { 17327 if (hasOwnProperty.call(obj, prop)) { 17328 result.push(prop); 17329 } 17330 } 17331 17332 if (hasDontEnumBug) { 17333 for (i = 0; i < dontEnumsLength; i++) { 17334 if (hasOwnProperty.call(obj, dontEnums[i])) { 17335 result.push(dontEnums[i]); 17336 } 17337 } 17338 } 17339 return result; 17340 }; 17341 }()); 17342 } 17343 17344 if (!String.prototype.trim) { 17345 String.prototype.trim = function () { 17346 return this.replace(/^\s+|\s+$/g, ''); 17347 }; 17348 } 17349 if (!String.prototype.trimLeft) { 17350 String.prototype.trimLeft = function() { 17351 return this.replace(/^\s+/g, ""); 17352 }; 17353 } 17354 if (!String.prototype.trimRight) { 17355 String.prototype.trimRight = function() { 17356 return this.replace(/\s+$/g, ""); 17357 }; 17358 } 17359 17360})(typeof self !== "undefined" && self || typeof window !== "undefined" && window || undefined); 17361 17362return jsPDF; 17363 17364})));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.