Compare commits

...

22 Commits
0.3.2 ... 0.3.3

Author SHA1 Message Date
c7d526c9ea simplified API and cleaned up code 2012-03-02 18:05:03 +02:00
bf994849e0 bug fixes for body background & firefox font 2012-03-02 14:43:25 +02:00
2dc8b9385e SVG taint fix, and additional taint testing options 2012-03-01 22:31:51 +02:00
6ef6c79f24 todo update proxy 2012-03-01 19:51:07 +02:00
3ad49efa00 added support for CORS images and option to create canvas as tainted 2012-03-01 19:44:25 +02:00
c86d12b915 test if canvas has become tainted 2012-03-01 18:44:52 +02:00
1447b031c6 html2canvas -> h2clog 2012-02-28 12:40:44 +02:00
e01d97df19 fixed an instance of html2canvas.log to h2clog 2012-02-28 12:39:38 +02:00
af60621d4f Update readme.md 2012-02-27 21:24:19 +02:00
f485028d30 Merge pull request #63 from cobexer/minification-improvements
Minification improvements
2012-02-27 11:09:26 -08:00
5878c201b3 added changelog entry 2012-02-26 23:19:39 +01:00
b6d6f44678 make FlashCanvas check closure compiler friendly 2012-02-26 23:04:46 +01:00
19f505214b cleanup .gitignore 2012-02-26 22:57:28 +01:00
c24223ca85 renamed html2canvas.log to h2clog (minifies better)
renamed the html2canvas.canvasContext to h2cRenderContext,
it's used by both backends and thus not canvas specific
2012-02-26 22:57:22 +01:00
b82be022b2 build: improve minification, more verbose 2012-02-26 22:30:34 +01:00
84a676403f Merge pull request #62 from cobexer/for-niklasvh
fix image loaded through the proxy hanging the preload process
2012-02-26 12:52:46 -08:00
afc358fb12 fix image loaded through the proxy hanging the preload process
images loaded through the proxy could hang the preload process if
they finish loading through the proxy but then fail to decode
and thus don't call the onload handler of the image object.
2012-02-26 13:14:16 +01:00
e925719151 added flashcanvas integration and some legacy IE bug fixes 2012-02-26 00:21:01 +02:00
b65357c55d added flashcanvas integration and some legacy IE bug fixes 2012-02-26 00:19:16 +02:00
c4cc1fe180 Update readme.md 2012-02-20 17:26:21 +02:00
abea4a89da Added changelog 2012-02-20 17:25:57 +02:00
0cb252ada9 add support for selecting single elements to render 2012-02-20 17:16:57 +02:00
25 changed files with 11001 additions and 322 deletions

3
.gitignore vendored
View File

