diff --git a/src/Core.js b/src/Core.js index 5148497..ed2257f 100644 --- a/src/Core.js +++ b/src/Core.js @@ -166,6 +166,20 @@ _html2canvas.Util.Bounds = function (element) { return bounds; }; +// TODO ideally, we'd want everything to go through this function instead of Util.Bounds, +// but would require further work to calculate the correct positions for elements with offsetParents +_html2canvas.Util.OffsetBounds = function (element) { + var parent = element.offsetParent ? _html2canvas.Util.OffsetBounds(element.offsetParent) : {top: 0, left: 0}; + + return { + top: element.offsetTop + parent.top, + bottom: element.offsetTop + element.offsetHeight + parent.top, + left: element.offsetLeft + parent.left, + width: element.offsetWidth, + height: element.offsetHeight + }; +}; + function toPX(element, attribute, value ) { var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute], left, @@ -295,14 +309,13 @@ function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroun if(prop !== 'backgroundSize') { left -= (backgroundSize || image).width*percentage; } - } else { if(prop === 'backgroundSize') { if(bgposition[0] === 'auto') { left = image.width; } else { if (/contain|cover/.test(bgposition[0])) { - var resized = _html2canvas.Util.resizeBounds( image.width, image.height, bounds.width, bounds.height, bgposition[0] ); + var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, bgposition[0]); left = resized.width; topPos = resized.height; } else { @@ -335,6 +348,7 @@ _html2canvas.Util.BackgroundPosition = function( el, bounds, image, imageIndex, var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize ); return { left: result[0], top: result[1] }; }; + _html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) { var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex ); return { width: result[0], height: result[1] }; diff --git a/src/Parse.js b/src/Parse.js index 5b9405c..f444255 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -48,16 +48,18 @@ _html2canvas.Parse = function (images, options) { } } + function capitalize(m, p1, p2) { + if (m.length > 0) { + return p1 + p2.toUpperCase(); + } + } + function textTransform (text, transform) { switch(transform){ case "lowercase": return text.toLowerCase(); case "capitalize": - return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { - if (m.length > 0) { - return p1 + p2.toUpperCase(); - } - } ); + return text.replace( /(^|\s|:|-|\(|\))([a-z])/g, capitalize); case "uppercase": return text.toUpperCase(); default: @@ -127,16 +129,16 @@ _html2canvas.Parse = function (images, options) { } } - function getTextBounds(state, text, textDecoration, isLast) { + function getTextBounds(state, text, textDecoration, isLast, transform) { var bounds; - if (support.rangeBounds) { + if (support.rangeBounds && !transform) { if (textDecoration !== "none" || Util.trimText(text).length !== 0) { bounds = textRangeBounds(text, state.node, state.textOffset); } state.textOffset += text.length; } else if (state.node && typeof state.node.nodeValue === "string" ){ var newTextNode = (isLast) ? state.node.splitText(text.length) : null; - bounds = textWrapperBounds(state.node); + bounds = textWrapperBounds(state.node, transform); state.node = newTextNode; } return bounds; @@ -149,7 +151,7 @@ _html2canvas.Parse = function (images, options) { return range.getBoundingClientRect(); } - function textWrapperBounds(oldTextNode) { + function textWrapperBounds(oldTextNode, transform) { var parent = oldTextNode.parentNode, wrapElement = doc.createElement('wrapper'), backupText = oldTextNode.cloneNode(true); @@ -157,7 +159,7 @@ _html2canvas.Parse = function (images, options) { wrapElement.appendChild(oldTextNode.cloneNode(true)); parent.replaceChild(wrapElement, oldTextNode); - var bounds = Util.Bounds(wrapElement); + var bounds = transform ? Util.OffsetBounds(wrapElement) : Util.Bounds(wrapElement); parent.replaceChild(backupText, wrapElement); return bounds; } @@ -195,7 +197,7 @@ _html2canvas.Parse = function (images, options) { } textList.forEach(function(text, index) { - var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1)); + var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix); if (bounds) { drawText(text, bounds.left, bounds.bottom, ctx); renderTextDecoration(ctx, textDecoration, bounds, metrics, color); @@ -947,14 +949,21 @@ _html2canvas.Parse = function (images, options) { return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1)); } - function setTransform(ctx, element, parentStack) { + function removePx(str) { + return str.replace("px", ""); + } + + var transformRegExp = /(matrix)\((.+)\)/; + + function getTransform(element, parentStack) { var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform"); - var transformOrigin = getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin"); + var transformOrigin = getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin") || "0px 0px"; + + transformOrigin = transformOrigin.split(" ").map(removePx).map(Util.asFloat); var matrix; - var TRANSFORM_REGEXP = /(matrix)\((.+)\)/; if (transform && transform !== "none") { - var match = transform.match(TRANSFORM_REGEXP); + var match = transform.match(transformRegExp); if (match) { switch(match[1]) { case "matrix": @@ -970,14 +979,14 @@ _html2canvas.Parse = function (images, options) { }; } - function createStack(element, parentStack, bounds) { + function createStack(element, parentStack, bounds, transform) { var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), stack = { ctx: ctx, opacity: setOpacity(ctx, element, parentStack), cssPosition: getCSS(element, "position"), borders: getBorderData(element), - transform: setTransform(ctx, element, parentStack), + transform: transform, clip: (parentStack && parentStack.clip) ? Util.Extend( {}, parentStack.clip ) : null }; @@ -1006,16 +1015,26 @@ _html2canvas.Parse = function (images, options) { return backgroundBounds; } - function renderElement(element, parentStack, pseudoElement){ - var bounds = Util.Bounds(element), + function getBounds(element, transform) { + var bounds = (transform.matrix) ? Util.OffsetBounds(element) : Util.Bounds(element); + transform.origin[0] += bounds.left; + transform.origin[1] += bounds.top; + return bounds; + } + + function renderElement(element, parentStack, pseudoElement) { + var transform = getTransform(element, parentStack), + bounds = getBounds(element, transform), image, bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"), - stack = createStack(element, parentStack, bounds), + stack = createStack(element, parentStack, bounds, transform), borders = stack.borders, ctx = stack.ctx, backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), borderData = parseBorders(element, bounds, borders); + + createShape(ctx, borderData.clip); ctx.save(); diff --git a/src/renderers/Canvas.js b/src/renderers/Canvas.js index b198be2..a6f7197 100644 --- a/src/renderers/Canvas.js +++ b/src/renderers/Canvas.js @@ -85,7 +85,9 @@ _html2canvas.Renderer.Canvas = function(options) { ctx.save(); if (storageContext.transform.matrix) { + ctx.translate(storageContext.transform.origin[0], storageContext.transform.origin[1]); ctx.transform.apply(ctx, storageContext.transform.matrix); + ctx.translate(-storageContext.transform.origin[0], -storageContext.transform.origin[1]); } if (storageContext.clip){