1- _html2canvas . Renderer . Canvas = function ( options ) {
1+ _html2canvas . Renderer . Canvas = function ( options ) {
22
33 options = options || { } ;
44
55 var doc = document ,
6- canvas = options . canvas || doc . createElement ( 'canvas' ) ,
7- usingFlashcanvas = false ,
8- _createCalled = false ,
9- canvasReadyToDraw = false ,
10- methods ,
11- flashMaxSize = 2880 ; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata
6+ safeImages = [ ] ,
7+ testCanvas = document . createElement ( "canvas" ) ,
8+ testctx = testCanvas . getContext ( "2d" ) ,
9+ canvas = options . canvas || doc . createElement ( 'canvas' ) ;
1210
1311
14- if ( canvas . getContext ) {
15- h2clog ( "html2canvas: Renderer: using canvas renderer" ) ;
16- canvasReadyToDraw = true ;
17- } else if ( options . flashcanvas !== undefined ) {
18- usingFlashcanvas = true ;
19- h2clog ( "html2canvas: Renderer: canvas not available, using flashcanvas" ) ;
20- var script = doc . createElement ( "script" ) ;
21- script . src = options . flashcanvas ;
22-
23- script . onload = ( function ( script , func ) {
24- var intervalFunc ;
25-
26- if ( script . onload === undefined ) {
27- // IE lack of support for script onload
28-
29- if ( script . onreadystatechange !== undefined ) {
30-
31- intervalFunc = function ( ) {
32- if ( script . readyState !== "loaded" && script . readyState !== "complete" ) {
33- window . setTimeout ( intervalFunc , 250 ) ;
34-
35- } else {
36- // it is loaded
37- func ( ) ;
38-
39- }
40-
41- } ;
42-
43- window . setTimeout ( intervalFunc , 250 ) ;
44-
45- } else {
46- h2clog ( "html2canvas: Renderer: Can't track when flashcanvas is loaded" ) ;
47- }
48-
49- } else {
50- return func ;
51- }
52-
53- } ) ( script , function ( ) {
54-
55- if ( typeof window . FlashCanvas !== "undefined" ) {
56- h2clog ( "html2canvas: Renderer: Flashcanvas initialized" ) ;
57- window . FlashCanvas . initElement ( canvas ) ;
58-
59- canvasReadyToDraw = true ;
60- if ( _createCalled !== false ) {
61- methods . _create . apply ( null , _createCalled ) ;
62- }
63- }
12+ function createShape ( ctx , args ) {
13+ ctx . beginPath ( ) ;
14+ args . forEach ( function ( arg ) {
15+ ctx [ arg . name ] . apply ( ctx , arg [ 'arguments' ] ) ;
6416 } ) ;
65-
66- doc . body . appendChild ( script ) ;
67-
17+ ctx . closePath ( ) ;
6818 }
6919
70- methods = {
71- _create : function ( zStack , options , doc , queue , _html2canvas ) {
72-
73- if ( ! canvasReadyToDraw ) {
74- _createCalled = arguments ;
75- return canvas ;
20+ function safeImage ( item ) {
21+ if ( safeImages . indexOf ( item [ 'arguments' ] [ 0 ] . src ) === - 1 ) {
22+ testctx . drawImage ( item [ 'arguments' ] [ 0 ] , 0 , 0 ) ;
23+ try {
24+ testctx . getImageData ( 0 , 0 , 1 , 1 ) ;
25+ } catch ( e ) {
26+ testCanvas = doc . createElement ( "canvas" ) ;
27+ testctx = testCanvas . getContext ( "2d" ) ;
28+ return false ;
7629 }
30+ safeImages . push ( item [ 'arguments' ] [ 0 ] . src ) ;
31+ }
32+ return true ;
33+ }
7734
78- var ctx = canvas . getContext ( "2d" ) ,
79- storageContext ,
80- i ,
81- queueLen ,
82- a ,
83- newCanvas ,
84- bounds ,
85- testCanvas = document . createElement ( "canvas" ) ,
86- hasCTX = ( testCanvas . getContext !== undefined ) ,
87- storageLen ,
88- renderItem ,
89- testctx = ( hasCTX ) ? testCanvas . getContext ( "2d" ) : { } ,
90- safeImages = [ ] ,
91- fstyle ;
92-
93- canvas . width = canvas . style . width = ( ! usingFlashcanvas ) ? options . width || zStack . ctx . width : Math . min ( flashMaxSize , ( options . width || zStack . ctx . width ) ) ;
94- canvas . height = canvas . style . height = ( ! usingFlashcanvas ) ? options . height || zStack . ctx . height : Math . min ( flashMaxSize , ( options . height || zStack . ctx . height ) ) ;
95-
96- fstyle = ctx . fillStyle ;
97- ctx . fillStyle = ( zStack . backgroundColor === "transparent" || zStack . backgroundColor === "rgba(0, 0, 0, 0)" ) ? "#fff" : zStack . backgroundColor ;
98- ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
99- ctx . fillStyle = fstyle ;
100-
101- var createShape = function ( args ) {
102- ctx . beginPath ( ) ;
103- args . forEach ( function ( arg ) {
104- ctx [ arg . name ] . apply ( ctx , arg [ 'arguments' ] ) ;
105- } ) ;
106- ctx . closePath ( ) ;
107- } ;
108-
109- if ( options . svgRendering && zStack . svgRender !== undefined ) {
110- // TODO: enable async rendering to support this
111- ctx . drawImage ( zStack . svgRender , 0 , 0 ) ;
112- } else {
113- for ( i = 0 , queueLen = queue . length ; i < queueLen ; i += 1 ) {
114-
115- storageContext = queue . splice ( 0 , 1 ) [ 0 ] ;
116- storageContext . canvasPosition = storageContext . canvasPosition || { } ;
117-
118- //this.canvasRenderContext(storageContext,parentctx);
119-
120- // set common settings for canvas
121- ctx . textBaseline = "bottom" ;
122-
123- if ( storageContext . clip ) {
124- ctx . save ( ) ;
125- ctx . beginPath ( ) ;
126- // console.log(storageContext);
127- ctx . rect ( storageContext . clip . left , storageContext . clip . top , storageContext . clip . width , storageContext . clip . height ) ;
128- ctx . clip ( ) ;
129-
130- }
131-
132- if ( storageContext . ctx . storage ) {
133-
134- for ( a = 0 , storageLen = storageContext . ctx . storage . length ; a < storageLen ; a += 1 ) {
135-
136- renderItem = storageContext . ctx . storage [ a ] ;
137-
138-
139- switch ( renderItem . type ) {
140- case "variable" :
141- ctx [ renderItem . name ] = renderItem [ 'arguments' ] ;
142- break ;
143- case "function" :
144- if ( renderItem . name === "fillRect" ) {
145- if ( ! usingFlashcanvas || renderItem [ 'arguments' ] [ 0 ] + renderItem [ 'arguments' ] [ 2 ] < flashMaxSize && renderItem [ 'arguments' ] [ 1 ] + renderItem [ 'arguments' ] [ 3 ] < flashMaxSize ) {
146- ctx . fillRect . apply ( ctx , renderItem [ 'arguments' ] ) ;
147- }
148- } else if ( renderItem . name === "createPattern" ) {
149- ctx . fillStyle = ctx . createPattern ( renderItem [ 'arguments' ] [ 0 ] , "repeat" ) ;
150- } else if ( renderItem . name === "drawShape" ) {
151- createShape ( renderItem [ 'arguments' ] ) ;
152- } else if ( renderItem . name === "fillText" ) {
153- if ( ! usingFlashcanvas || renderItem [ 'arguments' ] [ 1 ] < flashMaxSize && renderItem [ 'arguments' ] [ 2 ] < flashMaxSize ) {
154- ctx . fillText . apply ( ctx , renderItem [ 'arguments' ] ) ;
155- }
156- } else if ( renderItem . name === "drawImage" ) {
157-
158- if ( renderItem [ 'arguments' ] [ 8 ] > 0 && renderItem [ 'arguments' ] [ 7 ] > 0 ) {
159- if ( hasCTX && options . taintTest ) {
160- if ( safeImages . indexOf ( renderItem [ 'arguments' ] [ 0 ] . src ) === - 1 ) {
161- testctx . drawImage ( renderItem [ 'arguments' ] [ 0 ] , 0 , 0 ) ;
162- try {
163- testctx . getImageData ( 0 , 0 , 1 , 1 ) ;
164- } catch ( e ) {
165- testCanvas = doc . createElement ( "canvas" ) ;
166- testctx = testCanvas . getContext ( "2d" ) ;
167- continue ;
168- }
169-
170- safeImages . push ( renderItem [ 'arguments' ] [ 0 ] . src ) ;
171-
172- }
173- }
174- ctx . drawImage . apply ( ctx , renderItem [ 'arguments' ] ) ;
175- }
176- } else {
177- ctx [ renderItem . name ] . apply ( ctx , renderItem [ 'arguments' ] ) ;
178- }
179-
180-
181- break ;
182- default :
183-
184- }
35+ function isTransparent ( backgroundColor ) {
36+ return ( backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)" ) ;
37+ }
18538
39+ function renderItem ( ctx , item ) {
40+ switch ( item . type ) {
41+ case "variable" :
42+ ctx [ item . name ] = item [ 'arguments' ] ;
43+ break ;
44+ case "function" :
45+ if ( item . name === "createPattern" ) {
46+ if ( item [ 'arguments' ] [ 0 ] . width > 0 && item [ 'arguments' ] [ 0 ] . height > 0 ) {
47+ try {
48+ ctx . fillStyle = ctx . createPattern ( item [ 'arguments' ] [ 0 ] , "repeat" ) ;
49+ }
50+ catch ( e ) {
51+ h2clog ( "html2canvas: Renderer: Error creating pattern" , e . message ) ;
18652 }
187-
18853 }
189- if ( storageContext . clip ) {
190- ctx . restore ( ) ;
54+ } else if ( item . name === "drawShape" ) {
55+ createShape ( ctx , item [ 'arguments' ] ) ;
56+ } else if ( item . name === "drawImage" ) {
57+ if ( item [ 'arguments' ] [ 8 ] > 0 && item [ 'arguments' ] [ 7 ] > 0 ) {
58+ if ( options . taintTest || ( options . taintTest && safeImage ( item ) ) ) {
59+ ctx . drawImage . apply ( ctx , item [ 'arguments' ] ) ;
60+ }
19161 }
192-
62+ } else {
63+ ctx [ item . name ] . apply ( ctx , item [ 'arguments' ] ) ;
19364 }
194- }
195-
196- h2clog ( "html2canvas: Renderer: Canvas renderer done - returning canvas obj" ) ;
197-
198- queueLen = options . elements . length ;
199-
200- if ( queueLen === 1 ) {
201- if ( typeof options . elements [ 0 ] === "object" && options . elements [ 0 ] . nodeName !== "BODY" && usingFlashcanvas === false ) {
202- // crop image to the bounds of selected (single) element
203- bounds = _html2canvas . Util . Bounds ( options . elements [ 0 ] ) ;
204- newCanvas = doc . createElement ( 'canvas' ) ;
205- newCanvas . width = bounds . width ;
206- newCanvas . height = bounds . height ;
207- ctx = newCanvas . getContext ( "2d" ) ;
65+ break ;
66+ }
67+ }
20868
209- ctx . drawImage ( canvas , bounds . left , bounds . top , bounds . width , bounds . height , 0 , 0 , bounds . width , bounds . height ) ;
210- canvas = null ;
211- return newCanvas ;
69+ return function ( zStack , options , doc , queue , _html2canvas ) {
70+
71+ var ctx = canvas . getContext ( "2d" ) ,
72+ storageContext ,
73+ i ,
74+ queueLen ,
75+ newCanvas ,
76+ bounds ,
77+ fstyle ;
78+
79+ canvas . width = canvas . style . width = options . width || zStack . ctx . width ;
80+ canvas . height = canvas . style . height = options . height || zStack . ctx . height ;
81+
82+ fstyle = ctx . fillStyle ;
83+ ctx . fillStyle = ( isTransparent ( zStack . backgroundColor ) && options . background !== undefined ) ? options . background : zStack . backgroundColor ;
84+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
85+ ctx . fillStyle = fstyle ;
86+
87+
88+ if ( options . svgRendering && zStack . svgRender !== undefined ) {
89+ // TODO: enable async rendering to support this
90+ ctx . drawImage ( zStack . svgRender , 0 , 0 ) ;
91+ } else {
92+ for ( i = 0 , queueLen = queue . length ; i < queueLen ; i += 1 ) {
93+ storageContext = queue . splice ( 0 , 1 ) [ 0 ] ;
94+ storageContext . canvasPosition = storageContext . canvasPosition || { } ;
95+
96+ // set common settings for canvas
97+ ctx . textBaseline = "bottom" ;
98+
99+ if ( storageContext . clip ) {
100+ ctx . save ( ) ;
101+ ctx . beginPath ( ) ;
102+ // console.log(storageContext);
103+ ctx . rect ( storageContext . clip . left , storageContext . clip . top , storageContext . clip . width , storageContext . clip . height ) ;
104+ ctx . clip ( ) ;
212105 }
213- } /*else {
214- // TODO clip and resize multiple elements
215-
216- for ( i = 0; i < queueLen; i+=1 ) {
217- if (options.elements[ i ] instanceof Element) {
218106
219- }
107+ if ( storageContext . ctx . storage ) {
108+ storageContext . ctx . storage . forEach ( renderItem . bind ( null , ctx ) ) ;
109+ }
220110
221- }
111+ if ( storageContext . clip ) {
112+ ctx . restore ( ) ;
222113 }
223- */
114+ }
115+ }
224116
117+ h2clog ( "html2canvas: Renderer: Canvas renderer done - returning canvas obj" ) ;
225118
119+ queueLen = options . elements . length ;
226120
227- return canvas ;
228- }
229- } ;
121+ if ( queueLen === 1 ) {
122+ if ( typeof options . elements [ 0 ] === "object" && options . elements [ 0 ] . nodeName !== "BODY" ) {
123+ // crop image to the bounds of selected (single) element
124+ bounds = _html2canvas . Util . Bounds ( options . elements [ 0 ] ) ;
125+ newCanvas = doc . createElement ( 'canvas' ) ;
126+ newCanvas . width = bounds . width ;
127+ newCanvas . height = bounds . height ;
128+ ctx = newCanvas . getContext ( "2d" ) ;
230129
231- return methods ;
130+ ctx . drawImage ( canvas , bounds . left , bounds . top , bounds . width , bounds . height , 0 , 0 , bounds . width , bounds . height ) ;
131+ canvas = null ;
132+ return newCanvas ;
133+ }
134+ }
232135
136+ return canvas ;
137+ } ;
233138} ;
0 commit comments