fixed few minor bugs, added some support for overflow (hidden|scroll|auto)

This commit is contained in:
MoyuScript 2011-07-28 21:22:13 +03:00
parent db9a1d16ad
commit 9ae4ae51fe
11 changed files with 889 additions and 378 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
/dist/ /dist/
/build/tmp.js /build/tmp.js
index.html index.html
screenshots_local.html

View File

@ -1,5 +1,5 @@
/* /*
* html2canvas v0.25 <http://html2canvas.hertzen.com> * html2canvas v0.26 <http://html2canvas.hertzen.com>
* Copyright (c) 2011 Niklas von Hertzen. All rights reserved. * Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
* http://www.twitter.com/niklasvh * http://www.twitter.com/niklasvh
* *
@ -62,6 +62,7 @@ function html2canvas(el, userOptions) {
}, },
canvasWidth:0, canvasWidth:0,
canvasHeight:0, canvasHeight:0,
useOverflow: true,
renderOrder: "canvas flash html" renderOrder: "canvas flash html"
}); });
@ -81,7 +82,7 @@ function html2canvas(el, userOptions) {
this.ignoreElements = "IFRAME|OBJECT|PARAM"; this.ignoreElements = "IFRAME|OBJECT|PARAM";
this.needReorder = false; this.needReorder = false;
this.blockElements = new RegExp("(BR|PARAM)"); this.blockElements = new RegExp("(BR|PARAM)");
this.pageOrigin = window.location.protocol + window.location.hostname; this.pageOrigin = window.location.protocol + window.location.host;
this.ignoreRe = new RegExp("("+this.ignoreElements+")"); this.ignoreRe = new RegExp("("+this.ignoreElements+")");
@ -175,8 +176,8 @@ html2canvas.prototype.start = function(){
if (this.images.length == 0 || this.imagesLoaded==this.images.length/2){ if (this.images.length == 0 || this.imagesLoaded==this.images.length/2){
this.log('Finished loading '+this.imagesLoaded+' images, Started parsing'); this.log('Finished loading '+this.imagesLoaded+' images, Started parsing');
this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow; this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow;
document.getElementsByTagName('body')[0].style.overflow = "hidden"; document.getElementsByTagName('body')[0].style.overflow = "hidden";
var rootStack = new this.storageContext($(document).width(),$(document).height()); var rootStack = new this.storageContext($(document).width(),$(document).height());
rootStack.opacity = this.getCSS(this.element,"opacity"); rootStack.opacity = this.getCSS(this.element,"opacity");
var stack = this.newElement(this.element,rootStack); var stack = this.newElement(this.element,rootStack);
@ -239,6 +240,8 @@ html2canvas.prototype.storageContext = function(width,height){
}; };
this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){ this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){
this.storage.push( this.storage.push(
{ {
@ -602,7 +605,13 @@ html2canvas.prototype.getBorderData = function(el){
} }
html2canvas.prototype.drawBorders = function(el,ctx, x, y, w, h){ html2canvas.prototype.drawBorders = function(el,ctx, bounds,clip){
var x = bounds.left;
var y = bounds.top;
var w = bounds.width;
var h = bounds.height;
/* /*
* TODO add support for different border-style's than solid * TODO add support for different border-style's than solid
@ -637,7 +646,21 @@ html2canvas.prototype.drawBorders = function(el,ctx, x, y, w, h){
break; break;
} }
_.newRect(ctx,bx,by,bw,bh,borderData.color); var borderBounds = {
left:bx,
top:by,
width: bw,
height:bh
};
if (clip){
borderBounds = _.clipBounds(borderBounds,clip);
}
if (borderBounds.width>0 && borderBounds.height>0){
_.newRect(ctx,bx,by,borderBounds.width,borderBounds.height,borderData.color);
}
@ -649,10 +672,6 @@ html2canvas.prototype.drawBorders = function(el,ctx, x, y, w, h){
}; };
html2canvas.prototype.newElement = function(el,parentStack){ html2canvas.prototype.newElement = function(el,parentStack){
var bounds = this.getBounds(el); var bounds = this.getBounds(el);
@ -665,10 +684,10 @@ html2canvas.prototype.newElement = function(el,parentStack){
image; image;
var bgcolor = this.getCSS(el,"background-color"); var bgcolor = this.getCSS(el,"background-color");
var cssPosition = this.getCSS(el,"position");
parentStack = parentStack || {}; parentStack = parentStack || {};
var zindex = this.formatZ(this.getCSS(el,"zIndex"),this.getCSS(el,"position"),parentStack.zIndex,el.parentNode); var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode);
//console.log(el.nodeName+":"+zindex+":"+this.getCSS(el,"position")+":"+this.numDraws+":"+this.getCSS(el,"z-index")) //console.log(el.nodeName+":"+zindex+":"+this.getCSS(el,"position")+":"+this.numDraws+":"+this.getCSS(el,"z-index"))
@ -678,9 +697,29 @@ html2canvas.prototype.newElement = function(el,parentStack){
var stack = { var stack = {
ctx: new this.storageContext(), ctx: new this.storageContext(),
zIndex: zindex, zIndex: zindex,
opacity: opacity*parentStack.opacity opacity: opacity*parentStack.opacity,
cssPosition: cssPosition
}; };
// TODO correct overflow for absolute content residing under a static position
if (parentStack.clip){
stack.clip = $.extend({}, parentStack.clip);
//stack.clip = parentStack.clip;
stack.clip.height = stack.clip.height - parentStack.borders[2].width;
}
if (this.opts.useOverflow && /(hidden|scroll|auto)/.test(this.getCSS(el,"overflow")) && !/(BODY)/i.test(el.nodeName)){
if (stack.clip){
stack.clip = this.clipBounds(stack.clip,bounds);
}else{
stack.clip = bounds;
}
}
var stackLength = this.contextStacks.push(stack); var stackLength = this.contextStacks.push(stack);
var ctx = this.contextStacks[stackLength-1].ctx; var ctx = this.contextStacks[stackLength-1].ctx;
@ -688,9 +727,17 @@ html2canvas.prototype.newElement = function(el,parentStack){
this.setContextVariable(ctx,"globalAlpha",stack.opacity); this.setContextVariable(ctx,"globalAlpha",stack.opacity);
// draw element borders // draw element borders
var borders = this.drawBorders(el, ctx, bounds.left, bounds.top, bounds.width, bounds.height); var borders = this.drawBorders(el, ctx, bounds);
stack.borders = borders;
// let's modify clip area for child elements, so borders dont get overwritten
/*
if (stack.clip){
stack.clip.width = stack.clip.width-(borders[1].width);
stack.clip.height = stack.clip.height-(borders[2].width);
}
*/
if (this.ignoreRe.test(el.nodeName) && this.opts.iframeDefault != "transparent"){ if (this.ignoreRe.test(el.nodeName) && this.opts.iframeDefault != "transparent"){
if (this.opts.iframeDefault=="default"){ if (this.opts.iframeDefault=="default"){
bgcolor = "#efefef"; bgcolor = "#efefef";
@ -703,27 +750,40 @@ html2canvas.prototype.newElement = function(el,parentStack){
} }
// draw base element bgcolor // draw base element bgcolor
this.newRect(
ctx,
x+borders[3].width,
y+borders[0].width,
w-(borders[1].width+borders[3].width),
h-(borders[0].width+borders[2].width),
bgcolor
);
this.drawBackground(el,{ var bgbounds = {
left: x+borders[3].width, left: x+borders[3].width,
top: y+borders[0].width, top: y+borders[0].width,
width: w-(borders[1].width+borders[3].width), width: w-(borders[1].width+borders[3].width),
height: h-(borders[0].width+borders[2].width) height: h-(borders[0].width+borders[2].width)
};
//if (this.withinBounds(stack.clip,bgbounds)){
if (stack.clip){
bgbounds = this.clipBounds(bgbounds,stack.clip);
//}
}
if (bgbounds.height>0 && bgbounds.width>0){
this.newRect(
ctx,
bgbounds.left,
bgbounds.top,
bgbounds.width,
bgbounds.height,
bgcolor
);
this.drawBackground(el,bgbounds,ctx);
} }
,ctx);
if (el.nodeName=="IMG"){ if (el.nodeName=="IMG"){
image = _.loadImage(_.getAttr(el,'src')); image = _.loadImage(_.getAttr(el,'src'));
if (image){ if (image){
// console.log(image.width); // console.log(image.width);
this.drawImage( this.drawImage(
ctx, ctx,
image, image,
@ -975,7 +1035,7 @@ html2canvas.prototype.Renderer = function(queue){
}); });
// this.log('No renderer chosen, rendering quit'); // this.log('No renderer chosen, rendering quit');
return this; return this;
// this.canvasRenderer(queue); // this.canvasRenderer(queue);
@ -1008,6 +1068,186 @@ html2canvas.prototype.throttler = function(queue){
}; };
html2canvas.prototype.canvasRenderContext = function(storageContext,ctx){
// set common settings for canvas
ctx.textBaseline = "bottom";
var _ = this;
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){
_.each(storageContext.ctx.storage,function(a,renderItem){
switch(renderItem.type){
case "variable":
ctx[renderItem.name] = renderItem.arguments;
break;
case "function":
if (renderItem.name=="fillRect"){
ctx.fillRect(
renderItem.arguments[0]-(storageContext.canvasPosition.x || 0),
renderItem.arguments[1]-(storageContext.canvasPosition.y || 0),
renderItem.arguments[2],
renderItem.arguments[3]
);
}else if(renderItem.name=="fillText"){
// console.log(renderItem.arguments[0]);
ctx.fillText(renderItem.arguments[0],renderItem.arguments[1]-(storageContext.canvasPosition.x || 0),renderItem.arguments[2]-(storageContext.canvasPosition.y || 0));
/*
if (renderItem.arguments[0]=="Highlights"){
console.log(renderItem);
console.log(storageContext);
$('body').append(ctx.canvas);
}*/
}else if(renderItem.name=="drawImage"){
// console.log(renderItem);
// console.log(renderItem.arguments[0].width);
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]-(storageContext.canvasPosition.x || 0),
renderItem.arguments[6]-(storageContext.canvasPosition.y || 0),
renderItem.arguments[7],
renderItem.arguments[8]
);
}
}else{
_.log(renderItem);
}
break;
default:
}
});
}
if (storageContext.clip){
ctx.restore();
}
}
/*
html2canvas.prototype.canvasRenderContextChildren = function(storageContext,parentctx){
var ctx = storageContext.canvas.getContext('2d');
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,ctx);
for (var s = 0; s<this.queue.length;){
if (storageContext.parentStack && this.queue[s].canvas === storageContext.parentStack.canvas){
var substorageContext = this.queue.splice(s,1)[0];
if (substorageContext.ctx.storage[5] && substorageContext.ctx.storage[5].arguments[0]=="Highlights"){
console.log('ssssssadasda');
}
this.canvasRenderContextChildren(substorageContext,ctx);
// this.canvasRenderContext(substorageContext,ctx);
// this.canvasRenderStorage(this.queue,ctx);
}else{
s++;
}
}
if (storageContext.ctx.storage[5] && storageContext.ctx.storage[5].arguments[0]=="Highlights"){
$('body').append(parentctx.canvas);
}
//var parentctx = this.canvas.getContext("2d");
if (storageContext.canvas.width>0 && storageContext.canvas.height > 0){
parentctx.drawImage(storageContext.canvas,(storageContext.canvasPosition.x || 0),(storageContext.canvasPosition.y || 0));
}
}
*/
html2canvas.prototype.canvasRenderStorage = function(queue,parentctx){
for (var i = 0; i<queue.length;){
var storageContext = queue.splice(0,1)[0];
// storageContext.removeOverflow = storageContext.removeOverflow || {};
/*
if (storageContext.canvas){
this.canvasRenderContextChildren(storageContext,parentctx);
var ctx = storageContext.canvas.getContext('2d');
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,ctx);
for (var s = 0; s<this.queue.length;){
if (this.queue[s].canvas === storageContext.canvas){
var substorageContext = this.queue.splice(s,1)[0];
substorageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(substorageContext,ctx);
// this.canvasRenderStorage(this.queue,ctx);
}else{
s++;
}
}
//var parentctx = this.canvas.getContext("2d");
if (storageContext.canvas.width>0 && storageContext.canvas.height > 0){
parentctx.drawImage(storageContext.canvas,(storageContext.canvasPosition.x || 0),(storageContext.canvasPosition.y || 0));
}
ctx = parentctx;
}else{
*/
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,parentctx);
// }
/*
if (storageContext.newCanvas){
var ctx = _.canvas.getContext("2d");
ctx.drawImage(canvas,(storageContext.removeOverflow.left || 0),(storageContext.removeOverflow.top || 0));
_.ctx = ctx;
}*/
}
}
html2canvas.prototype.canvasRenderer = function(queue){ html2canvas.prototype.canvasRenderer = function(queue){
var _ = this; var _ = this;
@ -1023,59 +1263,10 @@ html2canvas.prototype.canvasRenderer = function(queue){
this.ctx = this.canvas.getContext("2d"); this.ctx = this.canvas.getContext("2d");
// set common settings for canvas this.canvasRenderStorage(queue,this.ctx);
this.ctx.textBaseline = "bottom";
this.each(queue,function(i,storageContext){
if (storageContext.ctx.storage){
_.each(storageContext.ctx.storage,function(a,renderItem){
switch(renderItem.type){
case "variable":
_.ctx[renderItem.name] = renderItem.arguments;
break;
case "function":
if (renderItem.name=="fillRect"){
_.ctx.fillRect(renderItem.arguments[0],renderItem.arguments[1],renderItem.arguments[2],renderItem.arguments[3]);
}else if(renderItem.name=="fillText"){
_.ctx.fillText(renderItem.arguments[0],renderItem.arguments[1],renderItem.arguments[2]);
}else if(renderItem.name=="drawImage"){
// console.log(renderItem);
// console.log(renderItem.arguments[0].width);
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]
);
}
}else{
this.log(renderItem);
}
break;
default:
}
});
}
});
}; };
/* /*
@ -1145,8 +1336,8 @@ html2canvas.prototype.setContextVariable = function(ctx,variable,value){
} }
html2canvas.prototype.newText = function(el,textNode,ctx){ html2canvas.prototype.newText = function(el,textNode,stack){
var ctx = stack.ctx;
var family = this.getCSS(el,"font-family"); var family = this.getCSS(el,"font-family");
var size = this.getCSS(el,"font-size"); var size = this.getCSS(el,"font-size");
var color = this.getCSS(el,"color"); var color = this.getCSS(el,"color");
@ -1171,7 +1362,7 @@ html2canvas.prototype.newText = function(el,textNode,ctx){
case 401: case 401:
bold = "bold"; bold = "bold";
break; break;
case 400: case 400:
bold = "normal"; bold = "normal";
break; break;
} }
@ -1191,20 +1382,29 @@ html2canvas.prototype.newText = function(el,textNode,ctx){
if (this.opts.letterRendering == false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ if (this.opts.letterRendering == false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){
// this.setContextVariable(ctx,"textAlign",text_align); // this.setContextVariable(ctx,"textAlign",text_align);
renderWords = true; renderWords = true;
renderList = textNode.nodeValue.split(/(\b| )/); renderList = textNode.nodeValue.split(/(\b| )/);
}else{ }else{
// this.setContextVariable(ctx,"textAlign","left"); // this.setContextVariable(ctx,"textAlign","left");
renderList = textNode.nodeValue.split(""); renderList = textNode.nodeValue.split("");
} }
this.setContextVariable(ctx,"fillStyle",color); this.setContextVariable(ctx,"fillStyle",color);
this.setContextVariable(ctx,"font",font); this.setContextVariable(ctx,"font",font);
/*
if (stack.clip){
ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height);
ctx.clip();
}
*/
@ -1214,56 +1414,62 @@ html2canvas.prototype.newText = function(el,textNode,ctx){
// TODO only do the splitting for non-range prints // TODO only do the splitting for non-range prints
var newTextNode = oldTextNode.splitText(renderList[c].length); var newTextNode = oldTextNode.splitText(renderList[c].length);
if (text_decoration!="none" || this.trim(oldTextNode.nodeValue).length != 0){
if (this.support.rangeBounds){
// getBoundingClientRect is supported for ranges
if (document.createRange){
var range = document.createRange(); if (this.support.rangeBounds){
range.selectNode(oldTextNode); // getBoundingClientRect is supported for ranges
if (document.createRange){
var range = document.createRange();
range.selectNode(oldTextNode);
}else{
// TODO add IE support
var range = document.body.createTextRange();
}
if (range.getBoundingClientRect()){
var bounds = range.getBoundingClientRect();
}else{
var bounds = {};
}
}else{ }else{
// TODO add IE support // it isn't supported, so let's wrap it inside an element instead and the bounds there
var range = document.body.createTextRange();
var parent = oldTextNode.parentNode;
var wrapElement = document.createElement('wrapper');
var backupText = oldTextNode.cloneNode(true);
wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement,oldTextNode);
var bounds = this.getBounds(wrapElement);
parent.replaceChild(backupText,wrapElement);
} }
if (range.getBoundingClientRect()){
var bounds = range.getBoundingClientRect();
}else{
var bounds = {};
// console.log(range);
// console.log("'"+oldTextNode.nodeValue+"'"+bounds.left)
this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx);
switch(text_decoration) {
case "underline":
// Draws a line at the baseline of the font
// TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
this.newRect(ctx,bounds.left,Math.round(bounds.top+metrics.baseline+metrics.lineWidth),bounds.width,1,color);
break;
case "overline":
this.newRect(ctx,bounds.left,bounds.top,bounds.width,1,color);
break;
case "line-through":
// TODO try and find exact position for line-through
this.newRect(ctx,bounds.left,Math.ceil(bounds.top+metrics.middle+metrics.lineWidth),bounds.width,1,color);
break;
} }
}else{
// it isn't supported, so let's wrap it inside an element instead and the bounds there
var parent = oldTextNode.parentNode;
var wrapElement = document.createElement('wrapper');
var backupText = oldTextNode.cloneNode(true);
wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement,oldTextNode);
var bounds = this.getBounds(wrapElement);
parent.replaceChild(backupText,wrapElement);
}
this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx);
switch(text_decoration) {
case "underline":
// Draws a line at the baseline of the font
// TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
this.newRect(ctx,bounds.left,Math.round(bounds.top+metrics.baseline+metrics.lineWidth),bounds.width,1,color);
break;
case "overline":
this.newRect(ctx,bounds.left,bounds.top,bounds.width,1,color);
break;
case "line-through":
// TODO try and find exact position for line-through
this.newRect(ctx,bounds.left,Math.ceil(bounds.top+metrics.middle+metrics.lineWidth),bounds.width,1,color);
break;
} }
@ -1305,6 +1511,8 @@ html2canvas.prototype.fontMetrics = function(font,fontSize){
var img = document.createElement('img'); var img = document.createElement('img');
// TODO add another image
img.src = "http://html2canvas.hertzen.com/images/8.jpg"; img.src = "http://html2canvas.hertzen.com/images/8.jpg";
img.width = 1; img.width = 1;
img.height = 1; img.height = 1;
@ -1411,49 +1619,27 @@ html2canvas.prototype.parsing = function(el,stack){
var _ = this; var _ = this;
//if (!this.blockElements.test(el.nodeName)){
stack = this.newElement(el,stack) || stack; stack = this.newElement(el,stack) || stack;
var ctx = stack.ctx; var ctx = stack.ctx;
if (!this.ignoreRe.test(el.nodeName)){ if (!this.ignoreRe.test(el.nodeName)){
// TODO remove jQuery dependancy // TODO remove jQuery dependancy
this.each(this.contentsInZ(el),function(cid,node){
var contents = this.contentsInZ(el); if (node.nodeType==1){
// element
_.parsing(node,stack);
if (contents.length == 1){ }else if (node.nodeType==3){
_.newText(el,node,stack);
// check nodeType
if (contents[0].nodeType==1){
// it's an element, so let's look inside it
this.parsing(contents[0],stack);
}else if (contents[0].nodeType==3){
// it's a text node, so lets print the text
this.newText(el,contents[0],stack.ctx);
} }
}else{
this.each(contents,function(cid,cel){ });
if (cel.nodeType==1){
// element
_.parsing(cel,stack);
}else if (cel.nodeType==3){
_.newText(el,cel,ctx);
}
});
}
} }
} }
// } // }
} }
@ -1468,6 +1654,35 @@ html2canvas.prototype.log = function(a){
} }
} }
html2canvas.prototype.withinBounds = function(src,dst){
if (!src) return true;
// return true;
return (
(src.left <= dst.left || dst.left+dst.width < src.left) &&
(src.top <= dst.top || dst.top+dst.height < src.top)
);
}
html2canvas.prototype.clipBounds = function(src,dst){
var x = Math.max(src.left,dst.left);
var y = Math.max(src.top,dst.top);
var x2 = Math.min((src.left+src.width),(dst.left+dst.width));
var y2 = Math.min((src.top+src.height),(dst.top+dst.height));
return {
left:x,
top:y,
width:x2-x,
height:y2-y
};
}
/** /**
* Function to provide bounds for element * Function to provide bounds for element
@ -1478,9 +1693,17 @@ html2canvas.prototype.getBounds = function(el){
window.scroll(0,0); window.scroll(0,0);
if (el.getBoundingClientRect){ if (el.getBoundingClientRect){
var bounds = el.getBoundingClientRect(); var clientRect = el.getBoundingClientRect();
bounds.top = bounds.top; var bounds = {};
bounds.left = bounds.left; // need to create new object, as clientrect bounds can't be modified, thanks pufuwozu
// TODO add scroll position to bounds, so no scrolling of window necessary
bounds.top = clientRect.top;
bounds.left = clientRect.left;
bounds.width = clientRect.width;
bounds.height = clientRect.height;
return bounds; return bounds;
}else{ }else{
@ -1622,6 +1845,6 @@ html2canvas.prototype.isSameOrigin = function(url){
var link = document.createElement("a"); var link = document.createElement("a");
link.href = url; link.href = url;
return ((link.protocol + link.hostname) == this.pageOrigin); return ((link.protocol + link.host) == this.pageOrigin);
} }

View File

@ -1,49 +1,50 @@
/* /*
* html2canvas v0.25 <http://html2canvas.hertzen.com> * html2canvas v0.26 <http://html2canvas.hertzen.com>
* Copyright (c) 2011 Niklas von Hertzen. All rights reserved. * Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
* http://www.twitter.com/niklasvh * http://www.twitter.com/niklasvh
* *
* Released under MIT License * Released under MIT License
*/ */
function html2canvas(a,b){this.opts=this.extendObj(b||{},{logging:!1,ready:function(a){document.body.appendChild(a.canvas)},storageReady:function(a){a.Renderer(a.contextStacks)},iframeDefault:"default",flashCanvasPath:"http://html2canvas.hertzen.com/external/flashcanvas/flashcanvas.js",renderViewport:!1,reorderZ:!0,throttle:!0,letterRendering:!1,proxyUrl:null,logger:function(a){window.console&&window.console.log?window.console.log(a):alert(a)},canvasWidth:0,canvasHeight:0,renderOrder:"canvas flash html"}); function html2canvas(a,b){this.opts=this.extendObj(b||{},{logging:!1,ready:function(a){document.body.appendChild(a.canvas)},storageReady:function(a){a.Renderer(a.contextStacks)},iframeDefault:"default",flashCanvasPath:"http://html2canvas.hertzen.com/external/flashcanvas/flashcanvas.js",renderViewport:!1,reorderZ:!0,throttle:!0,letterRendering:!1,proxyUrl:null,logger:function(a){window.console&&window.console.log?window.console.log(a):alert(a)},canvasWidth:0,canvasHeight:0,useOverflow:!0,renderOrder:"canvas flash html"});
this.element=a;this.imagesLoaded=0;this.images=[];this.fontData=[];this.numDraws=0;this.contextStacks=[];this.ignoreElements="IFRAME|OBJECT|PARAM";this.needReorder=!1;this.blockElements=/(BR|PARAM)/;this.pageOrigin=window.location.protocol+window.location.hostname;this.ignoreRe=RegExp("("+this.ignoreElements+")");this.support={rangeBounds:!1};if(document.createRange){var d=document.createRange();if(d.getBoundingClientRect){var c=document.createElement("boundtest");c.style.height="123px";c.style.display= this.element=a;this.imagesLoaded=0;this.images=[];this.fontData=[];this.numDraws=0;this.contextStacks=[];this.ignoreElements="IFRAME|OBJECT|PARAM";this.needReorder=!1;this.blockElements=/(BR|PARAM)/;this.pageOrigin=window.location.protocol+window.location.host;this.ignoreRe=RegExp("("+this.ignoreElements+")");this.support={rangeBounds:!1};if(document.createRange){var c=document.createRange();if(c.getBoundingClientRect){var d=document.createElement("boundtest");d.style.height="123px";d.style.display=
"block";document.getElementsByTagName("body")[0].appendChild(c);d.selectNode(c);if(d.getBoundingClientRect().height==123)this.support.rangeBounds=!0;document.getElementsByTagName("body")[0].removeChild(c)}}this.init();return this} "block";document.getElementsByTagName("body")[0].appendChild(d);c.selectNode(d);if(c.getBoundingClientRect().height==123)this.support.rangeBounds=!0;document.getElementsByTagName("body")[0].removeChild(d)}}this.init();return this}
html2canvas.prototype.init=function(){var a=this;this.log("Finding background-images");this.images.push("start");this.getImages(this.element);this.log("Finding images");this.each(this.element.ownerDocument.images,function(b,d){a.preloadImage(a.getAttr(d,"src"))});this.images.splice(0,1);this.images.length==0&&this.start()}; html2canvas.prototype.init=function(){var a=this;this.log("Finding background-images");this.images.push("start");this.getImages(this.element);this.log("Finding images");this.each(this.element.ownerDocument.images,function(b,c){a.preloadImage(a.getAttr(c,"src"))});this.images.splice(0,1);this.images.length==0&&this.start()};
html2canvas.prototype.start=function(){if(this.images.length==0||this.imagesLoaded==this.images.length/2){this.log("Finished loading "+this.imagesLoaded+" images, Started parsing");this.bodyOverflow=document.getElementsByTagName("body")[0].style.overflow;document.getElementsByTagName("body")[0].style.overflow="hidden";var a=new this.storageContext($(document).width(),$(document).height());a.opacity=this.getCSS(this.element,"opacity");this.parseElement(this.element,this.newElement(this.element,a))}}; html2canvas.prototype.start=function(){if(this.images.length==0||this.imagesLoaded==this.images.length/2){this.log("Finished loading "+this.imagesLoaded+" images, Started parsing");this.bodyOverflow=document.getElementsByTagName("body")[0].style.overflow;document.getElementsByTagName("body")[0].style.overflow="hidden";var a=new this.storageContext($(document).width(),$(document).height());a.opacity=this.getCSS(this.element,"opacity");this.parseElement(this.element,this.newElement(this.element,a))}};
html2canvas.prototype.stackingContext=function(a){this.canvas=document.createElement("canvas");this.canvas.width=a;this.canvas.height=a;if(this.canvas.getContext)this.ctx=this.canvas.getContext("2d");this.ctx.textBaseline="bottom";return this.ctx}; html2canvas.prototype.stackingContext=function(a){this.canvas=document.createElement("canvas");this.canvas.width=a;this.canvas.height=a;if(this.canvas.getContext)this.ctx=this.canvas.getContext("2d");this.ctx.textBaseline="bottom";return this.ctx};
html2canvas.prototype.storageContext=function(a,b){this.storage=[];this.width=a;this.height=b;this.fillRect=function(a,b,e,f){this.storage.push({type:"function",name:"fillRect",arguments:[a,b,e,f]})};this.drawImage=function(a,b,e,f,g,j,h,k,i){this.storage.push({type:"function",name:"drawImage",arguments:[a,b,e,f,g,j,h,k,i]})};this.fillText=function(a,b,e){this.storage.push({type:"function",name:"fillText",arguments:[a,b,e]})};return this}; html2canvas.prototype.storageContext=function(a,b){this.storage=[];this.width=a;this.height=b;this.fillRect=function(a,b,e,f){this.storage.push({type:"function",name:"fillRect",arguments:[a,b,e,f]})};this.drawImage=function(a,b,e,f,h,i,g,j,k){this.storage.push({type:"function",name:"drawImage",arguments:[a,b,e,f,h,i,g,j,k]})};this.fillText=function(a,b,e){this.storage.push({type:"function",name:"fillText",arguments:[a,b,e]})};return this};
html2canvas.prototype.finish=function(){this.log("Finished rendering");document.getElementsByTagName("body")[0].style.overflow=this.bodyOverflow;this.opts.ready(this)}; html2canvas.prototype.finish=function(){this.log("Finished rendering");document.getElementsByTagName("body")[0].style.overflow=this.bodyOverflow;this.opts.ready(this)};
html2canvas.prototype.drawBackground=function(a,b,d){var c=this.getCSS(a,"background-image"),e=this.getCSS(a,"background-repeat");if(typeof c!="undefined"&&/^(1|none)$/.test(c)==!1&&/^(-webkit|-moz|linear-gradient|-o-)/.test(c)==!1){var c=this.backgroundImageUrl(c),f=this.loadImage(c),a=this.getBackgroundPosition(a,b,f);if(f)switch(e){case "repeat-x":this.drawbackgroundRepeatX(d,f,a,b.left,b.top,b.width,b.height);break;case "repeat-y":this.drawbackgroundRepeatY(d,f,a,b.left,b.top,b.width,b.height); html2canvas.prototype.drawBackground=function(a,b,c){var d=this.getCSS(a,"background-image"),e=this.getCSS(a,"background-repeat");if(typeof d!="undefined"&&/^(1|none)$/.test(d)==!1&&/^(-webkit|-moz|linear-gradient|-o-)/.test(d)==!1){var d=this.backgroundImageUrl(d),f=this.loadImage(d),a=this.getBackgroundPosition(a,b,f);if(f)switch(e){case "repeat-x":this.drawbackgroundRepeatX(c,f,a,b.left,b.top,b.width,b.height);break;case "repeat-y":this.drawbackgroundRepeatY(c,f,a,b.left,b.top,b.width,b.height);
break;case "no-repeat":var c=b.width-a.left,e=b.height-a.top,g=a.left,j=a.top,h=a.left+b.left,k=a.top+b.top;g<0?(g=Math.abs(g),h+=g,c=Math.min(b.width,f.width-g)):(c=Math.min(c,f.width),g=0);j<0?(j=Math.abs(j),k+=j,e=Math.min(b.height,f.height-j)):(e=Math.min(e,f.height),j=0);if(e>0&&c>0){this.drawImage(d,f,g,j,c,e,h,k,c,e);break}default:a.top-=Math.ceil(a.top/f.height)*f.height;for(c=b.top+a.top;c<b.height+b.top;)e=Math.min(f.height,b.height+b.top-c),e=Math.floor(c+f.height)>e+c?e+c-c:f.height,c< break;case "no-repeat":var d=b.width-a.left,e=b.height-a.top,h=a.left,i=a.top,g=a.left+b.left,j=a.top+b.top;h<0?(h=Math.abs(h),g+=h,d=Math.min(b.width,f.width-h)):(d=Math.min(d,f.width),h=0);i<0?(i=Math.abs(i),j+=i,e=Math.min(b.height,f.height-i)):(e=Math.min(e,f.height),i=0);if(e>0&&d>0){this.drawImage(c,f,h,i,d,e,g,j,d,e);break}default:a.top-=Math.ceil(a.top/f.height)*f.height;for(d=b.top+a.top;d<b.height+b.top;)e=Math.min(f.height,b.height+b.top-d),e=Math.floor(d+f.height)>e+d?e+d-d:f.height,d<
b.top?(g=b.top-c,c=b.top):g=0,this.drawbackgroundRepeatX(d,f,a,b.left,c,b.width,e),g>0&&(a.top+=g),c=Math.floor(c+f.height)-g}else this.log("Error loading background:"+c)}};html2canvas.prototype.backgroundImageUrl=function(a){a.substr(0,5)=='url("'?(a=a.substr(5),a=a.substr(0,a.length-2)):(a=a.substr(4),a=a.substr(0,a.length-1));return a}; b.top?(h=b.top-d,d=b.top):h=0,this.drawbackgroundRepeatX(c,f,a,b.left,d,b.width,e),h>0&&(a.top+=h),d=Math.floor(d+f.height)-h}else this.log("Error loading background:"+d)}};html2canvas.prototype.backgroundImageUrl=function(a){a.substr(0,5)=='url("'?(a=a.substr(5),a=a.substr(0,a.length-2)):(a=a.substr(4),a=a.substr(0,a.length-1));return a};
html2canvas.prototype.getBackgroundPosition=function(a,b,d){var c=(this.getCSS(a,"backgroundPosition")||"0 0").split(" "),e;c.length==1&&(a=c,c=[],c[0]=a,c[1]=a);c[0].toString().indexOf("%")!=-1?(e=parseFloat(c[0])/100,a=b.width*e-d.width*e):a=parseInt(c[0],10);c[1].toString().indexOf("%")!=-1?(e=parseFloat(c[1])/100,b=b.height*e-d.height*e):b=parseInt(c[1],10);d={};d.top=b;d.left=a;return d}; html2canvas.prototype.getBackgroundPosition=function(a,b,c){var d=(this.getCSS(a,"backgroundPosition")||"0 0").split(" "),e;d.length==1&&(a=d,d=[],d[0]=a,d[1]=a);d[0].toString().indexOf("%")!=-1?(e=parseFloat(d[0])/100,a=b.width*e-c.width*e):a=parseInt(d[0],10);d[1].toString().indexOf("%")!=-1?(e=parseFloat(d[1])/100,b=b.height*e-c.height*e):b=parseInt(d[1],10);c={};c.top=b;c.left=a;return c};
html2canvas.prototype.drawbackgroundRepeatY=function(a,b,d,c,e,f,g){var j=Math.min(b.width,f),h;d.top-=Math.ceil(d.top/b.height)*b.height;for(h=e+d.top;h<g+e;)f=Math.floor(h+b.height)>g+e?g+e-h:b.height,this.drawBackgroundRepeat(a,b,c+d.left,h,j,f,c,e),h=Math.floor(h+b.height)}; html2canvas.prototype.drawbackgroundRepeatY=function(a,b,c,d,e,f,h){var i=Math.min(b.width,f),g;c.top-=Math.ceil(c.top/b.height)*b.height;for(g=e+c.top;g<h+e;)f=Math.floor(g+b.height)>h+e?h+e-g:b.height,this.drawBackgroundRepeat(a,b,d+c.left,g,i,f,d,e),g=Math.floor(g+b.height)};
html2canvas.prototype.drawbackgroundRepeatX=function(a,b,d,c,e,f,g){var g=Math.min(b.height,g),j,h;d.left-=Math.ceil(d.left/b.width)*b.width;for(h=c+d.left;h<f+c;)j=Math.floor(h+b.width)>f+c?f+c-h:b.width,this.drawBackgroundRepeat(a,b,h,e+d.top,j,g,c,e),h=Math.floor(h+b.width)};html2canvas.prototype.drawBackgroundRepeat=function(a,b,d,c,e,f,g,j){var h=0,k=0;g-d>0&&(h=g-d);j-c>0&&(k=j-c);this.drawImage(a,b,h,k,e-h,f-k,d+h,c+k,e-h,f-k)}; html2canvas.prototype.drawbackgroundRepeatX=function(a,b,c,d,e,f,h){var h=Math.min(b.height,h),i,g;c.left-=Math.ceil(c.left/b.width)*b.width;for(g=d+c.left;g<f+d;)i=Math.floor(g+b.width)>f+d?f+d-g:b.width,this.drawBackgroundRepeat(a,b,g,e+c.top,i,h,d,e),g=Math.floor(g+b.width)};html2canvas.prototype.drawBackgroundRepeat=function(a,b,c,d,e,f,h,i){var g=0,j=0;h-c>0&&(g=h-c);i-d>0&&(j=i-d);this.drawImage(a,b,g,j,e-g,f-j,c+g,d+j,e-g,f-j)};
html2canvas.prototype.getBorderData=function(a){var b=[],d=this;this.each(["top","right","bottom","left"],function(c,e){b.push({width:parseInt(d.getCSS(a,"border-"+e+"-width"),10),color:d.getCSS(a,"border-"+e+"-color")})});return b}; html2canvas.prototype.getBorderData=function(a){var b=[],c=this;this.each(["top","right","bottom","left"],function(d,e){b.push({width:parseInt(c.getCSS(a,"border-"+e+"-width"),10),color:c.getCSS(a,"border-"+e+"-color")})});return b};
html2canvas.prototype.drawBorders=function(a,b,d,c,e,f){var g=this.getBorderData(a),j=this;this.each(g,function(a,k){if(k.width>0){var i=d,l=c,m=e,n=f-g[2].width;switch(a){case 0:n=g[0].width;break;case 1:i=d+e-g[1].width;m=g[1].width;break;case 2:l=l+f-g[2].width;n=g[2].width;break;case 3:m=g[3].width}j.newRect(b,i,l,m,n,k.color)}});return g}; html2canvas.prototype.drawBorders=function(a,b,c,d){var e=c.left,f=c.top,h=c.width,i=c.height,g=this.getBorderData(a),j=this;this.each(g,function(a,c){if(c.width>0){var n=e,o=f,m=h,p=i-g[2].width;switch(a){case 0:p=g[0].width;break;case 1:n=e+h-g[1].width;m=g[1].width;break;case 2:o=o+i-g[2].width;p=g[2].width;break;case 3:m=g[3].width}m={left:n,top:o,width:m,height:p};d&&(m=j.clipBounds(m,d));m.width>0&&m.height>0&&j.newRect(b,n,o,m.width,m.height,c.color)}});return g};
html2canvas.prototype.newElement=function(a,b){var d=this.getBounds(a),c=d.left,e=d.top,f=d.width,g=d.height,j=this.getCSS(a,"background-color"),b=b||{},h=this.formatZ(this.getCSS(a,"zIndex"),this.getCSS(a,"position"),b.zIndex,a.parentNode),k=this.getCSS(a,"opacity"),i={ctx:new this.storageContext,zIndex:h,opacity:k*b.opacity},h=this.contextStacks.push(i),k=this.contextStacks[h-1].ctx;this.setContextVariable(k,"globalAlpha",i.opacity);i=this.drawBorders(a,k,d.left,d.top,d.width,d.height);this.ignoreRe.test(a.nodeName)&& html2canvas.prototype.newElement=function(a,b){var c=this.getBounds(a),d=c.left,e=c.top,f=c.width,h=c.height,i;i=this.getCSS(a,"background-color");var g=this.getCSS(a,"position"),b=b||{},j=this.formatZ(this.getCSS(a,"zIndex"),g,b.zIndex,a.parentNode),k=this.getCSS(a,"opacity"),l={ctx:new this.storageContext,zIndex:j,opacity:k*b.opacity,cssPosition:g};if(b.clip)l.clip=$.extend({},b.clip),l.clip.height-=b.borders[2].width;if(this.opts.useOverflow&&/(hidden|scroll|auto)/.test(this.getCSS(a,"overflow"))&&
this.opts.iframeDefault!="transparent"&&(j=this.opts.iframeDefault=="default"?"#efefef":this.opts.iframeDefault);this.newRect(k,c+i[3].width,e+i[0].width,f-(i[1].width+i[3].width),g-(i[0].width+i[2].width),j);this.drawBackground(a,{left:c+i[3].width,top:e+i[0].width,width:f-(i[1].width+i[3].width),height:g-(i[0].width+i[2].width)},k);a.nodeName=="IMG"&&((f=this.loadImage(this.getAttr(a,"src")))?this.drawImage(k,f,0,0,f.width,f.height,c+parseInt(this.getCSS(a,"padding-left"),10)+i[3].width,e+parseInt(this.getCSS(a, !/(BODY)/i.test(a.nodeName))l.clip=l.clip?this.clipBounds(l.clip,c):c;g=this.contextStacks.push(l);j=this.contextStacks[g-1].ctx;this.setContextVariable(j,"globalAlpha",l.opacity);k=this.drawBorders(a,j,c);l.borders=k;this.ignoreRe.test(a.nodeName)&&this.opts.iframeDefault!="transparent"&&(i=this.opts.iframeDefault=="default"?"#efefef":this.opts.iframeDefault);f={left:d+k[3].width,top:e+k[0].width,width:f-(k[1].width+k[3].width),height:h-(k[0].width+k[2].width)};l.clip&&(f=this.clipBounds(f,l.clip));
"padding-top"),10)+i[0].width,d.width-(i[1].width+i[3].width+parseInt(this.getCSS(a,"padding-left"),10)+parseInt(this.getCSS(a,"padding-right"),10)),d.height-(i[0].width+i[2].width+parseInt(this.getCSS(a,"padding-top"),10)+parseInt(this.getCSS(a,"padding-bottom"),10))):this.log("Error loading <img>:"+this.getAttr(a,"src")));return this.contextStacks[h-1]};html2canvas.prototype.printText=function(a,b,d,c){this.trim(a).length>0&&(c.fillText(a,b,d),this.numDraws++)}; f.height>0&&f.width>0&&(this.newRect(j,f.left,f.top,f.width,f.height,i),this.drawBackground(a,f,j));a.nodeName=="IMG"&&((i=this.loadImage(this.getAttr(a,"src")))?this.drawImage(j,i,0,0,i.width,i.height,d+parseInt(this.getCSS(a,"padding-left"),10)+k[3].width,e+parseInt(this.getCSS(a,"padding-top"),10)+k[0].width,c.width-(k[1].width+k[3].width+parseInt(this.getCSS(a,"padding-left"),10)+parseInt(this.getCSS(a,"padding-right"),10)),c.height-(k[0].width+k[2].width+parseInt(this.getCSS(a,"padding-top"),
html2canvas.prototype.newRect=function(a,b,d,c,e,f){f!="transparent"&&(this.setContextVariable(a,"fillStyle",f),a.fillRect(b,d,c,e),this.numDraws++)};html2canvas.prototype.drawImage=function(a,b,d,c,e,f,g,j,h,k){a.drawImage(b,d,c,e,f,g,j,h,k);this.numDraws++}; 10)+parseInt(this.getCSS(a,"padding-bottom"),10))):this.log("Error loading <img>:"+this.getAttr(a,"src")));return this.contextStacks[g-1]};html2canvas.prototype.printText=function(a,b,c,d){this.trim(a).length>0&&(d.fillText(a,b,c),this.numDraws++)};html2canvas.prototype.newRect=function(a,b,c,d,e,f){f!="transparent"&&(this.setContextVariable(a,"fillStyle",f),a.fillRect(b,c,d,e),this.numDraws++)};html2canvas.prototype.drawImage=function(a,b,c,d,e,f,h,i,g,j){a.drawImage(b,c,d,e,f,h,i,g,j);this.numDraws++};
html2canvas.prototype.getImages=function(a){var b=this;this.ignoreRe.test(a.nodeName)||this.each($(a).contents(),function(a,c){RegExp("("+this.ignoreElements+")").test(c.nodeName)||b.getImages(c)});if(a.nodeType==1||typeof a.nodeType=="undefined")(a=this.getCSS(a,"background-image"))&&a!="1"&&a!="none"&&a.substring(0,7)!="-webkit"&&a.substring(0,3)!="-o-"&&a.substring(0,4)!="-moz"&&this.preloadImage(this.backgroundImageUrl(a))}; html2canvas.prototype.getImages=function(a){var b=this;this.ignoreRe.test(a.nodeName)||this.each($(a).contents(),function(a,d){RegExp("("+this.ignoreElements+")").test(d.nodeName)||b.getImages(d)});if(a.nodeType==1||typeof a.nodeType=="undefined")(a=this.getCSS(a,"background-image"))&&a!="1"&&a!="none"&&a.substring(0,7)!="-webkit"&&a.substring(0,3)!="-o-"&&a.substring(0,4)!="-moz"&&this.preloadImage(this.backgroundImageUrl(a))};
html2canvas.prototype.loadImage=function(a){a=this.getIndex(this.images,a);return a!=-1?this.images[a+1]:!1};html2canvas.prototype.preloadImage=function(a){if(this.getIndex(this.images,a)==-1)if(this.isSameOrigin(a)){this.images.push(a);var b=new Image,d=this;$(b).load(function(){d.imagesLoaded++;d.start()});b.onerror=function(){d.images.splice(d.images.indexOf(b.src),2);d.start()};b.src=a;this.images.push(b)}else this.opts.proxyUrl&&(this.images.push(a),b=new Image,this.proxyGetImage(a,b),this.images.push(b))}; html2canvas.prototype.loadImage=function(a){a=this.getIndex(this.images,a);return a!=-1?this.images[a+1]:!1};html2canvas.prototype.preloadImage=function(a){if(this.getIndex(this.images,a)==-1)if(this.isSameOrigin(a)){this.images.push(a);var b=new Image,c=this;$(b).load(function(){c.imagesLoaded++;c.start()});b.onerror=function(){c.images.splice(c.images.indexOf(b.src),2);c.start()};b.src=a;this.images.push(b)}else this.opts.proxyUrl&&(this.images.push(a),b=new Image,this.proxyGetImage(a,b),this.images.push(b))};
html2canvas.prototype.proxyGetImage=function(a,b){var d=this,c=document.createElement("a");c.href=a;a=c.href;$.ajax({data:{xhr2:!1,url:a},url:this.opts.proxyUrl,dataType:"jsonp",success:function(c){c.substring(0,6)=="error:"?(d.images.splice(d.images.indexOf(a),2),d.start(),d.log("Proxy was unable to load "+a+" "+c)):(b.onload=function(){d.imagesLoaded++;d.start()},b.src=c)},error:function(){d.images.splice(d.images.indexOf(a),2);d.start()}})}; html2canvas.prototype.proxyGetImage=function(a,b){var c=this,d=document.createElement("a");d.href=a;a=d.href;$.ajax({data:{xhr2:!1,url:a},url:this.opts.proxyUrl,dataType:"jsonp",success:function(d){d.substring(0,6)=="error:"?(c.images.splice(c.images.indexOf(a),2),c.start(),c.log("Proxy was unable to load "+a+" "+d)):(b.onload=function(){c.imagesLoaded++;c.start()},b.src=d)},error:function(){c.images.splice(c.images.indexOf(a),2);c.start()}})};
html2canvas.prototype.Renderer=function(a){var b=this;this.log("Renderer initiated");this.each(this.opts.renderOrder.split(" "),function(d,c){switch(c){case "canvas":b.canvas=document.createElement("canvas");if(b.canvas.getContext)return b.canvasRenderer(a),b.log("Using canvas renderer"),!1;break;case "html":return b.log("Using HTML renderer"),!1}});return this};html2canvas.prototype.throttler=function(){}; html2canvas.prototype.Renderer=function(a){var b=this;this.log("Renderer initiated");this.each(this.opts.renderOrder.split(" "),function(c,d){switch(d){case "canvas":b.canvas=document.createElement("canvas");if(b.canvas.getContext)return b.canvasRenderer(a),b.log("Using canvas renderer"),!1;break;case "html":return b.log("Using HTML renderer"),!1}});return this};html2canvas.prototype.throttler=function(){};
html2canvas.prototype.canvasRenderer=function(a){var b=this,a=this.sortQueue(a);this.canvas.width=Math.max($(document).width(),this.opts.canvasWidth);this.canvas.height=Math.max($(document).height(),this.opts.canvasHeight);this.ctx=this.canvas.getContext("2d");this.ctx.textBaseline="bottom";this.each(a,function(a,c){c.ctx.storage&&b.each(c.ctx.storage,function(a,c){switch(c.type){case "variable":b.ctx[c.name]=c.arguments;break;case "function":c.name=="fillRect"?b.ctx.fillRect(c.arguments[0],c.arguments[1], html2canvas.prototype.canvasRenderContext=function(a,b){b.textBaseline="bottom";var c=this;a.clip&&(b.save(),b.beginPath(),b.rect(a.clip.left,a.clip.top,a.clip.width,a.clip.height),b.clip());a.ctx.storage&&c.each(a.ctx.storage,function(d,e){switch(e.type){case "variable":b[e.name]=e.arguments;break;case "function":e.name=="fillRect"?b.fillRect(e.arguments[0]-(a.canvasPosition.x||0),e.arguments[1]-(a.canvasPosition.y||0),e.arguments[2],e.arguments[3]):e.name=="fillText"?b.fillText(e.arguments[0],e.arguments[1]-
c.arguments[2],c.arguments[3]):c.name=="fillText"?b.ctx.fillText(c.arguments[0],c.arguments[1],c.arguments[2]):c.name=="drawImage"?c.arguments[8]>0&&c.arguments[7]&&b.ctx.drawImage(c.arguments[0],c.arguments[1],c.arguments[2],c.arguments[3],c.arguments[4],c.arguments[5],c.arguments[6],c.arguments[7],c.arguments[8]):this.log(c)}})})}; (a.canvasPosition.x||0),e.arguments[2]-(a.canvasPosition.y||0)):e.name=="drawImage"?e.arguments[8]>0&&e.arguments[7]&&b.drawImage(e.arguments[0],e.arguments[1],e.arguments[2],e.arguments[3],e.arguments[4],e.arguments[5]-(a.canvasPosition.x||0),e.arguments[6]-(a.canvasPosition.y||0),e.arguments[7],e.arguments[8]):c.log(e)}});a.clip&&b.restore()};
html2canvas.prototype.sortQueue=function(a){if(!this.opts.reorderZ||!this.needReorder)return a;var b=0;this.each(a,function(a,d){if(b<d.zIndex.length)b=d.zIndex.length});var d=0;this.each(a,function(c,e){for(var f=a.length.toString().length-d.toString().length;b>e.zIndex.length;)e.zIndex+="0";for(e.zIndex+=d;b+f+d.toString().length>e.zIndex.length;)e.zIndex+="0";d++});return a=a.sort(function(a,b){return a.zIndex<b.zIndex?-1:a.zIndex>b.zIndex?1:0})}; html2canvas.prototype.canvasRenderStorage=function(a,b){for(;0<a.length;){var c=a.splice(0,1)[0];c.canvasPosition=c.canvasPosition||{};this.canvasRenderContext(c,b)}};html2canvas.prototype.canvasRenderer=function(a){a=this.sortQueue(a);this.canvas.width=Math.max($(document).width(),this.opts.canvasWidth);this.canvas.height=Math.max($(document).height(),this.opts.canvasHeight);this.ctx=this.canvas.getContext("2d");this.canvasRenderStorage(a,this.ctx)};
html2canvas.prototype.setContextVariable=function(a,b,d){a.storage?a.storage.push({type:"variable",name:b,arguments:d}):a[b]=d}; html2canvas.prototype.sortQueue=function(a){if(!this.opts.reorderZ||!this.needReorder)return a;var b=0;this.each(a,function(a,c){if(b<c.zIndex.length)b=c.zIndex.length});var c=0;this.each(a,function(d,e){for(var f=a.length.toString().length-c.toString().length;b>e.zIndex.length;)e.zIndex+="0";for(e.zIndex+=c;b+f+c.toString().length>e.zIndex.length;)e.zIndex+="0";c++});return a=a.sort(function(a,b){return a.zIndex<b.zIndex?-1:a.zIndex>b.zIndex?1:0})};
html2canvas.prototype.newText=function(a,b,d){var c=this.getCSS(a,"font-family"),e=this.getCSS(a,"font-size"),f=this.getCSS(a,"color"),g=this.getCSS(a,"font-weight"),j=this.getCSS(a,"font-style"),h=this.getCSS(a,"font-variant"),k=this.getCSS(a,"text-decoration"),i=this.getCSS(a,"text-align"),l=this.getCSS(a,"letter-spacing");b.nodeValue=this.textTransform(b.nodeValue,this.getCSS(a,"text-transform"));if(this.trim(b.nodeValue).length>0){switch(g){case 401:g="bold";break;case 400:g="normal"}if(k!="none")var m= html2canvas.prototype.setContextVariable=function(a,b,c){a.storage?a.storage.push({type:"variable",name:b,arguments:c}):a[b]=c};
this.fontMetrics(c,e);a=h+" "+g+" "+j+" "+e+" "+c;i=i.replace(["-webkit-auto"],["auto"]);i=this.opts.letterRendering==!1&&/^(left|right|justify|auto)$/.test(i)&&/^(normal|none)$/.test(l)?b.nodeValue.split(/(\b| )/):b.nodeValue.split("");this.setContextVariable(d,"fillStyle",f);this.setContextVariable(d,"font",a);for(l=0;l<i.length;l++){a=b.splitText(i[l].length);this.support.rangeBounds?(document.createRange?(c=document.createRange(),c.selectNode(b)):c=document.body.createTextRange(),c=c.getBoundingClientRect()? html2canvas.prototype.newText=function(a,b,c){var c=c.ctx,d=this.getCSS(a,"font-family"),e=this.getCSS(a,"font-size"),f=this.getCSS(a,"color"),h=this.getCSS(a,"font-weight"),i=this.getCSS(a,"font-style"),g=this.getCSS(a,"font-variant"),j=this.getCSS(a,"text-decoration"),k=this.getCSS(a,"text-align"),l=this.getCSS(a,"letter-spacing");b.nodeValue=this.textTransform(b.nodeValue,this.getCSS(a,"text-transform"));if(this.trim(b.nodeValue).length>0){switch(h){case 401:h="bold";break;case 400:h="normal"}if(j!=
c.getBoundingClientRect():{}):(e=b.parentNode,g=document.createElement("wrapper"),j=b.cloneNode(!0),g.appendChild(b.cloneNode(!0)),e.replaceChild(g,b),c=this.getBounds(g),e.replaceChild(j,g));this.printText(b.nodeValue,c.left,c.bottom,d);switch(k){case "underline":this.newRect(d,c.left,Math.round(c.top+m.baseline+m.lineWidth),c.width,1,f);break;case "overline":this.newRect(d,c.left,c.top,c.width,1,f);break;case "line-through":this.newRect(d,c.left,Math.ceil(c.top+m.middle+m.lineWidth),c.width,1,f)}b= "none")var n=this.fontMetrics(d,e);d=g+" "+h+" "+i+" "+e+" "+d;k=k.replace(["-webkit-auto"],["auto"]);a=this.opts.letterRendering==!1&&/^(left|right|justify|auto)$/.test(k)&&/^(normal|none)$/.test(l)?b.nodeValue.split(/(\b| )/):b.nodeValue.split("");this.setContextVariable(c,"fillStyle",f);this.setContextVariable(c,"font",d);for(k=0;k<a.length;k++){l=b.splitText(a[k].length);if(j!="none"||this.trim(b.nodeValue).length!=0)switch(this.support.rangeBounds?(document.createRange?(d=document.createRange(),
a}}}; d.selectNode(b)):d=document.body.createTextRange(),d=d.getBoundingClientRect()?d.getBoundingClientRect():{}):(e=b.parentNode,h=document.createElement("wrapper"),i=b.cloneNode(!0),h.appendChild(b.cloneNode(!0)),e.replaceChild(h,b),d=this.getBounds(h),e.replaceChild(i,h)),this.printText(b.nodeValue,d.left,d.bottom,c),j){case "underline":this.newRect(c,d.left,Math.round(d.top+n.baseline+n.lineWidth),d.width,1,f);break;case "overline":this.newRect(c,d.left,d.top,d.width,1,f);break;case "line-through":this.newRect(c,
html2canvas.prototype.fontMetrics=function(a,b){var d=this.fontData.indexOf(a+"-"+b);if(d>-1)return this.fontData[d+1];d=document.createElement("div");document.getElementsByTagName("body")[0].appendChild(d);$(d).css({visibility:"hidden",fontFamily:a,fontSize:b,margin:0,padding:0});var c=document.createElement("img");c.src="http://html2canvas.hertzen.com/images/8.jpg";c.width=1;c.height=1;$(c).css({margin:0,padding:0});var e=document.createElement("span");$(e).css({fontFamily:a,fontSize:b,margin:0,padding:0}); d.left,Math.ceil(d.top+n.middle+n.lineWidth),d.width,1,f)}b=l}}};
e.appendChild(document.createTextNode("Hidden Text"));d.appendChild(e);d.appendChild(c);var f=c.offsetTop-e.offsetTop+1;d.removeChild(e);d.appendChild(document.createTextNode("Hidden Text"));$(d).css("line-height","normal");$(c).css("vertical-align","super");c={baseline:f,lineWidth:1,middle:c.offsetTop-d.offsetTop+1};this.fontData.push(a+"-"+b);this.fontData.push(c);$(d).remove();return c}; html2canvas.prototype.fontMetrics=function(a,b){var c=this.fontData.indexOf(a+"-"+b);if(c>-1)return this.fontData[c+1];c=document.createElement("div");document.getElementsByTagName("body")[0].appendChild(c);$(c).css({visibility:"hidden",fontFamily:a,fontSize:b,margin:0,padding:0});var d=document.createElement("img");d.src="http://html2canvas.hertzen.com/images/8.jpg";d.width=1;d.height=1;$(d).css({margin:0,padding:0});var e=document.createElement("span");$(e).css({fontFamily:a,fontSize:b,margin:0,
padding:0});e.appendChild(document.createTextNode("Hidden Text"));c.appendChild(e);c.appendChild(d);var f=d.offsetTop-e.offsetTop+1;c.removeChild(e);c.appendChild(document.createTextNode("Hidden Text"));$(c).css("line-height","normal");$(d).css("vertical-align","super");d={baseline:f,lineWidth:1,middle:d.offsetTop-c.offsetTop+1};this.fontData.push(a+"-"+b);this.fontData.push(d);$(c).remove();return d};
html2canvas.prototype.textTransform=function(a,b){switch(b){case "lowercase":return a.toLowerCase();case "capitalize":return a.replace(/(^|\s|:|-|\(|\))([a-z])/g,function(a,b,e){return b+e.toUpperCase()});case "uppercase":return a.toUpperCase();default:return a}};html2canvas.prototype.trim=function(a){return a.replace(/^\s*/,"").replace(/\s*$/,"")}; html2canvas.prototype.textTransform=function(a,b){switch(b){case "lowercase":return a.toLowerCase();case "capitalize":return a.replace(/(^|\s|:|-|\(|\))([a-z])/g,function(a,b,e){return b+e.toUpperCase()});case "uppercase":return a.toUpperCase();default:return a}};html2canvas.prototype.trim=function(a){return a.replace(/^\s*/,"").replace(/\s*$/,"")};
html2canvas.prototype.parseElement=function(a,b){var d=this;this.each(a.children,function(a,e){d.parsing(e,b)});this.log("Render queue stored");this.opts.storageReady(this);this.finish()}; html2canvas.prototype.parseElement=function(a,b){var c=this;this.each(a.children,function(a,e){c.parsing(e,b)});this.log("Render queue stored");this.opts.storageReady(this);this.finish()};html2canvas.prototype.parsing=function(a,b){if(this.getCSS(a,"display")!="none"&&this.getCSS(a,"visibility")!="hidden"){var c=this,b=this.newElement(a,b)||b;this.ignoreRe.test(a.nodeName)||this.each(this.contentsInZ(a),function(d,e){e.nodeType==1?c.parsing(e,b):e.nodeType==3&&c.newText(a,e,b)})}};
html2canvas.prototype.parsing=function(a,b){if(this.getCSS(a,"display")!="none"&&this.getCSS(a,"visibility")!="hidden"){var d=this,b=this.newElement(a,b)||b,c=b.ctx;if(!this.ignoreRe.test(a.nodeName)){var e=this.contentsInZ(a);e.length==1?e[0].nodeType==1?this.parsing(e[0],b):e[0].nodeType==3&&this.newText(a,e[0],b.ctx):this.each(e,function(e,g){g.nodeType==1?d.parsing(g,b):g.nodeType==3&&d.newText(a,g,c)})}}};html2canvas.prototype.log=function(a){this.opts.logging&&this.opts.logger(a)}; html2canvas.prototype.log=function(a){this.opts.logging&&this.opts.logger(a)};html2canvas.prototype.withinBounds=function(a,b){return!a?!0:(a.left<=b.left||b.left+b.width<a.left)&&(a.top<=b.top||b.top+b.height<a.top)};html2canvas.prototype.clipBounds=function(a,b){var c=Math.max(a.left,b.left),d=Math.max(a.top,b.top);return{left:c,top:d,width:Math.min(a.left+a.width,b.left+b.width)-c,height:Math.min(a.top+a.height,b.top+b.height)-d}};
html2canvas.prototype.getBounds=function(a){window.scroll(0,0);if(a.getBoundingClientRect)return a=a.getBoundingClientRect(),a.top=a.top,a.left=a.left,a;else{var b=$(a).offset();return{left:b.left+parseInt(this.getCSS(a,"border-left-width"),10),top:b.top+parseInt(this.getCSS(a,"border-top-width"),10),width:$(a).innerWidth(),height:$(a).innerHeight()}}};html2canvas.prototype.each=function(a,b){for(var b=b||function(){},d=0;d<a.length;d++)if(b(d,a[d])===!1)break};html2canvas.prototype.contentsInZ=function(a){return $(a).contents()}; html2canvas.prototype.getBounds=function(a){window.scroll(0,0);if(a.getBoundingClientRect){var a=a.getBoundingClientRect(),b={};b.top=a.top;b.left=a.left;b.width=a.width;b.height=a.height;return b}else return b=$(a).offset(),{left:b.left+parseInt(this.getCSS(a,"border-left-width"),10),top:b.top+parseInt(this.getCSS(a,"border-top-width"),10),width:$(a).innerWidth(),height:$(a).innerHeight()}};html2canvas.prototype.each=function(a,b){for(var b=b||function(){},c=0;c<a.length;c++)if(b(c,a[c])===!1)break};
html2canvas.prototype.getAttr=function(a,b){return a.getAttribute(b)};html2canvas.prototype.extendObj=function(a,b){for(var d in a)b[d]=a[d];return b};html2canvas.prototype.leadingZero=function(a,b){var d="000000000"+a;return d.substr(d.length-b)}; html2canvas.prototype.contentsInZ=function(a){return $(a).contents()};html2canvas.prototype.getAttr=function(a,b){return a.getAttribute(b)};html2canvas.prototype.extendObj=function(a,b){for(var c in a)b[c]=a[c];return b};html2canvas.prototype.leadingZero=function(a,b){var c="000000000"+a;return c.substr(c.length-b)};
html2canvas.prototype.formatZ=function(a,b,d,c){d||(d="0");if(b!="static"&&d.charAt(0)=="0")this.needReorder=!0,d="1"+d.slice(1);if(a=="auto")if(a=this.getCSS(c,"position"),a!="static"&&typeof a!="undefined")a=0;else return d;b=this.leadingZero(this.numDraws,9);a=this.leadingZero(a+1,9);return d+""+a+""+b};html2canvas.prototype.getContents=function(a){return a.nodeName=="iframe"?a.contentDocument||a.contentWindow.document:a.childNodes};html2canvas.prototype.getCSS=function(a,b){return $(a).css(b)}; html2canvas.prototype.formatZ=function(a,b,c,d){c||(c="0");if(b!="static"&&c.charAt(0)=="0")this.needReorder=!0,c="1"+c.slice(1);if(a=="auto")if(a=this.getCSS(d,"position"),a!="static"&&typeof a!="undefined")a=0;else return c;b=this.leadingZero(this.numDraws,9);a=this.leadingZero(a+1,9);return c+""+a+""+b};html2canvas.prototype.getContents=function(a){return a.nodeName=="iframe"?a.contentDocument||a.contentWindow.document:a.childNodes};html2canvas.prototype.getCSS=function(a,b){return $(a).css(b)};
html2canvas.prototype.getIndex=function(a,b){if(a.indexOf)return a.indexOf(b);else{for(var d=0;d<a.length;d++)if(this[d]==b)return d;return-1}};html2canvas.prototype.isSameOrigin=function(a){var b=document.createElement("a");b.href=a;return b.protocol+b.hostname==this.pageOrigin}; html2canvas.prototype.getIndex=function(a,b){if(a.indexOf)return a.indexOf(b);else{for(var c=0;c<a.length;c++)if(this[c]==b)return c;return-1}};html2canvas.prototype.isSameOrigin=function(a){var b=document.createElement("a");b.href=a;return b.protocol+b.host==this.pageOrigin};