@ -5,8 +5,7 @@
/tests/cache/ /tests/cache/
/tests/flashcanvas.html /tests/flashcanvas.html
/lib/ /lib/
/dist/ /build/
/build/*.js
index.html index.html
image.jpg image.jpg
screenshots.html screenshots.html

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml version="1.0" encoding="utf-8"?>
<project name="html2canvas" basedir="." default="build"> <project name="html2canvas" basedir="." default="build">
<property name="src.dir" location="src"/> <property name="src.dir" location="src"/>
<property name="lib.dir" location="../lib"/> <property name="lib.dir" location="../lib"/>
@ -11,44 +11,56 @@
<property name="JQUERY_PLUGIN_NAME" value="jquery.plugin.html2canvas.js"/> <property name="JQUERY_PLUGIN_NAME" value="jquery.plugin.html2canvas.js"/>
<loadfile property="version" srcfile="version.txt" /> <loadfile property="version" srcfile="version.txt" />
<fileset id="sourcefiles" dir="${src.dir}"> <path id="sourcefiles">
<include name="LICENSE"/> <filelist dir="${src.dir}">
<include name="Core.js"/> <file name="LICENSE"/>
<include name="Generate.js"/> <file name="html2canvas-pre.txt"/>
<include name="Parse.js"/> <file name="Core.js"/>
<include name="Preload.js"/> <file name="Generate.js"/>
<include name="Queue.js"/> <file name="Parse.js"/>
<include name="Renderer.js"/> <file name="Preload.js"/>
</fileset> <file name="Queue.js"/>
<file name="Renderer.js"/>
<file name="Util.js"/>
<file name="html2canvas-post.txt"/>
</filelist>
</path>
<path id="jquery-plugin"> <path id="jquery-plugin">
<fileset dir="${src.dir}" includes="LICENSE"/> <fileset dir="${src.dir}" includes="LICENSE"/>
<fileset dir="${src.dir}/plugins" includes="${JQUERY_PLUGIN_NAME}"/> <fileset dir="${src.dir}/plugins" includes="${JQUERY_PLUGIN_NAME}"/>
</path> </path>
<target name="plugins"> <target name="build-dir">
<echo>Creating directory ${build.dir}...</echo>
<mkdir dir="${build.dir}"/>
</target>
<target name="plugins" depends="build-dir">
<echo>Creating ${JQUERY_PLUGIN_NAME}...</echo>
<concat fixlastline="yes" destfile="${build.dir}/${JQUERY_PLUGIN_NAME}"> <concat fixlastline="yes" destfile="${build.dir}/${JQUERY_PLUGIN_NAME}">
<path refid="jquery-plugin"/> <path refid="jquery-plugin"/>
</concat> </concat>
<replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JQUERY_PLUGIN_NAME}" /> <replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JQUERY_PLUGIN_NAME}" />
</target> </target>
<pathconvert property="prettty-sourcefiles" pathsep="${line.separator}" refid="sourcefiles"></pathconvert>
<target name="build" depends="plugins"> <target name="build" depends="build-dir,plugins">
<echo>Concatenating files:${line.separator}${prettty-sourcefiles}${line.separator}into ${build.dir}/${JS_NAME}...</echo>
<concat fixlastline="yes" destfile="${build.dir}/${JS_NAME}"> <concat fixlastline="yes" destfile="${build.dir}/${JS_NAME}">
<fileset refid="sourcefiles"/> <path refid="sourcefiles"/>
</concat> </concat>
<replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JS_NAME}" /> <replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JS_NAME}" />
</target> </target>
<taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask" <taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask"
classpath="${lib.dir}/compiler.jar" onerror="report"/> classpath="${lib.dir}/compiler.jar" onerror="report"/>
<target name="release" depends="build"> <target name="syntaxcheck" depends="build-dir,build">
<jscomp compilationLevel="simple" warning="verbose" <jscomp compilationLevel="simple" warning="verbose"
debug="false" debug="false"
output="${build.dir}/${JS_NAME_MIN}"> output="${build.dir}/${JS_NAME_MIN}.tmp">
<externs dir="${lib.dir}"> <externs dir="${lib.dir}">
<file name="${jquery-externs}"/> <file name="${jquery-externs}"/>
</externs> </externs>
@ -61,15 +73,28 @@
<file name="Preload.js"/> <file name="Preload.js"/>
<file name="Queue.js"/> <file name="Queue.js"/>
<file name="Renderer.js"/> <file name="Renderer.js"/>
<file name="Util.js"/>
</sources>
</jscomp>
<delete file="${build.dir}/${JS_NAME_MIN}.tmp"></delete>
</target>
<target name="release" depends="build-dir,build,syntaxcheck">
<jscomp compilationLevel="simple" warning="verbose"
debug="false"
output="${build.dir}/${JS_NAME_MIN}">
<externs dir="${lib.dir}">
<file name="${jquery-externs}"/>
</externs>
<sources dir="${build.dir}">
<file name="${JS_NAME}"/>
</sources> </sources>
</jscomp> </jscomp>
<replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JS_NAME_MIN}" /> <replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JS_NAME_MIN}" />
</target> </target>
<target name="clean"> <target name="clean">
<delete file="${build.dir}/${JS_NAME}"></delete> <delete dir="${build.dir}"></delete>
<delete file="${build.dir}/${JS_NAME_MIN}"></delete>
<delete file="${build.dir}/${JQUERY_PLUGIN_NAME}"></delete>
</target> </target>
</project> </project>

View File

1194
external/flashcanvas.js vendored Normal file

File diff suppressed because it is too large Load Diff

28
external/flashcanvas.min.js vendored Normal file
View File

@ -0,0 +1,28 @@
/*
* FlashCanvas
*
* Copyright (c) 2009 Tim Cameron Ryan
* Copyright (c) 2009-2011 FlashCanvas Project
* Released under the MIT/X License
*/
window.ActiveXObject&&!window.CanvasRenderingContext2D&&function(h,j){function D(a){this.code=a;this.message=T[a]}function U(a){this.width=a}function E(a){this.id=a.C++}function t(a){this.G=a;this.id=a.C++}function u(a,b){this.canvas=a;this.B=b;this.d=a.uniqueID;this.D();this.C=0;this.t="";var c=this;setInterval(function(){n[c.d]===0&&c.e()},30)}function A(){if(j.readyState==="complete"){j.detachEvent(F,A);for(var a=j.getElementsByTagName(r),b=0,c=a.length;b<c;++b)B.initElement(a[b])}}function G(){var a=
event.srcElement,b=a.parentNode;a.blur();b.focus()}function H(){var a=event.propertyName;if(a==="width"||a==="height"){var b=event.srcElement,c=b[a],d=parseInt(c,10);if(isNaN(d)||d<0)d=a==="width"?300:150;if(c===d){b.style[a]=d+"px";b.getContext("2d").I(b.width,b.height)}else b[a]=d}}function I(){h.detachEvent(J,I);for(var a in s){var b=s[a],c=b.firstChild,d;for(d in c)if(typeof c[d]==="function")c[d]=k;for(d in b)if(typeof b[d]==="function")b[d]=k;c.detachEvent(K,G);b.detachEvent(L,H)}h[M]=k;h[N]=
k;h[O]=k;h[C]=k;h[P]=k}function V(){var a=j.getElementsByTagName("script");a=a[a.length-1];return j.documentMode>=8?a.src:a.getAttribute("src",4)}function v(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;")}function W(a){return a.toLowerCase()}function i(a){throw new D(a);}function Q(a){var b=parseInt(a.width,10),c=parseInt(a.height,10);if(isNaN(b)||b<0)b=300;if(isNaN(c)||c<0)c=150;a.width=b;a.height=c}var k=null,r="canvas",M="CanvasRenderingContext2D",N="CanvasGradient",O="CanvasPattern",
C="FlashCanvas",P="G_vmlCanvasManager",K="onfocus",L="onpropertychange",F="onreadystatechange",J="onunload",w=((h[C+"Options"]||{}).swfPath||V().replace(/[^\/]+$/,""))+"flashcanvas.swf",e=new function(a){for(var b=0,c=a.length;b<c;b++)this[a[b]]=b}(["toDataURL","save","restore","scale","rotate","translate","transform","setTransform","globalAlpha","globalCompositeOperation","strokeStyle","fillStyle","createLinearGradient","createRadialGradient","createPattern","lineWidth","lineCap","lineJoin","miterLimit",
"shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","clearRect","fillRect","strokeRect","beginPath","closePath","moveTo","lineTo","quadraticCurveTo","bezierCurveTo","arcTo","rect","arc","fill","stroke","clip","isPointInPath","font","textAlign","textBaseline","fillText","strokeText","measureText","drawImage","createImageData","getImageData","putImageData","addColorStop","direction","resize"]),x={},n={},s={},y={};u.prototype={save:function(){this.b();this.c();this.m();this.l();this.z();this.w();
this.F.push([this.f,this.g,this.A,this.u,this.j,this.h,this.i,this.k,this.p,this.q,this.n,this.o,this.v,this.r,this.s]);this.a.push(e.save)},restore:function(){var a=this.F;if(a.length){a=a.pop();this.globalAlpha=a[0];this.globalCompositeOperation=a[1];this.strokeStyle=a[2];this.fillStyle=a[3];this.lineWidth=a[4];this.lineCap=a[5];this.lineJoin=a[6];this.miterLimit=a[7];this.shadowOffsetX=a[8];this.shadowOffsetY=a[9];this.shadowBlur=a[10];this.shadowColor=a[11];this.font=a[12];this.textAlign=a[13];
this.textBaseline=a[14]}this.a.push(e.restore)},scale:function(a,b){this.a.push(e.scale,a,b)},rotate:function(a){this.a.push(e.rotate,a)},translate:function(a,b){this.a.push(e.translate,a,b)},transform:function(a,b,c,d,f,g){this.a.push(e.transform,a,b,c,d,f,g)},setTransform:function(a,b,c,d,f,g){this.a.push(e.setTransform,a,b,c,d,f,g)},b:function(){var a=this.a;if(this.f!==this.globalAlpha){this.f=this.globalAlpha;a.push(e.globalAlpha,this.f)}if(this.g!==this.globalCompositeOperation){this.g=this.globalCompositeOperation;
a.push(e.globalCompositeOperation,this.g)}},m:function(){if(this.A!==this.strokeStyle){var a=this.A=this.strokeStyle;this.a.push(e.strokeStyle,typeof a==="object"?a.id:a)}},l:function(){if(this.u!==this.fillStyle){var a=this.u=this.fillStyle;this.a.push(e.fillStyle,typeof a==="object"?a.id:a)}},createLinearGradient:function(a,b,c,d){isFinite(a)&&isFinite(b)&&isFinite(c)&&isFinite(d)||i(9);this.a.push(e.createLinearGradient,a,b,c,d);return new t(this)},createRadialGradient:function(a,b,c,d,f,g){isFinite(a)&&
isFinite(b)&&isFinite(c)&&isFinite(d)&&isFinite(f)&&isFinite(g)||i(9);if(c<0||g<0)i(1);this.a.push(e.createRadialGradient,a,b,c,d,f,g);return new t(this)},createPattern:function(a,b){a||i(17);var c=a.tagName,d,f=this.d;if(c){c=c.toLowerCase();if(c==="img")d=a.getAttribute("src",2);else if(c===r||c==="video")return;else i(17)}else if(a.src)d=a.src;else i(17);b==="repeat"||b==="no-repeat"||b==="repeat-x"||b==="repeat-y"||b===""||b===k||i(12);this.a.push(e.createPattern,v(d),b);if(x[f]){this.e();++n[f]}return new E(this)},
z:function(){var a=this.a;if(this.j!==this.lineWidth){this.j=this.lineWidth;a.push(e.lineWidth,this.j)}if(this.h!==this.lineCap){this.h=this.lineCap;a.push(e.lineCap,this.h)}if(this.i!==this.lineJoin){this.i=this.lineJoin;a.push(e.lineJoin,this.i)}if(this.k!==this.miterLimit){this.k=this.miterLimit;a.push(e.miterLimit,this.k)}},c:function(){var a=this.a;if(this.p!==this.shadowOffsetX){this.p=this.shadowOffsetX;a.push(e.shadowOffsetX,this.p)}if(this.q!==this.shadowOffsetY){this.q=this.shadowOffsetY;
a.push(e.shadowOffsetY,this.q)}if(this.n!==this.shadowBlur){this.n=this.shadowBlur;a.push(e.shadowBlur,this.n)}if(this.o!==this.shadowColor){this.o=this.shadowColor;a.push(e.shadowColor,this.o)}},clearRect:function(a,b,c,d){this.a.push(e.clearRect,a,b,c,d)},fillRect:function(a,b,c,d){this.b();this.c();this.l();this.a.push(e.fillRect,a,b,c,d)},strokeRect:function(a,b,c,d){this.b();this.c();this.m();this.z();this.a.push(e.strokeRect,a,b,c,d)},beginPath:function(){this.a.push(e.beginPath)},closePath:function(){this.a.push(e.closePath)},
moveTo:function(a,b){this.a.push(e.moveTo,a,b)},lineTo:function(a,b){this.a.push(e.lineTo,a,b)},quadraticCurveTo:function(a,b,c,d){this.a.push(e.quadraticCurveTo,a,b,c,d)},bezierCurveTo:function(a,b,c,d,f,g){this.a.push(e.bezierCurveTo,a,b,c,d,f,g)},arcTo:function(a,b,c,d,f){f<0&&isFinite(f)&&i(1);this.a.push(e.arcTo,a,b,c,d,f)},rect:function(a,b,c,d){this.a.push(e.rect,a,b,c,d)},arc:function(a,b,c,d,f,g){c<0&&isFinite(c)&&i(1);this.a.push(e.arc,a,b,c,d,f,g?1:0)},fill:function(){this.b();this.c();
this.l();this.a.push(e.fill)},stroke:function(){this.b();this.c();this.m();this.z();this.a.push(e.stroke)},clip:function(){this.a.push(e.clip)},w:function(){var a=this.a;if(this.v!==this.font)try{var b=y[this.d];b.style.font=this.v=this.font;var c=b.currentStyle;a.push(e.font,[c.fontStyle,c.fontWeight,b.offsetHeight,c.fontFamily].join(" "))}catch(d){}if(this.r!==this.textAlign){this.r=this.textAlign;a.push(e.textAlign,this.r)}if(this.s!==this.textBaseline){this.s=this.textBaseline;a.push(e.textBaseline,
this.s)}if(this.t!==this.canvas.currentStyle.direction){this.t=this.canvas.currentStyle.direction;a.push(e.direction,this.t)}},fillText:function(a,b,c,d){this.b();this.l();this.c();this.w();this.a.push(e.fillText,v(a),b,c,d===void 0?Infinity:d)},strokeText:function(a,b,c,d){this.b();this.m();this.c();this.w();this.a.push(e.strokeText,v(a),b,c,d===void 0?Infinity:d)},measureText:function(a){var b=y[this.d];try{b.style.font=this.font}catch(c){}b.innerText=a.replace(/[ \n\f\r]/g,"\t");return new U(b.offsetWidth)},
drawImage:function(a,b,c,d,f,g,o,l,z){a||i(17);var p=a.tagName,m,q=arguments.length,R=this.d;if(p){p=p.toLowerCase();if(p==="img")m=a.getAttribute("src",2);else if(p===r||p==="video")return;else i(17)}else if(a.src)m=a.src;else i(17);this.b();this.c();m=v(m);if(q===3)this.a.push(e.drawImage,q,m,b,c);else if(q===5)this.a.push(e.drawImage,q,m,b,c,d,f);else if(q===9){if(d===0||f===0)i(1);this.a.push(e.drawImage,q,m,b,c,d,f,g,o,l,z)}else return;if(x[R]){this.e();++n[R]}},D:function(){this.globalAlpha=
this.f=1;this.globalCompositeOperation=this.g="source-over";this.fillStyle=this.u=this.strokeStyle=this.A="#000000";this.lineWidth=this.j=1;this.lineCap=this.h="butt";this.lineJoin=this.i="miter";this.miterLimit=this.k=10;this.shadowBlur=this.n=this.shadowOffsetY=this.q=this.shadowOffsetX=this.p=0;this.shadowColor=this.o="rgba(0, 0, 0, 0.0)";this.font=this.v="10px sans-serif";this.textAlign=this.r="start";this.textBaseline=this.s="alphabetic";this.a=[];this.F=[]},H:function(){var a=this.a;this.a=
[];return a},e:function(){var a=this.H();if(a.length>0)return eval(this.B.CallFunction('<invoke name="executeCommand" returntype="javascript"><arguments><string>'+a.join("&#0;")+"</string></arguments></invoke>"))},I:function(a,b){this.e();this.D();if(a>0)this.B.width=a;if(b>0)this.B.height=b;this.a.push(e.resize,a,b)}};t.prototype={addColorStop:function(a,b){if(isNaN(a)||a<0||a>1)i(1);this.G.a.push(e.addColorStop,this.id,a,b)}};D.prototype=Error();var T={1:"INDEX_SIZE_ERR",9:"NOT_SUPPORTED_ERR",11:"INVALID_STATE_ERR",
12:"SYNTAX_ERR",17:"TYPE_MISMATCH_ERR",18:"SECURITY_ERR"},B={initElement:function(a){if(a.getContext)return a;var b=a.uniqueID,c="external"+b;x[b]=false;n[b]=1;Q(a);a.innerHTML='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+location.protocol+'//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="100%" height="100%" id="'+c+'"><param name="allowScriptAccess" value="always"><param name="flashvars" value="id='+c+'"><param name="wmode" value="transparent"></object><span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap"></span>';
s[b]=a;var d=a.firstChild;y[b]=a.lastChild;var f=j.body.contains;if(f(a))d.movie=w;else var g=setInterval(function(){if(f(a)){clearInterval(g);d.movie=w}},0);if(j.compatMode==="BackCompat"||!h.XMLHttpRequest)y[b].style.overflow="hidden";var o=new u(a,d);a.getContext=function(l){return l==="2d"?o:k};a.toDataURL=function(l,z){(""+l).replace(/[A-Z]+/g,W)==="image/jpeg"?o.a.push(e.toDataURL,l,typeof z==="number"?z:""):o.a.push(e.toDataURL,l);return o.e()};d.attachEvent(K,G);return a},saveImage:function(a){a.firstChild.saveImage()},
setOptions:function(){},trigger:function(a,b){s[a].fireEvent("on"+b)},unlock:function(a,b){n[a]&&--n[a];if(b){var c=s[a],d=c.firstChild,f,g;Q(c);f=c.width;g=c.height;c.style.width=f+"px";c.style.height=g+"px";if(f>0)d.width=f;if(g>0)d.height=g;d.resize(f,g);c.attachEvent(L,H);x[a]=true}}};j.createElement(r);j.createStyleSheet().cssText=r+"{display:inline-block;overflow:hidden;width:300px;height:150px}";j.readyState==="complete"?A():j.attachEvent(F,A);h.attachEvent(J,I);if(w.indexOf(location.protocol+
"//"+location.host+"/")===0){var S=new ActiveXObject("Microsoft.XMLHTTP");S.open("GET",w,false);S.send(k)}h[M]=u;h[N]=t;h[O]=E;h[C]=B;h[P]={init:function(){},init_:function(){},initElement:B.initElement};keep=u.measureText}(window,document);

BIN
external/flashcanvas.swf vendored Normal file

Binary file not shown.

8969
external/jquery-1.6.2.js vendored

File diff suppressed because one or more lines are too long

View File

@ -33,4 +33,22 @@ There are still a lot of CSS properties missing, including most CSS3 properties
### Examples ### ### Examples ###
For more information and examples, please visit the <a href="http://html2canvas.hertzen.com">homepage</a> or try the <a href="http://html2canvas.hertzen.com/screenshots.html">test console</a>. For more information and examples, please visit the <a href="http://html2canvas.hertzen.com">homepage</a> or try the <a href="http://html2canvas.hertzen.com/screenshots.html">test console</a>.
### Changelog ###
v0.33 - 2.3.2012
* SVG taint fix, and additional taint testing options for rendering (<a href="https://github.com/niklasvh/html2canvas/commit/2dc8b9385e656696cb019d615bdfa1d98b17d5d4">niklasvh</a>)
* Added support for CORS images and option to create canvas as tainted (<a href="https://github.com/niklasvh/html2canvas/commit/3ad49efa0032cde25c6ed32a39e35d1505d3b2ef">niklasvh</a>)
* Improved minification saved ~1K! (<a href="https://github.com/cobexer/html2canvas/commit/b82be022b2b9240bd503e078ac980bde2b953e43">cobexer</a>)
* Added integrated support for Flashcanvas (<a href="https://github.com/niklasvh/html2canvas/commit/e9257191519f67d74fd5e364d8dee3c0963ba5fc">niklasvh</a>)
* Fixed a variety of legacy IE bugs (<a href="https://github.com/niklasvh/html2canvas/commit/b65357c55d0701017bafcd357bc654b54d458f8f">niklasvh</a>)
v0.32 - 20.2.2012
* Added changelog!
* Added bookmarklet (<a href="https://github.com/niklasvh/html2canvas/commit/b320dd306e1a2d32a3bc5a71b6ebf6d8c060cde5">cobexer</a>)
* Option to select single element to render (<a href="https://github.com/niklasvh/html2canvas/commit/0cb252ada91c84ef411288b317c03e97da1f12ad">niklasvh</a>)
* Fixed closure compiler warnings (<a href="https://github.com/niklasvh/html2canvas/commit/36ff1ec7aadcbdf66851a0b77f0b9e87e4a8e4a1">cobexer</a>)
* Enable profiling in FF (<a href="https://github.com/niklasvh/html2canvas/commit/bbd75286a8406cf9e5aea01fdb7950d547edefb9">cobexer</a>)

View File

@ -5,20 +5,21 @@
Released under MIT License Released under MIT License
*/ */
"use strict";
var html2canvas = {}; var _html2canvas = {},
html2canvas;
html2canvas.logging = false;
html2canvas.log = function (a) { function h2clog(a) {
if (html2canvas.logging && window.console && window.console.log) { if (_html2canvas.logging && window.console && window.console.log) {
window.console.log(a); window.console.log(a);
} }
}; }
html2canvas.Util = {}; _html2canvas.Util = {};
html2canvas.Util.backgroundImage = function (src) { _html2canvas.Util.backgroundImage = function (src) {
if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) {
return src; return src;
@ -35,7 +36,7 @@ html2canvas.Util.backgroundImage = function (src) {
return src; return src;
}; };
html2canvas.Util.Bounds = function getBounds (el) { _html2canvas.Util.Bounds = function getBounds (el) {
var clientRect, var clientRect,
bounds = {}; bounds = {};
@ -47,12 +48,14 @@ html2canvas.Util.Bounds = function getBounds (el) {
bounds.top = clientRect.top; bounds.top = clientRect.top;
bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
bounds.left = clientRect.left; bounds.left = clientRect.left;
bounds.width = clientRect.width;
bounds.height = clientRect.height; // older IE doesn't have width/height, but top/bottom instead
bounds.width = clientRect.width || (clientRect.right - clientRect.left);
bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);
return bounds; return bounds;
} /*else{ } /*else{
p = $(el).offset(); p = $(el).offset();
@ -66,9 +69,9 @@ html2canvas.Util.Bounds = function getBounds (el) {
} */ } */
} };
html2canvas.Util.getCSS = function (el, attribute) { _html2canvas.Util.getCSS = function (el, attribute) {
// return jQuery(el).css(attribute); // return jQuery(el).css(attribute);
/* /*
var val, var val,
@ -109,12 +112,20 @@ html2canvas.Util.getCSS = function (el, attribute) {
}*/ }*/
// val = $(el).css(attribute); // val = $(el).css(attribute);
// } // }
/*
var val = $(el).css(attribute);
if (val === "medium") {
val = 3;
}*/
return $(el).css(attribute); return $(el).css(attribute);
}; };
html2canvas.Util.Extend = function (options, defaults) { _html2canvas.Util.Extend = function (options, defaults) {
var key; var key;
for (key in options) { for (key in options) {
if (options.hasOwnProperty(key)) { if (options.hasOwnProperty(key)) {
@ -124,14 +135,14 @@ html2canvas.Util.Extend = function (options, defaults) {
return defaults; return defaults;
}; };
html2canvas.Util.Children = function(el) { _html2canvas.Util.Children = function(el) {
// $(el).contents() !== el.childNodes, Opera / IE have issues with that // $(el).contents() !== el.childNodes, Opera / IE have issues with that
var children; var children;
try { try {
children = $(el).contents(); children = $(el).contents();
} catch (ex) { } catch (ex) {
html2canvas.log("html2canvas.Util.Children failed with exception: " + ex.message); h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
children = []; children = [];
} }
return children; return children;
} };

View File

@ -6,11 +6,11 @@
Released under MIT License Released under MIT License
*/ */
html2canvas.Generate = {}; _html2canvas.Generate = {};
html2canvas.Generate.Gradient = function(src, bounds) { _html2canvas.Generate.Gradient = function(src, bounds) {
var canvas = document.createElement('canvas'), var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'), ctx = canvas.getContext('2d'),
tmp, tmp,
@ -35,22 +35,24 @@ html2canvas.Generate.Gradient = function(src, bounds) {
var j = -1, var j = -1,
color = '', color = '',
chr; chr;
while( j++ < input.length ) { while( j++ < input.length ) {
chr = input.charAt( j ); chr = input.charAt( j );
if (chr === ')') { if (chr === ')') {
color += chr; color += chr;
steps.push( color ); steps.push( color );
color = ''; color = '';
while (j++ < input.length && input.charAt( j ) !== ',') { j = input.indexOf(",", j) + 1;
if (j === 0) {
break;
} }
// while (j++ < input.length && input.charAt( j ) !== ',') {}
} else { } else {
color += chr; color += chr;
} }
} }
} }
if ( tmp = src.match(/-webkit-linear-gradient\((.*)\)/) ) { if ( (tmp = src.match(/-webkit-linear-gradient\((.*)\)/)) !== null ) {
position = tmp[1].split( ",", 1 )[0]; position = tmp[1].split( ",", 1 )[0];
getColors( tmp[1].substr( position.length + 2 ) ); getColors( tmp[1].substr( position.length + 2 ) );
@ -78,7 +80,7 @@ html2canvas.Generate.Gradient = function(src, bounds) {
} }
} else if (tmp = src.match(/-webkit-gradient\(linear, (\d+)[%]{0,1} (\d+)[%]{0,1}, (\d+)[%]{0,1} (\d+)[%]{0,1}, from\((.*)\), to\((.*)\)\)/)) { } else if ( (tmp = src.match(/-webkit-gradient\(linear, (\d+)[%]{0,1} (\d+)[%]{0,1}, (\d+)[%]{0,1} (\d+)[%]{0,1}, from\((.*)\), to\((.*)\)\)/)) !== null ) {
p0 = (tmp[1] * bounds.width) / 100; p0 = (tmp[1] * bounds.width) / 100;
p1 = (tmp[2] * bounds.height) / 100; p1 = (tmp[2] * bounds.height) / 100;
@ -88,7 +90,7 @@ html2canvas.Generate.Gradient = function(src, bounds) {
steps.push(tmp[5]); steps.push(tmp[5]);
steps.push(tmp[6]); steps.push(tmp[6]);
} else if (tmp = src.match(/-moz-linear-gradient\((\d+)[%]{0,1} (\d+)[%]{0,1}, (.*)\)/)) { } else if ( (tmp = src.match(/-moz-linear-gradient\((\d+)[%]{0,1} (\d+)[%]{0,1}, (.*)\)/)) !== null ) {
p0 = (tmp[1] * bounds.width) / 100; p0 = (tmp[1] * bounds.width) / 100;
p1 = (tmp[2] * bounds.width) / 100; p1 = (tmp[2] * bounds.width) / 100;
@ -108,7 +110,7 @@ html2canvas.Generate.Gradient = function(src, bounds) {
lingrad.addColorStop(increment * i, steps[i]); lingrad.addColorStop(increment * i, steps[i]);
} }
catch(e) { catch(e) {
html2canvas.log(['failed to add color stop: ', e, '; tried to add: ', steps[i], '; stop: ', i, '; in: ', src]); h2clog(['failed to add color stop: ', e, '; tried to add: ', steps[i], '; stop: ', i, '; in: ', src]);
} }
} }
@ -122,9 +124,9 @@ html2canvas.Generate.Gradient = function(src, bounds) {
return img; return img;
} };
html2canvas.Generate.ListAlpha = function(number) { _html2canvas.Generate.ListAlpha = function(number) {
var tmp = "", var tmp = "",
modulus; modulus;
@ -135,9 +137,9 @@ html2canvas.Generate.ListAlpha = function(number) {
}while((number*26) > 26); }while((number*26) > 26);
return tmp; return tmp;
} };
html2canvas.Generate.ListRoman = function(number) { _html2canvas.Generate.ListRoman = function(number) {
var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
roman = "", roman = "",
@ -157,4 +159,4 @@ html2canvas.Generate.ListRoman = function(number) {
return roman; return roman;
} };

View File

@ -10,9 +10,8 @@
* New function for traversing elements * New function for traversing elements
*/ */
html2canvas.Parse = function (element, images, opts) { _html2canvas.Parse = function (element, images, options) {
window.scroll(0,0); window.scroll(0,0);
opts = opts || {};
// select body by default // select body by default
if (element === undefined) { if (element === undefined) {
@ -24,12 +23,7 @@ html2canvas.Parse = function (element, images, opts) {
rangeBounds: false rangeBounds: false
}, },
options = {
iframeDefault: "default",
ignoreElements: "IFRAME|OBJECT|PARAM",
useOverflow: true,
letterRendering: false
},
needReorder = false, needReorder = false,
numDraws = 0, numDraws = 0,
fontData = {}, fontData = {},
@ -47,7 +41,7 @@ html2canvas.Parse = function (element, images, opts) {
children, children,
childrenLen; childrenLen;
options = html2canvas.Util.Extend(opts, options);
images = images || {}; images = images || {};
@ -106,9 +100,10 @@ html2canvas.Parse = function (element, images, opts) {
} }
var getCSS = html2canvas.Util.getCSS; var getCSS = _html2canvas.Util.getCSS;
function getCSSInt(element, attribute) { function getCSSInt(element, attribute) {
return parseInt(getCSS(element, attribute), 10); var val = parseInt(getCSS(element, attribute), 10);
return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html
} }
// Drawing a rectangle // Drawing a rectangle
@ -285,9 +280,13 @@ html2canvas.Parse = function (element, images, opts) {
} }
ctx.setVariable("fillStyle", color); ctx.setVariable("fillStyle", color);
ctx.setVariable("font", font_variant + " " + bold + " " + font_style + " " + size + " " + family);
/*
need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand
to properly work in Firefox
*/
ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family);
if (align){ if (align){
ctx.setVariable("textAlign", "right"); ctx.setVariable("textAlign", "right");
}else{ }else{
@ -349,7 +348,7 @@ html2canvas.Parse = function (element, images, opts) {
wrapElement.appendChild(oldTextNode.cloneNode(true)); wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement, oldTextNode); parent.replaceChild(wrapElement, oldTextNode);
bounds = html2canvas.Util.Bounds(wrapElement); bounds = _html2canvas.Util.Bounds(wrapElement);
textValue = oldTextNode.nodeValue; textValue = oldTextNode.nodeValue;
@ -411,7 +410,7 @@ html2canvas.Parse = function (element, images, opts) {
element.insertBefore(boundElement, element.firstChild); element.insertBefore(boundElement, element.firstChild);
bounds = html2canvas.Util.Bounds( boundElement ); bounds = _html2canvas.Util.Bounds( boundElement );
element.removeChild( boundElement ); element.removeChild( boundElement );
element.style.listStyleType = type; element.style.listStyleType = type;
return bounds; return bounds;
@ -448,16 +447,16 @@ html2canvas.Parse = function (element, images, opts) {
} }
break; break;
case "upper-roman": case "upper-roman":
text = html2canvas.Generate.ListRoman( currentIndex ); text = _html2canvas.Generate.ListRoman( currentIndex );
break; break;
case "lower-roman": case "lower-roman":
text = html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase();
break; break;
case "lower-alpha": case "lower-alpha":
text = html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase();
break; break;
case "upper-alpha": case "upper-alpha":
text = html2canvas.Generate.ListAlpha( currentIndex ); text = _html2canvas.Generate.ListAlpha( currentIndex );
break; break;
} }
@ -490,20 +489,21 @@ html2canvas.Parse = function (element, images, opts) {
}else{ }else{
return; return;
/* /*
TODO really need to figure out some more accurate way to try and find the position. TODO really need to figure out some more accurate way to try and find the position.
as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser
may display it whatever way it feels like. may display it whatever way it feels like.
"The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order" "The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order"
*/
ctx.setVariable("textAlign", "right"); ctx.setVariable("textAlign", "right");
// this.setFont(stack.ctx, element, true); // this.setFont(stack.ctx, element, true);
x = elBounds.left - 10; x = elBounds.left - 10;
*/
} }
y = listBounds.bottom; y = listBounds.bottom;
drawText(text, x, y, ctx) drawText(text, x, y, ctx);
} }
@ -512,12 +512,12 @@ html2canvas.Parse = function (element, images, opts) {
} }
function loadImage (src){ function loadImage (src){
var img = images[src]; var img = images[src];
if (img && img.succeeded === true) { if (img && img.succeeded === true) {
return img.img; return img.img;
} else { } else {
return false; return false;
} }
} }
@ -543,15 +543,15 @@ html2canvas.Parse = function (element, images, opts) {
function setZ(zIndex, parentZ){ function setZ(zIndex, parentZ){
// TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them
var newContext;
if (!parentZ){ if (!parentZ){
this.zStack = h2czContext(0); newContext = h2czContext(0);
return this.zStack; return newContext;
} }
if (zIndex !== "auto"){ if (zIndex !== "auto"){
needReorder = true; needReorder = true;
var newContext = h2czContext(zIndex); newContext = h2czContext(zIndex);
parentZ.children.push(newContext); parentZ.children.push(newContext);
return newContext; return newContext;
@ -587,7 +587,7 @@ html2canvas.Parse = function (element, images, opts) {
borders.push({ borders.push({
width: getCSSInt(el, 'border' + sides[s] + 'Width'), width: getCSSInt(el, 'border' + sides[s] + 'Width'),
color: getCSS(el, 'border' + sides[s] + 'Color') color: getCSS(el, 'border' + sides[s] + 'Color')
}); });
} }
return borders; return borders;
@ -662,7 +662,13 @@ html2canvas.Parse = function (element, images, opts) {
for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){
style = cssArr[i]; style = cssArr[i];
valueWrap.style[style] = getCSS(el, style);
try {
valueWrap.style[style] = getCSS(el, style);
} catch( e ) {
// Older IE has issues with "border"
h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);
}
} }
@ -702,13 +708,22 @@ html2canvas.Parse = function (element, images, opts) {
function getBackgroundPosition(el, bounds, image){ function getBackgroundPosition(el, bounds, image){
// TODO add support for multi image backgrounds // TODO add support for multi image backgrounds
var bgpos = getCSS(el, "backgroundPosition").split(",")[0] || "0 0", var bgposition = (function( bgp ){
bgposition = bgpos.split(" "),
if (bgp !== undefined) {
return (bgp.split(",")[0] || "0 0").split(" ");
} else {
// Older IE uses -x and -y
return [ getCSS(el, "backgroundPositionX"), getCSS(el, "backgroundPositionY") ];
}
})( getCSS(el, "backgroundPosition") ),
topPos, topPos,
left, left,
percentage, percentage,
val; val;
if (bgposition.length === 1){ if (bgposition.length === 1){
val = bgposition; val = bgposition;
@ -862,7 +877,7 @@ html2canvas.Parse = function (element, images, opts) {
} }
if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) {
background_image = html2canvas.Util.backgroundImage( background_image ); background_image = _html2canvas.Util.backgroundImage( background_image );
image = loadImage( background_image ); image = loadImage( background_image );
@ -925,9 +940,7 @@ html2canvas.Parse = function (element, images, opts) {
bgsy = 0; bgsy = 0;
} }
// bgh = Math.abs(bgh);
// bgw = Math.abs(bgw);
if (bgh>0 && bgw > 0){ if (bgh>0 && bgw > 0){
renderImage( renderImage(
ctx, ctx,
@ -941,9 +954,7 @@ html2canvas.Parse = function (element, images, opts) {
bgw, // destination width : 18 bgw, // destination width : 18
bgh // destination height : 1677 bgh // destination height : 1677
); );
// ctx.drawImage(image,(bounds.left+bgp.left),(bounds.top+bgp.top));
} }
break; break;
default: default:
@ -986,7 +997,7 @@ html2canvas.Parse = function (element, images, opts) {
} }
}else{ }else{
html2canvas.log("html2canvas: Error loading background:" + background_image); h2clog("html2canvas: Error loading background:" + background_image);
//console.log(images); //console.log(images);
} }
@ -997,7 +1008,7 @@ html2canvas.Parse = function (element, images, opts) {
function renderElement(el, parentStack){ function renderElement(el, parentStack){
var bounds = html2canvas.Util.Bounds(el), var bounds = _html2canvas.Util.Bounds(el),
x = bounds.left, x = bounds.left,
y = bounds.top, y = bounds.top,
w = bounds.width, w = bounds.width,
@ -1035,7 +1046,7 @@ html2canvas.Parse = function (element, images, opts) {
stack = { stack = {
ctx: html2canvas.canvasContext( docDim.width || w , docDim.height || h ), ctx: h2cRenderContext( docDim.width || w , docDim.height || h ),
zIndex: zindex, zIndex: zindex,
opacity: opacity * parentStack.opacity, opacity: opacity * parentStack.opacity,
cssPosition: cssPosition cssPosition: cssPosition
@ -1046,7 +1057,7 @@ html2canvas.Parse = function (element, images, opts) {
// TODO correct overflow for absolute content residing under a static position // TODO correct overflow for absolute content residing under a static position
if (parentStack.clip){ if (parentStack.clip){
stack.clip = html2canvas.Util.Extend( {}, parentStack.clip ); stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip );
//stack.clip = parentStack.clip; //stack.clip = parentStack.clip;
// stack.clip.height = stack.clip.height - parentStack.borders[2].width; // stack.clip.height = stack.clip.height - parentStack.borders[2].width;
} }
@ -1106,7 +1117,7 @@ html2canvas.Parse = function (element, images, opts) {
} }
if (bgbounds.height > 0 && bgbounds.width > 0){ if (bgbounds.height > 0 && bgbounds.width > 0){
renderRect( renderRect(
ctx, ctx,
@ -1146,7 +1157,7 @@ html2canvas.Parse = function (element, images, opts) {
); );
}else{ }else{
html2canvas.log("html2canvas: Error loading <img>:" + imgSrc); h2clog("html2canvas: Error loading <img>:" + imgSrc);
} }
break; break;
case "INPUT": case "INPUT":
@ -1199,7 +1210,7 @@ html2canvas.Parse = function (element, images, opts) {
y + paddingTop + borders[0].width, // dy y + paddingTop + borders[0].width, // dy
bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw
bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh
); );
break; break;
} }
@ -1218,7 +1229,7 @@ html2canvas.Parse = function (element, images, opts) {
ctx = stack.ctx; ctx = stack.ctx;
if ( !ignoreElementsRegExp.test( el.nodeName ) ) { if ( !ignoreElementsRegExp.test( el.nodeName ) ) {
var elementChildren = html2canvas.Util.Children( el ), var elementChildren = _html2canvas.Util.Children( el ),
i, i,
node, node,
childrenLen; childrenLen;
@ -1244,6 +1255,9 @@ html2canvas.Parse = function (element, images, opts) {
parseElement(children[i], stack); parseElement(children[i], stack);
} }
stack.backgroundColor = getCSS( body, "backgroundColor" );
return stack; return stack;
}; };
@ -1253,4 +1267,4 @@ function h2czContext(zindex) {
zindex: zindex, zindex: zindex,
children: [] children: []
}; };
}; }

View File

@ -6,13 +6,9 @@
Released under MIT License Released under MIT License
*/ */
html2canvas.Preload = function(element, opts){ _html2canvas.Preload = function(element, options){
var options = { var images = {
proxy: "http://html2canvas.appspot.com/",
timeout: 0 // no timeout
},
images = {
numLoaded: 0, // also failed are counted here numLoaded: 0, // also failed are counted here
numFailed: 0, numFailed: 0,
numTotal: 0, numTotal: 0,
@ -26,26 +22,30 @@ html2canvas.Preload = function(element, opts){
domImages = doc.images, // TODO probably should limit it to images present in the element only domImages = doc.images, // TODO probably should limit it to images present in the element only
imgLen = domImages.length, imgLen = domImages.length,
link = doc.createElement("a"), link = doc.createElement("a"),
supportCORS = (function( img ){
return (img.crossOrigin !== undefined);
})(new Image()),
timeoutTimer; timeoutTimer;
link.href = window.location.href; link.href = window.location.href;
pageOrigin = link.protocol + link.host; pageOrigin = link.protocol + link.host;
opts = opts || {};
options = html2canvas.Util.Extend(opts, options);
element = element || doc.body; element = element || doc.body;
function isSameOrigin(url){ function isSameOrigin(url){
link.href = url; link.href = url;
var origin = link.protocol + link.host; link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/
return ":" === origin || (origin === pageOrigin); var origin = link.protocol + link.host;
return (origin === pageOrigin);
} }
function start(){ function start(){
html2canvas.log("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")");
if (!images.firstRun && images.numLoaded >= images.numTotal){ if (!images.firstRun && images.numLoaded >= images.numTotal){
/* /*
@ -57,21 +57,21 @@ html2canvas.Preload = function(element, opts){
options.complete(images); options.complete(images);
} }
html2canvas.log("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")");
} }
} }
function proxyGetImage(url, img){ // TODO modify proxy to serve images with CORS enabled, where available
function proxyGetImage(url, img, imageObj){
var callback_name, var callback_name,
scriptUrl = options.proxy, scriptUrl = options.proxy,
script, script;
imgObj = images[url];
link.href = url; link.href = url;
url = link.href; // work around for pages with base href="" set - WARNING: this may change the url -> so access imgObj from images map before changing that url! url = link.href; // work around for pages with base href="" set - WARNING: this may change the url
callback_name = 'html2canvas_' + count; callback_name = 'html2canvas_' + (count++);
imgObj.callbackname = callback_name; imageObj.callbackname = callback_name;
if (scriptUrl.indexOf("?") > -1) { if (scriptUrl.indexOf("?") > -1) {
scriptUrl += "&"; scriptUrl += "&";
@ -79,19 +79,16 @@ html2canvas.Preload = function(element, opts){
scriptUrl += "?"; scriptUrl += "?";
} }
scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name;
script = doc.createElement("script");
window[callback_name] = function(a){ window[callback_name] = function(a){
if (a.substring(0,6) === "error:"){ if (a.substring(0,6) === "error:"){
imgObj.succeeded = false; imageObj.succeeded = false;
images.numLoaded++; images.numLoaded++;
images.numFailed++; images.numFailed++;
start(); start();
} else { } else {
img.onload = function(){ setImageLoadHandlers(img, imageObj);
imgObj.succeeded = true;
images.numLoaded++;
start();
};
img.src = a; img.src = a;
} }
window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
@ -100,52 +97,15 @@ html2canvas.Preload = function(element, opts){
} catch(ex) {} } catch(ex) {}
script.parentNode.removeChild(script); script.parentNode.removeChild(script);
script = null; script = null;
imgObj.callbackname = undefined; delete imageObj.script;
delete imageObj.callbackname;
}; };
count += 1;
script = doc.createElement("script");
script.setAttribute("src", scriptUrl);
script.setAttribute("type", "text/javascript"); script.setAttribute("type", "text/javascript");
imgObj.script = script; script.setAttribute("src", scriptUrl);
imageObj.script = script;
window.document.body.appendChild(script); window.document.body.appendChild(script);
/*
// enable xhr2 requests where available (no need for base64 / json)
$.ajax({
data:{
xhr2:false,
url:url
},
url: options.proxy,
dataType: "jsonp",
success: function(a){
if (a.substring(0,6) === "error:"){
images.splice(getIndex(images, url), 2);
start();
}else{
img.onload = function(){
imagesLoaded+=1;
start();
};
img.src = a;
}
},
error: function(){
images.splice(getIndex(images, url), 2);
start();
}
});
*/
} }
function getImages (el) { function getImages (el) {
@ -155,7 +115,7 @@ html2canvas.Preload = function(element, opts){
// if (!this.ignoreRe.test(el.nodeName)){ // if (!this.ignoreRe.test(el.nodeName)){
// //
var contents = html2canvas.Util.Children(el), var contents = _html2canvas.Util.Children(el),
i, i,
contentsLen = contents.length, contentsLen = contents.length,
background_image, background_image,
@ -175,23 +135,30 @@ html2canvas.Preload = function(element, opts){
elNodeType = el.nodeType; elNodeType = el.nodeType;
} catch (ex) { } catch (ex) {
elNodeType = false; elNodeType = false;
html2canvas.log("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
} }
if (elNodeType === 1 || elNodeType === undefined){ if (elNodeType === 1 || elNodeType === undefined){
background_image = html2canvas.Util.getCSS(el, 'backgroundImage'); // opera throws exception on external-content.html
try {
background_image = _html2canvas.Util.getCSS(el, 'backgroundImage');
}catch(e) {
h2clog("html2canvas: failed to get background-image - Exception: " + e.message);
}
if ( background_image && background_image !== "1" && background_image !== "none" ) { if ( background_image && background_image !== "1" && background_image !== "none" ) {
// TODO add multi image background support // TODO add multi image background support
if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") { if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") {
img = html2canvas.Generate.Gradient( background_image, html2canvas.Util.Bounds( el ) ); img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) );
if ( img !== undefined ){ if ( img !== undefined ){
images[background_image] = { img: img, succeeded: true }; images[background_image] = {
img: img,
succeeded: true
};
images.numTotal++; images.numTotal++;
images.numLoaded++; images.numLoaded++;
start(); start();
@ -199,8 +166,8 @@ html2canvas.Preload = function(element, opts){
} }
} else { } else {
src = html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]);
methods.loadImage(src); methods.loadImage(src);
} }
/* /*
@ -212,47 +179,90 @@ html2canvas.Preload = function(element, opts){
} }
} }
function setImageLoadHandlers(img, imageObj) {
img.onload = function() {
if ( imageObj.timer !== undefined ) {
// CORS succeeded
window.clearTimeout( imageObj.timer );
}
images.numLoaded++;
imageObj.succeeded = true;
start();
};
img.onerror = function() {
if (img.crossOrigin === "anonymous") {
// CORS failed
window.clearTimeout( imageObj.timer );
// let's try with proxy instead
if ( options.proxy ) {
var src = img.src;
img = new Image();
imageObj.img = img;
img.src = src;
proxyGetImage( img.src, img, imageObj );
return;
}
}
images.numLoaded++;
images.numFailed++;
imageObj.succeeded = false;
start();
};
}
methods = { methods = {
loadImage: function( src ) { loadImage: function( src ) {
var img; var img, imageObj;
if ( src && images[src] === undefined ) { if ( src && images[src] === undefined ) {
img = new Image();
if ( src.match(/data:image\/.*;base64,/i) ) { if ( src.match(/data:image\/.*;base64,/i) ) {
//Base64 src
img = new Image();
img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, '');
images[src] = { img: img, succeeded: true }; imageObj = images[src] = {
images.numTotal++; img: img
images.numLoaded++;
start();
}else if ( isSameOrigin( src ) ) {
img = new Image();
images[src] = { img: img };
images.numTotal++;
img.onload = function() {
images.numLoaded++;
images[src].succeeded = true;
start();
};
img.onerror = function() {
images.numLoaded++;
images.numFailed++;
images[src].succeeded = false;
start();
}; };
img.src = src;
}else if ( options.proxy ){
// console.log('b'+src);
img = new Image();
images[src] = { img: img };
images.numTotal++; images.numTotal++;
proxyGetImage( src, img ); setImageLoadHandlers(img, imageObj);
} else if ( isSameOrigin( src ) || options.allowTaint === true ) {
imageObj = images[src] = {
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
img.src = src;
} else if ( supportCORS && !options.allowTaint && options.useCORS ) {
// attempt to load with CORS
img.crossOrigin = "anonymous";
imageObj = images[src] = {
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
img.src = src;
// work around for https://bugs.webkit.org/show_bug.cgi?id=80028
img.customComplete = function () {
if (!this.img.complete) {
this.timer = window.setTimeout(this.img.customComplete, 100);
} else {
this.img.onerror();
}
}.bind(imageObj);
img.customComplete();
} else if ( options.proxy ) {
imageObj = images[src] = {
img: img
};
images.numTotal++;
proxyGetImage( src, img, imageObj );
} }
} }
@ -261,9 +271,9 @@ html2canvas.Preload = function(element, opts){
var img, src; var img, src;
if (!images.cleanupDone) { if (!images.cleanupDone) {
if (cause && typeof cause === "string") { if (cause && typeof cause === "string") {
html2canvas.log("html2canvas: Cleanup because: " + cause); h2clog("html2canvas: Cleanup because: " + cause);
} else { } else {
html2canvas.log("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");
} }
for (src in images) { for (src in images) {
@ -281,7 +291,7 @@ html2canvas.Preload = function(element, opts){
} }
images.numLoaded++; images.numLoaded++;
images.numFailed++; images.numFailed++;
html2canvas.log("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal);
} }
} }
} }
@ -302,9 +312,9 @@ html2canvas.Preload = function(element, opts){
} }
}, },
renderingDone: function() { renderingDone: function() {
if (timeoutTimer) { if (timeoutTimer) {
window.clearTimeout(timeoutTimer); window.clearTimeout(timeoutTimer);
} }
} }
}; };
@ -312,20 +322,19 @@ html2canvas.Preload = function(element, opts){
if (options.timeout > 0) { if (options.timeout > 0) {
timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout);
} }
var startTime = (new Date()).getTime(); h2clog('html2canvas: Preload starts: finding background-images');
html2canvas.log('html2canvas: Preload starts: finding background-images');
images.firstRun = true; images.firstRun = true;
getImages( element ); getImages( element );
html2canvas.log('html2canvas: Preload: Finding images'); h2clog('html2canvas: Preload: Finding images');
// load <img> images // load <img> images
for (i = 0; i < imgLen; i+=1){ for (i = 0; i < imgLen; i+=1){
methods.loadImage( domImages[i].getAttribute( "src" ) ); methods.loadImage( domImages[i].getAttribute( "src" ) );
} }
images.firstRun = false; images.firstRun = false;
html2canvas.log('html2canvas: Preload: Done.'); h2clog('html2canvas: Preload: Done.');
if ( images.numTotal === images.numLoaded ) { if ( images.numTotal === images.numLoaded ) {
start(); start();
} }

View File

@ -5,7 +5,7 @@
Released under MIT License Released under MIT License
*/ */
html2canvas.canvasContext = function (width, height) { function h2cRenderContext(width, height) {
var storage = []; var storage = [];
return { return {
storage: storage, storage: storage,
@ -40,4 +40,4 @@ html2canvas.canvasContext = function (width, height) {
}); });
} }
}; };
}; }

View File

@ -5,19 +5,15 @@
Released under MIT License Released under MIT License
*/ */
html2canvas.Renderer = function(parseQueue, opts){ _html2canvas.Renderer = function(parseQueue, options){
var options = { var queue = [],
"width": null,
"height": null,
"renderer": "canvas"
},
queue = [],
canvas, canvas,
usingFlashcanvas = false,
flashMaxSize = 2880, // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata
doc = document; doc = document;
options = html2canvas.Util.Extend(opts, options);
@ -77,15 +73,21 @@ html2canvas.Renderer = function(parseQueue, opts){
i, i,
queueLen, queueLen,
a, a,
newCanvas,
bounds,
testCanvas = document.createElement("canvas"),
hasCTX = ( testCanvas.getContext !== undefined ),
storageLen, storageLen,
renderItem, renderItem,
testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {},
safeImages = [],
fstyle; fstyle;
canvas.width = options.width || zStack.ctx.width; canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) );
canvas.height = options.height || zStack.ctx.height; canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) );
fstyle = ctx.fillStyle; fstyle = ctx.fillStyle;
ctx.fillStyle = "#fff"; ctx.fillStyle = zStack.backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = fstyle; ctx.fillStyle = fstyle;
@ -114,7 +116,6 @@ html2canvas.Renderer = function(parseQueue, opts){
renderItem = storageContext.ctx.storage[a]; renderItem = storageContext.ctx.storage[a];
switch(renderItem.type){ switch(renderItem.type){
case "variable": case "variable":
@ -122,32 +123,33 @@ html2canvas.Renderer = function(parseQueue, opts){
break; break;
case "function": case "function":
if (renderItem.name === "fillRect") { if (renderItem.name === "fillRect") {
ctx.fillRect( if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) {
renderItem['arguments'][0], ctx.fillRect.apply( ctx, renderItem['arguments'] );
renderItem['arguments'][1], }
renderItem['arguments'][2],
renderItem['arguments'][3]
);
}else if(renderItem.name === "fillText") { }else if(renderItem.name === "fillText") {
// console.log(renderItem.arguments[0]); if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) {
ctx.fillText(renderItem['arguments'][0],renderItem['arguments'][1],renderItem['arguments'][2]); ctx.fillText.apply( ctx, renderItem['arguments'] );
}
}else if(renderItem.name === "drawImage") { }else if(renderItem.name === "drawImage") {
// console.log(renderItem);
// console.log(renderItem.arguments[0].width); if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){
if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ if ( hasCTX && options.taintTest ) {
ctx.drawImage( if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) {
renderItem['arguments'][0], testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 );
renderItem['arguments'][1], try {
renderItem['arguments'][2], testctx.getImageData( 0, 0, 1, 1 );
renderItem['arguments'][3], } catch(e) {
renderItem['arguments'][4], testCanvas = document.createElement("canvas");
renderItem['arguments'][5], testctx = testCanvas.getContext("2d");
renderItem['arguments'][6], continue;
renderItem['arguments'][7], }
renderItem['arguments'][8]
); safeImages.push( renderItem['arguments'][ 0 ].src );
}
}
ctx.drawImage.apply( ctx, renderItem['arguments'] );
} }
} }
@ -168,9 +170,38 @@ html2canvas.Renderer = function(parseQueue, opts){
} }
html2canvas.log("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj");
// this.canvasRenderStorage(queue,this.ctx); // this.canvasRenderStorage(queue,this.ctx);
queueLen = options.elements.length;
if (queueLen === 1) {
if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) {
// crop image to the bounds of selected (single) element
bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] );
newCanvas = doc.createElement('canvas');
newCanvas.width = bounds.width;
newCanvas.height = bounds.height;
ctx = newCanvas.getContext("2d");
ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height );
canvas = null;
return newCanvas;
}
} /*else {
// TODO clip and resize multiple elements
for ( i = 0; i < queueLen; i+=1 ) {
if (options.elements[ i ] instanceof Element) {
}
}
}
*/
return canvas; return canvas;
} }
@ -353,7 +384,7 @@ html2canvas.Renderer = function(parseQueue, opts){
html2canvas.log("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj"); h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj");
return svg; return svg;
@ -368,13 +399,62 @@ html2canvas.Renderer = function(parseQueue, opts){
case "canvas": case "canvas":
canvas = doc.createElement('canvas'); canvas = doc.createElement('canvas');
if (canvas.getContext){ if (canvas.getContext){
html2canvas.log("html2canvas: Renderer: using canvas renderer"); h2clog("html2canvas: Renderer: using canvas renderer");
return canvasRenderer(parseQueue); return canvasRenderer(parseQueue);
} else {
usingFlashcanvas = true;
h2clog("html2canvas: Renderer: canvas not available, using flashcanvas");
var script = doc.createElement("script");
script.src = options.flashcanvas;
script.onload = (function(script, func){
var intervalFunc;
if (script.onload === undefined) {
// IE lack of support for script onload
if( script.onreadystatechange !== undefined ) {
intervalFunc = function() {
if (script.readyState !== "loaded" && script.readyState !== "complete") {
window.setTimeout( intervalFunc, 250 );
} else {
// it is loaded
func();
}
};
window.setTimeout( intervalFunc, 250 );
} else {
h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded");
}
} else {
return func;
}
})(script, function(){
if (typeof window.FlashCanvas !== "undefined") {
h2clog("html2canvas: Renderer: Flashcanvas initialized");
window.FlashCanvas.initElement( canvas );
canvasRenderer(parseQueue);
}
});
doc.body.appendChild( script );
return canvas;
} }
break; break;
case "svg": case "svg":
if (doc.createElementNS){ if (doc.createElementNS){
html2canvas.log("html2canvas: Renderer: using SVG renderer"); h2clog("html2canvas: Renderer: using SVG renderer");
return svgRenderer(parseQueue); return svgRenderer(parseQueue);
} }
break; break;
@ -384,11 +464,10 @@ html2canvas.Renderer = function(parseQueue, opts){
//}); //});
return this; return this;
}; };

View File

@ -4,4 +4,79 @@
http://www.twitter.com/niklasvh http://www.twitter.com/niklasvh
Released under MIT License Released under MIT License
*/ */
html2canvas = function( elements, opts ) {
var queue,
canvas,
options = {
// general
logging: false,
// preload options
proxy: "http://html2canvas.appspot.com/",
timeout: 0, // no timeout
useCORS: false, // try to load images as CORS (where available), before falling back to proxy
allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true
// parse options
iframeDefault: "default",
ignoreElements: "IFRAME|OBJECT|PARAM",
useOverflow: true,
letterRendering: false,
// render options
width: null,
height: null,
renderer: "canvas",
taintTest: true // do a taint test with all images before applying to canvas
};
options = _html2canvas.Util.Extend(opts, options);
_html2canvas.logging = options.logging;
options.complete = function( images ) {
if (typeof options.onpreloaded === "function") {
if ( options.onpreloaded( images ) === false ) {
return;
}
}
queue = _html2canvas.Parse( elements, images, options);
if (typeof options.onparsed === "function") {
if ( options.onparsed( queue ) === false ) {
return;
}
}
canvas = _html2canvas.Renderer(queue, options);
if (typeof options.onrendered === "function") {
options.onrendered( canvas );
}
};
// for pages without images, we still want this to be async, i.e. return methods before executing
window.setTimeout( function(){
_html2canvas.Preload( elements, options );
}, 0 );
return {
render: function( queue, opts ) {
return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );
},
parse: function( elements, images, opts ) {
return _html2canvas.Parse( elements, images, _html2canvas.Util.Extend(opts, options) );
},
preload: function( elements, opts ) {
return _html2canvas.Preload( elements, _html2canvas.Util.Extend(opts, options) );
},
log: h2clog
};
};

