1/** 2 @license html2canvas v0.34 <http://html2canvas.hertzen.com> 3 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 4 http://www.twitter.com/niklasvh 5 6 Released under MIT License 7 */ 8(function(window, document, undefined){ 9 10/* 11 html2canvas v0.34 <http://html2canvas.hertzen.com> 12 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 13 http://www.twitter.com/niklasvh 14 15 Released under MIT License 16 */ 17"use strict"; 18 19var _html2canvas = {}, 20previousElement, 21computedCSS, 22html2canvas; 23 24 25function h2clog(a) { 26 if (_html2canvas.logging && window.console && window.console.log) { 27 window.console.log(a); 28 } 29} 30 31_html2canvas.Util = {}; 32 33_html2canvas.Util.backgroundImage = function (src) { 34 35 if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { 36 return src; 37 } 38 39 if (src.toLowerCase().substr( 0, 5 ) === 'url("') { 40 src = src.substr( 5 ); 41 src = src.substr( 0, src.length - 2 ); 42 } else { 43 src = src.substr( 4 ); 44 src = src.substr( 0, src.length - 1 ); 45 } 46 47 return src; 48}; 49 50_html2canvas.Util.Bounds = function getBounds (el) { 51 var clientRect, 52 bounds = {}; 53 54 if (el.getBoundingClientRect){ 55 clientRect = el.getBoundingClientRect(); 56 57 58 // TODO add scroll position to bounds, so no scrolling of window necessary 59 bounds.top = clientRect.top; 60 bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); 61 bounds.left = clientRect.left; 62 63 // older IE doesn't have width/height, but top/bottom instead 64 bounds.width = clientRect.width || (clientRect.right - clientRect.left); 65 bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); 66 67 return bounds; 68 69 } 70}; 71 72_html2canvas.Util.getCSS = function (el, attribute) { 73 // return $(el).css(attribute); 74 75 var val; 76 77 function toPX( attribute, val ) { 78 var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], 79 left, 80 style = el.style; 81 82 // Check if we are not dealing with pixels, (Opera has issues with this) 83 // Ported from jQuery css.js 84 // From the awesome hack by Dean Edwards 85 // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 86 87 // If we're not dealing with a regular pixel number 88 // but a number that has a weird ending, we need to convert it to pixels 89 90 if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) { 91 92 // Remember the original values 93 left = style.left; 94 95 // Put in the new values to get a computed value out 96 if ( rsLeft ) { 97 el.runtimeStyle.left = el.currentStyle.left; 98 } 99 style.left = attribute === "fontSize" ? "1em" : (val || 0); 100 val = style.pixelLeft + "px"; 101 102 // Revert the changed values 103 style.left = left; 104 if ( rsLeft ) { 105 el.runtimeStyle.left = rsLeft; 106 } 107 108 } 109 110 if (!/^(thin|medium|thick)$/i.test( val )) { 111 return Math.round(parseFloat( val )) + "px"; 112 } 113 114 return val; 115 116 } 117 118 119 if ( window.getComputedStyle ) { 120 if ( previousElement !== el ) { 121 computedCSS = document.defaultView.getComputedStyle(el, null); 122 } 123 val = computedCSS[ attribute ]; 124 125 if ( attribute === "backgroundPosition" ) { 126 127 val = (val.split(",")[0] || "0 0").split(" "); 128 129 val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ]; 130 val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always 131 val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ]; 132 } else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) { 133 var arr = val.split(" "); 134 if ( arr.length <= 1 ) { 135 arr[ 1 ] = arr[ 0 ]; 136 } 137 arr[ 0 ] = parseInt( arr[ 0 ], 10 ); 138 arr[ 1 ] = parseInt( arr[ 1 ], 10 ); 139 val = arr; 140 } 141 142 } else if ( el.currentStyle ) { 143 // IE 9> 144 if (attribute === "backgroundPosition") { 145 // Older IE uses -x and -y 146 val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ]; 147 } else { 148 149 val = toPX( attribute, el.currentStyle[ attribute ] ); 150 151 if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) { 152 switch (val) { 153 case "thin": 154 val = "1px"; 155 break; 156 case "medium":
157 val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around 158 break; 159 case "thick": 160 val = "5px"; 161 break; 162 } 163 } 164 } 165 166 167 168 } 169 170 171 172 173 return val; 174 175 176 177//return $(el).css(attribute); 178 179 180}; 181 182 183_html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) { 184 // TODO add support for multi image backgrounds 185 186 var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) , 187 topPos, 188 left, 189 percentage, 190 val; 191 192 if (bgposition.length === 1){ 193 val = bgposition; 194 195 bgposition = []; 196 197 bgposition[0] = val; 198 bgposition[1] = val; 199 } 200 201 202 203 if (bgposition[0].toString().indexOf("%") !== -1){ 204 percentage = (parseFloat(bgposition[0])/100); 205 left = ((bounds.width * percentage)-(image.width*percentage)); 206 207 }else{ 208 left = parseInt(bgposition[0],10); 209 } 210 211 if (bgposition[1].toString().indexOf("%") !== -1){ 212 213 percentage = (parseFloat(bgposition[1])/100); 214 topPos = ((bounds.height * percentage)-(image.height*percentage)); 215 }else{ 216 topPos = parseInt(bgposition[1],10); 217 } 218 219 220 221 222 return { 223 top: topPos, 224 left: left 225 }; 226 227}; 228 229_html2canvas.Util.Extend = function (options, defaults) { 230 for (var key in options) { 231 if (options.hasOwnProperty(key)) { 232 defaults[key] = options[key]; 233 } 234 } 235 return defaults; 236}; 237 238 239/* 240 * Derived from jQuery.contents() 241 * Copyright 2010, John Resig 242 * Dual licensed under the MIT or GPL Version 2 licenses. 243 * http://jquery.org/license 244 */ 245_html2canvas.Util.Children = function( elem ) { 246 247 248 var children; 249 try { 250 251 children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? 252 elem.contentDocument || elem.contentWindow.document : (function( array ){ 253 var ret = []; 254 255 if ( array !== null ) { 256 257 (function( first, second ) { 258 var i = first.length, 259 j = 0; 260 261 if ( typeof second.length === "number" ) { 262 for ( var l = second.length; j < l; j++ ) { 263 first[ i++ ] = second[ j ]; 264 } 265 266 } else { 267 while ( second[j] !== undefined ) { 268 first[ i++ ] = second[ j++ ]; 269 } 270 } 271 272 first.length = i; 273 274 return first; 275 })( ret, array ); 276 277 } 278 279 return ret; 280 })( elem.childNodes ); 281 282 } catch (ex) { 283 h2clog("html2canvas.Util.Children failed with exception: " + ex.message); 284 children = []; 285 } 286 return children; 287}; 288 289/* 290 html2canvas v0.34 <http://html2canvas.hertzen.com> 291 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 292 http://www.twitter.com/niklasvh 293 294 Contributor(s): 295 Niklas von Hertzen <http://www.twitter.com/niklasvh> 296 André Fiedler <http://www.twitter.com/sonnenkiste> 297 298 Released under MIT License 299 */ 300 301(function(){ 302 303_html2canvas.Generate = {}; 304 305var reGradients = [ 306 /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, 307 /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, 308 /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)-]+)\)$/, 309 /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/, 310 /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z-]+)([\w\d\.\s,%\(\)]+)\)$/, 311 /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z-]*)([\w\d\.\s,%\(\)]+)\)$/, 312 /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z-]+)([\w\d\.\s,%\(\)]+)\)$/ 313]; 314 315/* 316 * TODO: Add IE10 vendor prefix (-ms) support 317 * TODO: Add W3C gradient (linear-gradient) support
318 * TODO: Add old Webkit -webkit-gradient(radial, ...) support 319 * TODO: Maybe some RegExp optimizations are possible ;o) 320 */ 321_html2canvas.Generate.parseGradient = function(css, bounds) { 322 var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3; 323 324 for(i = 0; i < len; i+=1){ 325 m1 = css.match(reGradients[i]); 326 if(m1) break; 327 } 328 329 if(m1) { 330 switch(m1[1]) { 331 case '-webkit-linear-gradient': 332 case '-o-linear-gradient': 333 334 gradient = { 335 type: 'linear', 336 x0: null, 337 y0: null, 338 x1: null, 339 y1: null, 340 colorStops: [] 341 }; 342 343 // get coordinates 344 m2 = m1[2].match(/\w+/g); 345 if(m2){ 346 m2Len = m2.length; 347 for(i = 0; i < m2Len; i+=1){ 348 switch(m2[i]) { 349 case 'top': 350 gradient.y0 = 0; 351 gradient.y1 = bounds.height; 352 break; 353 354 case 'right': 355 gradient.x0 = bounds.width; 356 gradient.x1 = 0; 357 break; 358 359 case 'bottom': 360 gradient.y0 = bounds.height; 361 gradient.y1 = 0; 362 break; 363 364 case 'left': 365 gradient.x0 = 0; 366 gradient.x1 = bounds.width; 367 break; 368 } 369 } 370 } 371 if(gradient.x0 === null && gradient.x1 === null){ // center 372 gradient.x0 = gradient.x1 = bounds.width / 2; 373 } 374 if(gradient.y0 === null && gradient.y1 === null){ // center 375 gradient.y0 = gradient.y1 = bounds.height / 2; 376 } 377 378 // get colors and stops 379 m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); 380 if(m2){ 381 m2Len = m2.length; 382 step = 1 / Math.max(m2Len - 1, 1); 383 for(i = 0; i < m2Len; i+=1){ 384 m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); 385 if(m3[2]){ 386 stop = parseFloat(m3[2]); 387 if(m3[3] === '%'){ 388 stop /= 100; 389 } else { // px - stupid opera 390 stop /= bounds.width; 391 } 392 } else { 393 stop = i * step; 394 } 395 gradient.colorStops.push({ 396 color: m3[1], 397 stop: stop 398 }); 399 } 400 } 401 break; 402 403 case '-webkit-gradient': 404 405 gradient = { 406 type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions 407 x0: 0, 408 y0: 0, 409 x1: 0, 410 y1: 0, 411 colorStops: [] 412 }; 413 414 // get coordinates 415 m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/); 416 if(m2){ 417 gradient.x0 = (m2[1] * bounds.width) / 100; 418 gradient.y0 = (m2[2] * bounds.height) / 100; 419 gradient.x1 = (m2[3] * bounds.width) / 100; 420 gradient.y1 = (m2[4] * bounds.height) / 100; 421 } 422 423 // get colors and stops 424 m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g); 425 if(m2){ 426 m2Len = m2.length; 427 for(i = 0; i < m2Len; i+=1){ 428 m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/); 429 stop = parseFloat(m3[2]); 430 if(m3[1] === 'from') stop = 0.0; 431 if(m3[1] === 'to') stop = 1.0; 432 gradient.colorStops.push({ 433 color: m3[3], 434 stop: stop 435 }); 436 } 437 } 438 break; 439 440 case '-moz-linear-gradient': 441 442 gradient = { 443 type: 'linear', 444 x0: 0, 445 y0: 0, 446 x1: 0, 447 y1: 0, 448 colorStops: [] 449 }; 450 451 // get coordinates 452 m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); 453 454 // m2[1] == 0% -> left 455 // m2[1] == 50% -> center 456 // m2[1] == 100% -> right 457 458 // m2[2] == 0% -> top 459 // m2[2] == 50% -> center 460 // m2[2] == 100% -> bottom 461 462 if(m2){ 463 gradient.x0 = (m2[1] * bounds.width) / 100;
464 gradient.y0 = (m2[2] * bounds.height) / 100; 465 gradient.x1 = bounds.width - gradient.x0; 466 gradient.y1 = bounds.height - gradient.y0; 467 } 468 469 // get colors and stops 470 m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g); 471 if(m2){ 472 m2Len = m2.length; 473 step = 1 / Math.max(m2Len - 1, 1); 474 for(i = 0; i < m2Len; i+=1){ 475 m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/); 476 if(m3[2]){ 477 stop = parseFloat(m3[2]); 478 if(m3[3]){ // percentage 479 stop /= 100; 480 } 481 } else { 482 stop = i * step; 483 } 484 gradient.colorStops.push({ 485 color: m3[1], 486 stop: stop 487 }); 488 } 489 } 490 break; 491 492 case '-webkit-radial-gradient': 493 case '-moz-radial-gradient': 494 case '-o-radial-gradient': 495 496 gradient = { 497 type: 'circle', 498 x0: 0, 499 y0: 0, 500 x1: bounds.width, 501 y1: bounds.height, 502 cx: 0, 503 cy: 0, 504 rx: 0, 505 ry: 0, 506 colorStops: [] 507 }; 508 509 // center 510 m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); 511 if(m2){ 512 gradient.cx = (m2[1] * bounds.width) / 100; 513 gradient.cy = (m2[2] * bounds.height) / 100; 514 } 515 516 // size 517 m2 = m1[3].match(/\w+/); 518 m3 = m1[4].match(/[a-z-]*/); 519 if(m2 && m3){ 520 switch(m3[0]){ 521 case 'farthest-corner': 522 case 'cover': // is equivalent to farthest-corner 523 case '': // mozilla removes "cover" from definition :( 524 var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); 525 var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 526 var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 527 var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); 528 gradient.rx = gradient.ry = Math.max(tl, tr, br, bl); 529 break; 530 case 'closest-corner': 531 var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); 532 var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 533 var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 534 var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); 535 gradient.rx = gradient.ry = Math.min(tl, tr, br, bl); 536 break; 537 case 'farthest-side': 538 if(m2[0] === 'circle'){ 539 gradient.rx = gradient.ry = Math.max( 540 gradient.cx, 541 gradient.cy, 542 gradient.x1 - gradient.cx, 543 gradient.y1 - gradient.cy 544 ); 545 } else { // ellipse 546 547 gradient.type = m2[0]; 548
549 gradient.rx = Math.max( 550 gradient.cx, 551 gradient.x1 - gradient.cx 552 ); 553 gradient.ry = Math.max( 554 gradient.cy, 555 gradient.y1 - gradient.cy 556 ); 557 } 558 break; 559 case 'closest-side': 560 case 'contain': // is equivalent to closest-side 561 if(m2[0] === 'circle'){ 562 gradient.rx = gradient.ry = Math.min( 563 gradient.cx, 564 gradient.cy, 565 gradient.x1 - gradient.cx, 566 gradient.y1 - gradient.cy 567 ); 568 } else { // ellipse 569 570 gradient.type = m2[0]; 571 572 gradient.rx = Math.min( 573 gradient.cx, 574 gradient.x1 - gradient.cx 575 ); 576 gradient.ry = Math.min( 577 gradient.cy, 578 gradient.y1 - gradient.cy 579 ); 580 } 581 break; 582 583 // TODO: add support for "30px 40px" sizes (webkit only) 584 } 585 } 586 587 // color stops 588 m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); 589 if(m2){ 590 m2Len = m2.length; 591 step = 1 / Math.max(m2Len - 1, 1); 592 for(i = 0; i < m2Len; i+=1){ 593 m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); 594 if(m3[2]){ 595 stop = parseFloat(m3[2]); 596 if(m3[3] === '%'){ 597 stop /= 100; 598 } else { // px - stupid opera 599 stop /= bounds.width; 600 } 601 } else { 602 stop = i * step; 603 } 604 gradient.colorStops.push({ 605 color: m3[1], 606 stop: stop 607 }); 608 } 609 } 610 break; 611 } 612 } 613 614 return gradient; 615}; 616 617_html2canvas.Generate.Gradient = function(src, bounds) { 618 var canvas = document.createElement('canvas'), 619 ctx = canvas.getContext('2d'), 620 gradient, grad, i, len, img; 621 622 canvas.width = bounds.width; 623 canvas.height = bounds.height; 624 625 // TODO: add support for multi defined background gradients (like radial gradient example in background.html) 626 gradient = _html2canvas.Generate.parseGradient(src, bounds); 627 628 img = new Image(); 629 630 if(gradient){ 631 if(gradient.type === 'linear'){ 632 grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); 633 634 for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { 635 try { 636 grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); 637 } 638 catch(e) { 639 h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 640 } 641 } 642 643 ctx.fillStyle = grad; 644 ctx.fillRect(0, 0, bounds.width, bounds.height); 645 646 img.src = canvas.toDataURL(); 647 } else if(gradient.type === 'circle'){ 648 649 grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); 650 651 for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { 652 try { 653 grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); 654 } 655 catch(e) { 656 h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 657 } 658 } 659 660 ctx.fillStyle = grad; 661 ctx.fillRect(0, 0, bounds.width, bounds.height); 662 663 img.src = canvas.toDataURL(); 664 } else if(gradient.type === 'ellipse'){ 665 666 // draw circle 667 var canvasRadial = document.createElement('canvas'),
668 ctxRadial = canvasRadial.getContext('2d'), 669 ri = Math.max(gradient.rx, gradient.ry), 670 di = ri * 2, imgRadial; 671 672 canvasRadial.width = canvasRadial.height = di; 673 674 grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); 675 676 for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { 677 try { 678 grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); 679 } 680 catch(e) { 681 h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 682 } 683 } 684 685 ctxRadial.fillStyle = grad; 686 ctxRadial.fillRect(0, 0, di, di); 687 688 ctx.fillStyle = gradient.colorStops[i - 1].color; 689 ctx.fillRect(0, 0, canvas.width, canvas.height); 690 691 imgRadial = new Image(); 692 imgRadial.onload = function() { // wait until the image is filled 693 694 // transform circle to ellipse 695 ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); 696 697 img.src = canvas.toDataURL(); 698 699 } 700 imgRadial.src = canvasRadial.toDataURL(); 701 } 702 } 703 704 return img; 705}; 706 707_html2canvas.Generate.ListAlpha = function(number) { 708 var tmp = "", 709 modulus; 710 711 do { 712 modulus = number % 26; 713 tmp = String.fromCharCode((modulus) + 64) + tmp; 714 number = number / 26; 715 }while((number*26) > 26); 716 717 return tmp; 718}; 719 720_html2canvas.Generate.ListRoman = function(number) { 721 var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], 722 decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], 723 roman = "", 724 v, 725 len = romanArray.length; 726 727 if (number <= 0 || number >= 4000) { 728 return number; 729 } 730 731 for (v=0; v < len; v+=1) { 732 while (number >= decimal[v]) { 733 number -= decimal[v]; 734 roman += romanArray[v]; 735 } 736 } 737 738 return roman; 739 740}; 741 742})(); 743/* 744 html2canvas v0.34 <http://html2canvas.hertzen.com> 745 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 746 http://www.twitter.com/niklasvh 747 748 Released under MIT License 749 */ 750 751/* 752 * New function for traversing elements 753 */ 754 755_html2canvas.Parse = function ( images, options ) { 756 window.scroll(0,0); 757 758 var support = { 759 rangeBounds: false, 760 svgRendering: options.svgRendering && (function( ){ 761 var img = new Image(), 762 canvas = document.createElement("canvas"), 763 ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); 764 if (ctx === false) { 765 // browser doesn't support canvas, good luck supporting SVG on canvas 766 return false; 767 } 768 canvas.width = canvas.height = 10; 769 img.src = [ 770 "data:image/svg+xml,", 771 "<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>", 772 "<foreignObject width='10' height='10'>", 773 "<div xmlns='http://www.w3.org/1999/xhtml' style='width:10;height:10;'>", 774 "sup", 775 "</div>", 776 "</foreignObject>", 777 "</svg>" 778 ].join(""); 779 try { 780 ctx.drawImage(img, 0, 0); 781 canvas.toDataURL(); 782 } catch(e) { 783 return false; 784 } 785 h2clog('html2canvas: Parse: SVG powered rendering available'); 786 return true; 787 788 })() 789 }, 790 element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default 791 needReorder = false, 792 numDraws = 0, 793 fontData = {}, 794 doc = element.ownerDocument, 795 ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), 796 body = doc.body, 797 r, 798 testElement, 799 rangeBounds, 800 rangeHeight, 801 stack, 802 ctx, 803 docDim, 804 i, 805 children, 806 childrenLen; 807 808 809 function docSize(){ 810 811 return { 812 width: Math.max( 813 Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), 814 Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), 815 Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) 816 ), 817 height: Math.max( 818 Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), 819 Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), 820 Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) 821 ) 822 }; 823 824 } 825 826 images = images || {}; 827 828 // Test whether we can use ranges to measure bounding boxes 829 // Opera doesn't provide valid bounds.height/bottom even though it supports the method. 830 831 832 if (doc.createRange) { 833 r = doc.createRange(); 834 //this.support.rangeBounds = new Boolean(r.getBoundingClientRect); 835 if (r.getBoundingClientRect){ 836 testElement = doc.createElement('boundtest'); 837 testElement.style.height = "123px"; 838 testElement.style.display = "block"; 839 body.appendChild(testElement); 840 841 r.selectNode(testElement); 842 rangeBounds = r.getBoundingClientRect(); 843 rangeHeight = rangeBounds.height; 844 845 if (rangeHeight === 123) { 846 support.rangeBounds = true; 847 } 848 body.removeChild(testElement); 849 850 851 } 852 853 } 854 855 856 /* 857 var rootStack = new this.storageContext($(document).width(),$(document).height()); 858 rootStack.opacity = this.getCSS(this.element,"opacity"); 859 var stack = this.newElement(this.element,rootStack); 860 861 862 this.parseElement(this.element,stack); 863 */ 864 865 866 867 868 var getCSS = _html2canvas.Util.getCSS; 869 function getCSSInt(element, attribute) { 870 var val = parseInt(getCSS(element, attribute), 10); 871 return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html 872 } 873 874 // Drawing a rectangle 875 function renderRect (ctx, x, y, w, h, bgcolor) { 876 if ( bgcolor !== "transparent" ){ 877 ctx.setVariable("fillStyle", bgcolor); 878 ctx.fillRect (x, y, w, h); 879 numDraws+=1; 880 } 881 } 882 883 884 function textTransform (text, transform) { 885 switch(transform){ 886 case "lowercase": 887 return text.toLowerCase(); 888 889 case "capitalize": 890 return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { 891 if (m.length > 0) { 892 return p1 + p2.toUpperCase(); 893 } 894 } ); 895 896 case "uppercase": 897 return text.toUpperCase(); 898 899 default: 900 return text; 901 902 } 903 904 } 905 906 function trimText (text) { 907 return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); 908 } 909 910 function fontMetrics (font, fontSize) { 911 912 if (fontData[font + "-" + fontSize] !== undefined) { 913 return fontData[font + "-" + fontSize]; 914 } 915 916 917 var container = doc.createElement('div'), 918 img = doc.createElement('img'), 919 span = doc.createElement('span'), 920 baseline, 921 middle, 922 metricsObj; 923 924 925 container.style.visibility = "hidden"; 926 container.style.fontFamily = font; 927 container.style.fontSize = fontSize; 928 container.style.margin = 0; 929 container.style.padding = 0; 930 931 body.appendChild(container); 932 933 934 935 // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) 936 img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; 937 img.width = 1; 938 img.height = 1; 939 940 img.style.margin = 0; 941 img.style.padding = 0; 942 img.style.verticalAlign = "baseline"; 943 944 span.style.fontFamily = font; 945 span.style.fontSize = fontSize; 946 span.style.margin = 0; 947 span.style.padding = 0; 948 949 950 951 952 span.appendChild(doc.createTextNode('Hidden Text')); 953 container.appendChild(span); 954 container.appendChild(img); 955 baseline = (img.offsetTop - span.offsetTop) + 1; 956 957 container.removeChild(span); 958 container.appendChild(doc.createTextNode('Hidden Text')); 959 960 container.style.lineHeight = "normal"; 961 img.style.verticalAlign = "super"; 962 963 middle = (img.offsetTop-container.offsetTop) + 1; 964 metricsObj = { 965 baseline: baseline, 966 lineWidth: 1, 967 middle: middle 968 }; 969 970 971 fontData[font + "-" + fontSize] = metricsObj; 972 973 body.removeChild(container); 974 975 return metricsObj; 976 977 } 978 979 980 function drawText(currentText, x, y, ctx){ 981 if (trimText(currentText).length>0) { 982 ctx.fillText(currentText,x,y); 983 numDraws+=1; 984 } 985 } 986 987 988 function renderText(el, textNode, stack) { 989 var ctx = stack.ctx, 990 family = getCSS(el, "fontFamily"), 991 size = getCSS(el, "fontSize"), 992 color = getCSS(el, "color"), 993 text_decoration = getCSS(el, "textDecoration"), 994 text_align = getCSS(el, "textAlign"), 995 letter_spacing = getCSS(el, "letterSpacing"), 996 bounds, 997 text, 998 metrics, 999 renderList, 1000 listLen, 1001 bold = getCSS(el, "fontWeight"), 1002 font_style = getCSS(el, "fontStyle"), 1003 font_variant = getCSS(el, "fontVariant"), 1004 align = false, 1005 newTextNode, 1006 textValue, 1007 textOffset = 0, 1008 oldTextNode, 1009 c, 1010 range, 1011 parent, 1012 wrapElement, 1013 backupText; 1014 1015 // apply text-transform:ation to the text 1016 1017 1018 1019 textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); 1020 text = trimText(textNode.nodeValue); 1021 1022 if (text.length>0){ 1023 1024 if (text_decoration !== "none"){ 1025 metrics = fontMetrics(family, size); 1026 } 1027 1028 text_align = text_align.replace(["-webkit-auto"],["auto"]); 1029 1030 if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ 1031 // this.setContextVariable(ctx,"textAlign",text_align); 1032 renderList = textNode.nodeValue.split(/(\b| )/); 1033 1034 }else{ 1035 // this.setContextVariable(ctx,"textAlign","left"); 1036 renderList = textNode.nodeValue.split(""); 1037 } 1038 1039 switch(parseInt(bold, 10)){ 1040 case 401: 1041 bold = "bold"; 1042 break; 1043 case 400: 1044 bold = "normal"; 1045 break; 1046 } 1047 1048 ctx.setVariable("fillStyle", color); 1049 1050 /* 1051 need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand 1052 to properly work in Firefox 1053 */ 1054 ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family); 1055 1056 if (align){ 1057 ctx.setVariable("textAlign", "right"); 1058 }else{ 1059 ctx.setVariable("textAlign", "left"); 1060 } 1061 1062 1063 /* 1064 if (stack.clip){ 1065 ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); 1066 ctx.clip(); 1067 } 1068 */ 1069 1070 1071 oldTextNode = textNode; 1072 1073 1074 for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) { 1075 textValue = null; 1076 1077 1078 1079 if (support.rangeBounds){ 1080 // getBoundingClientRect is supported for ranges 1081 if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) { 1082 textValue = renderList[c]; 1083 if (doc.createRange){ 1084 range = doc.createRange(); 1085 1086 range.setStart(textNode, textOffset); 1087 range.setEnd(textNode, textOffset + textValue.length); 1088 }else{ 1089 // TODO add IE support 1090 range = body.createTextRange(); 1091 } 1092 1093 if (range.getBoundingClientRect()) { 1094 bounds = range.getBoundingClientRect(); 1095 }else{ 1096 bounds = {}; 1097 } 1098 1099 } 1100 }else{ 1101 // it isn't supported, so let's wrap it inside an element instead and get the bounds there 1102 1103 // IE 9 bug 1104 if (typeof oldTextNode.nodeValue !== "string" ){ 1105 continue; 1106 } 1107 1108 newTextNode = oldTextNode.splitText(renderList[c].length); 1109 1110 parent = oldTextNode.parentNode; 1111 wrapElement = doc.createElement('wrapper'); 1112 backupText = oldTextNode.cloneNode(true); 1113 1114 wrapElement.appendChild(oldTextNode.cloneNode(true)); 1115 parent.replaceChild(wrapElement, oldTextNode); 1116 1117 bounds = _html2canvas.Util.Bounds(wrapElement); 1118 1119 textValue = oldTextNode.nodeValue; 1120 1121 oldTextNode = newTextNode; 1122 parent.replaceChild(backupText, wrapElement); 1123 1124 1125 } 1126 1127 if (textValue !== null){ 1128 drawText(textValue, bounds.left, bounds.bottom, ctx); 1129 } 1130 1131 switch(text_decoration) { 1132 case "underline": 1133 // Draws a line at the baseline of the font 1134 // 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 1135 renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); 1136 break; 1137 case "overline": 1138 renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color); 1139 break; 1140 case "line-through": 1141 // TODO try and find exact position for line-through 1142 renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); 1143 break; 1144 1145 } 1146 1147 1148 1149 1150 1151 textOffset += renderList[c].length; 1152 1153 } 1154 1155 1156 1157 } 1158 1159 } 1160 1161 function listPosition (element, val) { 1162 var boundElement = doc.createElement( "boundelement" ), 1163 type, 1164 bounds; 1165 1166 boundElement.style.display = "inline"; 1167 //boundElement.style.width = "1px"; 1168 //boundElement.style.height = "1px"; 1169 1170 type = element.style.listStyleType; 1171 element.style.listStyleType = "none"; 1172
1173 boundElement.appendChild( doc.createTextNode( val ) ); 1174 1175 1176 element.insertBefore(boundElement, element.firstChild); 1177 1178 1179 bounds = _html2canvas.Util.Bounds( boundElement ); 1180 element.removeChild( boundElement ); 1181 element.style.listStyleType = type; 1182 return bounds; 1183 1184 } 1185 1186 1187 1188 function elementIndex( el ) { 1189 var i = -1, 1190 count = 1, 1191 childs = el.parentNode.childNodes; 1192 1193 if ( el.parentNode ) { 1194 while( childs[ ++i ] !== el ) { 1195 if ( childs[ i ].nodeType === 1 ) { 1196 count++; 1197 } 1198 } 1199 return count; 1200 } else { 1201 return -1; 1202 } 1203 1204 } 1205 1206 function renderListItem(element, stack, elBounds) { 1207 1208 1209 var position = getCSS(element, "listStylePosition"), 1210 x, 1211 y, 1212 type = getCSS(element, "listStyleType"), 1213 currentIndex, 1214 text, 1215 listBounds, 1216 bold = getCSS(element, "fontWeight"); 1217 1218 if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { 1219 1220 currentIndex = elementIndex( element ); 1221 1222 switch(type){ 1223 case "decimal": 1224 text = currentIndex; 1225 break; 1226 case "decimal-leading-zero": 1227 if (currentIndex.toString().length === 1){ 1228 text = currentIndex = "0" + currentIndex.toString(); 1229 }else{ 1230 text = currentIndex.toString(); 1231 } 1232 break; 1233 case "upper-roman": 1234 text = _html2canvas.Generate.ListRoman( currentIndex ); 1235 break; 1236 case "lower-roman": 1237 text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); 1238 break; 1239 case "lower-alpha": 1240 text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); 1241 break; 1242 case "upper-alpha": 1243 text = _html2canvas.Generate.ListAlpha( currentIndex ); 1244 break; 1245 } 1246 1247 1248 text += ". "; 1249 listBounds = listPosition(element, text); 1250 1251 1252 1253 switch(bold){ 1254 case 401: 1255 bold = "bold"; 1256 break; 1257 case 400: 1258 bold = "normal"; 1259 break; 1260 } 1261 1262 1263 1264 1265 ctx.setVariable( "fillStyle", getCSS(element, "color") ); 1266 ctx.setVariable( "font", getCSS(element, "fontVariant") + " " + bold + " " + getCSS(element, "fontStyle") + " " + getCSS(element, "fontSize") + " " + getCSS(element, "fontFamily") ); 1267 1268 1269 if ( position === "inside" ) { 1270 ctx.setVariable("textAlign", "left"); 1271 // this.setFont(stack.ctx, element, false); 1272 x = elBounds.left; 1273 1274 }else{ 1275 return; 1276 /* 1277 TODO really need to figure out some more accurate way to try and find the position. 1278 as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser 1279 may display it whatever way it feels like. 1280 "The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order" 1281 1282 ctx.setVariable("textAlign", "right"); 1283 // this.setFont(stack.ctx, element, true); 1284 x = elBounds.left - 10; 1285 */ 1286 } 1287 1288 y = listBounds.bottom; 1289 1290 drawText(text, x, y, ctx); 1291 1292 1293 } 1294 1295 1296 } 1297 1298 function loadImage (src){ 1299 var img = images[src]; 1300 if (img && img.succeeded === true) { 1301 return img.img; 1302 } else { 1303 return false; 1304 } 1305 } 1306 1307 1308 1309 1310 1311 1312 function clipBounds(src, dst){ 1313 1314 var x = Math.max(src.left, dst.left), 1315 y = Math.max(src.top, dst.top), 1316 x2 = Math.min((src.left + src.width), (dst.left + dst.width)), 1317 y2 = Math.min((src.top + src.height), (dst.top + dst.height)); 1318 1319 return { 1320 left:x, 1321 top:y, 1322 width:x2-x, 1323 height:y2-y 1324 }; 1325 1326 } 1327 1328 function setZ(zIndex, parentZ){ 1329 // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them 1330 var newContext; 1331 if (!parentZ){ 1332 newContext = h2czContext(0); 1333 return newContext; 1334 } 1335 1336 if (zIndex !== "auto"){ 1337 needReorder = true; 1338 newContext = h2czContext(zIndex); 1339 parentZ.children.push(newContext); 1340 return newContext; 1341 1342 } 1343 1344 return parentZ; 1345 1346 } 1347 1348 function renderBorders(el, ctx, bounds, clip){ 1349 1350 /* 1351 * TODO add support for different border-style's than solid 1352 */ 1353 1354 var x = bounds.left, 1355 y = bounds.top, 1356 w = bounds.width, 1357 h = bounds.height, 1358 borderSide, 1359 borderData, 1360 bx, 1361 by, 1362 bw, 1363 bh, 1364 rw, 1365 i, 1366 borderArgs, 1367 borderBounds, 1368 borders = (function(el){ 1369 var borders = [], 1370 sides = ["Top","Right","Bottom","Left"], 1371 s; 1372 1373 for (s = 0; s < 4; s+=1){ 1374 borders.push({ 1375 width: getCSSInt(el, 'border' + sides[s] + 'Width'), 1376 color: getCSS(el, 'border' + sides[s] + 'Color') 1377 }); 1378 } 1379 1380 return borders; 1381 1382 }(el)), 1383 // http://www.w3.org/TR/css3-background/#the-border-radius 1384 borderRadius = (function( el ) { 1385 var borders = [], 1386 sides = ["TopLeft","TopRight","BottomRight","BottomLeft"], 1387 s; 1388 1389 for (s = 0; s < 4; s+=1){ 1390 borders.push( getCSS(el, 'border' + sides[s] + 'Radius') ); 1391 } 1392 1393 return borders; 1394 })( el ); 1395 1396 1397 1398 for ( borderSide = 0; borderSide < 4; borderSide+=1 ) { 1399 borderData = borders[ borderSide ]; 1400 borderArgs = []; 1401 if (borderData.width>0){ 1402 bx = x; 1403 by = y; 1404 bw = w; 1405 bh = h - (borders[2].width); 1406 1407 switch(borderSide){ 1408 case 0: 1409 // top border 1410 bh = borders[0].width; 1411 1412 i = 0; 1413 borderArgs[ i++ ] = [ "line", bx, by ]; // top left 1414 borderArgs[ i++ ] = [ "line", bx + bw, b
1414y ]; // top right 1415 borderArgs[ i++ ] = [ "line", bx + bw - borders[ 1 ].width, by + bh ]; // bottom right 1416 borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by + bh ]; // bottom left 1417 1418 break; 1419 case 1: 1420 // right border 1421 bx = x + w - (borders[1].width); 1422 bw = borders[1].width; 1423 1424 i = 0; 1425 borderArgs[ i++ ] = [ "line", bx, by + borders[ 0 ].width]; // top left 1426 borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right 1427 borderArgs[ i++ ] = [ "line", bx + bw, by + bh + borders[ 2 ].width ]; // bottom right 1428 borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left 1429 1430 break; 1431 case 2: 1432 // bottom border 1433 by = (by + h) - (borders[2].width); 1434 bh = borders[2].width; 1435 1436 1437 i = 0; 1438 /* 1439 // top left 1440 if ( borderRadius[ 3 ][ 0 ] > 0 ) { 1441 1442 borderArgs[ i++ ] = [ "line", 1443 bx + borders[ 3 ].width, 1444 by - (borderRadius[ 3 ][ 1 ]) + borders[ 2 ].width 1445 ]; 1446 1447 borderArgs[ i++ ] = [ "quadraticCurve", 1448 bx + borders[ 3 ].width, 1449 by, 1450 bx + borderRadius[ 3 ][ 0 ], 1451 by, 1452 ]; 1453 1454 1455 rw = ( borders[ 3 ].width === 0 ) ? 1 : borders[ 2 ].width / (borders[ 3 ].width + borders[ 2 ].width); 1456 1457 borderArgs[ i++ ] = [ "line", 1458 bx + (borderRadius[ 3 ][ 0 ] * ( 1-rw )), 1459 by - (borderRadius[ 3 ][ 1 ] - borders[ 2 ].width) + (borderRadius[ 3 ][ 1 ] * ( 1-rw )) 1460 ]; 1461 1462 borderArgs[ i++ ] = [ "quadraticCurve", 1463 bx + borders[ 3 ].width, 1464 by, 1465 bx + borders[ 3 ].width + borderRadius[ 3 ][ 0 ], 1466 by, 1467 ]; 1468 1469 borderArgs[ i++ ] = [ "bezierCurve", 1470 bx + (borderRadius[ 3 ][ 0 ] * ( 1-rw )), 1471 by, 1472 bx + (borderRadius[ 3 ][ 0 ] * ( 1-rw )), 1473 by, 1474 bx + borders[ 3 ].width + borderRadius[ 3 ][ 0 ], 1475 by, 1476 ]; 1477 1478 } else { 1479 borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by ]; 1480 } 1481 */ 1482 borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by ]; 1483 borderArgs[ i++ ] = [ "line", bx + bw - borders[ 2 ].width, by ]; // top right 1484 borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right 1485 borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left 1486 1487 break; 1488 case 3: 1489 // left border 1490 bw = borders[3].width; 1491 1492 i = 0; 1493 borderArgs[ i++ ] = [ "line", bx, by ]; // top left 1494 borderArgs[ i++ ] = [ "line", bx + bw, b
1494y + borders[ 0 ].width ]; // top right 1495 borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right 1496 borderArgs[ i++ ] = [ "line", bx, by + bh + borders[ 2 ].width ]; // bottom left 1497 1498 break; 1499 } 1500 1501 borderBounds = { 1502 left:bx, 1503 top:by, 1504 width: bw, 1505 height:bh 1506 }; 1507 1508 if (clip){ 1509 borderBounds = clipBounds(borderBounds, clip); 1510 } 1511 1512 1513 if ( borderBounds.width > 0 && borderBounds.height > 0 ) { 1514 1515 if ( borderData.color !== "transparent" ){ 1516 ctx.setVariable( "fillStyle", borderData.color ); 1517 1518 var shape = ctx.drawShape(), 1519 numBorderArgs = borderArgs.length; 1520 1521 for ( i = 0; i < numBorderArgs; i++ ) { 1522 shape[( i === 0) ? "moveTo" : borderArgs[ i ][ 0 ] + "To" ].apply( null, borderArgs[ i ].slice(1) ); 1523 } 1524 1525 numDraws+=1; 1526 } 1527 1528 1529 1530 1531 // renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color); 1532 } 1533 1534 1535 } 1536 } 1537 1538 return borders; 1539 1540 } 1541 1542 1543 function renderFormValue (el, bounds, stack){ 1544 1545 var valueWrap = doc.createElement('valuewrap'), 1546 cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], 1547 i, 1548 textValue, 1549 textNode, 1550 arrLen, 1551 style; 1552 1553 for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ 1554 style = cssArr[i]; 1555 1556 try { 1557 valueWrap.style[style] = getCSS(el, style); 1558 } catch( e ) { 1559 // Older IE has issues with "border" 1560 h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); 1561 } 1562 } 1563 1564 1565 valueWrap.style.borderColor = "black"; 1566 valueWrap.style.borderStyle = "solid"; 1567 valueWrap.style.display = "block"; 1568 valueWrap.style.position = "absolute"; 1569 if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ 1570 valueWrap.style.lineHeight = getCSS(el, "height"); 1571 } 1572 1573 1574 valueWrap.style.top = bounds.top + "px"; 1575 valueWrap.style.left = bounds.left + "px"; 1576 1577 if (el.nodeName === "SELECT"){ 1578 // TODO increase accuracy of text position 1579 textValue = el.options[el.selectedIndex].text; 1580 } else{ 1581 textValue = el.value; 1582 } 1583 textNode = doc.createTextNode(textValue); 1584 1585 valueWrap.appendChild(textNode); 1586 body.appendChild(valueWrap); 1587 1588 1589 renderText(el, textNode, stack); 1590 body.removeChild(valueWrap); 1591 1592 1593 1594 } 1595 1596 1597 1598 1599 1600 function renderImage (ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) { 1601 ctx.drawImage( 1602 image, 1603 sx, //sx 1604 sy, //sy 1605 sw, //sw 1606 sh, //sh 1607 dx, //dx 1608 dy, // dy 1609 dw, //dw 1610 dh //dh 1611 ); 1612 numDraws+=1; 1613 1614 } 1615 1616 1617 function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ 1618 var sourceX = 0, 1619 sourceY=0; 1620 if (elx-x>0){ 1621 sourceX = elx-x; 1622 } 1623 1624 if (ely-y>0){ 1625 sourceY = ely-y; 1626 } 1627 1628 renderImage( 1629 ctx, 1630 image, 1631 sourceX, // source X 1632 sourceY, // source Y 1633 width-sourceX, // source Width 1634 height-sourceY, // source Height 1635 x+sourceX, // destination X 1636 y+sourceY, // destination Y 1637 width-sourceX, // destination width 1638 height-sourceY // destination height 1639 ); 1640 } 1641 1642
1643 function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){ 1644 1645 var height, 1646 width = Math.min(image.width,w),bgy; 1647 1648 bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; 1649 1650 1651 for(bgy=(y+bgp.top);bgy<h+y;){ 1652 1653 1654 if ( Math.floor(bgy+image.height)>h+y){ 1655 height = (h+y)-bgy; 1656 }else{ 1657 height = image.height; 1658 } 1659 renderBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y); 1660 1661 bgy = Math.floor(bgy+image.height); 1662 1663 } 1664 } 1665 1666 function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ 1667 1668 var height = Math.min(image.height,h), 1669 width,bgx; 1670 1671 1672 bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width; 1673 1674 1675 for (bgx=(x+bgp.left);bgx<w+x;) { 1676 1677 if (Math.floor(bgx+image.width)>w+x){ 1678 width = (w+x)-bgx; 1679 }else{ 1680 width = image.width; 1681 } 1682 1683 renderBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y); 1684 1685 bgx = Math.floor(bgx+image.width); 1686 1687 1688 } 1689 } 1690 1691 function renderBackground(el,bounds,ctx){ 1692 1693 // TODO add support for multi background-images 1694 var background_image = getCSS(el, "backgroundImage"), 1695 background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], 1696 image, 1697 bgp, 1698 bgy, 1699 bgw, 1700 bgsx, 1701 bgsy, 1702 bgdx, 1703 bgdy, 1704 bgh, 1705 h, 1706 height, 1707 add; 1708 1709 // if (typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)===false){ 1710 1711 if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) { 1712 background_image = background_image.split(",")[0]; 1713 } 1714 1715 if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { 1716 background_image = _html2canvas.Util.backgroundImage( background_image ); 1717 image = loadImage( background_image ); 1718 1719 1720 bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); 1721 1722 // TODO add support for background-origin 1723 if ( image ){ 1724 switch ( background_repeat ) { 1725 1726 case "repeat-x": 1727 renderBackgroundRepeatX( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); 1728 break; 1729 1730 case "repeat-y": 1731 renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); 1732 break; 1733 1734 case "no-repeat": 1735 /* 1736 this.drawBackgroundRepeat( 1737 ctx, 1738 image, 1739 bgp.left+bounds.left, // sx 1740 bgp.top+bounds.top, // sy 1741 Math.min(bounds.width,image.width), 1742 Math.min(bounds.height,image.height), 1743 bounds.left, 1744 bounds.top 1745 );*/ 1746 1747 1748 1749 bgw = bounds.width - bgp.left; 1750 bgh = bounds.height - bgp.top; 1751 bgsx = bgp.left; 1752 bgsy = bgp.top; 1753 bgdx = bgp.left+bounds.left; 1754 bgdy = bgp.top+bounds.top; 1755 1756 // 1757 // bgw = Math.min(bgw,image.width); 1758 // bgh = Math.min(bgh,image.height); 1759 1760 if (bgsx<0){ 1761 bgsx = Math.abs(bgsx); 1762 bgdx += bgsx; 1763 bgw = Math.min(bounds.width,image.width-bgsx); 1764 }else{ 1765 bgw = Math.min(bgw,image.width); 1766 bgsx = 0; 1767 } 1768 1769 if (bgsy<0){ 1770 bgsy = Math.abs(bgsy); 1771 bgdy += bgsy; 1772 // bgh = bgh-bgsy; 1773 bgh = Math.min(bounds.height,image.height-bgsy); 1774 }else{ 1775 bgh = Math.min(bgh,image.height); 1776 bgsy = 0; 1777 } 1778 1779 1780 if (bgh>0 && bgw > 0){ 1781 renderImage( 1782 ctx, 1783 image, 1784 bgsx, // source X : 0 1785 bgsy, // source Y : 1695 1786 bgw, // source Width : 18 1787 bgh, // source Height : 1677 1788 bgdx, // destination X :906 1789 bgdy, // destination Y : 1020 1790 bgw, // destination width : 18 1791 bgh // destination height : 1677 1792 ); 1793 1794 } 1795 break; 1796 default: 1797 1798 1799 1800 bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; 1801 1802 1803 for(bgy=(bounds.top+bgp.top);bgy<bounds.height+bounds.top;){ 1804 1805 1806 1807 h = Math.min(image.height,(bounds.height+bounds.top)-bgy); 1808 1809 1810 if ( Math.floor(bgy+image.height)>h+bgy){ 1811 height = (h+bgy)-bgy; 1812 }else{ 1813 height = image.height; 1814 } 1815 // console.log(height); 1816 1817 if (bgy<bounds.top){ 1818 add = bounds.top-bgy; 1819 bgy = bounds.top; 1820 1821 }else{ 1822 add = 0; 1823 } 1824
1825 renderBackgroundRepeatX(ctx,image,bgp,bounds.left,bgy,bounds.width,height); 1826 if (add>0){ 1827 bgp.top += add; 1828 } 1829 bgy = Math.floor(bgy+image.height)-add; 1830 } 1831 break; 1832 1833 1834 } 1835 }else{ 1836 h2clog("html2canvas: Error loading background:" + background_image); 1837 //console.log(images); 1838 } 1839 1840 } 1841 } 1842 1843 1844 1845 function renderElement(el, parentStack){ 1846 1847 var bounds = _html2canvas.Util.Bounds(el), 1848 x = bounds.left, 1849 y = bounds.top, 1850 w = bounds.width, 1851 h = bounds.height, 1852 image, 1853 bgcolor = getCSS(el, "backgroundColor"), 1854 cssPosition = getCSS(el, "position"), 1855 zindex, 1856 opacity = getCSS(el, "opacity"), 1857 stack, 1858 stackLength, 1859 borders, 1860 ctx, 1861 bgbounds, 1862 imgSrc, 1863 paddingLeft, 1864 paddingTop, 1865 paddingRight, 1866 paddingBottom; 1867 1868 if (!parentStack){ 1869 docDim = docSize(); 1870 parentStack = { 1871 opacity: 1 1872 }; 1873 }else{ 1874 docDim = {}; 1875 } 1876 1877 1878 //var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode); 1879 1880 zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); 1881 1882 1883 1884 stack = { 1885 ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), 1886 zIndex: zindex, 1887 opacity: opacity * parentStack.opacity, 1888 cssPosition: cssPosition 1889 }; 1890 1891 1892 1893 // TODO correct overflow for absolute content residing under a static position 1894 1895 if (parentStack.clip){ 1896 stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip ); 1897 //stack.clip = parentStack.clip; 1898 // stack.clip.height = stack.clip.height - parentStack.borders[2].width; 1899 } 1900 1901 1902 if ( options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false ){ 1903 if (stack.clip){ 1904 stack.clip = clipBounds(stack.clip, bounds); 1905 }else{ 1906 stack.clip = bounds; 1907 } 1908 } 1909 1910 1911 stackLength = zindex.children.push(stack); 1912 1913 ctx = zindex.children[stackLength-1].ctx; 1914 1915 ctx.setVariable("globalAlpha", stack.opacity); 1916 1917 // draw element borders 1918 borders = renderBorders(el, ctx, bounds, false); 1919 stack.borders = borders; 1920 1921 1922 // let's modify clip area for child elements, so borders dont get overwritten 1923 1924 /* 1925 if (stack.clip){ 1926 stack.clip.width = stack.clip.width-(borders[1].width); 1927 stack.clip.height = stack.clip.height-(borders[2].width); 1928 } 1929 */ 1930 if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ 1931 if (options.iframeDefault === "default"){ 1932 bgcolor = "#efefef"; 1933 }else{ 1934 bgcolor = options.iframeDefault; 1935 } 1936 } 1937 1938 // draw base element bgcolor 1939 1940 bgbounds = { 1941 left: x + borders[3].width, 1942 top: y + borders[0].width, 1943 width: w - (borders[1].width + borders[3].width), 1944 height: h - (borders[0].width + borders[2].width) 1945 }; 1946 1947 //if (this.withinBounds(stack.clip,bgbounds)){ 1948 1949 if (stack.clip){ 1950 bgbounds = clipBounds(bgbounds, stack.clip); 1951 1952 //} 1953 1954 } 1955 1956 1957 if (bgbounds.height > 0 && bgbounds.width > 0){ 1958 renderRect( 1959 ctx, 1960 bgbounds.left, 1961 bgbounds.top, 1962 bgbounds.width, 1963 bgbounds.height, 1964 bgcolor 1965 ); 1966 1967 renderBackground(el, bgbounds, ctx); 1968 } 1969 1970 switch(el.nodeName){ 1971 case "IMG": 1972 imgSrc = el.getAttribute('src'); 1973 image = loadImage(imgSrc); 1974 if (image){ 1975 1976 paddingLeft = getCSSInt(el, 'paddingLeft'); 1977 paddingTop = getCSSInt(el, 'paddingTop'); 1978 paddingRight = getCSSInt(el, 'paddingRight'); 1979 paddingBottom = getCSSInt(el, 'paddingBottom'); 1980 1981 1982 renderImage( 1983 ctx, 1984 image, 1985 0, //sx 1986 0, //sy 1987 image.width, //sw 1988 image.height, //sh 1989 x + paddingLeft + borders[3].width, //dx 1990 y + paddingTop + borders[0].width, // dy 1991 bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw 1992 bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh 1993 ); 1994 1995 }else{ 1996 h2clog("html2canvas: Error loading <img>:" + imgSrc); 1997 } 1998 break; 1999 case "INPUT": 2000 // TODO add all relevant type's, i.e. HTML5 new stuff 2001 // todo add support for placeholder attribute for browsers which support it 2002 if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){ 2003 2004 renderFormValue(el, bounds, stack); 2005 2006 2007 /* 2008 this just doesn't work well enough 2009 2010 this.newText(el,{ 2011 nodeValue:el.value, 2012 splitText: function(){ 2013 return this; 2014 }, 2015 formValue:true 2016 },stack); 2017 */ 2018 } 2019 break; 2020 case "TEXTAREA": 2021 if (el.value.length > 0){ 2022 renderFormValue(el, bounds, stack); 2023 } 2024 break; 2025 case "SELECT": 2026 if (el.options.length > 0){ 2027 renderFormValue(el, bounds, stack); 2028 } 2029 break; 2030 case "LI": 2031 renderListItem(el, stack, bgbounds); 2032 break; 2033 case "CANVAS": 2034 paddingLeft = getCSSInt(el, 'paddingLeft'); 2035 paddingTop = getCSSInt(el, 'paddingTop'); 2036 paddingRight = getCSSInt(el, 'paddingRight'); 2037 paddingBottom = getCSSInt(el, 'paddingBottom'); 2038 renderImage( 2039 ctx, 2040 el, 2041 0, //sx 2042 0, //sy 2043 el.width, //sw 2044 el.height, //sh 2045 x + paddingLeft + borders[3].width, //dx 2046 y + paddingTop + borders[0].width, // dy 2047 bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw 2048 bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh 2049 ); 2050 break; 2051 } 2052 2053 return zindex.children[stackLength - 1]; 2054 } 2055 2056 2057 2058 function parseElement (el, stack) { 2059 2060 // skip hidden elements and their children 2061 if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden" && !el.hasAttribute("data-html2canvas-ignore") ) { 2062 2063 stack = renderElement(el, stack) || stack; 2064 2065 ctx = stack.ctx; 2066 2067 if ( !ignoreElementsRegExp.test( el.nodeName ) ) { 2068 var elementChildren = _html2canvas.Util.Children( el ), 2069 i, 2070 node, 2071 childrenLen; 2072 for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) { 2073 node = elementChildren[i]; 2074 2075 if ( node.nodeType === 1 ) { 2076 parseElement(node, stack); 2077 }else if ( node.nodeType === 3 ) { 2078 renderText(el, node, stack); 2079 } 2080 2081 } 2082 2083 } 2084 } 2085 } 2086 2087 stack = renderElement(element, null); 2088 2089 /* 2090 SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards 2091 */ 2092 2093 if ( support.svgRendering ) { 2094 (function( body ){ 2095 var img = new Image(), 2096 size = docSize(), 2097 html = ""; 2098 2099 function parseDOM( el ) { 2100 var children = _html2canvas.Util.Children( el ), 2101 len = children.length, 2102 attr, 2103 a, 2104 alen, 2105 elm, 2106 i; 2107 for ( i = 0; i < len; i+=1 ) { 2108 elm = children[ i ]; 2109 if ( elm.nodeType === 3 ) { 2110 // Text node 2111 2112 html += elm.nodeValue.replace(/\</g,"<").replace(/\>/g,">"); 2113 } else if ( elm.nodeType === 1 ) { 2114 // Element 2115 if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { 2116 2117 html += "<" + elm.nodeName.toLowerCase(); 2118 2119 // add attributes 2120 if ( elm.hasAttributes() ) { 2121 attr = elm.attributes; 2122 alen = attr.length; 2123 for ( a = 0; a < alen; a+=1 ) { 2124 html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; 2125 } 2126 } 2127 2128 2129 html += '>'; 2130 2131 parseDOM( elm ); 2132 2133 2134 html += "</" + elm.nodeName.toLowerCase() + ">"; 2135 } 2136 } 2137 2138 } 2139 2140 } 2141 2142 parseDOM( body ); 2143 img.src = [ 2144 "data:image/svg+xml,", 2145 "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='" + size.width + "' height='" + size.height + "'>", 2146 "<foreignObject width='" + size.width + "' height='" + size.height + "'>", 2147 "<html xmlns='http://www.w3.org/1999/xhtml' style='margin:0;'>", 2148 html.replace(/\#/g,"%23"), 2149 "</html>", 2150 "</foreignObject>", 2151 "</svg>" 2152 ].join(""); 2153 2154 2155 2156 2157 img.onload = function() { 2158 stack.svgRender = img; 2159 }; 2160 2161 })( document.documentElement ); 2162 2163 } 2164 2165 2166 // parse every child element 2167 for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ 2168 parseElement(children[i], stack); 2169 } 2170 2171 2172 stack.backgroundColor = getCSS( document.documentElement, "backgroundColor" ); 2173 2174 return stack; 2175 2176}; 2177
2178function h2czContext(zindex) { 2179 return { 2180 zindex: zindex, 2181 children: [] 2182 }; 2183} 2184 2185/* 2186 html2canvas v0.34 <http://html2canvas.hertzen.com> 2187 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2188 http://www.twitter.com/niklasvh 2189 2190 Released under MIT License 2191 */ 2192 2193_html2canvas.Preload = function( options ) { 2194 2195 var images = { 2196 numLoaded: 0, // also failed are counted here 2197 numFailed: 0, 2198 numTotal: 0, 2199 cleanupDone: false 2200 }, 2201 pageOrigin, 2202 methods, 2203 i, 2204 count = 0, 2205 element = options.elements[0] || document.body, 2206 doc = element.ownerDocument, 2207 domImages = doc.images, // TODO probably should limit it to images present in the element only 2208 imgLen = domImages.length, 2209 link = doc.createElement("a"), 2210 supportCORS = (function( img ){ 2211 return (img.crossOrigin !== undefined); 2212 })(new Image()), 2213 timeoutTimer; 2214 2215 link.href = window.location.href; 2216 pageOrigin = link.protocol + link.host; 2217 2218 2219 2220 2221 2222 2223 function isSameOrigin(url){ 2224 link.href = url; 2225 link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ 2226 var origin = link.protocol + link.host; 2227 return (origin === pageOrigin); 2228 } 2229 2230 function start(){ 2231 h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); 2232 if (!images.firstRun && images.numLoaded >= images.numTotal){ 2233 h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); 2234 2235 if (typeof options.complete === "function"){ 2236 options.complete(images); 2237 } 2238 2239 } 2240 } 2241 2242 // TODO modify proxy to serve images with CORS enabled, where available 2243 function proxyGetImage(url, img, imageObj){ 2244 if(url.indexOf('flickr.com') > 0) { 2245 //return; 2246 } 2247 2248 var callback_name, 2249 scriptUrl = options.proxy, 2250 script; 2251 2252 link.href = url; 2253 url = link.href; // work around for pages with base href="" set - WARNING: this may change the url 2254 2255 callback_name = 'html2canvas_' + (count++); 2256 imageObj.callbackname = callback_name; 2257 2258 if (scriptUrl.indexOf("?") > -1) { 2259 scriptUrl += "&"; 2260 } else { 2261 scriptUrl += "?"; 2262 } 2263 scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; 2264 script = doc.createElement("script"); 2265 2266 window[callback_name] = function(a){ 2267 if (a.substring(0,6) === "error:"){ 2268 imageObj.succeeded = false; 2269 images.numLoaded++; 2270 images.numFailed++; 2271 start(); 2272 } else { 2273 setImageLoadHandlers(img, imageObj); 2274 img.src = a; 2275 } 2276 window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) 2277 try { 2278 delete window[callback_name]; // for all browser that support this 2279 } catch(ex) {} 2280 script.parentNode.removeChild(script); 2281 script = null; 2282 delete imageObj.script; 2283 delete imageObj.callbackname; 2284 }; 2285 2286 script.setAttribute("type", "text/javascript"); 2287 script.setAttribute("src", scriptUrl); 2288 imageObj.script = script; 2289 //console.log(script); 2290 window.document.body.appendChild(script); 2291 2292 } 2293 2294 function getImages (el) { 2295 2296 2297 2298 // if (!this.ignoreRe.test(el.nodeName)){ 2299 // 2300 2301 var contents = _html2canvas.Util.Children(el), 2302 i, 2303 background_image, 2304 src, 2305 img, 2306 elNodeType = false;
2307 2308 // Firefox fails with permission denied on pages with iframes 2309 try { 2310 var contentsLen = contents.length; 2311 for (i = 0; i < contentsLen; i+=1 ){ 2312 // var ignRe = new RegExp("("+this.ignoreElements+")"); 2313 // if (!ignRe.test(element.nodeName)){ 2314 getImages(contents[i]); 2315 // } 2316 } 2317 } 2318 catch( e ) {} 2319 2320 2321 // } 2322 try { 2323 elNodeType = el.nodeType; 2324 } catch (ex) { 2325 elNodeType = false; 2326 h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); 2327 } 2328 2329 if (elNodeType === 1 || elNodeType === undefined){ 2330 2331 // opera throws exception on external-content.html 2332 try { 2333 background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); 2334 }catch(e) { 2335 h2clog("html2canvas: failed to get background-image - Exception: " + e.message); 2336 } 2337 if ( background_image && background_image !== "1" && background_image !== "none" ) { 2338 2339 // TODO add multi image background support 2340 2341 if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { 2342 2343 img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) ); 2344 2345 if ( img !== undefined ){ 2346 images[background_image] = { 2347 img: img, 2348 succeeded: true 2349 }; 2350 images.numTotal++; 2351 images.numLoaded++; 2352 start(); 2353 2354 } 2355 2356 } else { 2357 src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); 2358 methods.loadImage(src); 2359 } 2360 2361 /* 2362 if (background_image && background_image !== "1" && background_image !== "none" && background_image.substring(0,7) !== "-webkit" && background_image.substring(0,3)!== "-o-" && background_image.substring(0,4) !== "-moz"){ 2363 // TODO add multi image background support 2364 src = _html2canvas.Util.backgroundImage(background_image.split(",")[0]); 2365 methods.loadImage(src); */ 2366 } 2367 } 2368 } 2369 2370 function setImageLoadHandlers(img, imageObj) { 2371 img.onload = function() { 2372 if ( imageObj.timer !== undefined ) { 2373 // CORS succeeded 2374 window.clearTimeout( imageObj.timer ); 2375 } 2376 2377 images.numLoaded++; 2378 imageObj.succeeded = true; 2379 img.onerror = img.onload = null; 2380 start(); 2381 }; 2382 img.onerror = function() { 2383 2384 if (img.crossOrigin === "anonymous") { 2385 // CORS failed 2386 window.clearTimeout( imageObj.timer ); 2387 2388 // let's try with proxy instead 2389 if ( options.proxy ) { 2390 var src = img.src; 2391 img = new Image(); 2392 imageObj.img = img; 2393 img.src = src; 2394 2395 proxyGetImage( img.src, img, imageObj ); 2396 return; 2397 } 2398 } 2399 2400 2401 images.numLoaded++; 2402 images.numFailed++; 2403 imageObj.succeeded = false; 2404 img.onerror = img.onload = null; 2405 start(); 2406 2407 }; 2408 2409 // TODO Opera has no load/error event for SVG images 2410 2411 // Opera ninja onload's cached images 2412 /* 2413 window.setTimeout(function(){ 2414 if ( img.width !== 0 && imageObj.succeeded === undefined ) { 2415 img.onload(); 2416 } 2417 }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does. 2418 */ 2419 } 2420 2421 2422 methods = { 2423 loadImage: function( src ) { 2424 var img, imageObj; 2425 if ( src && images[src] === undefined ) { 2426 img = new Image(); 2427 if ( src.match(/data:image\/.*;base64,/i) ) { 2428 img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); 2429 imageObj = images[src] = { 2430 img: img 2431 }; 2432 images.numTotal++; 2433 setImageLoadHandlers(img, imageObj); 2434 } else if ( isSameOrigin( src ) || options.allowTaint === true ) { 2435 imageObj = images[src] = { 2436 img: img 2437 }; 2438 images.numTotal++; 2439 setImageLoadHandlers(img, imageObj); 2440 img.src = src; 2441 } else if ( supportCORS && !options.allowTaint && options.useCORS ) { 2442 // attempt to load with CORS 2443 2444 img.crossOrigin = "anonymous"; 2445 imageObj = images[src] = { 2446 img: img 2447 }; 2448 images.numTotal++; 2449 setImageLoadHandlers(img, imageObj); 2450 img.src = src; 2451 2452 // work around for https://bugs.webkit.org/show_bug.cgi?id=80028 2453 img.customComplete = function () { 2454 if (!this.img.complete) { 2455 this.timer = window.setTimeout(this.img.customComplete, 100); 2456 } else { 2457 this.img.onerror(); 2458 } 2459 }.bind(imageObj); 2460 img.customComplete(); 2461 2462 } else if ( options.proxy ) { 2463 if(src.indexOf('flickr.com') == -1) { 2464 imageObj = images[src] = { 2465 img: img 2466 }; 2467 images.numTotal++; 2468 proxyGetImage( src, img, imageObj ); 2469 } 2470 } 2471 } 2472 2473 }, 2474 cleanupDOM: function(cause) { 2475 var img, src; 2476 if (!images.cleanupDone) { 2477 if (cause && typeof cause === "string") { 2478 h2clog("html2canvas: Cleanup because: " + cause); 2479 } else { 2480 h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); 2481 } 2482 2483 for (src in images) { 2484 if (images.hasOwnProperty(src)) { 2485 img = images[src]; 2486 if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { 2487 // cancel proxy image request 2488 window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) 2489 try { 2490 delete window[img.callbackname]; // for all browser that support this 2491 } catch(ex) {} 2492 if (img.script && img.script.parentNode) { 2493 img.script.setAttribute("src", "about:blank"); // try to cancel running request 2494 img.script.parentNode.removeChild(img.script); 2495 } 2496 images.numLoaded++; 2497 images.numFailed++; 2498 h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); 2499 } 2500 } 2501 } 2502
2503 // cancel any pending requests 2504 if(window.stop !== undefined) { 2505 window.stop(); 2506 } else if(document.execCommand !== undefined) { 2507 document.execCommand("Stop", false); 2508 } 2509 if (document.close !== undefined) { 2510 document.close(); 2511 } 2512 images.cleanupDone = true; 2513 if (!(cause && typeof cause === "string")) { 2514 start(); 2515 } 2516 } 2517 }, 2518 renderingDone: function() { 2519 if (timeoutTimer) { 2520 window.clearTimeout(timeoutTimer); 2521 } 2522 } 2523 2524 }; 2525 2526 if (options.timeout > 0) { 2527 timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); 2528 } 2529 h2clog('html2canvas: Preload starts: finding background-images'); 2530 images.firstRun = true; 2531 2532 getImages( element ); 2533 2534 h2clog('html2canvas: Preload: Finding images'); 2535 // load <img> images 2536 for (i = 0; i < imgLen; i+=1){ 2537 methods.loadImage( domImages[i].getAttribute( "src" ) ); 2538 } 2539 2540 images.firstRun = false; 2541 h2clog('html2canvas: Preload: Done.'); 2542 if ( images.numTotal === images.numLoaded ) { 2543 start(); 2544 } 2545 2546 return methods; 2547 2548}; 2549 2550 2551 2552 2553/* 2554 html2canvas v0.34 <http://html2canvas.hertzen.com> 2555 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2556 http://www.twitter.com/niklasvh 2557 2558 Released under MIT License 2559*/ 2560function h2cRenderContext(width, height) { 2561 var storage = []; 2562 return { 2563 storage: storage, 2564 width: width, 2565 height: height, 2566 fillRect: function () { 2567 storage.push({ 2568 type: "function", 2569 name: "fillRect", 2570 'arguments': arguments 2571 }); 2572 }, 2573 drawShape: function() { 2574 2575 var shape = []; 2576 2577 storage.push({ 2578 type: "function", 2579 name: "drawShape", 2580 'arguments': shape 2581 }); 2582 2583 return { 2584 moveTo: function() { 2585 shape.push({ 2586 name: "moveTo", 2587 'arguments': arguments 2588 }); 2589 }, 2590 lineTo: function() { 2591 shape.push({ 2592 name: "lineTo", 2593 'arguments': arguments 2594 }); 2595 }, 2596 bezierCurveTo: function() { 2597 shape.push({ 2598 name: "bezierCurveTo", 2599 'arguments': arguments 2600 }); 2601 }, 2602 quadraticCurveTo: function() { 2603 shape.push({ 2604 name: "quadraticCurveTo", 2605 'arguments': arguments 2606 }); 2607 } 2608 }; 2609 2610 }, 2611 drawImage: function () { 2612 storage.push({ 2613 type: "function", 2614 name: "drawImage", 2615 'arguments': arguments 2616 }); 2617 }, 2618 fillText: function () { 2619 storage.push({ 2620 type: "function", 2621 name: "fillText", 2622 'arguments': arguments 2623 }); 2624 }, 2625 setVariable: function (variable, value) { 2626 storage.push({ 2627 type: "variable", 2628 name: variable, 2629 'arguments': value 2630 }); 2631 } 2632 }; 2633} 2634 2635/* 2636 html2canvas v0.34 <http://html2canvas.hertzen.com> 2637 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2638 http://www.twitter.com/niklasvh 2639 2640 Released under MIT License 2641*/ 2642_html2canvas.Renderer = function(parseQueue, options){ 2643 2644 2645 var queue = []; 2646 2647 function sortZ(zStack){ 2648 var subStacks = [], 2649 stackValues = [], 2650 zStackChildren = zStack.children, 2651 s, 2652 i, 2653 stackLen, 2654 zValue, 2655 zLen, 2656 stackChild, 2657 b, 2658 subStackLen; 2659 2660 2661 for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ 2662 2663 stackChild = zStackChildren[s]; 2664 2665 if (stackChild.children && stackChild.children.length > 0){ 2666 subStacks.push(stackChild); 2667 stackValues.push(stackChild.zindex); 2668 }else{ 2669 queue.push(stackChild); 2670 } 2671 2672 } 2673 2674 stackValues.sort(function(a, b) { 2675 return a - b; 2676 }); 2677 2678 for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){ 2679 zValue = stackValues[i]; 2680 for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){ 2681 2682 if (subStacks[b].zindex === zValue){ 2683 stackChild = subStacks.splice(b, 1); 2684 sortZ(stackChild[0]); 2685 break; 2686 2687 } 2688 } 2689 } 2690 2691 } 2692 2693 2694 sortZ(parseQueue.zIndex); 2695 if ( typeof options._renderer._create !== "function" ) { 2696 throw new Error("Invalid renderer defined"); 2697 } 2698 return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); 2699 2700}; 2701 2702/* 2703 html2canvas v0.34 <http://html2canvas.hertzen.com> 2704 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2705 http://www.twitter.com/niklasvh 2706 2707 Released under MIT License 2708*/ 2709 2710 2711html2canvas = function( elements, opts ) { 2712 2713 var queue, 2714 canvas, 2715 options = { 2716 // general 2717 logging: false, 2718 elements: elements, 2719 2720 // preload options 2721 proxy: "http://html2canvas.appspot.com/", 2722 timeout: 0, // no timeout
2723 useCORS: false, // try to load images as CORS (where available), before falling back to proxy 2724 allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true 2725 2726 // parse options 2727 svgRendering: false, // use svg powered rendering where available (FF11+) 2728 iframeDefault: "default", 2729 ignoreElements: "IFRAME|OBJECT|PARAM", 2730 useOverflow: true, 2731 letterRendering: false, 2732 2733 // render options 2734 2735 flashcanvas: undefined, // path to flashcanvas 2736 width: null, 2737 height: null, 2738 taintTest: true, // do a taint test with all images before applying to canvas 2739 renderer: "Canvas" 2740 }, renderer; 2741 2742 options = _html2canvas.Util.Extend(opts, options); 2743 2744 if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { 2745 options._renderer = _html2canvas.Renderer[options.renderer]( options ); 2746 } else if (typeof options.renderer === "function") { 2747 options._renderer = options.renderer( options ); 2748 } else { 2749 throw("Unknown renderer"); 2750 } 2751 2752 _html2canvas.logging = options.logging; 2753 options.complete = function( images ) { 2754 2755 if (typeof options.onpreloaded === "function") { 2756 if ( options.onpreloaded( images ) === false ) { 2757 return; 2758 } 2759 } 2760 queue = _html2canvas.Parse( images, options ); 2761 2762 if (typeof options.onparsed === "function") { 2763 if ( options.onparsed( queue ) === false ) { 2764 return; 2765 } 2766 } 2767 2768 canvas = _html2canvas.Renderer( queue, options ); 2769 2770 if (typeof options.onrendered === "function") { 2771 options.onrendered( canvas ); 2772 } 2773 2774 2775 }; 2776 2777 // for pages without images, we still want this to be async, i.e. return methods before executing 2778 window.setTimeout( function(){ 2779 _html2canvas.Preload( options ); 2780 }, 0 ); 2781 2782 return { 2783 render: function( queue, opts ) { 2784 return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); 2785 }, 2786 parse: function( images, opts ) { 2787 return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); 2788 }, 2789 preload: function( opts ) { 2790 return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); 2791 }, 2792 log: h2clog 2793 }; 2794}; 2795 2796html2canvas.log = h2clog; // for renderers 2797html2canvas.Renderer = { 2798 Canvas: undefined // We are assuming this will be used 2799}; 2800 2801/* 2802 html2canvas v0.34 <http://html2canvas.hertzen.com> 2803 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2804 http://www.twitter.com/niklasvh 2805 2806 Released under MIT License 2807*/ 2808 2809 2810_html2canvas.Renderer.Canvas = function( options ) { 2811 2812 options = options || {}; 2813 2814 var doc = document, 2815 canvas = options.canvas || doc.createElement('canvas'), 2816 usingFlashcanvas = false, 2817 _createCalled = false, 2818 canvasReadyToDraw = false, 2819 methods, 2820 flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata 2821 2822 2823 if (canvas.getContext){ 2824 h2clog("html2canvas: Renderer: using canvas renderer"); 2825 canvasReadyToDraw = true; 2826 } else if ( options.flashcanvas !== undefined ){ 2827 usingFlashcanvas = true; 2828 h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); 2829 var script = doc.createElement("script"); 2830 script.src = options.flashcanvas; 2831 2832 script.onload = (function(script, func){ 2833 var intervalFunc; 2834 2835 if (script.onload === undefined) { 2836 // IE lack of support for script onload 2837 2838 if( script.onreadystatechange !== undefined ) { 2839 2840 intervalFunc = function() { 2841 if (script.readyState !== "loaded" && script.readyState !== "complete") { 2842 window.setTimeout( intervalFunc, 250 ); 2843 2844 } else { 2845 // it is loaded 2846 func(); 2847 2848 } 2849 2850 }; 2851 2852 window.setTimeout( intervalFunc, 250 ); 2853 2854 } else { 2855 h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded"); 2856 } 2857 2858 } else { 2859 return func; 2860 } 2861 2862 })(script, function(){ 2863
2864 if (typeof window.FlashCanvas !== "undefined") { 2865 h2clog("html2canvas: Renderer: Flashcanvas initialized"); 2866 window.FlashCanvas.initElement( canvas ); 2867 2868 canvasReadyToDraw = true; 2869 if ( _createCalled !== false ) { 2870 methods._create.apply( null, _createCalled ); 2871 } 2872 } 2873 }); 2874 2875 doc.body.appendChild( script ); 2876 2877 } 2878 2879 methods = { 2880 _create: function( zStack, options, doc, queue, _html2canvas ) { 2881 2882 if ( !canvasReadyToDraw ) { 2883 _createCalled = arguments; 2884 return canvas; 2885 } 2886 2887 var ctx = canvas.getContext("2d"), 2888 storageContext, 2889 i, 2890 queueLen, 2891 a, 2892 newCanvas, 2893 bounds, 2894 testCanvas = document.createElement("canvas"), 2895 hasCTX = ( testCanvas.getContext !== undefined ), 2896 storageLen, 2897 renderItem, 2898 testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {}, 2899 safeImages = [], 2900 fstyle; 2901 2902 canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) ); 2903 canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) ); 2904 2905 fstyle = ctx.fillStyle; 2906 ctx.fillStyle = zStack.backgroundColor; 2907 ctx.fillRect(0, 0, canvas.width, canvas.height); 2908 ctx.fillStyle = fstyle; 2909 2910 if ( options.svgRendering && zStack.svgRender !== undefined ) { 2911 // TODO: enable async rendering to support this 2912 ctx.drawImage( zStack.svgRender, 0, 0 ); 2913 } else { 2914 for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) { 2915 2916 storageContext = queue.splice(0, 1)[0]; 2917 storageContext.canvasPosition = storageContext.canvasPosition || {}; 2918 2919 //this.canvasRenderContext(storageContext,parentctx); 2920 2921 // set common settings for canvas 2922 ctx.textBaseline = "bottom"; 2923 2924 if (storageContext.clip){ 2925 ctx.save(); 2926 ctx.beginPath(); 2927 // console.log(storageContext); 2928 ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); 2929 ctx.clip(); 2930 2931 } 2932 2933 if (storageContext.ctx.storage){ 2934 2935 for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ 2936 2937 renderItem = storageContext.ctx.storage[a]; 2938 2939 2940 switch(renderItem.type){ 2941 case "variable": 2942 ctx[renderItem.name] = renderItem['arguments']; 2943 break; 2944 case "function": 2945 if (renderItem.name === "fillRect") { 2946 2947 if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { 2948 ctx.fillRect.apply( ctx, renderItem['arguments'] ); 2949 } 2950 } else if (renderItem.name === "drawShape") { 2951 2952 ( function( args ) { 2953 2954 var i, len = args.length; 2955 ctx.beginPath(); 2956 for ( i = 0; i < len; i++ ) { 2957 ctx[ args[ i ].name ].apply( ctx, args[ i ]['arguments'] ); 2958 } 2959 ctx.closePath(); 2960 ctx.fill(); 2961 })( renderItem['arguments'] ); 2962 2963 } else if (renderItem.name === "fillText") { 2964 if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { 2965 ctx.fillText.apply( ctx, renderItem['arguments'] ); 2966 } 2967 } else if (renderItem.name === "drawImage") { 2968 2969 if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ 2970 if ( hasCTX && options.taintTest ) { 2971 if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { 2972 testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); 2973 try { 2974 testctx.getImageData( 0, 0, 1, 1 ); 2975 } catch(e) { 2976 testCanvas = doc.createElement("canvas"); 2977 testctx = testCanvas.getContext("2d"); 2978 continue; 2979 } 2980 2981 safeImages.push( renderItem['arguments'][ 0 ].src ); 2982 2983 } 2984 } 2985 ctx.drawImage.apply( ctx, renderItem['arguments'] ); 2986 } 2987 } 2988 2989 2990 break; 2991 default: 2992 2993 } 2994 2995 } 2996 2997 } 2998 if (storageContext.clip){
2999 ctx.restore(); 3000 } 3001 3002 } 3003 } 3004 3005 h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); 3006 3007 queueLen = options.elements.length; 3008 3009 if (queueLen === 1) { 3010 if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) { 3011 // crop image to the bounds of selected (single) element 3012 bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] ); 3013 newCanvas = doc.createElement('canvas'); 3014 newCanvas.width = bounds.width; 3015 newCanvas.height = bounds.height; 3016 ctx = newCanvas.getContext("2d"); 3017 3018 ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height ); 3019 canvas = null; 3020 return newCanvas; 3021 } 3022 } /*else { 3023 // TODO clip and resize multiple elements 3024 3025 for ( i = 0; i < queueLen; i+=1 ) { 3026 if (options.elements[ i ] instanceof Element) { 3027 3028 } 3029 3030 } 3031 } 3032 */ 3033 3034 3035 3036 return canvas; 3037 } 3038 }; 3039 3040 return methods; 3041 3042}; 3043 3044/* 3045 html2canvas v0.34 <http://html2canvas.hertzen.com> 3046 Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 3047 http://www.twitter.com/niklasvh 3048 3049 Released under MIT License 3050*/ 3051 3052 3053// WARNING THIS file is outdated, and hasn't been tested in quite a while 3054 3055_html2canvas.Renderer.SVG = function( options ) { 3056 3057 options = options || {}; 3058 3059 var doc = document, 3060 svgNS = "http://www.w3.org/2000/svg", 3061 svg = doc.createElementNS(svgNS, "svg"), 3062 xlinkNS = "http://www.w3.org/1999/xlink", 3063 defs = doc.createElementNS(svgNS, "defs"), 3064 i, 3065 a, 3066 queueLen, 3067 storageLen, 3068 storageContext, 3069 renderItem, 3070 el, 3071 settings = {}, 3072 text, 3073 fontStyle, 3074 clipId = 0, 3075 methods; 3076 3077 3078 methods = { 3079 _create: function( zStack, options, doc, queue, _html2canvas ) { 3080 svg.setAttribute("version", "1.1"); 3081 svg.setAttribute("baseProfile", "full"); 3082 3083 svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height)); 3084 svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px"); 3085 svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px"); 3086 svg.setAttribute("preserveAspectRatio", "none"); 3087 svg.appendChild(defs); 3088 3089 3090 3091 for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ 3092 3093 storageContext = queue.splice(0, 1)[0]; 3094 storageContext.canvasPosition = storageContext.canvasPosition || {}; 3095 3096 //this.canvasRenderContext(storageContext,parentctx); 3097 3098 3099 /* 3100 if (storageContext.clip){ 3101 ctx.save(); 3102 ctx.beginPath(); 3103 // console.log(storageContext); 3104 ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); 3105 ctx.clip(); 3106 3107 }*/ 3108 3109 if (storageContext.ctx.storage){ 3110 3111 for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ 3112 3113 renderItem = storageContext.ctx.storage[a]; 3114 3115 3116 3117 switch(renderItem.type){ 3118 case "variable": 3119 settings[renderItem.name] = renderItem['arguments']; 3120 break; 3121 case "function": 3122 if (renderItem.name === "fillRect") { 3123 3124 el = doc.createElementNS(svgNS, "rect"); 3125 el.setAttribute("x", renderItem['arguments'][0]); 3126 el.setAttribute("y", renderItem['arguments'][1]); 3127 el.setAttribute("width", renderItem['arguments'][2]); 3128 el.setAttribute("height", renderItem['arguments'][3]); 3129 el.setAttribute("fill", settings.fillStyle); 3130 svg.appendChild(el); 3131 3132 } else if(renderItem.name === "fillText") { 3133 el = doc.createElementNS(svgNS, "text"); 3134 3135 fontStyle = settings.font.split(" "); 3136 3137 el.style.fontVariant = fontStyle.splice(0, 1)[0]; 3138 el.style.fontWeight = fontStyle.splice(0, 1)[0]; 3139 el.style.fontStyle = fontStyle.splice(0, 1)[0]; 3140 el.style.fontSize = fontStyle.splice(0, 1)[0]; 3141 3142 el.setAttribute("x", renderItem['arguments'][1]); 3143 el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3)); 3144 3145 el.setAttribute("fill", settings.fillStyle); 3146 3147 3148 3149 3150 // TODO get proper baseline 3151 el.style.dominantBaseline = "text-before-edge"; 3152 el.style.fontFamily = fontStyle.join(" "); 3153 3154 text = doc.createTextNode(renderItem['arguments'][0]); 3155 el.appendChild(text); 3156 3157 3158 svg.appendChild(el); 3159 3160 3161 3162 } else if(renderItem.name === "drawImage") { 3163 3164 if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ 3165 3166 // TODO check whether even any clipping is necessary for this particular image 3167 el = doc.createElementNS(svgNS, "clipPath"); 3168 el.setAttribute("id", "clipId" + clipId); 3169 3170 text = doc.createElementNS(svgNS, "rect"); 3171 text.setAttribute("x", renderItem['arguments'][5] ); 3172 text.setAttribute("y", renderItem['arguments'][6]); 3173 3174 text.setAttribute("width", renderItem['arguments'][3]); 3175 text.setAttribute("height", renderItem['arguments'][4]); 3176 el.appendChild(text); 3177 defs.appendChild(el); 3178 3179 el = doc.createElementNS(svgNS, "image"); 3180 el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src); 3181 el.setAttribute("width", renderItem['arguments'][7]); 3182 el.setAttribute("height", renderItem['arguments'][8]); 3183 el.setAttribute("x", renderItem['arguments'][5]); 3184 el.setAttribute("y", renderItem['arguments'][6]); 3185 el.setAttribute("clip-path", "url(#clipId" + clipId + ")"); 3186 // el.setAttribute("xlink:href", ); 3187 3188 3189 el.setAttribute("preserveAspectRatio", "none"); 3190 3191 svg.appendChild(el); 3192 3193 3194 clipId += 1; 3195 /* 3196 ctx.drawImage( 3197 renderItem['arguments'][0], 3198 renderItem['arguments'][1], 3199 renderItem['arguments'][2],
3200 renderItem['arguments'][3], 3201 renderItem['arguments'][4], 3202 renderItem['arguments'][5], 3203 renderItem['arguments'][6], 3204 renderItem['arguments'][7], 3205 renderItem['arguments'][8] 3206 ); 3207 */ 3208 } 3209 } 3210 3211 3212 3213 break; 3214 default: 3215 3216 } 3217 3218 } 3219 3220 } 3221 /* 3222 if (storageContext.clip){ 3223 ctx.restore(); 3224 } 3225 */ 3226 3227 3228 3229 } 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj"); 3241 3242 return svg; 3243 } 3244 }; 3245 3246 return methods; 3247 3248 3249}; 3250 3251window.html2canvas = html2canvas; 3252}(window, document)); 3253
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.