View File

@ -17,7 +17,13 @@ html2canvas.prototype.getBorderData = function(el){
} }
html2canvas.prototype.drawBorders = function(el,ctx, x, y, w, h){ html2canvas.prototype.drawBorders = function(el,ctx, bounds,clip){
var x = bounds.left;
var y = bounds.top;
var w = bounds.width;
var h = bounds.height;
/* /*
* TODO add support for different border-style's than solid * TODO add support for different border-style's than solid
@ -52,7 +58,21 @@ html2canvas.prototype.drawBorders = function(el,ctx, x, y, w, h){
break; break;
} }
_.newRect(ctx,bx,by,bw,bh,borderData.color); var borderBounds = {
left:bx,
top:by,
width: bw,
height:bh
};
if (clip){
borderBounds = _.clipBounds(borderBounds,clip);
}
if (borderBounds.width>0 && borderBounds.height>0){
_.newRect(ctx,bx,by,borderBounds.width,borderBounds.height,borderData.color);
}

View File

@ -32,6 +32,7 @@ function html2canvas(el, userOptions) {
}, },
canvasWidth:0, canvasWidth:0,
canvasHeight:0, canvasHeight:0,
useOverflow: true,
renderOrder: "canvas flash html" renderOrder: "canvas flash html"
}); });
@ -51,7 +52,7 @@ function html2canvas(el, userOptions) {
this.ignoreElements = "IFRAME|OBJECT|PARAM"; this.ignoreElements = "IFRAME|OBJECT|PARAM";
this.needReorder = false; this.needReorder = false;
this.blockElements = new RegExp("(BR|PARAM)"); this.blockElements = new RegExp("(BR|PARAM)");
this.pageOrigin = window.location.protocol + window.location.hostname; this.pageOrigin = window.location.protocol + window.location.host;
this.ignoreRe = new RegExp("("+this.ignoreElements+")"); this.ignoreRe = new RegExp("("+this.ignoreElements+")");
@ -145,8 +146,8 @@ html2canvas.prototype.start = function(){
if (this.images.length == 0 || this.imagesLoaded==this.images.length/2){ if (this.images.length == 0 || this.imagesLoaded==this.images.length/2){
this.log('Finished loading '+this.imagesLoaded+' images, Started parsing'); this.log('Finished loading '+this.imagesLoaded+' images, Started parsing');
this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow; this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow;
document.getElementsByTagName('body')[0].style.overflow = "hidden"; document.getElementsByTagName('body')[0].style.overflow = "hidden";
var rootStack = new this.storageContext($(document).width(),$(document).height()); var rootStack = new this.storageContext($(document).width(),$(document).height());
rootStack.opacity = this.getCSS(this.element,"opacity"); rootStack.opacity = this.getCSS(this.element,"opacity");
var stack = this.newElement(this.element,rootStack); var stack = this.newElement(this.element,rootStack);
@ -209,6 +210,8 @@ html2canvas.prototype.storageContext = function(width,height){
}; };
this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){ this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){
this.storage.push( this.storage.push(
{ {

View File

@ -1,7 +1,3 @@
html2canvas.prototype.newElement = function(el,parentStack){ html2canvas.prototype.newElement = function(el,parentStack){
var bounds = this.getBounds(el); var bounds = this.getBounds(el);
@ -14,10 +10,10 @@ html2canvas.prototype.newElement = function(el,parentStack){
image; image;
var bgcolor = this.getCSS(el,"background-color"); var bgcolor = this.getCSS(el,"background-color");
var cssPosition = this.getCSS(el,"position");
parentStack = parentStack || {}; parentStack = parentStack || {};
var zindex = this.formatZ(this.getCSS(el,"zIndex"),this.getCSS(el,"position"),parentStack.zIndex,el.parentNode); var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode);
//console.log(el.nodeName+":"+zindex+":"+this.getCSS(el,"position")+":"+this.numDraws+":"+this.getCSS(el,"z-index")) //console.log(el.nodeName+":"+zindex+":"+this.getCSS(el,"position")+":"+this.numDraws+":"+this.getCSS(el,"z-index"))
@ -27,9 +23,29 @@ html2canvas.prototype.newElement = function(el,parentStack){
var stack = { var stack = {
ctx: new this.storageContext(), ctx: new this.storageContext(),
zIndex: zindex, zIndex: zindex,
opacity: opacity*parentStack.opacity opacity: opacity*parentStack.opacity,
cssPosition: cssPosition
}; };
// TODO correct overflow for absolute content residing under a static position
if (parentStack.clip){
stack.clip = $.extend({}, parentStack.clip);
//stack.clip = parentStack.clip;
stack.clip.height = stack.clip.height - parentStack.borders[2].width;
}
if (this.opts.useOverflow && /(hidden|scroll|auto)/.test(this.getCSS(el,"overflow")) && !/(BODY)/i.test(el.nodeName)){
if (stack.clip){
stack.clip = this.clipBounds(stack.clip,bounds);
}else{
stack.clip = bounds;
}
}
var stackLength = this.contextStacks.push(stack); var stackLength = this.contextStacks.push(stack);
var ctx = this.contextStacks[stackLength-1].ctx; var ctx = this.contextStacks[stackLength-1].ctx;
@ -37,9 +53,17 @@ html2canvas.prototype.newElement = function(el,parentStack){
this.setContextVariable(ctx,"globalAlpha",stack.opacity); this.setContextVariable(ctx,"globalAlpha",stack.opacity);
// draw element borders // draw element borders
var borders = this.drawBorders(el, ctx, bounds.left, bounds.top, bounds.width, bounds.height); var borders = this.drawBorders(el, ctx, bounds);
stack.borders = borders;
// let's modify clip area for child elements, so borders dont get overwritten
/*
if (stack.clip){
stack.clip.width = stack.clip.width-(borders[1].width);
stack.clip.height = stack.clip.height-(borders[2].width);
}
*/
if (this.ignoreRe.test(el.nodeName) && this.opts.iframeDefault != "transparent"){ if (this.ignoreRe.test(el.nodeName) && this.opts.iframeDefault != "transparent"){
if (this.opts.iframeDefault=="default"){ if (this.opts.iframeDefault=="default"){
bgcolor = "#efefef"; bgcolor = "#efefef";
@ -52,27 +76,40 @@ html2canvas.prototype.newElement = function(el,parentStack){
} }
// draw base element bgcolor // draw base element bgcolor
this.newRect(
ctx,
x+borders[3].width,
y+borders[0].width,
w-(borders[1].width+borders[3].width),
h-(borders[0].width+borders[2].width),
bgcolor
);
this.drawBackground(el,{ var bgbounds = {
left: x+borders[3].width, left: x+borders[3].width,
top: y+borders[0].width, top: y+borders[0].width,
width: w-(borders[1].width+borders[3].width), width: w-(borders[1].width+borders[3].width),
height: h-(borders[0].width+borders[2].width) height: h-(borders[0].width+borders[2].width)
};
//if (this.withinBounds(stack.clip,bgbounds)){
if (stack.clip){
bgbounds = this.clipBounds(bgbounds,stack.clip);
//}
}
if (bgbounds.height>0 && bgbounds.width>0){
this.newRect(
ctx,
bgbounds.left,
bgbounds.top,
bgbounds.width,
bgbounds.height,
bgcolor
);
this.drawBackground(el,bgbounds,ctx);
} }
,ctx);
if (el.nodeName=="IMG"){ if (el.nodeName=="IMG"){
image = _.loadImage(_.getAttr(el,'src')); image = _.loadImage(_.getAttr(el,'src'));
if (image){ if (image){
// console.log(image.width); // console.log(image.width);
this.drawImage( this.drawImage(
ctx, ctx,
image, image,

View File

@ -51,7 +51,7 @@ html2canvas.prototype.Renderer = function(queue){
}); });
// this.log('No renderer chosen, rendering quit'); // this.log('No renderer chosen, rendering quit');
return this; return this;
// this.canvasRenderer(queue); // this.canvasRenderer(queue);
@ -84,6 +84,186 @@ html2canvas.prototype.throttler = function(queue){
}; };
html2canvas.prototype.canvasRenderContext = function(storageContext,ctx){
// set common settings for canvas
ctx.textBaseline = "bottom";
var _ = this;
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){
_.each(storageContext.ctx.storage,function(a,renderItem){
switch(renderItem.type){
case "variable":
ctx[renderItem.name] = renderItem.arguments;
break;
case "function":
if (renderItem.name=="fillRect"){
ctx.fillRect(
renderItem.arguments[0]-(storageContext.canvasPosition.x || 0),
renderItem.arguments[1]-(storageContext.canvasPosition.y || 0),
renderItem.arguments[2],
renderItem.arguments[3]
);
}else if(renderItem.name=="fillText"){
// console.log(renderItem.arguments[0]);
ctx.fillText(renderItem.arguments[0],renderItem.arguments[1]-(storageContext.canvasPosition.x || 0),renderItem.arguments[2]-(storageContext.canvasPosition.y || 0));
/*
if (renderItem.arguments[0]=="Highlights"){
console.log(renderItem);
console.log(storageContext);
$('body').append(ctx.canvas);
}*/
}else if(renderItem.name=="drawImage"){
// console.log(renderItem);
// console.log(renderItem.arguments[0].width);
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]-(storageContext.canvasPosition.x || 0),
renderItem.arguments[6]-(storageContext.canvasPosition.y || 0),
renderItem.arguments[7],
renderItem.arguments[8]
);
}
}else{
_.log(renderItem);
}
break;
default:
}
});
}
if (storageContext.clip){
ctx.restore();
}
}
/*
html2canvas.prototype.canvasRenderContextChildren = function(storageContext,parentctx){
var ctx = storageContext.canvas.getContext('2d');
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,ctx);
for (var s = 0; s<this.queue.length;){
if (storageContext.parentStack && this.queue[s].canvas === storageContext.parentStack.canvas){
var substorageContext = this.queue.splice(s,1)[0];
if (substorageContext.ctx.storage[5] && substorageContext.ctx.storage[5].arguments[0]=="Highlights"){
console.log('ssssssadasda');
}
this.canvasRenderContextChildren(substorageContext,ctx);
// this.canvasRenderContext(substorageContext,ctx);
// this.canvasRenderStorage(this.queue,ctx);
}else{
s++;
}
}
if (storageContext.ctx.storage[5] && storageContext.ctx.storage[5].arguments[0]=="Highlights"){
$('body').append(parentctx.canvas);
}
//var parentctx = this.canvas.getContext("2d");
if (storageContext.canvas.width>0 && storageContext.canvas.height > 0){
parentctx.drawImage(storageContext.canvas,(storageContext.canvasPosition.x || 0),(storageContext.canvasPosition.y || 0));
}
}
*/
html2canvas.prototype.canvasRenderStorage = function(queue,parentctx){
for (var i = 0; i<queue.length;){
var storageContext = queue.splice(0,1)[0];
// storageContext.removeOverflow = storageContext.removeOverflow || {};
/*
if (storageContext.canvas){
this.canvasRenderContextChildren(storageContext,parentctx);
var ctx = storageContext.canvas.getContext('2d');
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,ctx);
for (var s = 0; s<this.queue.length;){
if (this.queue[s].canvas === storageContext.canvas){
var substorageContext = this.queue.splice(s,1)[0];
substorageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(substorageContext,ctx);
// this.canvasRenderStorage(this.queue,ctx);
}else{
s++;
}
}
//var parentctx = this.canvas.getContext("2d");
if (storageContext.canvas.width>0 && storageContext.canvas.height > 0){
parentctx.drawImage(storageContext.canvas,(storageContext.canvasPosition.x || 0),(storageContext.canvasPosition.y || 0));
}
ctx = parentctx;
}else{
*/
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,parentctx);
// }
/*
if (storageContext.newCanvas){
var ctx = _.canvas.getContext("2d");
ctx.drawImage(canvas,(storageContext.removeOverflow.left || 0),(storageContext.removeOverflow.top || 0));
_.ctx = ctx;
}*/
}
}
html2canvas.prototype.canvasRenderer = function(queue){ html2canvas.prototype.canvasRenderer = function(queue){
var _ = this; var _ = this;
@ -99,59 +279,10 @@ html2canvas.prototype.canvasRenderer = function(queue){
this.ctx = this.canvas.getContext("2d"); this.ctx = this.canvas.getContext("2d");
// set common settings for canvas this.canvasRenderStorage(queue,this.ctx);
this.ctx.textBaseline = "bottom";
this.each(queue,function(i,storageContext){
if (storageContext.ctx.storage){
_.each(storageContext.ctx.storage,function(a,renderItem){
switch(renderItem.type){
case "variable":
_.ctx[renderItem.name] = renderItem.arguments;
break;
case "function":
if (renderItem.name=="fillRect"){
_.ctx.fillRect(renderItem.arguments[0],renderItem.arguments[1],renderItem.arguments[2],renderItem.arguments[3]);
}else if(renderItem.name=="fillText"){
_.ctx.fillText(renderItem.arguments[0],renderItem.arguments[1],renderItem.arguments[2]);
}else if(renderItem.name=="drawImage"){
// console.log(renderItem);
// console.log(renderItem.arguments[0].width);
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]
);
}
}else{
this.log(renderItem);
}
break;
default:
}
});
}
});
}; };
/* /*

View File

@ -1,6 +1,6 @@
html2canvas.prototype.newText = function(el,textNode,ctx){ html2canvas.prototype.newText = function(el,textNode,stack){
var ctx = stack.ctx;
var family = this.getCSS(el,"font-family"); var family = this.getCSS(el,"font-family");
var size = this.getCSS(el,"font-size"); var size = this.getCSS(el,"font-size");
var color = this.getCSS(el,"color"); var color = this.getCSS(el,"color");
@ -25,7 +25,7 @@ html2canvas.prototype.newText = function(el,textNode,ctx){
case 401: case 401:
bold = "bold"; bold = "bold";
break; break;
case 400: case 400:
bold = "normal"; bold = "normal";
break; break;
} }
@ -45,20 +45,29 @@ html2canvas.prototype.newText = function(el,textNode,ctx){
if (this.opts.letterRendering == false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ if (this.opts.letterRendering == false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){
// this.setContextVariable(ctx,"textAlign",text_align); // this.setContextVariable(ctx,"textAlign",text_align);
renderWords = true; renderWords = true;
renderList = textNode.nodeValue.split(/(\b| )/); renderList = textNode.nodeValue.split(/(\b| )/);
}else{ }else{
// this.setContextVariable(ctx,"textAlign","left"); // this.setContextVariable(ctx,"textAlign","left");
renderList = textNode.nodeValue.split(""); renderList = textNode.nodeValue.split("");
} }
this.setContextVariable(ctx,"fillStyle",color); this.setContextVariable(ctx,"fillStyle",color);
this.setContextVariable(ctx,"font",font); this.setContextVariable(ctx,"font",font);
/*
if (stack.clip){
ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height);
ctx.clip();
}
*/
@ -68,56 +77,62 @@ html2canvas.prototype.newText = function(el,textNode,ctx){
// TODO only do the splitting for non-range prints // TODO only do the splitting for non-range prints
var newTextNode = oldTextNode.splitText(renderList[c].length); var newTextNode = oldTextNode.splitText(renderList[c].length);
if (text_decoration!="none" || this.trim(oldTextNode.nodeValue).length != 0){
if (this.support.rangeBounds){
// getBoundingClientRect is supported for ranges
if (document.createRange){
var range = document.createRange(); if (this.support.rangeBounds){
range.selectNode(oldTextNode); // getBoundingClientRect is supported for ranges
if (document.createRange){
var range = document.createRange();
range.selectNode(oldTextNode);
}else{
// TODO add IE support
var range = document.body.createTextRange();
}
if (range.getBoundingClientRect()){
var bounds = range.getBoundingClientRect();
}else{
var bounds = {};
}
}else{ }else{
// TODO add IE support // it isn't supported, so let's wrap it inside an element instead and the bounds there
var range = document.body.createTextRange();
var parent = oldTextNode.parentNode;
var wrapElement = document.createElement('wrapper');
var backupText = oldTextNode.cloneNode(true);
wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement,oldTextNode);
var bounds = this.getBounds(wrapElement);
parent.replaceChild(backupText,wrapElement);
} }
if (range.getBoundingClientRect()){
var bounds = range.getBoundingClientRect();
}else{
var bounds = {};
// console.log(range);
// console.log("'"+oldTextNode.nodeValue+"'"+bounds.left)
this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx);
switch(text_decoration) {
case "underline":
// Draws a line at the baseline of the font
// TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
this.newRect(ctx,bounds.left,Math.round(bounds.top+metrics.baseline+metrics.lineWidth),bounds.width,1,color);
break;
case "overline":
this.newRect(ctx,bounds.left,bounds.top,bounds.width,1,color);
break;
case "line-through":
// TODO try and find exact position for line-through
this.newRect(ctx,bounds.left,Math.ceil(bounds.top+metrics.middle+metrics.lineWidth),bounds.width,1,color);
break;
} }
}else{
// it isn't supported, so let's wrap it inside an element instead and the bounds there
var parent = oldTextNode.parentNode;
var wrapElement = document.createElement('wrapper');
var backupText = oldTextNode.cloneNode(true);
wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement,oldTextNode);
var bounds = this.getBounds(wrapElement);
parent.replaceChild(backupText,wrapElement);
}
this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx);
switch(text_decoration) {
case "underline":
// Draws a line at the baseline of the font
// TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
this.newRect(ctx,bounds.left,Math.round(bounds.top+metrics.baseline+metrics.lineWidth),bounds.width,1,color);
break;
case "overline":
this.newRect(ctx,bounds.left,bounds.top,bounds.width,1,color);
break;
case "line-through":
// TODO try and find exact position for line-through
this.newRect(ctx,bounds.left,Math.ceil(bounds.top+metrics.middle+metrics.lineWidth),bounds.width,1,color);
break;
} }
@ -159,6 +174,8 @@ html2canvas.prototype.fontMetrics = function(font,fontSize){
var img = document.createElement('img'); var img = document.createElement('img');
// TODO add another image
img.src = "http://html2canvas.hertzen.com/images/8.jpg"; img.src = "http://html2canvas.hertzen.com/images/8.jpg";
img.width = 1; img.width = 1;
img.height = 1; img.height = 1;

View File

@ -19,49 +19,27 @@ html2canvas.prototype.parsing = function(el,stack){
var _ = this; var _ = this;
//if (!this.blockElements.test(el.nodeName)){
stack = this.newElement(el,stack) || stack; stack = this.newElement(el,stack) || stack;
var ctx = stack.ctx; var ctx = stack.ctx;
if (!this.ignoreRe.test(el.nodeName)){ if (!this.ignoreRe.test(el.nodeName)){
// TODO remove jQuery dependancy // TODO remove jQuery dependancy
this.each(this.contentsInZ(el),function(cid,node){
var contents = this.contentsInZ(el); if (node.nodeType==1){
// element
_.parsing(node,stack);
if (contents.length == 1){ }else if (node.nodeType==3){
_.newText(el,node,stack);
// check nodeType
if (contents[0].nodeType==1){
// it's an element, so let's look inside it
this.parsing(contents[0],stack);
}else if (contents[0].nodeType==3){
// it's a text node, so lets print the text
this.newText(el,contents[0],stack.ctx);
} }
}else{
this.each(contents,function(cid,cel){ });
if (cel.nodeType==1){
// element
_.parsing(cel,stack);
}else if (cel.nodeType==3){
_.newText(el,cel,ctx);
}
});
}
} }
} }
// } // }
} }

