Add font metrics and text-decorations

This commit is contained in:
Niklas von Hertzen 2014-02-01 19:36:43 +02:00
parent 1f90defbfa
commit e27c41efd3
5 changed files with 85 additions and 10 deletions

View File

@ -14,5 +14,5 @@
"jQuery": true
},
"predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise",
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "log"]
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "log", "smallImage"]
}

View File

@ -37,6 +37,10 @@ function documentHeight () {
);
}
function smallImage() {
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
}
function createWindowClone(ownerDocument, width, height) {
var documentElement = ownerDocument.documentElement.cloneNode(true),
container = ownerDocument.createElement("iframe");
@ -51,7 +55,7 @@ function createWindowClone(ownerDocument, width, height) {
return new Promise(function(resolve) {
var loadedTimer = function() {
/* 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") {
documentClone.body.removeChild(div);
@ -75,8 +79,8 @@ function createWindowClone(ownerDocument, width, height) {
div.className = "html2canvas-ready-test";
documentClone.body.appendChild(div);
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);
window.setTimeout(loadedTimer, 1000);
window.setTimeout(loadedTimer, 10);
});
}

48
src/font.js Normal file
View File

@ -0,0 +1,48 @@
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;
}

10
src/fontmetrics.js Normal file
View File

@ -0,0 +1,10 @@
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];
};

View File

@ -10,6 +10,7 @@ function NodeParser(element, renderer, support, imageLoader, options) {
this.nodes = [parent].concat(this.getChildren(parent)).filter(function(container) {
return container.visible = container.isElementVisible();
});
this.fontMetrics = new FontMetrics();
log("Fetched nodes");
this.images = imageLoader.fetch(this.nodes.filter(isElement));
log("Creating stacking contexts");
@ -196,16 +197,28 @@ NodeParser.prototype.paintText = function(container) {
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
if (bounds) {
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);
};
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) {
var nodeBounds = container.bounds;
var radius = getBorderRadiusData(container);