2
src/html2canvas-post.txt Normal file
View File

@ -0,0 +1,2 @@
window.html2canvas = html2canvas;
}(window, document));

1
src/html2canvas-pre.txt Normal file
View File

@ -0,0 +1 @@
(function(window, document, undefined){

View File

@ -1,6 +1,6 @@
(function() { (function() {
/* options, customize to your needs */ /* options, customize to your needs */
var server = '//html2canvas.hertzen.com/build', var server = '//html2canvas.hertzen.com/js',
proxy = '//html2canvas.appspot.com', proxy = '//html2canvas.appspot.com',
debug = false, debug = false,
profile = false; profile = false;

View File

@ -7,29 +7,45 @@
console.profile(); console.profile();
} }
var date = new Date(), var date = new Date(),
html2obj,
$message = null, $message = null,
timeoutTimer = false, timeoutTimer = false,
timer = date.getTime(); timer = date.getTime();
html2canvas.logging = options && options.logging; options = options || {};
html2canvas.Preload(this[0], $.extend({ options.elements = this;
complete: function(images){
var queue = html2canvas.Parse(this[0], images, options),
$canvas = $(html2canvas.Renderer(queue, options)),
finishTime = new Date();
if (options && options.profile && window.console && window.console.profileEnd) { options.onrendered = function( canvas ) {
console.profileEnd(); var $canvas = $(canvas),
} finishTime = new Date();
$canvas.css({ position: 'absolute', left: 0, top: 0 }).appendTo(document.body);
$canvas.siblings().toggle();
$(window).click(function(){ if (options && options.profile && window.console && window.console.profileEnd) {
$canvas.toggle().siblings().toggle(); console.profileEnd();
throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden"));
});
throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms<br />",4000);
} }
}, options)); $canvas.css({
position: 'absolute',
left: 0,
top: 0
}).appendTo(document.body);
$canvas.siblings().toggle();
$(window).click(function(){
$canvas.toggle().siblings().toggle();
throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden"));
});
throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms<br />",4000);
// test if canvas is read-able
try {
$canvas[0].toDataURL();
} catch(e) {
if ($canvas[0].nodeName.toLowerCase() === "canvas") {
// TODO, maybe add a bit less offensive way to present this, but still something that can easily be noticed
alert("Canvas is tainted, unable to read data");
}
}
};
html2obj = html2canvas(this[0], options);
function throwMessage(msg,duration){ function throwMessage(msg,duration){
window.clearTimeout(timeoutTimer); window.clearTimeout(timeoutTimer);
@ -59,7 +75,7 @@
textDecoration:'none', textDecoration:'none',
display:'none' display:'none'
}).appendTo(document.body).fadeIn(); }).appendTo(document.body).fadeIn();
html2canvas.log(msg); html2obj.log(msg);
} }
}; };
})( jQuery ); })( jQuery );