View File

@ -8,6 +8,35 @@ html2canvas.prototype.log = function(a){
} }
} }
html2canvas.prototype.withinBounds = function(src,dst){
if (!src) return true;
// return true;
return (
(src.left <= dst.left || dst.left+dst.width < src.left) &&
(src.top <= dst.top || dst.top+dst.height < src.top)
);
}
html2canvas.prototype.clipBounds = function(src,dst){
var x = Math.max(src.left,dst.left);
var y = Math.max(src.top,dst.top);
var x2 = Math.min((src.left+src.width),(dst.left+dst.width));
var y2 = Math.min((src.top+src.height),(dst.top+dst.height));
return {
left:x,
top:y,
width:x2-x,
height:y2-y
};
}
/** /**
* Function to provide bounds for element * Function to provide bounds for element
@ -18,9 +47,17 @@ html2canvas.prototype.getBounds = function(el){
window.scroll(0,0); window.scroll(0,0);
if (el.getBoundingClientRect){ if (el.getBoundingClientRect){
var bounds = el.getBoundingClientRect(); var clientRect = el.getBoundingClientRect();
bounds.top = bounds.top; var bounds = {};
bounds.left = bounds.left; // need to create new object, as clientrect bounds can't be modified, thanks pufuwozu
// TODO add scroll position to bounds, so no scrolling of window necessary
bounds.top = clientRect.top;
bounds.left = clientRect.left;
bounds.width = clientRect.width;
bounds.height = clientRect.height;
return bounds; return bounds;
}else{ }else{
@ -162,5 +199,5 @@ html2canvas.prototype.isSameOrigin = function(url){
var link = document.createElement("a"); var link = document.createElement("a");
link.href = url; link.href = url;
return ((link.protocol + link.hostname) == this.pageOrigin); return ((link.protocol + link.host) == this.pageOrigin);
} }

63
tests/overflow.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title>Overflow tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="#" type="text/css" rel="stylesheet">
<script type="text/javascript" src="../external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="../build/html2canvas.js"></script>
<script type="text/javascript" src="../build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript">
$(window).ready(function() {
$('body').html2canvas({letterRendering:true});
});
</script>
<style>
.small{
font-size:14px;
}
.medium{
font-size:18px;
}
.large{
font-size:24px;
}
div{
background: #ccc;
border:6px solid black;
width:300px;
height:200px;
margin: 0 0 60px 0;
}
h1 {
margin:0;
}
</style>
</head>
<body>
<h1>Overflow: visible</h1>
<div>
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
</div>
<h1>Overflow: hidden</h1>
<div style="overflow:hidden;">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
</div>
</div>
</body>
</html>