Refactor background parsing

This commit is contained in:
Niklas von Hertzen 2013-12-23 16:07:49 +02:00
parent e228fc57ce
commit 517fd8cd1d
5 changed files with 110 additions and 112 deletions

View File

@ -57,7 +57,6 @@ _html2canvas.Util.asFloat = function(v) {
}; };
})(); })();
_html2canvas.Util.parseBackgroundImage = function (value) { _html2canvas.Util.parseBackgroundImage = function (value) {
var whitespace = ' \r\n\t', var whitespace = ' \r\n\t',
method, definition, prefix, prefix_i, block, results = [], method, definition, prefix, prefix_i, block, results = [],
@ -231,6 +230,10 @@ function asInt(val) {
return parseInt(val, 10); return parseInt(val, 10);
} }
function isPercentage(value) {
return value.toString().indexOf("%") !== -1;
}
function parseBackgroundSizePosition(value, element, attribute, index) { function parseBackgroundSizePosition(value, element, attribute, index) {
value = (value || '').split(','); value = (value || '').split(',');
value = value[index || 0] || value[0] || 'auto'; value = value[index || 0] || value[0] || 'auto';
@ -303,7 +306,7 @@ _html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageInd
backgroundPosition = [backgroundPosition[0], backgroundPosition[0]]; backgroundPosition = [backgroundPosition[0], backgroundPosition[0]];
} }
if (backgroundPosition[0].toString().indexOf("%") !== -1){ if (isPercentage(backgroundPosition[0])){
leftPosition = (bounds.width - (backgroundSize || image).width) * (parseFloat(backgroundPosition[0]) / 100); leftPosition = (bounds.width - (backgroundSize || image).width) * (parseFloat(backgroundPosition[0]) / 100);
} else { } else {
leftPosition = parseInt(backgroundPosition[0], 10); leftPosition = parseInt(backgroundPosition[0], 10);
@ -311,7 +314,7 @@ _html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageInd
if (backgroundPosition[1] === 'auto') { if (backgroundPosition[1] === 'auto') {
topPosition = leftPosition / image.width * image.height; topPosition = leftPosition / image.width * image.height;
} else if (backgroundPosition[1].toString().indexOf("%") !== -1){ } else if (isPercentage(backgroundPosition[1])){
topPosition = (bounds.height - (backgroundSize || image).height) * parseFloat(backgroundPosition[1]) / 100; topPosition = (bounds.height - (backgroundSize || image).height) * parseFloat(backgroundPosition[1]) / 100;
} else { } else {
topPosition = parseInt(backgroundPosition[1], 10); topPosition = parseInt(backgroundPosition[1], 10);
@ -325,41 +328,40 @@ _html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageInd
}; };
_html2canvas.Util.BackgroundSize = function(element, bounds, image, imageIndex) { _html2canvas.Util.BackgroundSize = function(element, bounds, image, imageIndex) {
var backgroundSize = _html2canvas.Util.getCSS(element, 'backgroundSize', imageIndex), var backgroundSize = _html2canvas.Util.getCSS(element, 'backgroundSize', imageIndex), width, height;
width,
height;
if (backgroundSize.length === 1){ if (backgroundSize.length === 1) {
backgroundSize = [backgroundSize[0], backgroundSize[0]]; backgroundSize = [backgroundSize[0], backgroundSize[0]];
} }
if (backgroundSize[0].toString().indexOf("%") !== -1){ if (isPercentage(backgroundSize[0])) {
width = bounds.width * parseFloat(backgroundSize[0]) / 100; width = bounds.width * parseFloat(backgroundSize[0]) / 100;
} else if(backgroundSize[0] === 'auto') { } else if (/contain|cover/.test(backgroundSize[0])) {
width = image.width; return _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, backgroundSize[0]);
} else { } else {
if (/contain|cover/.test(backgroundSize[0])) { width = parseInt(backgroundSize[0], 10);
var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, backgroundSize[0]); }
return {width: resized.width, height: resized.height};
} else {
width = parseInt(backgroundSize[0], 10);
}
}
if(backgroundSize[1] === 'auto') { if (backgroundSize[0] === 'auto' && backgroundSize[1] === 'auto') {
height = width / image.width * image.height; height = image.height;
} else if (backgroundSize[1].toString().indexOf("%") !== -1){ } else if (backgroundSize[1] === 'auto') {
height = bounds.height * parseFloat(backgroundSize[1]) / 100; height = width / image.width * image.height;
} else { } else if (isPercentage(backgroundSize[1])) {
height = parseInt(backgroundSize[1],10); height = bounds.height * parseFloat(backgroundSize[1]) / 100;
} } else {
height = parseInt(backgroundSize[1], 10);
}
if (backgroundSize[0] === 'auto') {
width = height / image.height * image.width;
}
if (backgroundSize[0] === 'auto') { return {width: width, height: height};
width = height / image.height * image.width; };
}
return {width: width, height: height}; _html2canvas.Util.BackgroundRepeat = function(element, imageIndex) {
var backgroundRepeat = _html2canvas.Util.getCSS(element, "backgroundRepeat").split(",").map(_html2canvas.Util.trimText);
return backgroundRepeat[imageIndex] || backgroundRepeat[0];
}; };
_html2canvas.Util.Extend = function (options, defaults) { _html2canvas.Util.Extend = function (options, defaults) {
@ -416,6 +418,7 @@ _html2canvas.Util.Children = function( elem ) {
_html2canvas.Util.isTransparent = function(backgroundColor) { _html2canvas.Util.isTransparent = function(backgroundColor) {
return (!backgroundColor || backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)"); return (!backgroundColor || backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)");
}; };
_html2canvas.Util.Font = (function () { _html2canvas.Util.Font = (function () {
var fontData = {}; var fontData = {};
@ -2036,28 +2039,25 @@ _html2canvas.Parse = function (images, options, cb) {
function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) { function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) {
var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex), var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex),
backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize), backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize),
backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(Util.trimText); backgroundRepeat = Util.BackgroundRepeat(el, imageIndex);
image = resizeImage(image, backgroundSize); image = resizeImage(image, backgroundSize);
backgroundRepeat = backgroundRepeat[imageIndex] || backgroundRepeat[0];
switch (backgroundRepeat) { switch (backgroundRepeat) {
case "repeat-x": case "repeat-x":
case "repeat no-repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds, backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left, bounds.top + backgroundPosition.top, 99999, image.height); bounds.left, bounds.top + backgroundPosition.top, 99999, image.height);
break; break;
case "repeat-y": case "repeat-y":
case "no-repeat repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds, backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top, image.width, 99999); bounds.left + backgroundPosition.left, bounds.top, image.width, 99999);
break; break;
case "no-repeat": case "no-repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds, backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height); bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height);
break; break;
default: default:
renderBackgroundRepeat(ctx, image, backgroundPosition, { renderBackgroundRepeat(ctx, image, backgroundPosition, {
top: bounds.top, top: bounds.top,
@ -2623,6 +2623,15 @@ _html2canvas.Preload = function( options ) {
}; };
_html2canvas.Renderer = function(parseQueue, options){ _html2canvas.Renderer = function(parseQueue, options){
function sortZindex(a, b) {
if (a === 'children') {
return -1;
} else if (b === 'children') {
return 1;
} else {
return a - b;
}
}
// http://www.w3.org/TR/CSS21/zindex.html // http://www.w3.org/TR/CSS21/zindex.html
function createRenderQueue(parseQueue) { function createRenderQueue(parseQueue) {
@ -2640,8 +2649,9 @@ _html2canvas.Renderer = function(parseQueue, options){
childrenDest = specialParent; // where children without z-index should be pushed into childrenDest = specialParent; // where children without z-index should be pushed into
if (node.zIndex.ownStacking) { if (node.zIndex.ownStacking) {
// '!' comes before numbers in sorted array contextForChildren = stub.context = {
contextForChildren = stub.context = { '!': [{node:node, children: []}]}; children: [{node:node, children: []}]
};
childrenDest = undefined; childrenDest = undefined;
} else if (isPositioned || isFloated) { } else if (isPositioned || isFloated) {
childrenDest = stub.children = []; childrenDest = stub.children = [];
@ -2663,7 +2673,7 @@ _html2canvas.Renderer = function(parseQueue, options){
})(parseQueue); })(parseQueue);
function sortZ(context) { function sortZ(context) {
Object.keys(context).sort().forEach(function(zi) { Object.keys(context).sort(sortZindex).forEach(function(zi) {
var nonPositioned = [], var nonPositioned = [],
floated = [], floated = [],
positioned = [], positioned = [],
@ -2885,7 +2895,7 @@ _html2canvas.Renderer.Canvas = function(options) {
} }
function safeImage(item) { function safeImage(item) {
if (safeImages.indexOf(item['arguments'][0].src ) === -1) { if (safeImages.indexOf(item['arguments'][0].src) === -1) {
testctx.drawImage(item['arguments'][0], 0, 0); testctx.drawImage(item['arguments'][0], 0, 0);
try { try {
testctx.getImageData(0, 0, 1, 1); testctx.getImageData(0, 0, 1, 1);
@ -2910,8 +2920,7 @@ _html2canvas.Renderer.Canvas = function(options) {
if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) { if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) {
try { try {
ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat"); ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat");
} } catch(e) {
catch(e) {
Util.log("html2canvas: Renderer: Error creating pattern", e.message); Util.log("html2canvas: Renderer: Error creating pattern", e.message);
} }
} }
@ -2947,7 +2956,6 @@ _html2canvas.Renderer.Canvas = function(options) {
ctx.fillStyle = (Util.isTransparent(parsedData.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor; ctx.fillStyle = (Util.isTransparent(parsedData.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = fstyle; ctx.fillStyle = fstyle;
queue.forEach(function(storageContext) { queue.forEach(function(storageContext) {
// set common settings for canvas // set common settings for canvas
ctx.textBaseline = "bottom"; ctx.textBaseline = "bottom";

File diff suppressed because one or more lines are too long

View File

@ -48,7 +48,6 @@ _html2canvas.Util.asFloat = function(v) {
}; };
})(); })();
_html2canvas.Util.parseBackgroundImage = function (value) { _html2canvas.Util.parseBackgroundImage = function (value) {
var whitespace = ' \r\n\t', var whitespace = ' \r\n\t',
method, definition, prefix, prefix_i, block, results = [], method, definition, prefix, prefix_i, block, results = [],
@ -222,6 +221,10 @@ function asInt(val) {
return parseInt(val, 10); return parseInt(val, 10);
} }
function isPercentage(value) {
return value.toString().indexOf("%") !== -1;
}
function parseBackgroundSizePosition(value, element, attribute, index) { function parseBackgroundSizePosition(value, element, attribute, index) {
value = (value || '').split(','); value = (value || '').split(',');
value = value[index || 0] || value[0] || 'auto'; value = value[index || 0] || value[0] || 'auto';
@ -250,13 +253,6 @@ _html2canvas.Util.getCSS = function (element, attribute, index) {
} }
var value = computedCSS[attribute]; var value = computedCSS[attribute];
if(attribute==="backgroundRepeat" && value.indexOf(" ")!==-1){
value = (
"no-repeat repeat"===value ? "repeat-y" : (
"repeat no-repeat"===value ? "repeat-x" : value
)
);
}
if (/^background(Size|Position)$/.test(attribute)) { if (/^background(Size|Position)$/.test(attribute)) {
return parseBackgroundSizePosition(value, element, attribute, index); return parseBackgroundSizePosition(value, element, attribute, index);
@ -301,7 +297,7 @@ _html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageInd
backgroundPosition = [backgroundPosition[0], backgroundPosition[0]]; backgroundPosition = [backgroundPosition[0], backgroundPosition[0]];
} }
if (backgroundPosition[0].toString().indexOf("%") !== -1){ if (isPercentage(backgroundPosition[0])){
leftPosition = (bounds.width - (backgroundSize || image).width) * (parseFloat(backgroundPosition[0]) / 100); leftPosition = (bounds.width - (backgroundSize || image).width) * (parseFloat(backgroundPosition[0]) / 100);
} else { } else {
leftPosition = parseInt(backgroundPosition[0], 10); leftPosition = parseInt(backgroundPosition[0], 10);
@ -309,7 +305,7 @@ _html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageInd
if (backgroundPosition[1] === 'auto') { if (backgroundPosition[1] === 'auto') {
topPosition = leftPosition / image.width * image.height; topPosition = leftPosition / image.width * image.height;
} else if (backgroundPosition[1].toString().indexOf("%") !== -1){ } else if (isPercentage(backgroundPosition[1])){
topPosition = (bounds.height - (backgroundSize || image).height) * parseFloat(backgroundPosition[1]) / 100; topPosition = (bounds.height - (backgroundSize || image).height) * parseFloat(backgroundPosition[1]) / 100;
} else { } else {
topPosition = parseInt(backgroundPosition[1], 10); topPosition = parseInt(backgroundPosition[1], 10);
@ -323,41 +319,40 @@ _html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageInd
}; };
_html2canvas.Util.BackgroundSize = function(element, bounds, image, imageIndex) { _html2canvas.Util.BackgroundSize = function(element, bounds, image, imageIndex) {
var backgroundSize = _html2canvas.Util.getCSS(element, 'backgroundSize', imageIndex), var backgroundSize = _html2canvas.Util.getCSS(element, 'backgroundSize', imageIndex), width, height;
width,
height;
if (backgroundSize.length === 1){ if (backgroundSize.length === 1) {
backgroundSize = [backgroundSize[0], backgroundSize[0]]; backgroundSize = [backgroundSize[0], backgroundSize[0]];
} }
if (backgroundSize[0].toString().indexOf("%") !== -1){ if (isPercentage(backgroundSize[0])) {
width = bounds.width * parseFloat(backgroundSize[0]) / 100; width = bounds.width * parseFloat(backgroundSize[0]) / 100;
} else if(backgroundSize[0] === 'auto') { } else if (/contain|cover/.test(backgroundSize[0])) {
width = image.width; return _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, backgroundSize[0]);
} else { } else {
if (/contain|cover/.test(backgroundSize[0])) { width = parseInt(backgroundSize[0], 10);
var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, backgroundSize[0]); }
return {width: resized.width, height: resized.height};
} else {
width = parseInt(backgroundSize[0], 10);
}
}
if(backgroundSize[1] === 'auto') { if (backgroundSize[0] === 'auto' && backgroundSize[1] === 'auto') {
height = width / image.width * image.height; height = image.height;
} else if (backgroundSize[1].toString().indexOf("%") !== -1){ } else if (backgroundSize[1] === 'auto') {
height = bounds.height * parseFloat(backgroundSize[1]) / 100; height = width / image.width * image.height;
} else { } else if (isPercentage(backgroundSize[1])) {
height = parseInt(backgroundSize[1],10); height = bounds.height * parseFloat(backgroundSize[1]) / 100;
} } else {
height = parseInt(backgroundSize[1], 10);
}
if (backgroundSize[0] === 'auto') {
width = height / image.height * image.width;
}
if (backgroundSize[0] === 'auto') { return {width: width, height: height};
width = height / image.height * image.width; };
}
return {width: width, height: height}; _html2canvas.Util.BackgroundRepeat = function(element, imageIndex) {
var backgroundRepeat = _html2canvas.Util.getCSS(element, "backgroundRepeat").split(",").map(_html2canvas.Util.trimText);
return backgroundRepeat[imageIndex] || backgroundRepeat[0];
}; };
_html2canvas.Util.Extend = function (options, defaults) { _html2canvas.Util.Extend = function (options, defaults) {

View File

@ -1006,28 +1006,25 @@ _html2canvas.Parse = function (images, options, cb) {
function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) { function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) {
var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex), var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex),
backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize), backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize),
backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(Util.trimText); backgroundRepeat = Util.BackgroundRepeat(el, imageIndex);
image = resizeImage(image, backgroundSize); image = resizeImage(image, backgroundSize);
backgroundRepeat = backgroundRepeat[imageIndex] || backgroundRepeat[0];
switch (backgroundRepeat) { switch (backgroundRepeat) {
case "repeat-x": case "repeat-x":
case "repeat no-repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds, backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left, bounds.top + backgroundPosition.top, 99999, image.height); bounds.left, bounds.top + backgroundPosition.top, 99999, image.height);
break; break;
case "repeat-y": case "repeat-y":
case "no-repeat repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds, backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top, image.width, 99999); bounds.left + backgroundPosition.left, bounds.top, image.width, 99999);
break; break;
case "no-repeat": case "no-repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds, backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height); bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height);
break; break;
default: default:
renderBackgroundRepeat(ctx, image, backgroundPosition, { renderBackgroundRepeat(ctx, image, backgroundPosition, {
top: bounds.top, top: bounds.top,

View File

@ -17,7 +17,7 @@ _html2canvas.Renderer.Canvas = function(options) {
} }
function safeImage(item) { function safeImage(item) {
if (safeImages.indexOf(item['arguments'][0].src ) === -1) { if (safeImages.indexOf(item['arguments'][0].src) === -1) {
testctx.drawImage(item['arguments'][0], 0, 0); testctx.drawImage(item['arguments'][0], 0, 0);
try { try {
testctx.getImageData(0, 0, 1, 1); testctx.getImageData(0, 0, 1, 1);
@ -42,8 +42,7 @@ _html2canvas.Renderer.Canvas = function(options) {
if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) { if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) {
try { try {
ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat"); ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat");
} } catch(e) {
catch(e) {
Util.log("html2canvas: Renderer: Error creating pattern", e.message); Util.log("html2canvas: Renderer: Error creating pattern", e.message);
} }
} }
@ -79,7 +78,6 @@ _html2canvas.Renderer.Canvas = function(options) {
ctx.fillStyle = (Util.isTransparent(parsedData.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor; ctx.fillStyle = (Util.isTransparent(parsedData.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = fstyle; ctx.fillStyle = fstyle;
queue.forEach(function(storageContext) { queue.forEach(function(storageContext) {
// set common settings for canvas // set common settings for canvas
ctx.textBaseline = "bottom"; ctx.textBaseline = "bottom";