mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Switch to using Promises
This commit is contained in:
parent
27ace9727c
commit
69390d6b8f
@ -13,7 +13,17 @@ window.html2canvas = function(nodeList, options) {
|
|||||||
window.html2canvas.logging = true;
|
window.html2canvas.logging = true;
|
||||||
window.html2canvas.start = Date.now();
|
window.html2canvas.start = Date.now();
|
||||||
}
|
}
|
||||||
createWindowClone(document, window.innerWidth, window.innerHeight).then(function(container) {
|
return renderDocument(document, options, window.innerWidth, window.innerHeight).then(function(canvas) {
|
||||||
|
if (typeof(options.onrendered) === "function") {
|
||||||
|
log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
|
||||||
|
options.onrendered(canvas);
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderDocument(document, options, width, height) {
|
||||||
|
return createWindowClone(document, width, height).then(function(container) {
|
||||||
log("Document cloned");
|
log("Document cloned");
|
||||||
var clonedWindow = container.contentWindow;
|
var clonedWindow = container.contentWindow;
|
||||||
//var element = (nodeList === undefined) ? document.body : nodeList[0];
|
//var element = (nodeList === undefined) ? document.body : nodeList[0];
|
||||||
@ -21,14 +31,16 @@ window.html2canvas = function(nodeList, options) {
|
|||||||
var support = new Support();
|
var support = new Support();
|
||||||
var imageLoader = new ImageLoader(options, support);
|
var imageLoader = new ImageLoader(options, support);
|
||||||
var bounds = NodeParser.prototype.getBounds(node);
|
var bounds = NodeParser.prototype.getBounds(node);
|
||||||
var width = options.type === "view" ? Math.min(bounds.width, window.innerWidth) : documentWidth();
|
var width = options.type === "view" ? Math.min(bounds.width, width) : documentWidth();
|
||||||
var height = options.type === "view" ? Math.min(bounds.height, window.innerHeight) : documentHeight();
|
var height = options.type === "view" ? Math.min(bounds.height, height) : documentHeight();
|
||||||
var renderer = new CanvasRenderer(width, height, imageLoader);
|
var renderer = new CanvasRenderer(width, height, imageLoader);
|
||||||
var parser = new NodeParser(node, renderer, support, imageLoader, options);
|
var parser = new NodeParser(node, renderer, support, imageLoader, options);
|
||||||
|
return parser.ready.then(function() {
|
||||||
window.console.log(parser);
|
container.parentNode.removeChild(container);
|
||||||
|
return renderer.canvas;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function documentWidth () {
|
function documentWidth () {
|
||||||
return Math.max(
|
return Math.max(
|
||||||
@ -46,11 +58,15 @@ function documentHeight () {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function smallImage() {
|
||||||
|
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
|
}
|
||||||
|
|
||||||
function createWindowClone(ownerDocument, width, height) {
|
function createWindowClone(ownerDocument, width, height) {
|
||||||
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
||||||
container = ownerDocument.createElement("iframe");
|
container = ownerDocument.createElement("iframe");
|
||||||
|
|
||||||
container.style.display = "hidden";
|
container.style.visibility = "hidden";
|
||||||
container.style.position = "absolute";
|
container.style.position = "absolute";
|
||||||
container.width = width;
|
container.width = width;
|
||||||
container.height = height;
|
container.height = height;
|
||||||
@ -60,7 +76,7 @@ function createWindowClone(ownerDocument, width, height) {
|
|||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
var loadedTimer = function() {
|
var loadedTimer = function() {
|
||||||
/* Chrome doesn't detect relative background-images assigned in style sheets when fetched through getComputedStyle,
|
/* Chrome doesn't detect relative background-images assigned in style sheets when fetched through getComputedStyle,
|
||||||
before a certain time has passed
|
before a certain time has passed
|
||||||
*/
|
*/
|
||||||
if (container.contentWindow.getComputedStyle(div, null)['backgroundImage'] !== "none") {
|
if (container.contentWindow.getComputedStyle(div, null)['backgroundImage'] !== "none") {
|
||||||
documentClone.body.removeChild(div);
|
documentClone.body.removeChild(div);
|
||||||
@ -84,12 +100,88 @@ function createWindowClone(ownerDocument, width, height) {
|
|||||||
div.className = "html2canvas-ready-test";
|
div.className = "html2canvas-ready-test";
|
||||||
documentClone.body.appendChild(div);
|
documentClone.body.appendChild(div);
|
||||||
var style = documentClone.createElement("style");
|
var style = documentClone.createElement("style");
|
||||||
style.innerHTML = "body div.html2canvas-ready-test { background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); }";
|
style.innerHTML = "body div.html2canvas-ready-test { background-image:url(" + smallImage() + "); }";
|
||||||
documentClone.body.appendChild(style);
|
documentClone.body.appendChild(style);
|
||||||
window.setTimeout(loadedTimer, 1000);
|
window.setTimeout(loadedTimer, 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Font(family, size) {
|
||||||
|
var container = document.createElement('div'),
|
||||||
|
img = document.createElement('img'),
|
||||||
|
span = document.createElement('span'),
|
||||||
|
sampleText = 'Hidden Text',
|
||||||
|
baseline,
|
||||||
|
middle;
|
||||||
|
|
||||||
|
container.style.visibility = "hidden";
|
||||||
|
container.style.fontFamily = family;
|
||||||
|
container.style.fontSize = size;
|
||||||
|
container.style.margin = 0;
|
||||||
|
container.style.padding = 0;
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
img.src = smallImage();
|
||||||
|
img.width = 1;
|
||||||
|
img.height = 1;
|
||||||
|
|
||||||
|
img.style.margin = 0;
|
||||||
|
img.style.padding = 0;
|
||||||
|
img.style.verticalAlign = "baseline";
|
||||||
|
|
||||||
|
span.style.fontFamily = family;
|
||||||
|
span.style.fontSize = size;
|
||||||
|
span.style.margin = 0;
|
||||||
|
span.style.padding = 0;
|
||||||
|
|
||||||
|
span.appendChild(document.createTextNode(sampleText));
|
||||||
|
container.appendChild(span);
|
||||||
|
container.appendChild(img);
|
||||||
|
baseline = (img.offsetTop - span.offsetTop) + 1;
|
||||||
|
|
||||||
|
container.removeChild(span);
|
||||||
|
container.appendChild(document.createTextNode(sampleText));
|
||||||
|
|
||||||
|
container.style.lineHeight = "normal";
|
||||||
|
img.style.verticalAlign = "super";
|
||||||
|
|
||||||
|
middle = (img.offsetTop-container.offsetTop) + 1;
|
||||||
|
|
||||||
|
document.body.removeChild(container);
|
||||||
|
|
||||||
|
this.baseline = baseline;
|
||||||
|
this.lineWidth = 1;
|
||||||
|
this.middle = middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FontMetrics() {
|
||||||
|
this.data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
FontMetrics.prototype.getMetrics = function(family, size) {
|
||||||
|
if (this.data[family + "-" + size] === undefined) {
|
||||||
|
this.data[family + "-" + size] = new Font(family, size);
|
||||||
|
}
|
||||||
|
return this.data[family + "-" + size];
|
||||||
|
};
|
||||||
|
|
||||||
|
function GradientContainer(imageData) {
|
||||||
|
this.src = imageData.value;
|
||||||
|
this.colorStops = [];
|
||||||
|
this.type = null;
|
||||||
|
this.x0 = 0.5;
|
||||||
|
this.y0 = 0.5;
|
||||||
|
this.x1 = 0.5;
|
||||||
|
this.y1 = 0.5;
|
||||||
|
this.promise = Promise.resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
GradientContainer.prototype.TYPES = {
|
||||||
|
LINEAR: 1,
|
||||||
|
RADIAL: 2
|
||||||
|
};
|
||||||
|
|
||||||
function ImageContainer(src, cors) {
|
function ImageContainer(src, cors) {
|
||||||
this.src = src;
|
this.src = src;
|
||||||
this.image = new Image();
|
this.image = new Image();
|
||||||
@ -121,7 +213,7 @@ ImageLoader.prototype.findImages = function(nodes) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ImageLoader.prototype.findBackgroundImage = function(images, container) {
|
ImageLoader.prototype.findBackgroundImage = function(images, container) {
|
||||||
container.parseBackgroundImages().filter(this.isImageBackground).map(this.getBackgroundUrl).forEach(this.addImage(images, this.loadImage), this);
|
container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this);
|
||||||
return images;
|
return images;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,30 +221,33 @@ ImageLoader.prototype.addImage = function(images, callback) {
|
|||||||
return function(newImage) {
|
return function(newImage) {
|
||||||
if (!this.imageExists(images, newImage)) {
|
if (!this.imageExists(images, newImage)) {
|
||||||
images.splice(0, 0, callback.apply(this, arguments));
|
images.splice(0, 0, callback.apply(this, arguments));
|
||||||
log('Added image #' + (images.length), newImage.substring(0, 100));
|
log('Added image #' + (images.length), newImage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageLoader.prototype.getBackgroundUrl = function(imageData) {
|
ImageLoader.prototype.hasImageBackground = function(imageData) {
|
||||||
return imageData.args[0];
|
return imageData.method !== "none";
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageLoader.prototype.isImageBackground = function(imageData) {
|
ImageLoader.prototype.loadImage = function(imageData) {
|
||||||
return imageData.method === "url";
|
if (imageData.method === "url") {
|
||||||
};
|
var src = imageData.args[0];
|
||||||
|
if (src.match(/data:image\/.*;base64,/i)) {
|
||||||
ImageLoader.prototype.loadImage = function(src) {
|
return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
|
||||||
if (src.match(/data:image\/.*;base64,/i)) {
|
} else if (this.isSameOrigin(src) || this.options.allowTaint === true) {
|
||||||
return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
|
return new ImageContainer(src, false);
|
||||||
} else if (this.isSameOrigin(src) || this.options.allowTaint === true) {
|
} else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {
|
||||||
return new ImageContainer(src, false);
|
return new ImageContainer(src, true);
|
||||||
} else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {
|
} else if (this.options.proxy) {
|
||||||
return new ImageContainer(src, true);
|
return new ProxyImageContainer(src);
|
||||||
} else if (this.options.proxy) {
|
} else {
|
||||||
return new ProxyImageContainer(src);
|
return new DummyImageContainer(src);
|
||||||
} else {
|
}
|
||||||
return new DummyImageContainer(src);
|
} else if (imageData.method === "linear-gradient") {
|
||||||
|
return new LinearGradientContainer(imageData);
|
||||||
|
} else if (imageData.method === "gradient") {
|
||||||
|
return new WebkitGradientContainer(imageData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -203,6 +298,40 @@ function src(container) {
|
|||||||
return container.node.src;
|
return container.node.src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LinearGradientContainer(imageData) {
|
||||||
|
GradientContainer.apply(this, arguments);
|
||||||
|
this.type = this.TYPES.LINEAR;
|
||||||
|
|
||||||
|
imageData.args[0].split(" ").forEach(function(position) {
|
||||||
|
switch(position) {
|
||||||
|
case "left":
|
||||||
|
this.x0 = 0;
|
||||||
|
this.x1 = 1;
|
||||||
|
break;
|
||||||
|
case "top":
|
||||||
|
this.y0 = 0;
|
||||||
|
this.y1 = 1;
|
||||||
|
break;
|
||||||
|
case "right":
|
||||||
|
this.x0 = 1;
|
||||||
|
this.x1 = 0;
|
||||||
|
break;
|
||||||
|
case "bottom":
|
||||||
|
this.y0 = 1;
|
||||||
|
this.y1 = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
imageData.args.slice(1).forEach(function(colorStop) {
|
||||||
|
// console.log(colorStop, colorStop.match(this.stepRegExp));
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearGradientContainer.prototype = Object.create(GradientContainer.prototype);
|
||||||
|
|
||||||
|
LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/;
|
||||||
|
|
||||||
function log() {
|
function log() {
|
||||||
if (window.html2canvas.logging && window.console && window.console.log) {
|
if (window.html2canvas.logging && window.console && window.console.log) {
|
||||||
window.console.log.apply(window.console, [(Date.now() - window.html2canvas.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0)));
|
window.console.log.apply(window.console, [(Date.now() - window.html2canvas.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0)));
|
||||||
@ -427,6 +556,28 @@ NodeContainer.prototype.parseBackgroundRepeat = function(index) {
|
|||||||
return this.cssList("backgroundRepeat", index)[0];
|
return this.cssList("backgroundRepeat", index)[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NodeContainer.prototype.parseTextShadows = function() {
|
||||||
|
var textShadow = this.css("textShadow");
|
||||||
|
var results = [];
|
||||||
|
|
||||||
|
if (textShadow && textShadow !== 'none') {
|
||||||
|
var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY);
|
||||||
|
for (var i = 0; shadows && (i < shadows.length); i++) {
|
||||||
|
var s = shadows[i].match(this.TEXT_SHADOW_VALUES);
|
||||||
|
results.push({
|
||||||
|
color: s[0],
|
||||||
|
offsetX: s[1] ? s[1].replace('px', '') : 0,
|
||||||
|
offsetY: s[2] ? s[2].replace('px', '') : 0,
|
||||||
|
blur: s[3] ? s[3].replace('px', '') : 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g;
|
||||||
|
NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g;
|
||||||
|
|
||||||
function isPercentage(value) {
|
function isPercentage(value) {
|
||||||
return value.toString().indexOf("%") !== -1;
|
return value.toString().indexOf("%") !== -1;
|
||||||
}
|
}
|
||||||
@ -443,17 +594,17 @@ function NodeParser(element, renderer, support, imageLoader, options) {
|
|||||||
this.nodes = [parent].concat(this.getChildren(parent)).filter(function(container) {
|
this.nodes = [parent].concat(this.getChildren(parent)).filter(function(container) {
|
||||||
return container.visible = container.isElementVisible();
|
return container.visible = container.isElementVisible();
|
||||||
});
|
});
|
||||||
|
this.fontMetrics = new FontMetrics();
|
||||||
log("Fetched nodes");
|
log("Fetched nodes");
|
||||||
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
||||||
log("Creating stacking contexts");
|
log("Creating stacking contexts");
|
||||||
this.createStackingContexts();
|
this.createStackingContexts();
|
||||||
log("Sorting stacking contexts");
|
log("Sorting stacking contexts");
|
||||||
this.sortStackingContexts(this.stack);
|
this.sortStackingContexts(this.stack);
|
||||||
this.images.ready.then(bind(function() {
|
this.ready = this.images.ready.then(bind(function() {
|
||||||
log("Images loaded, starting parsing");
|
log("Images loaded, starting parsing");
|
||||||
this.parse(this.stack);
|
this.parse(this.stack);
|
||||||
log("Finished rendering");
|
log("Finished rendering");
|
||||||
options.onrendered(renderer.canvas);
|
|
||||||
}, this));
|
}, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,11 +656,13 @@ NodeParser.prototype.getBounds = function(node) {
|
|||||||
if (node.getBoundingClientRect) {
|
if (node.getBoundingClientRect) {
|
||||||
var clientRect = node.getBoundingClientRect();
|
var clientRect = node.getBoundingClientRect();
|
||||||
var isBody = node.nodeName === "BODY";
|
var isBody = node.nodeName === "BODY";
|
||||||
|
var width = isBody ? node.scrollWidth : node.offsetWidth;
|
||||||
return {
|
return {
|
||||||
top: clientRect.top,
|
top: clientRect.top,
|
||||||
bottom: clientRect.bottom || (clientRect.top + clientRect.height),
|
bottom: clientRect.bottom || (clientRect.top + clientRect.height),
|
||||||
|
right: clientRect.left + width,
|
||||||
left: clientRect.left,
|
left: clientRect.left,
|
||||||
width: isBody ? node.scrollWidth : node.offsetWidth,
|
width: width,
|
||||||
height: isBody ? node.scrollHeight : node.offsetHeight
|
height: isBody ? node.scrollHeight : node.offsetHeight
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -519,8 +672,8 @@ NodeParser.prototype.getBounds = function(node) {
|
|||||||
NodeParser.prototype.parseTextBounds = function(container) {
|
NodeParser.prototype.parseTextBounds = function(container) {
|
||||||
return function(text, index, textList) {
|
return function(text, index, textList) {
|
||||||
if (container.parent.css("textDecoration") !== "none" || text.trim().length !== 0) {
|
if (container.parent.css("textDecoration") !== "none" || text.trim().length !== 0) {
|
||||||
var offset = textList.slice(0, index).join("").length;
|
|
||||||
if (this.support.rangeBounds) {
|
if (this.support.rangeBounds) {
|
||||||
|
var offset = textList.slice(0, index).join("").length;
|
||||||
return this.getRangeBounds(container.node, offset, text.length);
|
return this.getRangeBounds(container.node, offset, text.length);
|
||||||
} else if (container.node && typeof(container.node.data) === "string") {
|
} else if (container.node && typeof(container.node.data) === "string") {
|
||||||
var replacementNode = container.node.splitText(text.length);
|
var replacementNode = container.node.splitText(text.length);
|
||||||
@ -528,7 +681,10 @@ NodeParser.prototype.parseTextBounds = function(container) {
|
|||||||
container.node = replacementNode;
|
container.node = replacementNode;
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
} else if (!this.support.rangeBounds) {
|
||||||
|
container.node = container.node.splitText(text.length);
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -621,21 +777,41 @@ NodeParser.prototype.paintText = function(container) {
|
|||||||
var weight = container.parent.fontWeight();
|
var weight = container.parent.fontWeight();
|
||||||
var size = container.parent.css('fontSize');
|
var size = container.parent.css('fontSize');
|
||||||
var family = container.parent.css('fontFamily');
|
var family = container.parent.css('fontFamily');
|
||||||
|
var shadows = container.parent.parseTextShadows();
|
||||||
|
|
||||||
this.renderer.font(container.parent.css('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
|
this.renderer.font(container.parent.css('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
|
||||||
|
if (shadows.length) {
|
||||||
|
// TODO: support multiple text shadows
|
||||||
|
this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur);
|
||||||
|
} else {
|
||||||
|
this.renderer.clearShadow();
|
||||||
|
}
|
||||||
|
|
||||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||||
if (bounds) {
|
if (bounds) {
|
||||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||||
// renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
|
||||||
}
|
}
|
||||||
/* var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix);
|
|
||||||
if (bounds) {
|
|
||||||
drawText(text, bounds.left, bounds.bottom, ctx);
|
|
||||||
renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
|
||||||
} */
|
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) {
|
||||||
|
switch(container.css("textDecoration").split(" ")[0]) {
|
||||||
|
case "underline":
|
||||||
|
// Draws a line at the baseline of the font
|
||||||
|
// TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
|
||||||
|
this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.css("color"));
|
||||||
|
break;
|
||||||
|
case "overline":
|
||||||
|
this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.css("color"));
|
||||||
|
break;
|
||||||
|
case "line-through":
|
||||||
|
// TODO try and find exact position for line-through
|
||||||
|
this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.css("color"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
NodeParser.prototype.parseBorders = function(container) {
|
NodeParser.prototype.parseBorders = function(container) {
|
||||||
var nodeBounds = container.bounds;
|
var nodeBounds = container.bounds;
|
||||||
var radius = getBorderRadiusData(container);
|
var radius = getBorderRadiusData(container);
|
||||||
@ -1031,13 +1207,26 @@ Renderer.prototype.renderBorder = function(data) {
|
|||||||
Renderer.prototype.renderBackgroundImage = function(container, bounds) {
|
Renderer.prototype.renderBackgroundImage = function(container, bounds) {
|
||||||
var backgroundImages = container.parseBackgroundImages();
|
var backgroundImages = container.parseBackgroundImages();
|
||||||
backgroundImages.reverse().forEach(function(backgroundImage, index, arr) {
|
backgroundImages.reverse().forEach(function(backgroundImage, index, arr) {
|
||||||
if (backgroundImage.method === "url") {
|
switch(backgroundImage.method) {
|
||||||
var image = this.images.get(backgroundImage.args[0]);
|
case "url":
|
||||||
if (image) {
|
var image = this.images.get(backgroundImage.args[0]);
|
||||||
this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1));
|
if (image) {
|
||||||
} else {
|
this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1));
|
||||||
log("Error loading background-image", backgroundImage.args[0]);
|
} else {
|
||||||
}
|
log("Error loading background-image", backgroundImage.args[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "linear-gradient":
|
||||||
|
case "gradient":
|
||||||
|
var gradientImage = this.images.get(backgroundImage.value);
|
||||||
|
if (gradientImage) {
|
||||||
|
this.renderBackgroundGradient(gradientImage, bounds);
|
||||||
|
} else {
|
||||||
|
log("Error loading background-image", backgroundImage.args[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log("Unknown background-image type", backgroundImage.args[0]);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
@ -1076,6 +1265,7 @@ function CanvasRenderer(width, height) {
|
|||||||
this.canvas.height = height;
|
this.canvas.height = height;
|
||||||
this.ctx = this.canvas.getContext("2d");
|
this.ctx = this.canvas.getContext("2d");
|
||||||
this.ctx.textBaseline = "bottom";
|
this.ctx.textBaseline = "bottom";
|
||||||
|
this.variables = {};
|
||||||
log("Initialized CanvasRenderer");
|
log("Initialized CanvasRenderer");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1119,10 +1309,29 @@ CanvasRenderer.prototype.font = function(color, style, variant, weight, size, fa
|
|||||||
this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ");
|
this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) {
|
||||||
|
this.setVariable("shadowColor", color)
|
||||||
|
.setVariable("shadowOffsetY", offsetX)
|
||||||
|
.setVariable("shadowOffsetX", offsetY)
|
||||||
|
.setVariable("shadowBlur", blur);
|
||||||
|
};
|
||||||
|
|
||||||
|
CanvasRenderer.prototype.clearShadow = function() {
|
||||||
|
this.setVariable("shadowColor", "rgba(0,0,0,0)");
|
||||||
|
};
|
||||||
|
|
||||||
CanvasRenderer.prototype.setOpacity = function(opacity) {
|
CanvasRenderer.prototype.setOpacity = function(opacity) {
|
||||||
this.ctx.globalAlpha = opacity;
|
this.ctx.globalAlpha = opacity;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CanvasRenderer.prototype.setVariable = function(property, value) {
|
||||||
|
if (this.variables[property] !== value) {
|
||||||
|
this.variables[property] = this.ctx[property] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
CanvasRenderer.prototype.text = function(text, left, bottom) {
|
CanvasRenderer.prototype.text = function(text, left, bottom) {
|
||||||
this.ctx.fillText(text, left, bottom);
|
this.ctx.fillText(text, left, bottom);
|
||||||
};
|
};
|
||||||
@ -1147,6 +1356,13 @@ CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backg
|
|||||||
this.ctx.translate(-offsetX, -offsetY);
|
this.ctx.translate(-offsetX, -offsetY);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) {
|
||||||
|
if (gradientImage instanceof LinearGradientContainer) {
|
||||||
|
var gradient = this.ctx.createLinearGradient(bounds.left, bounds.top, bounds.right, bounds.bottom);
|
||||||
|
//console.log(gradientImage, bounds, gradient);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
|
CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
|
||||||
var image = imageContainer.image;
|
var image = imageContainer.image;
|
||||||
if(image.width === size.width && image.height === size.height) {
|
if(image.width === size.width && image.height === size.height) {
|
||||||
@ -1240,4 +1456,11 @@ function capitalize(m, p1, p2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function WebkitGradientContainer(imageData) {
|
||||||
|
GradientContainer.apply(this, arguments);
|
||||||
|
this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);
|
||||||
|
|
||||||
})(window,document);
|
})(window,document);
|
2
build/html2canvas.min.js
vendored
2
build/html2canvas.min.js
vendored
File diff suppressed because one or more lines are too long
13
readme.md
13
readme.md
@ -33,17 +33,12 @@ As each CSS property needs to be manually built to be supported, there are a num
|
|||||||
|
|
||||||
### Usage ###
|
### Usage ###
|
||||||
To render an `element` with html2canvas, simply call:
|
To render an `element` with html2canvas, simply call:
|
||||||
` html2canvas(element, options);`
|
` html2canvas(element[, options]);`
|
||||||
|
|
||||||
To access the created canvas, provide the `onrendered` event in the options which returns the canvas element as the first argument, as such:
|
The function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing the `<canvas>` element. Simply add a promise fullfillment handler to the promise using `then`:
|
||||||
|
|
||||||
html2canvas(document.body, {
|
html2canvas(document.body).then(function(canvas) {
|
||||||
onrendered: function(canvas) {
|
document.body.appendChild(canvas);
|
||||||
/* canvas is the actual canvas element,
|
|
||||||
to append it to the page call for example
|
|
||||||
document.body.appendChild( canvas );
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
### Building ###
|
### Building ###
|
||||||
|
28
src/core.js
28
src/core.js
@ -4,7 +4,17 @@ window.html2canvas = function(nodeList, options) {
|
|||||||
window.html2canvas.logging = true;
|
window.html2canvas.logging = true;
|
||||||
window.html2canvas.start = Date.now();
|
window.html2canvas.start = Date.now();
|
||||||
}
|
}
|
||||||
createWindowClone(document, window.innerWidth, window.innerHeight).then(function(container) {
|
return renderDocument(document, options, window.innerWidth, window.innerHeight).then(function(canvas) {
|
||||||
|
if (typeof(options.onrendered) === "function") {
|
||||||
|
log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
|
||||||
|
options.onrendered(canvas);
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderDocument(document, options, width, height) {
|
||||||
|
return createWindowClone(document, width, height).then(function(container) {
|
||||||
log("Document cloned");
|
log("Document cloned");
|
||||||
var clonedWindow = container.contentWindow;
|
var clonedWindow = container.contentWindow;
|
||||||
//var element = (nodeList === undefined) ? document.body : nodeList[0];
|
//var element = (nodeList === undefined) ? document.body : nodeList[0];
|
||||||
@ -12,14 +22,16 @@ window.html2canvas = function(nodeList, options) {
|
|||||||
var support = new Support();
|
var support = new Support();
|
||||||
var imageLoader = new ImageLoader(options, support);
|
var imageLoader = new ImageLoader(options, support);
|
||||||
var bounds = NodeParser.prototype.getBounds(node);
|
var bounds = NodeParser.prototype.getBounds(node);
|
||||||
var width = options.type === "view" ? Math.min(bounds.width, window.innerWidth) : documentWidth();
|
var width = options.type === "view" ? Math.min(bounds.width, width) : documentWidth();
|
||||||
var height = options.type === "view" ? Math.min(bounds.height, window.innerHeight) : documentHeight();
|
var height = options.type === "view" ? Math.min(bounds.height, height) : documentHeight();
|
||||||
var renderer = new CanvasRenderer(width, height, imageLoader);
|
var renderer = new CanvasRenderer(width, height, imageLoader);
|
||||||
var parser = new NodeParser(node, renderer, support, imageLoader, options);
|
var parser = new NodeParser(node, renderer, support, imageLoader, options);
|
||||||
|
return parser.ready.then(function() {
|
||||||
window.console.log(parser);
|
container.parentNode.removeChild(container);
|
||||||
|
return renderer.canvas;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function documentWidth () {
|
function documentWidth () {
|
||||||
return Math.max(
|
return Math.max(
|
||||||
@ -45,7 +57,7 @@ function createWindowClone(ownerDocument, width, height) {
|
|||||||
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
var documentElement = ownerDocument.documentElement.cloneNode(true),
|
||||||
container = ownerDocument.createElement("iframe");
|
container = ownerDocument.createElement("iframe");
|
||||||
|
|
||||||
container.style.display = "hidden";
|
container.style.visibility = "hidden";
|
||||||
container.style.position = "absolute";
|
container.style.position = "absolute";
|
||||||
container.width = width;
|
container.width = width;
|
||||||
container.height = height;
|
container.height = height;
|
||||||
@ -81,6 +93,6 @@ function createWindowClone(ownerDocument, width, height) {
|
|||||||
var style = documentClone.createElement("style");
|
var style = documentClone.createElement("style");
|
||||||
style.innerHTML = "body div.html2canvas-ready-test { background-image:url(" + smallImage() + "); }";
|
style.innerHTML = "body div.html2canvas-ready-test { background-image:url(" + smallImage() + "); }";
|
||||||
documentClone.body.appendChild(style);
|
documentClone.body.appendChild(style);
|
||||||
window.setTimeout(loadedTimer, 10);
|
window.setTimeout(loadedTimer, 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,10 @@ function NodeParser(element, renderer, support, imageLoader, options) {
|
|||||||
this.createStackingContexts();
|
this.createStackingContexts();
|
||||||
log("Sorting stacking contexts");
|
log("Sorting stacking contexts");
|
||||||
this.sortStackingContexts(this.stack);
|
this.sortStackingContexts(this.stack);
|
||||||
this.images.ready.then(bind(function() {
|
this.ready = this.images.ready.then(bind(function() {
|
||||||
log("Images loaded, starting parsing");
|
log("Images loaded, starting parsing");
|
||||||
this.parse(this.stack);
|
this.parse(this.stack);
|
||||||
log("Finished rendering");
|
log("Finished rendering");
|
||||||
options.onrendered(renderer.canvas);
|
|
||||||
}, this));
|
}, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,21 +4,20 @@
|
|||||||
(function( $ ){
|
(function( $ ){
|
||||||
$.fn.html2canvas = function(options) {
|
$.fn.html2canvas = function(options) {
|
||||||
if (options && options.profile && window.console && window.console.profile && window.location.search !== "?selenium2") {
|
if (options && options.profile && window.console && window.console.profile && window.location.search !== "?selenium2") {
|
||||||
console.profile();
|
window.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();
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
options.onrendered = options.onrendered || function( canvas ) {
|
html2canvas(this, options).then(function(canvas) {
|
||||||
var $canvas = $(canvas),
|
var $canvas = $(canvas),
|
||||||
finishTime = new Date();
|
finishTime = new Date();
|
||||||
|
|
||||||
if (options && options.profile && window.console && window.console.profileEnd) {
|
if (options && options.profile && window.console && window.console.profileEnd) {
|
||||||
console.profileEnd();
|
window.console.profileEnd();
|
||||||
}
|
}
|
||||||
$canvas.addClass("html2canvas")
|
$canvas.addClass("html2canvas")
|
||||||
.css({
|
.css({
|
||||||
@ -43,12 +42,10 @@
|
|||||||
} catch(e) {
|
} catch(e) {
|
||||||
if ($canvas[0].nodeName.toLowerCase() === "canvas") {
|
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
|
// 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");
|
window.alert("Canvas is tainted, unable to read data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
html2obj = html2canvas(this, options);
|
|
||||||
|
|
||||||
function throwMessage(msg,duration){
|
function throwMessage(msg,duration){
|
||||||
window.clearTimeout(timeoutTimer);
|
window.clearTimeout(timeoutTimer);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user