diff --git a/src/Parse.js b/src/Parse.js index 85f2105..de6ec6a 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -7,7 +7,14 @@ _html2canvas.Parse = function (images, options) { support = _html2canvas.Util.Support(options, doc), ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), body = doc.body, - getCSS = _html2canvas.Util.getCSS; + getCSS = _html2canvas.Util.getCSS, + pseudoHide = "___html2canvas___pseudoelement", + hidePseudoElements = doc.createElement('style'); + + hidePseudoElements.innerHTML = '.' + pseudoHide + '-before:before { content: "" !important; display: none !important; }' + + '.' + pseudoHide + '-after:after { content: "" !important; display: none !important; }'; + + body.appendChild(hidePseudoElements); images = images || {}; @@ -750,6 +757,64 @@ _html2canvas.Parse = function (images, options) { numDraws+=1; } + function getPseudoElement(el, which) { + var elStyle = window.getComputedStyle(el, which); + if(!elStyle || !elStyle.content || elStyle.content === "none" || elStyle.content === "-moz-alt-content") { + return; + } + var content = elStyle.content + '', + first = content.substr( 0, 1 ); + //strips quotes + if(first === content.substr( content.length - 1 ) && first.match(/'|"/)) { + content = content.substr( 1, content.length - 2 ); + } + + var isImage = content.substr( 0, 3 ) === 'url', + elps = document.createElement( isImage ? 'img' : 'span' ); + + elps.className = pseudoHide + "-before " + pseudoHide + "-after"; + + Object.keys(elStyle).filter(indexedProperty).forEach(function(prop) { + elps.style[prop] = elStyle[prop]; + }); + + if(isImage) { + elps.src = _html2canvas.Util.parseBackgroundImage(content)[0].args[0]; + } else { + elps.innerHTML = content; + } + return elps; + } + + function indexedProperty(property) { + return (isNaN(window.parseInt(property, 10))); + } + + function injectPseudoElements(el, stack) { + var before = getPseudoElement(el, ':before'), + after = getPseudoElement(el, ':after'); + if(!before && !after) { + return; + } + + if(before) { + el.className += " " + pseudoHide + "-before"; + el.parentNode.insertBefore(before, el); + parseElement(before, stack, true); + el.parentNode.removeChild(before); + el.className = el.className.replace(pseudoHide + "-before", "").trim(); + } + + if (after) { + el.className += " " + pseudoHide + "-after"; + el.appendChild(after); + parseElement(after, stack, true); + el.removeChild(after); + el.className = el.className.replace(pseudoHide + "-after", "").trim(); + } + + } + function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { var offsetX = Math.round(bounds.left + backgroundPosition.left), offsetY = Math.round(bounds.top + backgroundPosition.top); @@ -787,7 +852,9 @@ _html2canvas.Parse = function (images, options) { function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) { var backgroundSize = _html2canvas.Util.BackgroundSize(el, bounds, image, imageIndex), backgroundPosition = _html2canvas.Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize), - backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(function(value) { return value.trim(); }); + backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(function(value) { + return value.trim(); + }); image = resizeImage(image, backgroundSize); @@ -810,7 +877,12 @@ _html2canvas.Parse = function (images, options) { break; default: - renderBackgroundRepeat(ctx, image, backgroundPosition, { top: bounds.top, left: bounds.left, width: image.width, height: image.height }); + renderBackgroundRepeat(ctx, image, backgroundPosition, { + top: bounds.top, + left: bounds.left, + width: image.width, + height: image.height + }); break; } } @@ -829,8 +901,8 @@ _html2canvas.Parse = function (images, options) { } var key = backgroundImage.method === 'url' ? - backgroundImage.args[0] : - backgroundImage.value; + backgroundImage.args[0] : + backgroundImage.value; image = loadImage(key); @@ -899,7 +971,7 @@ _html2canvas.Parse = function (images, options) { return backgroundBounds; } - function renderElement(element, parentStack){ + function renderElement(element, parentStack, pseudoElement){ var bounds = _html2canvas.Util.Bounds(element), image, bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"), @@ -925,6 +997,10 @@ _html2canvas.Parse = function (images, options) { renderBorders(ctx, border.args, border.color); }); + if (!pseudoElement) { + injectPseudoElements(element, stack); + } + switch(element.nodeName){ case "IMG": if ((image = loadImage(element.getAttribute('src')))) { @@ -965,14 +1041,14 @@ _html2canvas.Parse = function (images, options) { return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore")); } - function parseElement (el, stack) { + function parseElement (el, stack, pseudoElement) { if (isElementVisible(el)) { - stack = renderElement(el, stack) || stack; + stack = renderElement(el, stack, pseudoElement) || stack; if (!ignoreElementsRegExp.test(el.nodeName)) { _html2canvas.Util.Children(el).forEach(function(node) { if (node.nodeType === 1) { - parseElement(node, stack); + parseElement(node, stack, pseudoElement); } else if (node.nodeType === 3) { renderText(el, node, stack); } @@ -1059,7 +1135,7 @@ _html2canvas.Parse = function (images, options) { }); stack.backgroundColor = getCSS(document.documentElement, "backgroundColor"); - + body.removeChild(hidePseudoElements); return stack; } diff --git a/src/Preload.js b/src/Preload.js index e49d84f..609e5f1 100644 --- a/src/Preload.js +++ b/src/Preload.js @@ -89,16 +89,64 @@ _html2canvas.Preload = function( options ) { } - function getImages (el) { - el.__html2canvas__id = uid++; + function loadPseudoElement(element, type) { + var style = window.getComputedStyle(element, type), + content = style.content; + if (content.substr(0, 3) === 'url') { + methods.loadImage(_html2canvas.Util.parseBackgroundImage(content)[0].args[0]); + } + loadBackgroundImages(style.backgroundImage, element); + } - var contents = _html2canvas.Util.Children(el), - i, - background_image, - background_images, + function loadPseudoElementImages(element) { + loadPseudoElement(element, ":before"); + loadPseudoElement(element, ":after"); + } + + function loadBackgroundImages(background_image, el) { + var background_images, src, img, - bounds, + bounds; + // opera throws exception on external-content.html + + background_images = _html2canvas.Util.parseBackgroundImage(background_image); + for(var imageIndex = background_images.length; imageIndex-- > 0;) { + background_image = background_images[imageIndex]; + + if(!background_image || + !background_image.method || + !background_image.args || + background_image.args.length === 0 ) { + continue; + } + if (background_image.method === 'url') { + src = background_image.args[0]; + methods.loadImage(src); + + } else if( background_image.method.match( /\-?gradient$/ ) ) { + if(bounds === undefined) { + bounds = _html2canvas.Util.Bounds(el); + } + + img = _html2canvas.Generate.Gradient( background_image.value, bounds); + + if ( img !== undefined ){ + images[background_image.value] = { + img: img, + succeeded: true + }; + images.numTotal++; + images.numLoaded++; + start(); + } + } + } + } + + function getImages (el) { + var contents = _html2canvas.Util.Children(el), + i, elNodeType = false; // Firefox fails with permission denied on pages with iframes @@ -117,46 +165,14 @@ _html2canvas.Preload = function( options ) { h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); } - if (elNodeType === 1 || elNodeType === undefined){ - - // opera throws exception on external-content.html + if (elNodeType === 1 || elNodeType === undefined) { + loadPseudoElementImages(el); try { - background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); + loadBackgroundImages(_html2canvas.Util.getCSS(el, 'backgroundImage'), el); } catch(e) { h2clog("html2canvas: failed to get background-image - Exception: " + e.message); } - background_images = _html2canvas.Util.parseBackgroundImage(background_image); - for(var imageIndex = background_images.length; imageIndex-- > 0;) { - background_image = background_images[imageIndex]; - - if(!background_image || - !background_image.method || - !background_image.args || - background_image.args.length === 0 ) { - continue; - } - if (background_image.method === 'url') { - src = background_image.args[0]; - methods.loadImage(src); - - } else if( background_image.method.match( /\-?gradient$/ ) ) { - if(bounds === undefined) { - bounds = _html2canvas.Util.Bounds( el ); - } - - img = _html2canvas.Generate.Gradient( background_image.value, bounds); - - if ( img !== undefined ){ - images[background_image.value] = { - img: img, - succeeded: true - }; - images.numTotal++; - images.numLoaded++; - start(); - } - } - } + loadBackgroundImages(el); } } @@ -211,94 +227,6 @@ _html2canvas.Preload = function( options ) { */ } - var uid = 0, injectStyle; - function injectPseudoElements(el) { - if(!_html2canvas.Util.isElementVisible(el)) { - return; - } - - var before = getPseudoElement(el, ':before'), - after = getPseudoElement(el, ':after'); - if(!before && !after) { - return; - } - if(!el.id) { - el.id = '__html2canvas__' + (uid++); - } - if(!injectStyle) { - injectStyle = document.createElement('style'); - } - - if(before) { - el.__html2canvas_before = before; - injectStyle.innerHTML += '#' + el.id + ':before { content: "" !important; display: none !important; }\n'; - if(el.childNodes.length > 0) { - el.insertBefore(before, el.childNodes[0]); - } else { - el.appendChild(before); - } - } - - if (after) { - el.__html2canvas_after = after; - injectStyle.innerHTML += '#' + el.id + ':after { content: "" !important; display: none !important; }\n'; - el.appendChild(after); - } - } - - function removePseudoElements(el) { - var before = el.__html2canvas_before, - after = el.__html2canvas_after; - if(before) { - el.__html2canvas_before = undefined; - el.removeChild(before); - } - if(after) { - el.__html2canvas_after = undefined; - el.removeChild(after); - } - } - - function cleanupPseudoElements(){ - if(!injectStyle) { - return; - } - injectStyle.parentNode.removeChild(injectStyle); - injectStyle = undefined; - - [].slice.apply(element.all || element.getElementsByTagName('*')) - .forEach(removePseudoElements); - } - - function indexedProperty(property) { - return (!isNaN(window.parseInt(property, 10))); - } - - function getPseudoElement(el, which) { - var elStyle = window.getComputedStyle(el, which); - if(!elStyle || !elStyle.content || elStyle.content === "none" || elStyle.content === "-moz-alt-content") { return; } - var content = elStyle.content + '', - first = content.substr( 0, 1 ); - //strips quotes - if(first === content.substr( content.length - 1 ) && first.match(/'|"/)) { - content = content.substr( 1, content.length - 2 ); - } - - var isImage = content.substr( 0, 3 ) === 'url', - elps = document.createElement( isImage ? 'img' : 'span' ); - - elps.className = '__html2canvas__' + which.substr(1); - Object.keys(elStyle).filter(indexedProperty).forEach(function(prop) { - elps.style[prop] = elStyle[prop]; - }); - if(isImage) { - elps.src = _html2canvas.Util.parseBackgroundImage(content)[0].args[0]; - } else { - elps.innerHTML = content; - } - return elps; - } - methods = { loadImage: function( src ) { var img, imageObj; @@ -392,15 +320,12 @@ _html2canvas.Preload = function( options ) { start(); } } - - cleanupPseudoElements(); }, renderingDone: function() { if (timeoutTimer) { window.clearTimeout(timeoutTimer); } - cleanupPseudoElements(); } }; @@ -408,17 +333,10 @@ _html2canvas.Preload = function( options ) { timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); } - [].slice.apply(element.all || element.getElementsByTagName('*')) - .forEach(injectPseudoElements); - if(injectStyle) { - element.appendChild(injectStyle); - } - - h2clog('html2canvas: Preload starts: finding background-images'); images.firstRun = true; - getImages( element ); + getImages(element); h2clog('html2canvas: Preload: Finding images'); // load images diff --git a/src/Util.js b/src/Util.js index 74b5f27..a77c35b 100644 --- a/src/Util.js +++ b/src/Util.js @@ -8,7 +8,7 @@ window.html2canvas = function(elements, opts) { background: "#fff", // preload options - proxy: "", + proxy: null, 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 @@ -18,7 +18,7 @@ window.html2canvas = function(elements, opts) { ignoreElements: "IFRAME|OBJECT|PARAM", useOverflow: true, letterRendering: false, - chinese: false, + chinese: false, // render options diff --git a/tests/readme.md b/tests/readme.md index 51db39c..2ffdd03 100644 --- a/tests/readme.md +++ b/tests/readme.md @@ -2,7 +2,7 @@ background/clip.html100%100%99.89% background/encoded.html100%100%100% background/linear-gradient.html89.87%90.73%100% -background/multi.html96.6%96.45%96.85% +background/multi.html100%100%99.93% background/position.html100%100%99.87% background/radial-gradient.html73.23%70.32%94.02% background/repeat.html100%100%99.92% @@ -22,11 +22,13 @@ list/lower-alpha.html99.65%99.73%35.89% list/upper-roman.html99.45%99.61%35.94% overflow.html96.85%97.49%96.5% +pseudoelements.html97.36%97.94%99.37% +text/chinese.html99.75%99.74%65.76% text/linethrough.html97.14%94.12%47.08% text/text.html95.71%94.67%85.01% text/underline-lineheight.html97.06%92.35%53% text/underline.html97.65%93.5%47.02% -visibility.html99.19%98.81%99.39% +visibility.html99.19%98.92%99.39% zindex/z-index1.html96.99%99.27%99.44% zindex/z-index2.html95.85%98.06%97.72% zindex/z-index3.html98.6%98.29%98.56%