46
tests/image.svg Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" height="252.89000pt" width="493.28000pt" version="1.0" y="0.00000000" x="0.00000000" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="lg0">
<stop style="stop-color:#ff0000;stop-opacity:1.0000000" offset="0.00000000"/>
<stop style="stop-color:#00ff00;stop-opacity:1.0000000" offset="0.50000000"/>
<stop style="stop-color:#0000ff;stop-opacity:1.0000000" offset="1.0000000"/>
</linearGradient>
<linearGradient id="lg1">
<stop style="stop-color:#ff0000;stop-opacity:0.27450982" offset="0.00000000"/>
<stop style="stop-color:#ff0000;stop-opacity:1.0000000" offset="1.0000000"/>
</linearGradient>
<radialGradient id="rd0" fx="550.28571" fy="155.11731" xlink:href="#lg1" gradientUnits="userSpaceOnUse" cy="155.11731" cx="550.28571" gradientTransform="matrix(0.652228,-1.522906,1.403595,0.601129,-26.34767,869.2927)" r="127.00000"/>
<radialGradient id="rd1" fx="492.85715" fy="379.50504" xlink:href="#lg3" gradientUnits="userSpaceOnUse" cy="379.50504" cx="492.85715" gradientTransform="matrix(0.944964,4.150569e-2,-4.340623e-2,0.988234,43.59757,-15.99113)" r="184.96443"/>
<radialGradient id="rd2" fx="449.12918" fy="345.23175" xlink:href="#lg2" gradientUnits="userSpaceOnUse" cy="345.23175" cx="449.12918" gradientTransform="matrix(1.06455,-4.457048e-3,4.186833e-3,1.000012,-30.43703,1.997764)" r="184.96443"/>
<linearGradient id="lg2">
<stop style="stop-color:#fa4;stop-opacity:1" offset="0"/>
<stop style="stop-color:#c3791f;stop-opacity:1" offset="0.5"/>
<stop style="stop-color:#935000;stop-opacity:1" offset="1"/>
</linearGradient>
<linearGradient id="lg3">
<stop style="stop-color:black;stop-opacity:1" offset="0"/>
<stop style="stop-color:black;stop-opacity:1" offset="0.5"/>
<stop style="stop-color:black;stop-opacity:1" offset="0.75"/>
<stop style="stop-color:black;stop-opacity:0.72164947" offset="0.875"/>
<stop style="stop-color:black;stop-opacity:0.50515461" offset="0.9375"/>
<stop style="stop-color:black;stop-opacity:0.3298969" offset="0.96875"/>
<stop style="stop-color:black;stop-opacity:0" offset="1"/>
</linearGradient>
</defs>
<path d="M300 252.36218C300 307.59065 255.22847 352.36218 200 352.36218 144.77153 352.36218 100 307.59065 100 252.36218 100 197.13371 144.77153 152.36218 200 152.36218 255.22847 152.36218 300 197.13371 300 252.36218L300 252.36218z" style="fill:#00ffff;fill-opacity:0.49999997;fill-rule:evenodd;stroke:#00ffff;stroke-width:4.0000000;stroke-linecap:butt;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" transform="translate(-91.79890,-143.8324)"/>
<path d="M500 252.36218C500 307.59065 455.22847 352.36218 400 352.36218 344.77153 352.36218 300 307.59065 300 252.36218 300 197.13371 344.77153 152.36218 400 152.36218 455.22847 152.36218 500 197.13371 500 252.36218L500 252.36218z" style="fill:#ffff00;fill-opacity:0.49999997;fill-rule:evenodd;stroke:#ffff00;stroke-width:4.0000000;stroke-linecap:butt;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" transform="translate(-242.7989,-42.83241)"/>
<path d="M400 452.36218C400 507.59065 355.22847 552.36218 300 552.36218 244.77153 552.36218 200 507.59065 200 452.36218 200 397.13371 244.77153 352.36218 300 352.36218 355.22847 352.36218 400 397.13371 400 452.36218L400 452.36218z" style="fill:#ff00ff;fill-opacity:0.49999997;fill-rule:evenodd;stroke:#ff00ff;stroke-width:4.0000000;stroke-linecap:butt;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" transform="translate(-90.79890,-342.8324)"/>
<rect style="fill:url(#rd0);fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000;stroke-width:4.0000000;stroke-linecap:butt;stroke-miterlimit:4.0000000;stroke-dasharray:8.0000000 4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" rx="41.428570" ry="41.428570" transform="translate(6.201104,5.167586)" width="250.00000" y="2.3621826" x="351.00000" height="150.00000"/>
<text style="font-size:72px;font-style:oblique;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr;text-anchor:start;fill:#fff;fill-opacity:0.49999997;stroke:#000;stroke-width:3.0000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:6.0000000 3.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" xml:space="preserve" transform="translate(6.201104,5.167586)" y="101.34265" x="398.91016"><tspan y="101.34265" x="398.91016">SVG</tspan></text>
<g transform="matrix(0.403355,0.000000,0.000000,0.403355,284.7118,53.56855)">
<path style="fill:url(#rd1);fill-opacity:1.0000000;stroke:none;stroke-width:4.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" d="M675.82158 379.50504A182.96443 182.96443 0 1 0 309.89272 379.50504 182.96443 182.96443 0 1 0 675.82158 379.50504z" transform="translate(25.71677,42.14162)"/>
<path style="fill:url(#rd2);fill-opacity:1.0000000;stroke:none;stroke-width:4.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" d="M675.82158 379.50504A182.96443 182.96443 0 1 0 309.89272 379.50504 182.96443 182.96443 0 1 0 675.82158 379.50504z" transform="translate(3.000000,1.000000)"/>
<path style="color:#000;fill:#000;fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:4.0000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000;marker:none" d="M448.21432 203.83901C450.36313 204.6315 453.75174 205.94795 456.34375 207.71875 458.93576 209.48955 460.70727 211.5991 460.84375 214 461.1565 219.5018 462.73056 224.22855 456.3125 234.21875 449.89444 244.20895 435.16134 259.07637 402.75 282.4375 341.89198 326.30215 327.69756 419.11497 324.82774 445.4561L327.9384 453.22053C327.9384 453.22053 336.06337 335.44254 405.09375 285.6875 437.69027 262.19289 452.72065 247.17079 459.65625 236.375 466.59185 225.57921 465.12192 218.64356 464.84375 213.75 464.60642 209.57479 461.69349 206.55518 458.59375 204.4375 457.315 203.56388 455.94644 202.87002 454.65334 202.2368L448.21432 203.83901z"/>
<path style="color:#000;fill:#000;fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:4.0000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000;marker:none" d="M509.01528 198.02937C499.53358 209.87282 477.91722 245.5091 465.15625 336.75 449.39628 449.43374 450.70852 546.83082 450.91598 557.84038L454.9375 558.75C454.9375 558.75 452.43678 456.60195 469.125 337.28125 482.74755 239.88008 506.43369 206.85787 513.90048 198.46178 513.90048 198.46178 509.01528 198.02937 509.01528 198.02937z"/>
<path style="color:#000;fill:#000;fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:4.0000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000;marker:none" d="M556.6875 211.625C547.0438 211.8095 537.01703 214.51544 529.96875 222.90625 520.74474 233.88721 520.91652 245.76284 524.5 256.1875 528.08348 266.61216 534.88069 275.92531 538.96875 282.875 541.20112 286.67003 547.45814 295.57779 555.4375 309.1875 563.41686 322.79721 573.02573 340.97669 581.71875 362.78125 599.10479 406.39038 612.72572 464.47037 601.9375 529.59375 601.9375 529.59375 606.5655 526.08172 606.5655 526.08172 616.26061 461.73706 602.63933 404.42832 585.4375 361.28125 576.65129 339.24295 566.92996 320.8949 558.875 307.15625 550.82004 293.4176 544.35231 284.15204 542.40625 280.84375 538.13746 273.5868 531.59217 264.50677 528.28125 254.875 524.97033 245.24323 524.70586 235.41118 533.03125 225.5 541.18293 215.79562 554.20308 214.66443 565.5625 216.125 576.92192 217.58557 586.26153 221.51972 586.26153 221.51972 586.26153 221.51972 568.49535 212.55885 568.49535 212.55885 567.63548 212.41794 566.98202 212.27046 566.09375 212.15625 563.09277 211.77039 559.90207 211.5635 556.6875 211.625z"/>
<path style="color:#000;fill:#000;fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:4.0000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000;marker:none" d="M458.15625 224.96875C424.33124 226.01594 399.1972 233.81099 381.5 242.40625 376.05652 245.05007 371.36415 247.88263 367.2855 250.51507 362.72383 255.08465 357.48708 260.80582 350.5625 269.40625 350.5625 269.40625 360.3001 257.14641 383.25 246 406.1999 234.85359 442.22471 224.97829 494.53125 230.375 599.18056 241.17215 643.20884 296.98475 675.71875 347 675.71875 347 673.37503 336.37355 673.37503 336.37355 641.03129 288.32441 594.99108 236.69798 494.9375 226.375 481.69006 225.0082 469.43125 224.61969 458.15625 224.96875z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -8,13 +8,17 @@
<script type="text/javascript" src="test.js"></script> <script type="text/javascript" src="test.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function setUp() { function setUp() {
var ctx = $('#testcanvas')[0].getContext('2d'); if ($('#testcanvas')[0].getContext) {
var ctx = $('#testcanvas')[0].getContext('2d');
ctx.fillStyle = "rgb(200,0,0)"; ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50); ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50); ctx.fillRect (30, 30, 55, 50);
} else {
$('#testcanvas').remove();
}
}; };
</script> </script>
@ -56,11 +60,13 @@
<img src="image.jpg" style="width:0px;height:0px;border:1px solid black" /> <img src="image.jpg" style="width:0px;height:0px;border:1px solid black" />
<img src="image.jpg" style="width:0px;height:0px;" /> <img src="image.jpg" style="width:0px;height:0px;" />
<canvas id="testcanvas" style="width:100px;height:100px;"></canvas> <canvas id="testcanvas" style="width:100px;height:100px;"></canvas>
<br /> <br />
Image without src attribute, should not crash: Image without src attribute, should not crash:
<img style="width:50px;height:50px;border:1px solid red;display:block;" /> <img style="width:50px;height:50px;border:1px solid red;display:block;" />
SVG taints image:<br /> <!-- http://fi.wikipedia.org/wiki/Tiedosto:Svg.svg -->
<img src="image.svg" />
</body> </body>
</html> </html>

