You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.3 KiB
JavaScript
208 lines
7.3 KiB
JavaScript
/*
|
|
This library rewrites the Canvas2D "measureText" function
|
|
so that it returns a more complete metrics object.
|
|
|
|
** -----------------------------------------------------------------------------
|
|
|
|
CHANGELOG:
|
|
|
|
2012-01-21 - Whitespace handling added by Joe Turner
|
|
(https://github.com/oampo)
|
|
|
|
** -----------------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
@license
|
|
fontmetrics.js - https://github.com/Pomax/fontmetrics.js
|
|
|
|
Copyright (C) 2011 by Mike "Pomax" Kamermans
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
**/
|
|
|
|
/*
|
|
var NAME = "FontMetrics Library"
|
|
var VERSION = "1-2012.0121.1300";
|
|
|
|
// if there is no getComputedStyle, this library won't work.
|
|
if(!document.defaultView.getComputedStyle) {
|
|
throw("ERROR: 'document.defaultView.getComputedStyle' not found. This library only works in browsers that can report computed CSS values.");
|
|
}
|
|
|
|
// store the old text metrics function on the Canvas2D prototype
|
|
CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText;
|
|
*/
|
|
/**
|
|
* shortcut function for getting computed CSS values
|
|
*/
|
|
var getCSSValue = function(element, property) {
|
|
return document.defaultView.getComputedStyle(element,null).getPropertyValue(property);
|
|
};
|
|
/*
|
|
// debug function
|
|
var show = function(canvas, ctx, xstart, w, h, metrics)
|
|
{
|
|
document.body.appendChild(canvas);
|
|
ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(xstart,0);
|
|
ctx.lineTo(xstart,h);
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(xstart+metrics.bounds.maxx,0);
|
|
ctx.lineTo(xstart+metrics.bounds.maxx,h);
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(0,h/2-metrics.ascent);
|
|
ctx.lineTo(w,h/2-metrics.ascent);
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(0,h/2+metrics.descent);
|
|
ctx.lineTo(w,h/2+metrics.descent);
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
}
|
|
*/
|
|
/**
|
|
* The new text metrics function
|
|
*/
|
|
var measureText = function(context2D, textstring, stroke, fill) {
|
|
var metrics = context2D.measureText(textstring),
|
|
fontFamily = getCSSValue(context2D.canvas,"font-family"),
|
|
fontSize = getCSSValue(context2D.canvas,"font-size").replace("px",""),
|
|
fontStyle = getCSSValue(context2D.canvas,"font-style"),
|
|
fontWeight = getCSSValue(context2D.canvas,"font-weight"),
|
|
isSpace = !(/\S/.test(textstring));
|
|
metrics.fontsize = fontSize;
|
|
|
|
// for text lead values, we meaure a multiline text container.
|
|
var leadDiv = document.createElement("div");
|
|
leadDiv.style.position = "absolute";
|
|
leadDiv.style.opacity = 0;
|
|
leadDiv.style.font = fontStyle + " " + fontWeight + " " + fontSize + "px " + fontFamily;
|
|
leadDiv.innerHTML = textstring + "<br/>" + textstring;
|
|
document.body.appendChild(leadDiv);
|
|
|
|
// make some initial guess at the text leading (using the standard TeX ratio)
|
|
metrics.leading = 1.2 * fontSize;
|
|
|
|
// then we try to get the real value from the browser
|
|
var leadDivHeight = getCSSValue(leadDiv,"height");
|
|
leadDivHeight = leadDivHeight.replace("px","");
|
|
if (leadDivHeight >= fontSize * 2) { metrics.leading = (leadDivHeight/2) | 0; }
|
|
document.body.removeChild(leadDiv);
|
|
|
|
// if we're not dealing with white space, we can compute metrics
|
|
if (!isSpace) {
|
|
// Have characters, so measure the text
|
|
var canvas = document.createElement("canvas");
|
|
var padding = 100;
|
|
canvas.width = metrics.width + padding;
|
|
canvas.height = 3*fontSize;
|
|
canvas.style.opacity = 1;
|
|
canvas.style.fontFamily = fontFamily;
|
|
canvas.style.fontSize = fontSize;
|
|
canvas.style.fontStyle = fontStyle;
|
|
canvas.style.fontWeight = fontWeight;
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.font = fontStyle + " " + fontWeight + " " + fontSize + "px " + fontFamily;
|
|
|
|
var w = canvas.width,
|
|
h = canvas.height,
|
|
baseline = h/2;
|
|
|
|
// Set all canvas pixeldata values to 255, with all the content
|
|
// data being 0. This lets us scan for data[i] != 255.
|
|
ctx.fillStyle = "white";
|
|
ctx.fillRect(-1, -1, w + 2, h + 2);
|
|
|
|
if (stroke) {
|
|
ctx.strokeStyle = "black";
|
|
ctx.lineWidth = context2D.lineWidth;
|
|
ctx.strokeText(textstring, (padding / 2), baseline);
|
|
}
|
|
|
|
if (fill) {
|
|
ctx.fillStyle = "black";
|
|
ctx.fillText(textstring, padding / 2, baseline);
|
|
}
|
|
|
|
var pixelData = ctx.getImageData(0, 0, w, h).data;
|
|
|
|
// canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
|
|
// consecutive values in the array, rather than stored as 32 bit ints.
|
|
var i = 0,
|
|
w4 = w * 4,
|
|
len = pixelData.length;
|
|
|
|
// Finding the ascent uses a normal, forward scanline
|
|
while (++i < len && pixelData[i] === 255) {}
|
|
var ascent = (i/w4)|0;
|
|
|
|
// Finding the descent uses a reverse scanline
|
|
i = len - 1;
|
|
while (--i > 0 && pixelData[i] === 255) {}
|
|
var descent = (i/w4)|0;
|
|
|
|
// find the min-x coordinate
|
|
for(i = 0; i<len && pixelData[i] === 255; ) {
|
|
i += w4;
|
|
if(i>=len) { i = (i-len) + 4; }}
|
|
var minx = ((i%w4)/4) | 0;
|
|
|
|
// find the max-x coordinate
|
|
var step = 1;
|
|
for(i = len-3; i>=0 && pixelData[i] === 255; ) {
|
|
i -= w4;
|
|
if(i<0) { i = (len - 3) - (step++)*4; }}
|
|
var maxx = ((i%w4)/4) + 1 | 0;
|
|
|
|
// set font metrics
|
|
metrics.ascent = (baseline - ascent);
|
|
metrics.descent = (descent - baseline);
|
|
metrics.bounds = { minx: minx - (padding/2),
|
|
maxx: maxx - (padding/2),
|
|
miny: 0,
|
|
maxy: descent-ascent };
|
|
metrics.height = 1+(descent - ascent);
|
|
}
|
|
|
|
// if we ARE dealing with whitespace, most values will just be zero.
|
|
else {
|
|
// Only whitespace, so we can't measure the text
|
|
metrics.ascent = 0;
|
|
metrics.descent = 0;
|
|
metrics.bounds = { minx: 0,
|
|
maxx: metrics.width, // Best guess
|
|
miny: 0,
|
|
maxy: 0 };
|
|
metrics.height = 0;
|
|
}
|
|
return metrics;
|
|
};
|
|
|
|
export default measureText;
|