From 69d87d549748e2acbca7aff90a83e8dae7ffa7c1 Mon Sep 17 00:00:00 2001 From: MoyuScript Date: Tue, 6 Mar 2012 17:11:10 +0200 Subject: [PATCH] add svg powered rendering --- src/Parse.js | 192 ++++++++++++++++++++-------------------- src/Util.js | 1 + src/renderers/Canvas.js | 123 ++++++++++++------------- 3 files changed, 160 insertions(+), 156 deletions(-) diff --git a/src/Parse.js b/src/Parse.js index 6c3703c..9176578 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -14,8 +14,8 @@ _html2canvas.Parse = function ( images, options ) { window.scroll(0,0); var support = { - rangeBounds: false - /*,svgRendering: (function( ){ + rangeBounds: false, + svgRendering: options.svgRendering && (function( ){ var img = new Image(), canvas = document.createElement("canvas"), ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); @@ -43,7 +43,7 @@ _html2canvas.Parse = function ( images, options ) { h2clog('html2canvas: Parse: SVG powered rendering available'); return true; - })()*/ + })() }, element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default needReorder = false, @@ -63,86 +63,24 @@ _html2canvas.Parse = function ( images, options ) { children, childrenLen; - /* - SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards, - but due to bug https://bugzilla.mozilla.org/show_bug.cgi?id=733345 excluding this code out for now - if (support.svgRendering || true) { - (function( body, width, height ){ - var img = new Image(), - html = ""; - - function parseDOM( el ) { - var children = _html2canvas.Util.Children( el ), - len = children.length, - attr, - a, - alen, - elm, - i; - for ( i = 0; i < len; i+=1 ) { - elm = children[ i ]; - if ( elm.nodeType === 3 ) { - // Text node - - html += elm.nodeValue.replace(/\/g,">"); - } else if ( elm.nodeType === 1 ) { - // Element - if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { - - html += "<" + elm.nodeName.toLowerCase(); - - // add attributes - if ( elm.hasAttributes() ) { - attr = elm.attributes; - alen = attr.length; - for ( a = 0; a < alen; a+=1 ) { - html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; - } - } - - - html += '>'; - - parseDOM( elm ); - - - html += ""; - } - } - - } - - } - - parseDOM( body ); - img.src = [ - "data:image/svg+xml,", - "", - "", - "", - html, - "", - "", - "" - ].join(""); - - console.log(img.src); - img.onerror = function(e) { - console.log(e); - }; - - img.onload = function() { - console.log("loaded"); - }; - - document.body.appendChild(img); - })( document.documentElement, 1280, 1024 ); + function docSize(){ + + return { + width: Math.max( + Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), + Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), + Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) + ), + height: Math.max( + Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), + Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), + Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) + ) + }; } - return; - */ - + images = images || {}; // Test whether we can use ranges to measure bounding boxes @@ -183,22 +121,7 @@ _html2canvas.Parse = function ( images, options ) { */ - function docSize(){ - return { - width: Math.max( - Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), - Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), - Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) - ), - height: Math.max( - Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), - Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), - Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) - ) - }; - - } var getCSS = _html2canvas.Util.getCSS; function getCSSInt(element, attribute) { @@ -930,7 +853,7 @@ _html2canvas.Parse = function ( images, options ) { bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); - + // TODO add support for background-origin if ( image ){ switch ( background_repeat ) { @@ -1297,6 +1220,83 @@ _html2canvas.Parse = function ( images, options ) { stack = renderElement(element, null); + /* + SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards + */ + + if ( support.svgRendering ) { + (function( body ){ + var img = new Image(), + size = docSize(), + html = ""; + + function parseDOM( el ) { + var children = _html2canvas.Util.Children( el ), + len = children.length, + attr, + a, + alen, + elm, + i; + for ( i = 0; i < len; i+=1 ) { + elm = children[ i ]; + if ( elm.nodeType === 3 ) { + // Text node + + html += elm.nodeValue.replace(/\/g,">"); + } else if ( elm.nodeType === 1 ) { + // Element + if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { + + html += "<" + elm.nodeName.toLowerCase(); + + // add attributes + if ( elm.hasAttributes() ) { + attr = elm.attributes; + alen = attr.length; + for ( a = 0; a < alen; a+=1 ) { + html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; + } + } + + + html += '>'; + + parseDOM( elm ); + + + html += ""; + } + } + + } + + } + + parseDOM( body ); + img.src = [ + "data:image/svg+xml,", + "", + "", + "", + html.replace(/\#/g,"%23"), + "", + "", + "" + ].join(""); + + + + + img.onload = function() { + stack.svgRender = img; + }; + + })( document.documentElement ); + + } + + // parse every child element for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ parseElement(children[i], stack); diff --git a/src/Util.js b/src/Util.js index e97125e..63708dd 100644 --- a/src/Util.js +++ b/src/Util.js @@ -23,6 +23,7 @@ html2canvas = function( elements, opts ) { allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true // parse options + svgRendering: false, // use svg powered rendering where available (FF11+) iframeDefault: "default", ignoreElements: "IFRAME|OBJECT|PARAM", useOverflow: true, diff --git a/src/renderers/Canvas.js b/src/renderers/Canvas.js index d563df2..edabb0f 100644 --- a/src/renderers/Canvas.js +++ b/src/renderers/Canvas.js @@ -107,85 +107,88 @@ html2canvas.Renderer.Canvas = function( options ) { ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = fstyle; - for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ + if ( options.svgRendering && zStack.svgRender !== undefined ) { + // TODO: enable async rendering to support this + ctx.drawImage( zStack.svgRender, 0, 0 ); + } else { + for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) { - storageContext = queue.splice(0, 1)[0]; - storageContext.canvasPosition = storageContext.canvasPosition || {}; + storageContext = queue.splice(0, 1)[0]; + storageContext.canvasPosition = storageContext.canvasPosition || {}; - //this.canvasRenderContext(storageContext,parentctx); + //this.canvasRenderContext(storageContext,parentctx); - // set common settings for canvas - ctx.textBaseline = "bottom"; + // set common settings for canvas + ctx.textBaseline = "bottom"; - if (storageContext.clip){ - ctx.save(); - ctx.beginPath(); - // console.log(storageContext); - ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); - ctx.clip(); + if (storageContext.clip){ + ctx.save(); + ctx.beginPath(); + // console.log(storageContext); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); - } + } - if (storageContext.ctx.storage){ + if (storageContext.ctx.storage){ - for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ + for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ - renderItem = storageContext.ctx.storage[a]; + renderItem = storageContext.ctx.storage[a]; - switch(renderItem.type){ - case "variable": - ctx[renderItem.name] = renderItem['arguments']; - break; - case "function": - if (renderItem.name === "fillRect") { + switch(renderItem.type){ + case "variable": + ctx[renderItem.name] = renderItem['arguments']; + break; + case "function": + if (renderItem.name === "fillRect") { - if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { - ctx.fillRect.apply( ctx, renderItem['arguments'] ); - } - }else if(renderItem.name === "fillText") { - if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { - ctx.fillText.apply( ctx, renderItem['arguments'] ); - } - }else if(renderItem.name === "drawImage") { - - if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ - if ( hasCTX && options.taintTest ) { - if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { - testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); - try { - testctx.getImageData( 0, 0, 1, 1 ); - } catch(e) { - testCanvas = doc.createElement("canvas"); - testctx = testCanvas.getContext("2d"); - continue; - } - - safeImages.push( renderItem['arguments'][ 0 ].src ); - - } + if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { + ctx.fillRect.apply( ctx, renderItem['arguments'] ); } - ctx.drawImage.apply( ctx, renderItem['arguments'] ); - } - } + }else if(renderItem.name === "fillText") { + if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { + ctx.fillText.apply( ctx, renderItem['arguments'] ); + } + }else if(renderItem.name === "drawImage") { + + if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ + if ( hasCTX && options.taintTest ) { + if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { + testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); + try { + testctx.getImageData( 0, 0, 1, 1 ); + } catch(e) { + testCanvas = doc.createElement("canvas"); + testctx = testCanvas.getContext("2d"); + continue; + } + + safeImages.push( renderItem['arguments'][ 0 ].src ); + + } + } + ctx.drawImage.apply( ctx, renderItem['arguments'] ); + } + } - break; - default: + break; + default: - } + } + } + + } + if (storageContext.clip){ + ctx.restore(); } - - } - if (storageContext.clip){ - ctx.restore(); - } - - - + } } + h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); queueLen = options.elements.length;