mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Correctly handle overflow content
This commit is contained in:
parent
b1f948bb60
commit
1d8a316f13
321
dist/html2canvas.js
vendored
321
dist/html2canvas.js
vendored
@ -1067,6 +1067,9 @@ function NodeContainer(node, parent) {
|
||||
this.parent = parent;
|
||||
this.stack = null;
|
||||
this.bounds = null;
|
||||
this.borders = null;
|
||||
this.clip = [];
|
||||
this.backgroundClip = [];
|
||||
this.offsetBounds = null;
|
||||
this.visible = null;
|
||||
this.computedStyles = null;
|
||||
@ -1127,12 +1130,12 @@ NodeContainer.prototype.cssFloat = function(attribute) {
|
||||
NodeContainer.prototype.fontWeight = function() {
|
||||
var weight = this.css("fontWeight");
|
||||
switch(parseInt(weight, 10)){
|
||||
case 401:
|
||||
weight = "bold";
|
||||
break;
|
||||
case 400:
|
||||
weight = "normal";
|
||||
break;
|
||||
case 401:
|
||||
weight = "bold";
|
||||
break;
|
||||
case 400:
|
||||
weight = "normal";
|
||||
break;
|
||||
}
|
||||
return weight;
|
||||
};
|
||||
@ -1328,55 +1331,54 @@ function parseBackgrounds(backgroundImage) {
|
||||
return;
|
||||
}
|
||||
switch(c) {
|
||||
case '"':
|
||||
if(!quote) {
|
||||
quote = c;
|
||||
}
|
||||
else if(quote === c) {
|
||||
quote = null;
|
||||
}
|
||||
case '"':
|
||||
if(!quote) {
|
||||
quote = c;
|
||||
} else if(quote === c) {
|
||||
quote = null;
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
if(quote) {
|
||||
break;
|
||||
case '(':
|
||||
if(quote) {
|
||||
break;
|
||||
} else if(mode === 0) {
|
||||
mode = 1;
|
||||
} else if(mode === 0) {
|
||||
mode = 1;
|
||||
block += c;
|
||||
return;
|
||||
} else {
|
||||
numParen++;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 1) {
|
||||
if(numParen === 0) {
|
||||
mode = 0;
|
||||
block += c;
|
||||
return;
|
||||
} else {
|
||||
numParen++;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 1) {
|
||||
if(numParen === 0) {
|
||||
mode = 0;
|
||||
block += c;
|
||||
appendResult();
|
||||
return;
|
||||
} else {
|
||||
numParen--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 0) {
|
||||
appendResult();
|
||||
return;
|
||||
} else if (mode === 1) {
|
||||
if (numParen === 0 && !method.match(/^url$/i)) {
|
||||
args.push(definition);
|
||||
definition = '';
|
||||
block += c;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
numParen--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 0) {
|
||||
appendResult();
|
||||
return;
|
||||
} else if (mode === 1) {
|
||||
if (numParen === 0 && !method.match(/^url$/i)) {
|
||||
args.push(definition);
|
||||
definition = '';
|
||||
block += c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
block += c;
|
||||
@ -1450,12 +1452,14 @@ function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
this.fontMetrics = new FontMetrics();
|
||||
log("Fetched nodes");
|
||||
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
||||
log("Creating stacking contexts");
|
||||
this.createStackingContexts();
|
||||
log("Sorting stacking contexts");
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.ready = this.images.ready.then(bind(function() {
|
||||
log("Images loaded, starting parsing");
|
||||
log("Calculate overflow clips");
|
||||
this.calculateOverflowClips();
|
||||
log("Creating stacking contexts");
|
||||
this.createStackingContexts();
|
||||
log("Sorting stacking contexts");
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.parse(this.stack);
|
||||
log("Render queue created with " + this.renderQueue.length + " items");
|
||||
return new Promise(bind(function(resolve) {
|
||||
@ -1472,6 +1476,24 @@ function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
}, this));
|
||||
}
|
||||
|
||||
NodeParser.prototype.calculateOverflowClips = function() {
|
||||
this.nodes.forEach(function(container) {
|
||||
if (isElement(container)) {
|
||||
container.borders = this.parseBorders(container);
|
||||
var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : [];
|
||||
container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip;
|
||||
container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip;
|
||||
} else if (isTextNode(container)) {
|
||||
container.clip = hasParentClip(container) ? container.parent.clip : [];
|
||||
}
|
||||
container.bounds = null;
|
||||
}, this);
|
||||
};
|
||||
|
||||
function hasParentClip(container) {
|
||||
return container.parent && container.parent.clip.length;
|
||||
}
|
||||
|
||||
NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) {
|
||||
asyncTimer = asyncTimer || Date.now();
|
||||
this.paint(queue[this.renderIndex++]);
|
||||
@ -1561,6 +1583,10 @@ NodeParser.prototype.getChildren = function(parentContainer) {
|
||||
NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
|
||||
var stack = new StackingContext(hasOwnStacking, container.cssFloat('opacity'), container.node, container.parent);
|
||||
stack.visible = container.visible;
|
||||
stack.borders = container.borders;
|
||||
stack.bounds = container.bounds;
|
||||
stack.clip = container.clip;
|
||||
stack.backgroundClip = container.backgroundClip;
|
||||
var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack;
|
||||
parentStack.contexts.push(stack);
|
||||
container.stack = stack;
|
||||
@ -1675,17 +1701,19 @@ NodeParser.prototype.paintNode = function(container) {
|
||||
}
|
||||
}
|
||||
var bounds = container.parseBounds();
|
||||
var borderData = this.parseBorders(container);
|
||||
this.renderer.clip(borderData.clip, function() {
|
||||
this.renderer.renderBackground(container, bounds, borderData.borders.map(getWidth));
|
||||
this.renderer.clip(container.backgroundClip, function() {
|
||||
this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth));
|
||||
}, this);
|
||||
this.renderer.renderBorders(borderData.borders);
|
||||
switch(container.node.nodeName) {
|
||||
|
||||
this.renderer.clip(container.clip, function() {
|
||||
this.renderer.renderBorders(container.borders.borders);
|
||||
|
||||
switch (container.node.nodeName) {
|
||||
case "svg":
|
||||
case "IFRAME":
|
||||
var imgContainer = this.images.get(container.node);
|
||||
if (imgContainer) {
|
||||
this.renderer.renderImage(container, bounds, borderData, imgContainer);
|
||||
this.renderer.renderImage(container, bounds, container.borders, imgContainer);
|
||||
} else {
|
||||
log("Error loading <" + container.node.nodeName + ">", container.node);
|
||||
}
|
||||
@ -1693,7 +1721,7 @@ NodeParser.prototype.paintNode = function(container) {
|
||||
case "IMG":
|
||||
var imageContainer = this.images.get(container.node.src);
|
||||
if (imageContainer) {
|
||||
this.renderer.renderImage(container, bounds, borderData, imageContainer);
|
||||
this.renderer.renderImage(container, bounds, container.borders, imageContainer);
|
||||
} else {
|
||||
log("Error loading <img>", container.node.src);
|
||||
}
|
||||
@ -1703,7 +1731,8 @@ NodeParser.prototype.paintNode = function(container) {
|
||||
case "TEXTAREA":
|
||||
this.paintFormValue(container);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.paintFormValue = function(container) {
|
||||
@ -1754,33 +1783,35 @@ NodeParser.prototype.paintText = function(container) {
|
||||
this.renderer.clearShadow();
|
||||
}
|
||||
|
||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||
if (bounds) {
|
||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||
this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
|
||||
}
|
||||
this.renderer.clip(container.parent.clip, function() {
|
||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||
if (bounds) {
|
||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||
this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
|
||||
}
|
||||
}, 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;
|
||||
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) {
|
||||
var nodeBounds = container.bounds;
|
||||
var nodeBounds = container.parseBounds();
|
||||
var radius = getBorderRadiusData(container);
|
||||
var borders = ["Top", "Right", "Bottom", "Left"].map(function(side) {
|
||||
return {
|
||||
@ -1801,53 +1832,53 @@ NodeParser.prototype.parseBorders = function(container) {
|
||||
var bh = nodeBounds.height - (borders[2].width);
|
||||
|
||||
switch(borderSide) {
|
||||
case 0:
|
||||
// top border
|
||||
bh = borders[0].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by],
|
||||
c2: [bx + bw, by],
|
||||
c3: [bx + bw - borders[1].width, by + bh],
|
||||
c4: [bx + borders[3].width, by + bh]
|
||||
}, radius[0], radius[1],
|
||||
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
|
||||
break;
|
||||
case 1:
|
||||
// right border
|
||||
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
|
||||
bw = borders[1].width;
|
||||
case 0:
|
||||
// top border
|
||||
bh = borders[0].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by],
|
||||
c2: [bx + bw, by],
|
||||
c3: [bx + bw - borders[1].width, by + bh],
|
||||
c4: [bx + borders[3].width, by + bh]
|
||||
}, radius[0], radius[1],
|
||||
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
|
||||
break;
|
||||
case 1:
|
||||
// right border
|
||||
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
|
||||
bw = borders[1].width;
|
||||
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by],
|
||||
c2: [bx + bw, by + bh + borders[2].width],
|
||||
c3: [bx, by + bh],
|
||||
c4: [bx, by + borders[0].width]
|
||||
}, radius[1], radius[2],
|
||||
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
|
||||
break;
|
||||
case 2:
|
||||
// bottom border
|
||||
by = (by + nodeBounds.height) - (borders[2].width);
|
||||
bh = borders[2].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by + bh],
|
||||
c2: [bx, by + bh],
|
||||
c3: [bx + borders[3].width, by],
|
||||
c4: [bx + bw - borders[3].width, by]
|
||||
}, radius[2], radius[3],
|
||||
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
|
||||
break;
|
||||
case 3:
|
||||
// left border
|
||||
bw = borders[3].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by + bh + borders[2].width],
|
||||
c2: [bx, by],
|
||||
c3: [bx + bw, by + borders[0].width],
|
||||
c4: [bx + bw, by + bh]
|
||||
}, radius[3], radius[0],
|
||||
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
|
||||
break;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by],
|
||||
c2: [bx + bw, by + bh + borders[2].width],
|
||||
c3: [bx, by + bh],
|
||||
c4: [bx, by + borders[0].width]
|
||||
}, radius[1], radius[2],
|
||||
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
|
||||
break;
|
||||
case 2:
|
||||
// bottom border
|
||||
by = (by + nodeBounds.height) - (borders[2].width);
|
||||
bh = borders[2].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by + bh],
|
||||
c2: [bx, by + bh],
|
||||
c3: [bx + borders[3].width, by],
|
||||
c4: [bx + bw - borders[3].width, by]
|
||||
}, radius[2], radius[3],
|
||||
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
|
||||
break;
|
||||
case 3:
|
||||
// left border
|
||||
bw = borders[3].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by + bh + borders[2].width],
|
||||
c2: [bx, by],
|
||||
c3: [bx + bw, by + borders[0].width],
|
||||
c4: [bx + bw, by + bh]
|
||||
}, radius[3], radius[0],
|
||||
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return border;
|
||||
@ -1860,20 +1891,20 @@ NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, bor
|
||||
borderArgs = [];
|
||||
|
||||
switch(backgroundClip) {
|
||||
case "content-box":
|
||||
case "padding-box":
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
|
||||
break;
|
||||
case "content-box":
|
||||
case "padding-box":
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
|
||||
break;
|
||||
|
||||
default:
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
|
||||
break;
|
||||
default:
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
|
||||
break;
|
||||
}
|
||||
|
||||
return borderArgs;
|
||||
@ -2148,7 +2179,7 @@ function isWordBoundary(characterCode) {
|
||||
}
|
||||
|
||||
function hasUnicode(string) {
|
||||
return /[^\u0000-\u00ff]/.test(string);
|
||||
return (/[^\u0000-\u00ff]/).test(string);
|
||||
}
|
||||
|
||||
function Proxy(src, proxyUrl, document) {
|
||||
@ -2605,9 +2636,11 @@ CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx
|
||||
}
|
||||
};
|
||||
|
||||
CanvasRenderer.prototype.clip = function(shape, callback, context) {
|
||||
CanvasRenderer.prototype.clip = function(shapes, callback, context) {
|
||||
this.ctx.save();
|
||||
this.shape(shape).clip();
|
||||
shapes.filter(hasEntries).forEach(function(shape) {
|
||||
this.shape(shape).clip();
|
||||
}, this);
|
||||
callback.call(context);
|
||||
this.ctx.restore();
|
||||
};
|
||||
@ -2665,7 +2698,7 @@ CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgr
|
||||
["line", Math.round(left + width), Math.round(height + top)],
|
||||
["line", Math.round(left), Math.round(height + top)]
|
||||
];
|
||||
this.clip(shape, function() {
|
||||
this.clip([shape], function() {
|
||||
this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]);
|
||||
}, this);
|
||||
};
|
||||
@ -2706,4 +2739,8 @@ CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
|
||||
return canvas;
|
||||
};
|
||||
|
||||
function hasEntries(array) {
|
||||
return array.length > 0;
|
||||
}
|
||||
|
||||
})(window, document);
|
4
dist/html2canvas.min.js
vendored
4
dist/html2canvas.min.js
vendored
File diff suppressed because one or more lines are too long
@ -3,6 +3,9 @@ function NodeContainer(node, parent) {
|
||||
this.parent = parent;
|
||||
this.stack = null;
|
||||
this.bounds = null;
|
||||
this.borders = null;
|
||||
this.clip = [];
|
||||
this.backgroundClip = [];
|
||||
this.offsetBounds = null;
|
||||
this.visible = null;
|
||||
this.computedStyles = null;
|
||||
@ -63,12 +66,12 @@ NodeContainer.prototype.cssFloat = function(attribute) {
|
||||
NodeContainer.prototype.fontWeight = function() {
|
||||
var weight = this.css("fontWeight");
|
||||
switch(parseInt(weight, 10)){
|
||||
case 401:
|
||||
weight = "bold";
|
||||
break;
|
||||
case 400:
|
||||
weight = "normal";
|
||||
break;
|
||||
case 401:
|
||||
weight = "bold";
|
||||
break;
|
||||
case 400:
|
||||
weight = "normal";
|
||||
break;
|
||||
}
|
||||
return weight;
|
||||
};
|
||||
@ -264,55 +267,54 @@ function parseBackgrounds(backgroundImage) {
|
||||
return;
|
||||
}
|
||||
switch(c) {
|
||||
case '"':
|
||||
if(!quote) {
|
||||
quote = c;
|
||||
}
|
||||
else if(quote === c) {
|
||||
quote = null;
|
||||
}
|
||||
case '"':
|
||||
if(!quote) {
|
||||
quote = c;
|
||||
} else if(quote === c) {
|
||||
quote = null;
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
if(quote) {
|
||||
break;
|
||||
case '(':
|
||||
if(quote) {
|
||||
break;
|
||||
} else if(mode === 0) {
|
||||
mode = 1;
|
||||
} else if(mode === 0) {
|
||||
mode = 1;
|
||||
block += c;
|
||||
return;
|
||||
} else {
|
||||
numParen++;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 1) {
|
||||
if(numParen === 0) {
|
||||
mode = 0;
|
||||
block += c;
|
||||
return;
|
||||
} else {
|
||||
numParen++;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 1) {
|
||||
if(numParen === 0) {
|
||||
mode = 0;
|
||||
block += c;
|
||||
appendResult();
|
||||
return;
|
||||
} else {
|
||||
numParen--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 0) {
|
||||
appendResult();
|
||||
return;
|
||||
} else if (mode === 1) {
|
||||
if (numParen === 0 && !method.match(/^url$/i)) {
|
||||
args.push(definition);
|
||||
definition = '';
|
||||
block += c;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
numParen--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if(mode === 0) {
|
||||
appendResult();
|
||||
return;
|
||||
} else if (mode === 1) {
|
||||
if (numParen === 0 && !method.match(/^url$/i)) {
|
||||
args.push(definition);
|
||||
definition = '';
|
||||
block += c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
block += c;
|
||||
|
@ -20,12 +20,14 @@ function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
this.fontMetrics = new FontMetrics();
|
||||
log("Fetched nodes");
|
||||
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
||||
log("Creating stacking contexts");
|
||||
this.createStackingContexts();
|
||||
log("Sorting stacking contexts");
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.ready = this.images.ready.then(bind(function() {
|
||||
log("Images loaded, starting parsing");
|
||||
log("Calculate overflow clips");
|
||||
this.calculateOverflowClips();
|
||||
log("Creating stacking contexts");
|
||||
this.createStackingContexts();
|
||||
log("Sorting stacking contexts");
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.parse(this.stack);
|
||||
log("Render queue created with " + this.renderQueue.length + " items");
|
||||
return new Promise(bind(function(resolve) {
|
||||
@ -42,6 +44,24 @@ function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
}, this));
|
||||
}
|
||||
|
||||
NodeParser.prototype.calculateOverflowClips = function() {
|
||||
this.nodes.forEach(function(container) {
|
||||
if (isElement(container)) {
|
||||
container.borders = this.parseBorders(container);
|
||||
var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : [];
|
||||
container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip;
|
||||
container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip;
|
||||
} else if (isTextNode(container)) {
|
||||
container.clip = hasParentClip(container) ? container.parent.clip : [];
|
||||
}
|
||||
container.bounds = null;
|
||||
}, this);
|
||||
};
|
||||
|
||||
function hasParentClip(container) {
|
||||
return container.parent && container.parent.clip.length;
|
||||
}
|
||||
|
||||
NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) {
|
||||
asyncTimer = asyncTimer || Date.now();
|
||||
this.paint(queue[this.renderIndex++]);
|
||||
@ -131,6 +151,10 @@ NodeParser.prototype.getChildren = function(parentContainer) {
|
||||
NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
|
||||
var stack = new StackingContext(hasOwnStacking, container.cssFloat('opacity'), container.node, container.parent);
|
||||
stack.visible = container.visible;
|
||||
stack.borders = container.borders;
|
||||
stack.bounds = container.bounds;
|
||||
stack.clip = container.clip;
|
||||
stack.backgroundClip = container.backgroundClip;
|
||||
var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack;
|
||||
parentStack.contexts.push(stack);
|
||||
container.stack = stack;
|
||||
@ -245,17 +269,19 @@ NodeParser.prototype.paintNode = function(container) {
|
||||
}
|
||||
}
|
||||
var bounds = container.parseBounds();
|
||||
var borderData = this.parseBorders(container);
|
||||
this.renderer.clip(borderData.clip, function() {
|
||||
this.renderer.renderBackground(container, bounds, borderData.borders.map(getWidth));
|
||||
this.renderer.clip(container.backgroundClip, function() {
|
||||
this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth));
|
||||
}, this);
|
||||
this.renderer.renderBorders(borderData.borders);
|
||||
switch(container.node.nodeName) {
|
||||
|
||||
this.renderer.clip(container.clip, function() {
|
||||
this.renderer.renderBorders(container.borders.borders);
|
||||
|
||||
switch (container.node.nodeName) {
|
||||
case "svg":
|
||||
case "IFRAME":
|
||||
var imgContainer = this.images.get(container.node);
|
||||
if (imgContainer) {
|
||||
this.renderer.renderImage(container, bounds, borderData, imgContainer);
|
||||
this.renderer.renderImage(container, bounds, container.borders, imgContainer);
|
||||
} else {
|
||||
log("Error loading <" + container.node.nodeName + ">", container.node);
|
||||
}
|
||||
@ -263,7 +289,7 @@ NodeParser.prototype.paintNode = function(container) {
|
||||
case "IMG":
|
||||
var imageContainer = this.images.get(container.node.src);
|
||||
if (imageContainer) {
|
||||
this.renderer.renderImage(container, bounds, borderData, imageContainer);
|
||||
this.renderer.renderImage(container, bounds, container.borders, imageContainer);
|
||||
} else {
|
||||
log("Error loading <img>", container.node.src);
|
||||
}
|
||||
@ -273,7 +299,8 @@ NodeParser.prototype.paintNode = function(container) {
|
||||
case "TEXTAREA":
|
||||
this.paintFormValue(container);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.paintFormValue = function(container) {
|
||||
@ -324,33 +351,35 @@ NodeParser.prototype.paintText = function(container) {
|
||||
this.renderer.clearShadow();
|
||||
}
|
||||
|
||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||
if (bounds) {
|
||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||
this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
|
||||
}
|
||||
this.renderer.clip(container.parent.clip, function() {
|
||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||
if (bounds) {
|
||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||
this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
|
||||
}
|
||||
}, 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;
|
||||
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) {
|
||||
var nodeBounds = container.bounds;
|
||||
var nodeBounds = container.parseBounds();
|
||||
var radius = getBorderRadiusData(container);
|
||||
var borders = ["Top", "Right", "Bottom", "Left"].map(function(side) {
|
||||
return {
|
||||
@ -371,53 +400,53 @@ NodeParser.prototype.parseBorders = function(container) {
|
||||
var bh = nodeBounds.height - (borders[2].width);
|
||||
|
||||
switch(borderSide) {
|
||||
case 0:
|
||||
// top border
|
||||
bh = borders[0].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by],
|
||||
c2: [bx + bw, by],
|
||||
c3: [bx + bw - borders[1].width, by + bh],
|
||||
c4: [bx + borders[3].width, by + bh]
|
||||
}, radius[0], radius[1],
|
||||
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
|
||||
break;
|
||||
case 1:
|
||||
// right border
|
||||
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
|
||||
bw = borders[1].width;
|
||||
case 0:
|
||||
// top border
|
||||
bh = borders[0].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by],
|
||||
c2: [bx + bw, by],
|
||||
c3: [bx + bw - borders[1].width, by + bh],
|
||||
c4: [bx + borders[3].width, by + bh]
|
||||
}, radius[0], radius[1],
|
||||
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
|
||||
break;
|
||||
case 1:
|
||||
// right border
|
||||
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
|
||||
bw = borders[1].width;
|
||||
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by],
|
||||
c2: [bx + bw, by + bh + borders[2].width],
|
||||
c3: [bx, by + bh],
|
||||
c4: [bx, by + borders[0].width]
|
||||
}, radius[1], radius[2],
|
||||
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
|
||||
break;
|
||||
case 2:
|
||||
// bottom border
|
||||
by = (by + nodeBounds.height) - (borders[2].width);
|
||||
bh = borders[2].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by + bh],
|
||||
c2: [bx, by + bh],
|
||||
c3: [bx + borders[3].width, by],
|
||||
c4: [bx + bw - borders[3].width, by]
|
||||
}, radius[2], radius[3],
|
||||
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
|
||||
break;
|
||||
case 3:
|
||||
// left border
|
||||
bw = borders[3].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by + bh + borders[2].width],
|
||||
c2: [bx, by],
|
||||
c3: [bx + bw, by + borders[0].width],
|
||||
c4: [bx + bw, by + bh]
|
||||
}, radius[3], radius[0],
|
||||
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
|
||||
break;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by],
|
||||
c2: [bx + bw, by + bh + borders[2].width],
|
||||
c3: [bx, by + bh],
|
||||
c4: [bx, by + borders[0].width]
|
||||
}, radius[1], radius[2],
|
||||
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
|
||||
break;
|
||||
case 2:
|
||||
// bottom border
|
||||
by = (by + nodeBounds.height) - (borders[2].width);
|
||||
bh = borders[2].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by + bh],
|
||||
c2: [bx, by + bh],
|
||||
c3: [bx + borders[3].width, by],
|
||||
c4: [bx + bw - borders[3].width, by]
|
||||
}, radius[2], radius[3],
|
||||
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
|
||||
break;
|
||||
case 3:
|
||||
// left border
|
||||
bw = borders[3].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by + bh + borders[2].width],
|
||||
c2: [bx, by],
|
||||
c3: [bx + bw, by + borders[0].width],
|
||||
c4: [bx + bw, by + bh]
|
||||
}, radius[3], radius[0],
|
||||
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return border;
|
||||
@ -430,20 +459,20 @@ NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, bor
|
||||
borderArgs = [];
|
||||
|
||||
switch(backgroundClip) {
|
||||
case "content-box":
|
||||
case "padding-box":
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
|
||||
break;
|
||||
case "content-box":
|
||||
case "padding-box":
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
|
||||
break;
|
||||
|
||||
default:
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
|
||||
break;
|
||||
default:
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
|
||||
break;
|
||||
}
|
||||
|
||||
return borderArgs;
|
||||
@ -718,5 +747,5 @@ function isWordBoundary(characterCode) {
|
||||
}
|
||||
|
||||
function hasUnicode(string) {
|
||||
return /[^\u0000-\u00ff]/.test(string);
|
||||
return (/[^\u0000-\u00ff]/).test(string);
|
||||
}
|
||||
|
@ -47,9 +47,11 @@ CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx
|
||||
}
|
||||
};
|
||||
|
||||
CanvasRenderer.prototype.clip = function(shape, callback, context) {
|
||||
CanvasRenderer.prototype.clip = function(shapes, callback, context) {
|
||||
this.ctx.save();
|
||||
this.shape(shape).clip();
|
||||
shapes.filter(hasEntries).forEach(function(shape) {
|
||||
this.shape(shape).clip();
|
||||
}, this);
|
||||
callback.call(context);
|
||||
this.ctx.restore();
|
||||
};
|
||||
@ -107,7 +109,7 @@ CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgr
|
||||
["line", Math.round(left + width), Math.round(height + top)],
|
||||
["line", Math.round(left), Math.round(height + top)]
|
||||
];
|
||||
this.clip(shape, function() {
|
||||
this.clip([shape], function() {
|
||||
this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]);
|
||||
}, this);
|
||||
};
|
||||
@ -147,3 +149,7 @@ CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
|
||||
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height );
|
||||
return canvas;
|
||||
};
|
||||
|
||||
function hasEntries(array) {
|
||||
return array.length > 0;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
<div style="overflow:hidden;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
|
Loading…
Reference in New Issue
Block a user