diff --git a/src/Generate.js b/src/Generate.js index 4ba48c6..007232a 100644 --- a/src/Generate.js +++ b/src/Generate.js @@ -327,18 +327,16 @@ var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), - gradient, grad, i, len, img; + gradient, grad, i, len; canvas.width = bounds.width; canvas.height = bounds.height; - // TODO: add support for multi defined background gradients (like radial gradient example in background.html) + // TODO: add support for multi defined background gradients gradient = _html2canvas.Generate.parseGradient(src, bounds); - img = new Image(); - - if(gradient){ - if(gradient.type === 'linear'){ + if(gradient) { + if(gradient.type === 'linear') { grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { @@ -353,8 +351,7 @@ ctx.fillStyle = grad; ctx.fillRect(0, 0, bounds.width, bounds.height); - img.src = canvas.toDataURL(); - } else if(gradient.type === 'circle'){ + } else if(gradient.type === 'circle') { grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); @@ -370,8 +367,7 @@ ctx.fillStyle = grad; ctx.fillRect(0, 0, bounds.width, bounds.height); - img.src = canvas.toDataURL(); - } else if(gradient.type === 'ellipse'){ + } else if(gradient.type === 'ellipse') { // draw circle var canvasRadial = document.createElement('canvas'), @@ -382,7 +378,7 @@ canvasRadial.width = canvasRadial.height = di; grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); - + for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { try { grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); @@ -397,21 +393,12 @@ ctx.fillStyle = gradient.colorStops[i - 1].color; ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); - imgRadial = new Image(); - imgRadial.onload = function() { // wait until the image is filled - - // transform circle to ellipse - ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); - - img.src = canvas.toDataURL(); - - }; - imgRadial.src = canvasRadial.toDataURL(); } } - return img; + return canvas; }; _html2canvas.Generate.ListAlpha = function(number) { diff --git a/src/Parse.js b/src/Parse.js index c2789bc..9b863fb 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -326,144 +326,372 @@ _html2canvas.Parse = function (images, options) { ); } - function renderBorders(el, ctx, bounds, clip){ + function getBorderData(element) { + return ["Top", "Right", "Bottom", "Left"].map(function(side) { + return { + width: getCSSInt(element, 'border' + side + 'Width'), + color: getCSS(element, 'border' + side + 'Color') + }; + }); + } + + function getBorderRadiusData(element) { + return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) { + return getCSS(element, 'border' + side + 'Radius'); + }); + } + + var getCurvePoints = (function(kappa) { + + return function(x, y, r1, r2) { + var ox = (r1) * kappa, // control point offset horizontal + oy = (r2) * kappa, // control point offset vertical + xm = x + r1, // x-middle + ym = y + r2; // y-middle + return { + topLeft: bezierCurve({ + x:x, + y:ym + }, { + x:x, + y:ym - oy + }, { + x:xm - ox, + y:y + }, { + x:xm, + y:y + }), + topRight: bezierCurve({ + x:x, + y:y + }, { + x:x + ox, + y:y + }, { + x:xm, + y:ym - oy + }, { + x:xm, + y:ym + }), + bottomRight: bezierCurve({ + x:xm, + y:y + }, { + x:xm, + y:y + oy + }, { + x:x + ox, + y:ym + }, { + x:x, + y:ym + }), + bottomLeft: bezierCurve({ + x:xm, + y:ym + }, { + x:xm - ox, + y:ym + }, { + x:x, + y:y + oy + }, { + x:x, + y:y + }) + }; + }; + })(4 * ((Math.sqrt(2) - 1) / 3)); + + function bezierCurve(start, startControl, endControl, end) { + + var lerp = function (a, b, t) { + return { + x:a.x + (b.x - a.x) * t, + y:a.y + (b.y - a.y) * t + }; + }; + + return { + start: start, + startControl: startControl, + endControl: endControl, + end: end, + subdivide: function(t) { + var ab = lerp(start, startControl, t), + bc = lerp(startControl, endControl, t), + cd = lerp(endControl, end, t), + abbc = lerp(ab, bc, t), + bccd = lerp(bc, cd, t), + dest = lerp(abbc, bccd, t); + return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)]; + }, + curveTo: function(borderArgs) { + borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]); + }, + curveToReversed: function(borderArgs) { + borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]); + } + }; + } + + function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) { + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]); + corner1[0].curveTo(borderArgs); + corner1[1].curveTo(borderArgs); + } else { + borderArgs.push(["line", x, y]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]); + } + } + + function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { + var borderArgs = []; + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]); + outer1[1].curveTo(borderArgs); + } else { + borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]); + outer2[0].curveTo(borderArgs); + borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]); + inner2[0].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c2[0], borderData.c2[1]]); + borderArgs.push([ "line", borderData.c3[0], borderData.c3[1]]); + } + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]); + inner1[1].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c4[0], borderData.c4[1]]); + } + + return borderArgs; + } + + function calculateCurvePoints(bounds, borderRadius, borders) { + var x = bounds.left, y = bounds.top, - w = bounds.width, - h = bounds.height, + width = bounds.width, + height = bounds.height, + + tlh = borderRadius[0][0], + tlv = borderRadius[0][1], + trh = borderRadius[1][0], + trv = borderRadius[1][1], + brv = borderRadius[2][0], + brh = borderRadius[2][1], + blh = borderRadius[3][0], + blv = borderRadius[3][1], + + topWidth = width - trh, + rightHeight = height - brv, + bottomWidth = width - brh, + leftHeight = height - blv; + + return { + topLeftOuter: getCurvePoints( + x, + y, + tlh, + tlv + ).topLeft.subdivide(0.5), + + topLeftInner: getCurvePoints( + x + borders[3].width, + y + borders[0].width, + Math.max(0, tlh - borders[3].width), + Math.max(0, tlv - borders[0].width) + ).topLeft.subdivide(0.5), + + topRightOuter: getCurvePoints( + x + topWidth, + y, + trh, + trv + ).topRight.subdivide(0.5), + + topRightInner: getCurvePoints( + x + Math.min(topWidth, width + borders[3].width), + y + borders[0].width, + (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, + trv - borders[0].width + ).topRight.subdivide(0.5), + + bottomRightOuter: getCurvePoints( + x + bottomWidth, + y + rightHeight, + brh, + brv + ).bottomRight.subdivide(0.5), + + bottomRightInner: getCurvePoints( + x + Math.min(bottomWidth, width + borders[3].width), + y + Math.min(rightHeight, height + borders[0].width), + Math.max(0, brh - borders[1].width), + Math.max(0, brv - borders[2].width) + ).bottomRight.subdivide(0.5), + + bottomLeftOuter: getCurvePoints( + x, + y + leftHeight, + blh, + blv + ).bottomLeft.subdivide(0.5), + + bottomLeftInner: getCurvePoints( + x + borders[3].width, + y + leftHeight, + Math.max(0, blh - borders[3].width), + Math.max(0, blv - borders[2].width) + ).bottomLeft.subdivide(0.5) + }; + } + + function getBorderClip(element, borderPoints, borders, radius, bounds) { + var backgroundClip = getCSS(element, 'backgroundClip'), + borderArgs = []; + + switch(backgroundClip) { + case "content-box": + case "padding-box": + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width); + break; + + default: + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height); + break; + } + + return borderArgs; + } + + function parseBorders(element, bounds, borders){ + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, borderSide, - borderData, bx, by, bw, bh, - i, borderArgs, - borderBounds, - borders = (function(el){ - var borders = [], - sides = ["Top","Right","Bottom","Left"], - s; - - for (s = 0; s < 4; s+=1){ - borders.push({ - width: getCSSInt(el, 'border' + sides[s] + 'Width'), - color: getCSS(el, 'border' + sides[s] + 'Color') - }); - } - - return borders; - - }(el)), // http://www.w3.org/TR/css3-background/#the-border-radius - borderRadius = (function( el ) { - var borders = [], - sides = ["TopLeft","TopRight","BottomRight","BottomLeft"], - s; + borderRadius = getBorderRadiusData(element), + borderPoints = calculateCurvePoints(bounds, borderRadius, borders), + borderData = { + clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds), + borders: [] + }; - for (s = 0; s < 4; s+=1){ - borders.push( getCSS(el, 'border' + sides[s] + 'Radius') ); - } + for (borderSide = 0; borderSide < 4; borderSide++) { - return borders; - })( el ); - - - - for ( borderSide = 0; borderSide < 4; borderSide+=1 ) { - borderData = borders[ borderSide ]; - borderArgs = []; - if (borderData.width>0){ + if (borders[borderSide].width > 0) { bx = x; by = y; - bw = w; - bh = h - (borders[2].width); + bw = width; + bh = height - (borders[2].width); - switch(borderSide){ + switch(borderSide) { case 0: // top border bh = borders[0].width; - i = 0; - borderArgs[ i++ ] = [ "line", bx, by ]; // top left - borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw - borders[ 1 ].width, by + bh ]; // bottom right - borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by + bh ]; // bottom left - + borderArgs = drawSide({ + c1: [bx, by], + c2: [bx + bw, by], + c3: [bx + bw - borders[1].width, by + bh], + c4: [bx + borders[3].width, by + bh] + }, borderRadius[0], borderRadius[1], + borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner); break; case 1: // right border - bx = x + w - (borders[1].width); + bx = x + width - (borders[1].width); bw = borders[1].width; - i = 0; - borderArgs[ i++ ] = [ "line", bx, by + borders[ 0 ].width]; // top left - borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw, by + bh + borders[ 2 ].width ]; // bottom right - borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left - + borderArgs = drawSide({ + c1: [bx + bw, by], + c2: [bx + bw, by + bh + borders[2].width], + c3: [bx, by + bh], + c4: [bx, by + borders[0].width] + }, borderRadius[1], borderRadius[2], + borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner); break; case 2: // bottom border - by = (by + h) - (borders[2].width); + by = (by + height) - (borders[2].width); bh = borders[2].width; - i = 0; - borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by ]; // top left - borderArgs[ i++ ] = [ "line", bx + bw - borders[ 2 ].width, by ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right - borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left - + borderArgs = drawSide({ + c1: [bx + bw, by + bh], + c2: [bx, by + bh], + c3: [bx + borders[3].width, by], + c4: [bx + bw - borders[2].width, by] + }, borderRadius[2], borderRadius[3], + borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner); break; case 3: // left border bw = borders[3].width; - i = 0; - borderArgs[ i++ ] = [ "line", bx, by ]; // top left - borderArgs[ i++ ] = [ "line", bx + bw, by + borders[ 0 ].width ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right - borderArgs[ i++ ] = [ "line", bx, by + bh + borders[ 2 ].width ]; // bottom left - + borderArgs = drawSide({ + c1: [bx, by + bh + borders[2].width], + c2: [bx, by], + c3: [bx + bw, by + borders[0].width], + c4: [bx + bw, by + bh] + }, borderRadius[3], borderRadius[0], + borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner); break; } - borderBounds = { - left:bx, - top:by, - width: bw, - height:bh - }; - - if (clip){ - borderBounds = clipBounds(borderBounds, clip); - } - - - if ( borderBounds.width > 0 && borderBounds.height > 0 ) { - - if ( borderData.color !== "transparent" ){ - ctx.setVariable( "fillStyle", borderData.color ); - - var shape = ctx.drawShape(), - numBorderArgs = borderArgs.length; - - for ( i = 0; i < numBorderArgs; i++ ) { - shape[( i === 0) ? "moveTo" : borderArgs[ i ][ 0 ] + "To" ].apply( null, borderArgs[ i ].slice(1) ); - } - - numDraws+=1; - } - - } - + borderData.borders.push({ + args: borderArgs, + color: borders[borderSide].color + }); } } - return borders; + return borderData; } + function createShape(ctx, args) { + var shape = ctx.drawShape(); + args.forEach(function(border, index) { + shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1)); + }); + return shape; + } + + function renderBorders(ctx, borderArgs, color) { + if (color !== "transparent") { + ctx.setVariable( "fillStyle", color); + createShape(ctx, borderArgs); + ctx.fill(); + numDraws+=1; + } + } function renderFormValue (el, bounds, stack){ @@ -512,118 +740,27 @@ _html2canvas.Parse = function (images, options) { numDraws+=1; } - function renderBackgroundSlice (ctx, image, x, y, width, height, elx, ely){ - var sourceX = (elx - x > 0) ? elx-x :0, - sourceY= (ely - y > 0) ? ely-y : 0; - - drawImage( - ctx, - image, - Math.floor(sourceX), // source X - Math.floor(sourceY), // source Y - Math.ceil(image.width-sourceX), // source Width - Math.ceil(image.height-sourceY), // source Height - Math.ceil(x+sourceX), // destination X - Math.ceil(y+sourceY), // destination Y - Math.ceil(width-sourceX), // destination width - Math.ceil(height-sourceY) // destination height - ); - } - function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { - var bgy, - height, - add, - h; + var offsetX = Math.round(bounds.left + backgroundPosition.left), + offsetY = Math.round(bounds.top + backgroundPosition.top); - backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height; - - for(bgy = (bounds.top + backgroundPosition.top); bgy < (bounds.height + bounds.top); bgy = Math.floor(bgy+image.height) - add) { - h = Math.min(image.height,(bounds.height + bounds.top) - bgy); - - height = (Math.floor(bgy + image.height) > h + bgy) ? (h + bgy) - bgy : image.height; - - if (bgy < bounds.top){ - add = bounds.top - bgy; - bgy = bounds.top; - } else { - add = 0; - } - - renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bgy, bounds.width, height); - - if (add > 0){ - backgroundPosition.top += add; - } - } + ctx.createPattern(image); + ctx.translate(offsetX, offsetY); + ctx.fill(); + ctx.translate(-offsetX, -offsetY); } - function renderBackgroundNoRepeat(ctx, image, backgroundPosition, x, y, w, h) { - var bgdw = w - backgroundPosition.left, - bgdh = h - backgroundPosition.top, - bgsx = backgroundPosition.left, - bgsy = backgroundPosition.top, - bgdx = backgroundPosition.left + x, - bgdy = backgroundPosition.top + y; - - if (bgsx<0){ - bgsx = Math.abs( bgsx ); - bgdx += bgsx; - bgdw = Math.min( w, image.width - bgsx ); - } else { - bgdw = Math.min( bgdw, image.width ); - bgsx = 0; - } - - if (bgsy < 0){ - bgsy = Math.abs( bgsy ); - bgdy += bgsy; - bgdh = Math.min( h, image.height - bgsy ); - } else { - bgdh = Math.min( bgdh, image.height ); - bgsy = 0; - } - - if (bgdh > 0 && bgdw > 0){ - drawImage( - ctx, - image, - bgsx, - bgsy, - image.width, - image.height, - bgdx, - bgdy, - bgdw + backgroundPosition.left, - bgdh + backgroundPosition.top - ); - } - } - - function renderBackgroundRepeatY (ctx, image, backgroundPosition, x, y, w, h){ - var height, - width = Math.min(image.width, w), - bgy; - - backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height; - - for (bgy = y + backgroundPosition.top; bgy < h + y; bgy = Math.round(bgy + image.height)){ - height = (Math.floor(bgy + image.height) > h + y) ? (h+y) - bgy : image.height; - renderBackgroundSlice(ctx, image, x + backgroundPosition.left, bgy,width, height, x, y); - } - } - - function renderBackgroundRepeatX(ctx, image, backgroundPosition, x, y, w, h){ - var height = Math.min(image.height, h), - width, - bgx; - - backgroundPosition.left -= Math.ceil(backgroundPosition.left / image.width) * image.width; - - for (bgx = x + backgroundPosition.left; bgx < w + x; bgx = Math.round(bgx + image.width)) { - width = (Math.floor(bgx + image.width) > w + x) ? (w + x) - bgx : image.width; - renderBackgroundSlice(ctx, image, bgx,(y + backgroundPosition.top), width, height, x, y); - } + function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height) { + var args = []; + args.push(["line", Math.round(left), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(height + top)]); + args.push(["line", Math.round(left), Math.round(height + top)]); + createShape(ctx, args); + ctx.save(); + ctx.clip(); + renderBackgroundRepeat(ctx, image, backgroundPosition, bounds); + ctx.restore(); } function renderBackgroundColor(ctx, backgroundBounds, bgcolor) { @@ -646,15 +783,18 @@ _html2canvas.Parse = function (images, options) { switch (backgroundRepeat) { case "repeat-x": - renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bounds.top, backgroundSize.width, backgroundSize.height); + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left, bounds.top + backgroundPosition.top, 99999, backgroundSize.height); break; case "repeat-y": - renderBackgroundRepeatY(ctx, image, backgroundPosition, bounds.left, bounds.top, backgroundSize.width, backgroundSize.height); + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top, backgroundSize.width, 99999); break; case "no-repeat": - renderBackgroundNoRepeat(ctx, image, backgroundPosition, bounds.left, bounds.top, backgroundSize.width, backgroundSize.height); + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, backgroundSize.width, backgroundSize.height); break; default: @@ -704,7 +844,7 @@ _html2canvas.Parse = function (images, options) { zIndex: setZ(getCSS(element, "zIndex"), (parentStack) ? parentStack.zIndex : null), opacity: setOpacity(ctx, element, parentStack), cssPosition: getCSS(element, "position"), - borders: renderBorders(element, ctx, bounds, false), + borders: getBorderData(element), clip: (parentStack && parentStack.clip) ? _html2canvas.Util.Extend( {}, parentStack.clip ) : null }; @@ -740,13 +880,25 @@ _html2canvas.Parse = function (images, options) { stack = createStack(element, parentStack, bounds), borders = stack.borders, ctx = stack.ctx, - backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip); + backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), + borderData = parseBorders(element, bounds, borders); + + createShape(ctx, borderData.clip); + + ctx.save(); + ctx.clip(); if (backgroundBounds.height > 0 && backgroundBounds.width > 0){ - renderBackgroundColor(ctx, backgroundBounds, bgcolor); + renderBackgroundColor(ctx, bounds, bgcolor); renderBackgroundImage(element, backgroundBounds, ctx); } + ctx.restore(); + + borderData.borders.forEach(function(border) { + renderBorders(ctx, border.args, border.color); + }); + switch(element.nodeName){ case "IMG": if ((image = loadImage(element.getAttribute('src')))) { diff --git a/src/Preload.js b/src/Preload.js index 22cf4fa..90d4074 100644 --- a/src/Preload.js +++ b/src/Preload.js @@ -122,10 +122,9 @@ _html2canvas.Preload = function( options ) { // opera throws exception on external-content.html try { background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); - }catch(e) { + } 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]; @@ -136,7 +135,6 @@ _html2canvas.Preload = function( options ) { background_image.args.length === 0 ) { continue; } - if (background_image.method === 'url') { src = background_image.args[0]; methods.loadImage(src); diff --git a/src/Queue.js b/src/Queue.js index bf8dcdf..3a8d22c 100644 --- a/src/Queue.js +++ b/src/Queue.js @@ -4,6 +4,41 @@ function h2cRenderContext(width, height) { storage: storage, width: width, height: height, + clip: function() { + storage.push({ + type: "function", + name: "clip", + 'arguments': arguments + }); + }, + translate: function() { + storage.push({ + type: "function", + name: "translate", + 'arguments': arguments + }); + }, + fill: function() { + storage.push({ + type: "function", + name: "fill", + 'arguments': arguments + }); + }, + save: function() { + storage.push({ + type: "function", + name: "save", + 'arguments': arguments + }); + }, + restore: function() { + storage.push({ + type: "function", + name: "restore", + 'arguments': arguments + }); + }, fillRect: function () { storage.push({ type: "function", @@ -11,6 +46,13 @@ function h2cRenderContext(width, height) { 'arguments': arguments }); }, + createPattern: function() { + storage.push({ + type: "function", + name: "createPattern", + 'arguments': arguments + }); + }, drawShape: function() { var shape = []; @@ -34,6 +76,12 @@ function h2cRenderContext(width, height) { 'arguments': arguments }); }, + arcTo: function() { + shape.push({ + name: "arcTo", + 'arguments': arguments + }); + }, bezierCurveTo: function() { shape.push({ name: "bezierCurveTo", diff --git a/src/renderers/Canvas.js b/src/renderers/Canvas.js index a8fe43c..78e8d7e 100644 --- a/src/renderers/Canvas.js +++ b/src/renderers/Canvas.js @@ -98,16 +98,12 @@ _html2canvas.Renderer.Canvas = function( options ) { ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = fstyle; - var drawShape = function(args) { - - var i, len = args.length; + var createShape = function(args) { ctx.beginPath(); - for ( i = 0; i < len; i++ ) { - ctx[ args[ i ].name ].apply( ctx, args[ i ]['arguments'] ); - } + args.forEach(function(arg) { + ctx[arg.name].apply(ctx, arg['arguments']); + }); ctx.closePath(); - ctx.fill(); - }; if ( options.svgRendering && zStack.svgRender !== undefined ) { @@ -149,8 +145,10 @@ _html2canvas.Renderer.Canvas = function( options ) { 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 === "createPattern") { + ctx.fillStyle = ctx.createPattern(renderItem['arguments'][0], "repeat"); } else if (renderItem.name === "drawShape") { - drawShape(renderItem['arguments']); + createShape(renderItem['arguments']); } else if (renderItem.name === "fillText") { if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { ctx.fillText.apply( ctx, renderItem['arguments'] ); @@ -175,6 +173,8 @@ _html2canvas.Renderer.Canvas = function( options ) { } ctx.drawImage.apply( ctx, renderItem['arguments'] ); } + } else { + ctx[renderItem.name].apply(ctx, renderItem['arguments']); } diff --git a/tests/cases/background/clip.html b/tests/cases/background/clip.html new file mode 100644 index 0000000..00bbb22 --- /dev/null +++ b/tests/cases/background/clip.html @@ -0,0 +1,60 @@ + + + + Background attribute tests + + + + + + + +
+
+
+
+
+
+ +
+
+
+
+
+
+ + + diff --git a/tests/cases/border/radius.html b/tests/cases/border/radius.html new file mode 100644 index 0000000..6b883fe --- /dev/null +++ b/tests/cases/border/radius.html @@ -0,0 +1,60 @@ + + + + Borders tests + + + + + +
 