186
tests/origin.html Normal file
View File

@ -0,0 +1,186 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<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">
$(document).ready(function() {
$('#bar').html2canvas();
// var ss = $('ul').offset();
// alert(ss.left);
});
</script>
<title>
display/box/float/clear test
</title>
<style type="text/css">
/* last modified: 1 Dec 98 */
html {
font: 10px/1 Verdana, sans-serif;
background-color: blue;
color: white;
}
body {
margin: 1.5em;
border: .5em solid black;
padding: 0;
width: 48em;
background-color: white;
}
dl {
margin: 0;
border: 0;
padding: .5em;
}
dt {
background-color: rgb(204,0,0);
margin: 0;
padding: 1em;
width: 10.638%; /* refers to parent element's width of 47em. = 5em or 50px */
height: 28em;
border: .5em solid black;
float: left;
}
dd {
float: right;
margin: 0 0 0 1em;
border: 1em solid black;
padding: 1em;
width: 34em;
height: 27em;
}
ul {
margin: 0;
border: 0;
padding: 0;
}
li {
display: block; /* i.e., suppress marker */
color: black;
height: 9em;
width: 5em;
margin: 0;
border: .5em solid black;
padding: 1em;
float: left;
background-color: #FC0;
}
#bar {
background-color: black;
color: white;
width: 41.17%; /* = 14em */
border: 0;
margin: 0 1em;
}
#baz {
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
background-color: black;
color: white;
}
form {
margin: 0;
display: inline;
}
p {
margin: 0;
}
form p {
line-height: 1.9;
}
blockquote {
margin: 1em 1em 1em 2em;
border-width: 1em 1.5em 2em .5em;
border-style: solid;
border-color: black;
padding: 1em 0;
width: 5em;
height: 9em;
float: left;
background-color: #FC0;
color: black;
}
address {
font-style: normal;
}
h1 {
background-color: black;
color: white;
float: left;
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
font-weight: normal;
font-size: 1em;
}
</style>
</head>
<body>
<dl>
<dt>
toggle
</dt>
<dd>
<ul>
<li>
the way
</li>
<li id="bar">
<p>
the world ends
</p>
<form action="./" method="get">
<p>
bang
<input type="radio" name="foo" value="off">
</p>
<p>
whimper
<input type="radio" name="foo2" value="on">
</p>
</form>
</li>
<li>
i grow old
</li>
<li id="baz">
pluot?
</li>
</ul>
<blockquote>
<address>
bar maids,
</address>
</blockquote>
<h1>
sing to me, erbarme dich
</h1>
</dd>
</dl>
<p style="color: black; font-size: 1em; line-height: 1.3em; clear: both">
This is a nonsensical document, but syntactically valid HTML 4.0. All 100% conformant CSS1 agents should be able to render the document elements above this paragraph <b>indistinguishably</b> (to the pixel) from this reference rendering, (except font rasterization and form widgets). All discrepancies should be traceable to CSS1 implementation shortcomings. Once you have finished evaluating this test, you can return to the <A HREF="sec5526c.htm" style="text-decoration:none">parent page</A>.
</p>
</body>
</html>

