From 9ae4ae51fe7698528e13513f3ea0747a257e047b Mon Sep 17 00:00:00 2001 From: MoyuScript Date: Thu, 28 Jul 2011 21:22:13 +0300 Subject: [PATCH] fixed few minor bugs, added some support for overflow (hidden|scroll|auto) --- .gitignore | 3 +- build/html2canvas.js | 563 +++++++++++++++++++++++++++------------ build/html2canvas.min.js | 77 +++--- src/Border.js | 24 +- src/Core.js | 9 +- src/Draw.js | 85 ++++-- src/Renderer.js | 233 ++++++++++++---- src/Text.js | 113 ++++---- src/Traversing.js | 52 ++-- src/Util.js | 45 +++- tests/overflow.html | 63 +++++ 11 files changed, 889 insertions(+), 378 deletions(-) create mode 100644 tests/overflow.html diff --git a/.gitignore b/.gitignore index 76906b6..959cbcf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /tests/cache/ /dist/ /build/tmp.js -index.html \ No newline at end of file +index.html +screenshots_local.html \ No newline at end of file diff --git a/build/html2canvas.js b/build/html2canvas.js index 6d17d7f..cc8ac4d 100644 --- a/build/html2canvas.js +++ b/build/html2canvas.js @@ -1,5 +1,5 @@ /* - * html2canvas v0.25 + * html2canvas v0.26 * Copyright (c) 2011 Niklas von Hertzen. All rights reserved. * http://www.twitter.com/niklasvh * @@ -62,6 +62,7 @@ function html2canvas(el, userOptions) { }, canvasWidth:0, canvasHeight:0, + useOverflow: true, renderOrder: "canvas flash html" }); @@ -81,7 +82,7 @@ function html2canvas(el, userOptions) { this.ignoreElements = "IFRAME|OBJECT|PARAM"; this.needReorder = false; 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+")"); @@ -175,8 +176,8 @@ 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"; + this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow; + document.getElementsByTagName('body')[0].style.overflow = "hidden"; var rootStack = new this.storageContext($(document).width(),$(document).height()); rootStack.opacity = this.getCSS(this.element,"opacity"); var stack = this.newElement(this.element,rootStack); @@ -238,6 +239,8 @@ html2canvas.prototype.storageContext = function(width,height){ }); }; + + this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){ 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 @@ -637,7 +646,21 @@ html2canvas.prototype.drawBorders = function(el,ctx, x, y, w, h){ 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){ var bounds = this.getBounds(el); @@ -665,21 +684,41 @@ html2canvas.prototype.newElement = function(el,parentStack){ image; var bgcolor = this.getCSS(el,"background-color"); - + var cssPosition = this.getCSS(el,"position"); 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")) var opacity = this.getCSS(el,"opacity"); - + var stack = { ctx: new this.storageContext(), 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); @@ -688,9 +727,17 @@ html2canvas.prototype.newElement = function(el,parentStack){ this.setContextVariable(ctx,"globalAlpha",stack.opacity); // 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.opts.iframeDefault=="default"){ bgcolor = "#efefef"; @@ -702,28 +749,41 @@ html2canvas.prototype.newElement = function(el,parentStack){ } } - // 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,{ + // draw base element bgcolor + + var bgbounds = { left: x+borders[3].width, top: y+borders[0].width, width: w-(borders[1].width+borders[3].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"){ image = _.loadImage(_.getAttr(el,'src')); if (image){ - // console.log(image.width); + // console.log(image.width); this.drawImage( ctx, 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; // 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; s0 && 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; i0 && 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){ var _ = this; @@ -1023,58 +1263,9 @@ html2canvas.prototype.canvasRenderer = function(queue){ this.ctx = this.canvas.getContext("2d"); - // set common settings for canvas - this.ctx.textBaseline = "bottom"; + this.canvasRenderStorage(queue,this.ctx); - - - 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 size = this.getCSS(el,"font-size"); var color = this.getCSS(el,"color"); @@ -1171,7 +1362,7 @@ html2canvas.prototype.newText = function(el,textNode,ctx){ case 401: bold = "bold"; break; - case 400: + case 400: bold = "normal"; break; } @@ -1191,21 +1382,30 @@ html2canvas.prototype.newText = function(el,textNode,ctx){ 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; renderList = textNode.nodeValue.split(/(\b| )/); }else{ - // this.setContextVariable(ctx,"textAlign","left"); + // this.setContextVariable(ctx,"textAlign","left"); renderList = textNode.nodeValue.split(""); } + + this.setContextVariable(ctx,"fillStyle",color); this.setContextVariable(ctx,"font",font); + /* + if (stack.clip){ + ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); + ctx.clip(); + } +*/ + var oldTextNode = textNode; @@ -1213,60 +1413,66 @@ html2canvas.prototype.newText = function(el,textNode,ctx){ // TODO only do the splitting for non-range prints var newTextNode = oldTextNode.splitText(renderList[c].length); - - - if (this.support.rangeBounds){ - // 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{ - // it isn't supported, so let's wrap it inside an element instead and the bounds there + + if (text_decoration!="none" || this.trim(oldTextNode.nodeValue).length != 0){ - var parent = oldTextNode.parentNode; - var wrapElement = document.createElement('wrapper'); - var backupText = oldTextNode.cloneNode(true); - wrapElement.appendChild(oldTextNode.cloneNode(true)); - parent.replaceChild(wrapElement,oldTextNode); + + + + if (this.support.rangeBounds){ + // 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{ + // 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); + var bounds = this.getBounds(wrapElement); - parent.replaceChild(backupText,wrapElement); - } + parent.replaceChild(backupText,wrapElement); + } - - - this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx); + // 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; + 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; - } + } + } + oldTextNode = newTextNode; @@ -1305,6 +1511,8 @@ html2canvas.prototype.fontMetrics = function(font,fontSize){ var img = document.createElement('img'); + + // TODO add another image img.src = "http://html2canvas.hertzen.com/images/8.jpg"; img.width = 1; img.height = 1; @@ -1410,50 +1618,28 @@ html2canvas.prototype.parsing = function(el,stack){ if (this.getCSS(el,'display') != "none" && this.getCSS(el,'visibility')!="hidden"){ var _ = this; - - //if (!this.blockElements.test(el.nodeName)){ - + stack = this.newElement(el,stack) || stack; - - + var ctx = stack.ctx; - - + if (!this.ignoreRe.test(el.nodeName)){ - - - - // TODO remove jQuery dependancy - - var contents = this.contentsInZ(el); - - - if (contents.length == 1){ - - // 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){ + // TODO remove jQuery dependancy + this.each(this.contentsInZ(el),function(cid,node){ - if (cel.nodeType==1){ - // element - _.parsing(cel,stack); - }else if (cel.nodeType==3){ - _.newText(el,cel,ctx); - } + if (node.nodeType==1){ + // element + _.parsing(node,stack); + }else if (node.nodeType==3){ + _.newText(el,node,stack); + } - }); + }); - } } + + + } // } } @@ -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 @@ -1478,9 +1693,17 @@ html2canvas.prototype.getBounds = function(el){ window.scroll(0,0); if (el.getBoundingClientRect){ - var bounds = el.getBoundingClientRect(); - bounds.top = bounds.top; - bounds.left = bounds.left; + var clientRect = el.getBoundingClientRect(); + var bounds = {}; + // 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; }else{ @@ -1622,6 +1845,6 @@ html2canvas.prototype.isSameOrigin = function(url){ var link = document.createElement("a"); link.href = url; - return ((link.protocol + link.hostname) == this.pageOrigin); + return ((link.protocol + link.host) == this.pageOrigin); } diff --git a/build/html2canvas.min.js b/build/html2canvas.min.js index 416b2ff..5f7966d 100644 --- a/build/html2canvas.min.js +++ b/build/html2canvas.min.js @@ -1,49 +1,50 @@ /* - * html2canvas v0.25 + * html2canvas v0.26 * Copyright (c) 2011 Niklas von Hertzen. All rights reserved. * http://www.twitter.com/niklasvh * * 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"}); -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= -"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} -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()}; +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.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(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,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.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.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); -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;ce+c?e+c-c:f.height,c< -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}; -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.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;hg+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.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;hf+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.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.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.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)&& -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, -"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 :"+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++)}; -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++}; -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.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.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.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.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], -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)}})})}; -html2canvas.prototype.sortQueue=function(a){if(!this.opts.reorderZ||!this.needReorder)return a;var b=0;this.each(a,function(a,d){if(be.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.zIndexb.zIndex?1:0})}; -html2canvas.prototype.setContextVariable=function(a,b,d){a.storage?a.storage.push({type:"variable",name:b,arguments:d}):a[b]=d}; -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= -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-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}); -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.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 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;de+d?e+d-d:f.height,d< +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,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,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;gh+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,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;gf+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=[],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,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 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"))&& +!/(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)); +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"), +10)+parseInt(this.getCSS(a,"padding-bottom"),10))):this.log("Error loading :"+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,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,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 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(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.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]- +(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.canvasRenderStorage=function(a,b){for(;0e.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.zIndexb.zIndex?1:0})}; +html2canvas.prototype.setContextVariable=function(a,b,c){a.storage?a.storage.push({type:"variable",name:b,arguments:c}):a[b]=c}; +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!= +"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-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.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.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.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;d0 && borderBounds.height>0){ + _.newRect(ctx,bx,by,borderBounds.width,borderBounds.height,borderData.color); + } diff --git a/src/Core.js b/src/Core.js index f564808..ee27fa9 100644 --- a/src/Core.js +++ b/src/Core.js @@ -32,6 +32,7 @@ function html2canvas(el, userOptions) { }, canvasWidth:0, canvasHeight:0, + useOverflow: true, renderOrder: "canvas flash html" }); @@ -51,7 +52,7 @@ function html2canvas(el, userOptions) { this.ignoreElements = "IFRAME|OBJECT|PARAM"; this.needReorder = false; 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+")"); @@ -145,8 +146,8 @@ 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"; + this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow; + document.getElementsByTagName('body')[0].style.overflow = "hidden"; var rootStack = new this.storageContext($(document).width(),$(document).height()); rootStack.opacity = this.getCSS(this.element,"opacity"); var stack = this.newElement(this.element,rootStack); @@ -208,6 +209,8 @@ html2canvas.prototype.storageContext = function(width,height){ }); }; + + this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){ this.storage.push( diff --git a/src/Draw.js b/src/Draw.js index 39f72a1..5f4062c 100644 --- a/src/Draw.js +++ b/src/Draw.js @@ -1,7 +1,3 @@ - - - - html2canvas.prototype.newElement = function(el,parentStack){ var bounds = this.getBounds(el); @@ -14,21 +10,41 @@ html2canvas.prototype.newElement = function(el,parentStack){ image; var bgcolor = this.getCSS(el,"background-color"); - + var cssPosition = this.getCSS(el,"position"); 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")) var opacity = this.getCSS(el,"opacity"); - + var stack = { ctx: new this.storageContext(), 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); @@ -37,9 +53,17 @@ html2canvas.prototype.newElement = function(el,parentStack){ this.setContextVariable(ctx,"globalAlpha",stack.opacity); // 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.opts.iframeDefault=="default"){ bgcolor = "#efefef"; @@ -51,28 +75,41 @@ html2canvas.prototype.newElement = function(el,parentStack){ } } - // 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,{ + // draw base element bgcolor + + var bgbounds = { left: x+borders[3].width, top: y+borders[0].width, width: w-(borders[1].width+borders[3].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"){ image = _.loadImage(_.getAttr(el,'src')); if (image){ - // console.log(image.width); + // console.log(image.width); this.drawImage( ctx, image, diff --git a/src/Renderer.js b/src/Renderer.js index 9c9057a..ae484fe 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -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; // 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; s0 && 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; i0 && 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){ var _ = this; @@ -99,58 +279,9 @@ html2canvas.prototype.canvasRenderer = function(queue){ this.ctx = this.canvas.getContext("2d"); - // set common settings for canvas - this.ctx.textBaseline = "bottom"; + this.canvasRenderStorage(queue,this.ctx); - - - 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: - - } - - - }); - } - - }); - }; diff --git a/src/Text.js b/src/Text.js index 6a0625f..cdfe5f5 100644 --- a/src/Text.js +++ b/src/Text.js @@ -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 size = this.getCSS(el,"font-size"); var color = this.getCSS(el,"color"); @@ -25,7 +25,7 @@ html2canvas.prototype.newText = function(el,textNode,ctx){ case 401: bold = "bold"; break; - case 400: + case 400: bold = "normal"; break; } @@ -45,21 +45,30 @@ html2canvas.prototype.newText = function(el,textNode,ctx){ 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; renderList = textNode.nodeValue.split(/(\b| )/); }else{ - // this.setContextVariable(ctx,"textAlign","left"); + // this.setContextVariable(ctx,"textAlign","left"); renderList = textNode.nodeValue.split(""); } + + this.setContextVariable(ctx,"fillStyle",color); this.setContextVariable(ctx,"font",font); + /* + if (stack.clip){ + ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); + ctx.clip(); + } +*/ + var oldTextNode = textNode; @@ -67,60 +76,66 @@ html2canvas.prototype.newText = function(el,textNode,ctx){ // TODO only do the splitting for non-range prints var newTextNode = oldTextNode.splitText(renderList[c].length); - - - if (this.support.rangeBounds){ - // 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{ - // it isn't supported, so let's wrap it inside an element instead and the bounds there + + if (text_decoration!="none" || this.trim(oldTextNode.nodeValue).length != 0){ - var parent = oldTextNode.parentNode; - var wrapElement = document.createElement('wrapper'); - var backupText = oldTextNode.cloneNode(true); - wrapElement.appendChild(oldTextNode.cloneNode(true)); - parent.replaceChild(wrapElement,oldTextNode); + + + + if (this.support.rangeBounds){ + // 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{ + // 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); + var bounds = this.getBounds(wrapElement); - parent.replaceChild(backupText,wrapElement); - } + parent.replaceChild(backupText,wrapElement); + } - - - this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx); + // 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; + 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; - } + } + } + oldTextNode = newTextNode; @@ -159,6 +174,8 @@ html2canvas.prototype.fontMetrics = function(font,fontSize){ var img = document.createElement('img'); + + // TODO add another image img.src = "http://html2canvas.hertzen.com/images/8.jpg"; img.width = 1; img.height = 1; diff --git a/src/Traversing.js b/src/Traversing.js index 05b2939..9e5bee3 100644 --- a/src/Traversing.js +++ b/src/Traversing.js @@ -18,50 +18,28 @@ html2canvas.prototype.parsing = function(el,stack){ if (this.getCSS(el,'display') != "none" && this.getCSS(el,'visibility')!="hidden"){ var _ = this; - - //if (!this.blockElements.test(el.nodeName)){ - + stack = this.newElement(el,stack) || stack; - - + var ctx = stack.ctx; - - + if (!this.ignoreRe.test(el.nodeName)){ - - - - // TODO remove jQuery dependancy - - var contents = this.contentsInZ(el); - - - if (contents.length == 1){ - - // 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){ + // TODO remove jQuery dependancy + this.each(this.contentsInZ(el),function(cid,node){ - if (cel.nodeType==1){ - // element - _.parsing(cel,stack); - }else if (cel.nodeType==3){ - _.newText(el,cel,ctx); - } + if (node.nodeType==1){ + // element + _.parsing(node,stack); + }else if (node.nodeType==3){ + _.newText(el,node,stack); + } - }); + }); - } } + + + } // } } diff --git a/src/Util.js b/src/Util.js index b749192..875d715 100644 --- a/src/Util.js +++ b/src/Util.js @@ -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 @@ -18,9 +47,17 @@ html2canvas.prototype.getBounds = function(el){ window.scroll(0,0); if (el.getBoundingClientRect){ - var bounds = el.getBoundingClientRect(); - bounds.top = bounds.top; - bounds.left = bounds.left; + var clientRect = el.getBoundingClientRect(); + var bounds = {}; + // 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; }else{ @@ -162,5 +199,5 @@ html2canvas.prototype.isSameOrigin = function(url){ var link = document.createElement("a"); link.href = url; - return ((link.protocol + link.hostname) == this.pageOrigin); + return ((link.protocol + link.host) == this.pageOrigin); } diff --git a/tests/overflow.html b/tests/overflow.html new file mode 100644 index 0000000..741a8c8 --- /dev/null +++ b/tests/overflow.html @@ -0,0 +1,63 @@ + + + + Overflow tests + + + + + + + + + + + + +

Overflow: visible

+
+ 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 Aldus PageMaker including versions of Lorem Ipsum. +
+ +

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
a
Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. + + +
position:relative within a overflow:hidden element

+ + 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 Aldus PageMaker including versions of Lorem Ipsum. + +
+
+ + + +