diff --git a/readme.md b/readme.md index 637dc46ff..bd800bc11 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ For more information and examples, please visit the niklasvh) * Improved minification saved ~1K! (cobexer) * Added integrated support for Flashcanvas (niklasvh) * Fixed a variety of legacy IE bugs (niklasvh) diff --git a/src/Preload.js b/src/Preload.js index d45558434..05d0a0972 100644 --- a/src/Preload.js +++ b/src/Preload.js @@ -10,7 +10,9 @@ html2canvas.Preload = function(element, opts){ var options = { proxy: "http://html2canvas.appspot.com/", - timeout: 0 // no timeout + timeout: 0, // no timeout + useCORS: false, // try to load images as CORS (where available), before falling back to proxy + allowTaint: false // whether to allow images to taint the canvas, won't need proxy if set to true }, images = { numLoaded: 0, // also failed are counted here @@ -26,6 +28,9 @@ html2canvas.Preload = function(element, opts){ domImages = doc.images, // TODO probably should limit it to images present in the element only imgLen = domImages.length, link = doc.createElement("a"), + supportCORS = (function( img ){ + return (img.crossOrigin !== undefined); + })(new Image()), timeoutTimer; link.href = window.location.href; @@ -41,7 +46,7 @@ html2canvas.Preload = function(element, opts){ function isSameOrigin(url){ link.href = url; var origin = link.protocol + link.host; - return ":" === origin || (origin === pageOrigin); + return (origin === pageOrigin); } function start(){ @@ -215,36 +220,78 @@ html2canvas.Preload = function(element, opts){ function setImageLoadHandlers(img, imageObj) { img.onload = function() { + if ( imageObj.timer !== undefined ) { + // CORS succeeded + window.clearTimeout( imageObj.timer ); + } images.numLoaded++; imageObj.succeeded = true; start(); }; img.onerror = function() { + + if (img.crossOrigin === "anonymous") { + // CORS failed + window.clearTimeout( imageObj.timer ); + + // let's try with proxy instead + if ( options.proxy ) { + var src = img.src; + img = new Image(); + imageObj.img = img; + img.src = src; + + proxyGetImage( img.src, img, imageObj ); + return; + } + } + + images.numLoaded++; images.numFailed++; imageObj.succeeded = false; start(); + }; } + + // work around for https://bugs.webkit.org/show_bug.cgi?id=80028 + function isComplete() { + if (!this.img.complete) { + this.timer = window.setTimeout(this.img.customComplete, 100) + } else { + this.img.onerror(); + } + } methods = { loadImage: function( src ) { - var img, imageObj; + var img, imageObj; if ( src && images[src] === undefined ) { - img = new Image(); + img = new Image(); if ( src.match(/data:image\/.*;base64,/i) ) { img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); imageObj = images[src] = { img: img }; images.numTotal++; setImageLoadHandlers(img, imageObj); - } - else if ( isSameOrigin( src ) ) { + } else if ( isSameOrigin( src ) || options.allowTaint === true ) { imageObj = images[src] = { img: img }; images.numTotal++; setImageLoadHandlers(img, imageObj); img.src = src; - } - else if ( options.proxy ) { + } else if ( supportCORS && !options.allowTaint && options.useCORS ) { + // attempt to load with CORS + + img.crossOrigin = "anonymous"; + imageObj = images[src] = { img: img }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + + img.customComplete = isComplete.bind(imageObj); + img.customComplete(); + + } else if ( options.proxy ) { imageObj = images[src] = { img: img }; images.numTotal++; proxyGetImage( src, img, imageObj ); diff --git a/src/Renderer.js b/src/Renderer.js index bd0949362..7348bc37b 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -127,35 +127,16 @@ html2canvas.Renderer = function(parseQueue, opts){ if (renderItem.name === "fillRect") { if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { - ctx.fillRect( - renderItem['arguments'][0], - renderItem['arguments'][1], - renderItem['arguments'][2], - renderItem['arguments'][3] - ); + ctx.fillRect.apply( ctx, renderItem['arguments'] ); } }else if(renderItem.name === "fillText") { if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { - ctx.fillText( - renderItem['arguments'][0], - renderItem['arguments'][1], - renderItem['arguments'][2] - ); + ctx.fillText.apply( ctx, renderItem['arguments'] ); } }else if(renderItem.name === "drawImage") { if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ - ctx.drawImage( - renderItem['arguments'][0], - renderItem['arguments'][1], - renderItem['arguments'][2], - renderItem['arguments'][3], - renderItem['arguments'][4], - renderItem['arguments'][5], - renderItem['arguments'][6], - renderItem['arguments'][7], - renderItem['arguments'][8] - ); + ctx.drawImage.apply( ctx, renderItem['arguments'] ); } } diff --git a/tests/proxy.html b/tests/proxy.html index c918e10ff..ca55b111e 100644 --- a/tests/proxy.html +++ b/tests/proxy.html @@ -5,7 +5,7 @@ - +

External image

@@ -14,5 +14,8 @@

External image

External image (using <base> href)

+

External image (CORS)

+ + diff --git a/tests/test.js b/tests/test.js index 6de231e19..a7b265b38 100644 --- a/tests/test.js +++ b/tests/test.js @@ -19,7 +19,8 @@ setTimeout(function() { $(document.body).html2canvas({ logging: true, - profile: true + profile: true, + useCORS: true }); }, 100); };