+
 
+
 
+
 
+ + diff --git a/tests/rangetest.html b/tests/rangetest.html new file mode 100644 index 0000000..bbd3308 --- /dev/null +++ b/tests/rangetest.html @@ -0,0 +1,37 @@ + + + + Range tests + + + + +
+ + + + diff --git a/tests/readme.md b/tests/readme.md index bf29ecd..51db39c 100644 --- a/tests/readme.md +++ b/tests/readme.md @@ -1,31 +1,33 @@ + - - - - - + + + + + + - + - - - - - - - - - - - - - + + + + + + + + + + + + +
chrome
23.0.1271.97
firefox
12.0
iexplorer
9
background/clip.html100%100%99.89%
background/encoded.html100%100%100%
background/linear-gradient.html82.27%85.64%100%
background/multi.html96.6%96.45%96.89%
background/position.html97.03%96.97%97.03%
background/radial-gradient.html57.9%54.87%94.02%
background/repeat.html100%100%100%
background/linear-gradient.html89.87%90.73%100%
background/multi.html96.6%96.45%96.85%
background/position.html100%100%99.87%
background/radial-gradient.html73.23%70.32%94.02%
background/repeat.html100%100%99.92%
border/dashed.html96.45%98.38%97.7%
border/dotted.html97.41%96.46%95.93%
border/double.html97.96%97.87%97.95%
border/radius.html99.74%99.77%99.75%
border/solid.html99.97%99.97%99.98%
forms.html95.96%94.55%95.02%
forms.html95.96%94.55%95.01%
images/canvas.html99.86%100%100%
images/cross-origin.html97.99%97.58%99.35%
images/empty.html99.86%99.87%99.85%
images/images.html83.72%96.93%55.09%
images/svg.html99.92%96.79%99.93%
list/decimal-leading-zero.html99.63%99.72%35.04%
list/decimal.html99.64%99.73%35.06%
list/lower-alpha.html99.65%99.73%35.05%
list/upper-roman.html99.45%99.61%35.11%
overflow.html96.85%97.49%96.51%
text/linethrough.html97.14%94.12%45.74%
text/text.html95.71%94.67%79.85%
text/underline-lineheight.html97.06%92.35%51.38%
text/underline.html97.65%93.5%45.69%
visibility.html99.19%98.92%99.39%
zindex/z-index1.html97.09%99.38%99.54%
zindex/z-index2.html95.94%98.16%97.81%
zindex/z-index3.html98.98%98.55%98.68%
list/decimal-leading-zero.html99.63%99.72%35.88%
list/decimal.html99.64%99.73%35.89%
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%
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%
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%
\ No newline at end of file diff --git a/tests/test.js b/tests/test.js index 77079c7..cc720a3 100644 --- a/tests/test.js +++ b/tests/test.js @@ -13,7 +13,7 @@ var h2cSelector, h2cOptions; document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd); var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'Font', 'renderers/Canvas'], i; for (i = 0; i < html2canvas.length; ++i) { - document.write(srcStart + '/src/' + html2canvas[i] + '.js' + scrEnd); + document.write(srcStart + '/src/' + html2canvas[i] + '.js?' + Math.random() + scrEnd); } window.onload = function() { h2cSelector = [document.body];