Skip to content

Commit a313524

Browse files
committed
refactored renderer
1 parent 7d7deca commit a313524

3 files changed

Lines changed: 115 additions & 209 deletions

File tree

src/Renderer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ _html2canvas.Renderer = function(parseQueue, options){
2222

2323
stackValues.forEach(function(zValue) {
2424
var index;
25-
25+
2626
subStacks.some(function(stack, i){
2727
index = i;
2828
return (stack.zindex === zValue);
@@ -48,11 +48,11 @@ _html2canvas.Renderer = function(parseQueue, options){
4848
throw new Error("Unknown renderer");
4949
}
5050

51-
if ( typeof renderer._create !== "function" ) {
51+
if ( typeof renderer !== "function" ) {
5252
throw new Error("Invalid renderer defined");
5353
}
5454
return renderer;
5555
}
5656

57-
return getRenderer(options.renderer)._create(parseQueue, options, document, createRenderQueue(parseQueue), _html2canvas);
57+
return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue), _html2canvas);
5858
};

src/Util.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ window.html2canvas = function(elements, opts) {
55
// general
66
logging: false,
77
elements: elements,
8+
background: "#fff",
89

910
// preload options
1011
proxy: "",

src/renderers/Canvas.js

Lines changed: 111 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -1,233 +1,138 @@
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

Comments
 (0)