View File

@ -5,7 +5,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="test.js"></script> <script type="text/javascript" src="test.js"></script>
<base href="http://www.google.com/" /> <base href="http://www.google.com/" />
</head> </head>
<body> <body>
<h1>External image</h1> <h1>External image</h1>
@ -14,5 +14,8 @@
<h1>External image (using &lt;base&gt; href)</h1> <h1>External image (using &lt;base&gt; href)</h1>
<img src="/logos/2011/gregormendel11-res.jpg" /> <img src="/logos/2011/gregormendel11-res.jpg" />
<h1>External image (CORS)</h1>
<img src="http://publishmydata.com/assets/home/blue_bg.png" />
</body> </body>
</html> </html>

View File

@ -6,21 +6,23 @@
Released under MIT License Released under MIT License
*/ */
(function(document, window) { (function(document, window) {
var scrStart = '<script type="text/javascript" src="', scrEnd = '"></script>'; var scrStart = '<script type="text/javascript" src="', scrEnd = '"></script>';
document.write(scrStart + '../external/jquery-1.6.2.min.js' + scrEnd); document.write(scrStart + '../external/jquery-1.6.2.js' + scrEnd);
var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'plugins/jquery.plugin.html2canvas'], i; var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'plugins/jquery.plugin.html2canvas'], i;
for (i = 0; i < html2canvas.length; ++i) { for (i = 0; i < html2canvas.length; ++i) {
document.write(scrStart + '../src/' + html2canvas[i] + '.js' + scrEnd); document.write(scrStart + '../src/' + html2canvas[i] + '.js' + scrEnd);
}
window.onload = function() {
if (window.setUp) {
window.setUp();
} }
setTimeout(function() { window.onload = function() {
$(document.body).html2canvas({ if (window.setUp) {
logging: true, window.setUp();
profile: true }
}); setTimeout(function() {
}, 100); $(document.body).html2canvas({
}; flashcanvas: "../external/flashcanvas.min.js",
logging: true,
profile: true,
useCORS: true
});
}, 100);
};
}(document, window)); }(document, window));

View File

@ -1 +1 @@
v0.32 v0.33