I am trying to solving one problem , I have a textarea which works fine and exporting text to canvas , but I don't know how to achieve the same effect and add background image option to this, it could be a static background image from the beginning, here is script .When i was trying to set background image via html , i had background image behind the text , but when i was exporting i had only text in my png.
var canvas = $('#canvas');
var ctx = canvas.get(0).getContext('2d');
canvas.width = 600;
canvas.height = 400;
$('#submit').click(function (e) {
e.preventDefault();
var text = $('#text').val(),
fontSize = parseInt($('#font-size').val()),
width = parseInt($('#width').val()),
lines = [],
line = '',
lineTest = '',
words = text.split(' '),
currentY = 0;
ctx.font = fontSize + 'px Arial';
for (var i = 0, len = words.length; i < len; i++) {
lineTest = line + words[i] + ' ';
// Check total width of line or last word
if (ctx.measureText(lineTest).width > width) {
// Calculate the new height
currentY = lines.length * fontSize + fontSize;
// Record and reset the current line
lines.push({ text: line, height: currentY });
line = words[i] + ' ';
} else {
line = lineTest;
}
}
// Catch last line in-case something is left over
if (line.length > 0) {
currentY = lines.length * fontSize + fontSize;
lines.push({ text: line.trim(), height: currentY });
}
// Visually output text
ctx.clearRect(0, 0, 500, 500);
for (var i = 0, len = lines.length; i < len; i++) {
ctx.fillText(lines[i].text, 0, lines[i].height);
}
var canvasCtx = document.getElementById("canvas").getContext('2d');
var img = document.getElementById("yourimg");
canvasCtx.drawImage(img,x,y);
var img = new Image();
img.onload = function(){
canvasCtx.drawImage(img,x,y);
};
img.src = "https://i.stack.imgur.com/7Whkw.png";
});
i have solved it!
var canvas = $('#canvas');
var ctx = canvas.get(0).getContext('2d');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img = new Image();
var height = 600;
var width = 600;
img.onload = function() {
context.save();
context.rect(0, 0, 200, 200);//Здесь первый 0=X
context.drawImage(img,0, 0,width,height);//Первый 0=X
context.restore();
};
img.src = 'https://i.stack.imgur.com/5uvJN.png';
$('#submit').click(function (e) {
e.preventDefault();
var text = $('#text').val(),
fontSize = parseInt($('#font-size').val()),
width = parseInt($('#width').val()),
lines = [],
line = '',
lineTest = '',
words = text.split(' '),
currentY = 0;
ctx.font = fontSize + 'px Arial';
for (var i = 0, len = words.length; i < len; i++) {
lineTest = line + words[i] + ' ';
// Check total width of line or last word
if (ctx.measureText(lineTest).width > width) {
// Calculate the new height
currentY = lines.length * fontSize + fontSize;
// Record and reset the current line
lines.push({ text: line, height: currentY });
line = words[i] + ' ';
} else {
line = lineTest;
}
}
// Catch last line in-case something is left over
if (line.length > 0) {
currentY = lines.length * fontSize + fontSize;
lines.push({ text: line.trim(), height: currentY });
}
// Visually output text
ctx.clearRect(0, 500, 500, 500);
for (var i = 0, len = lines.length; i < len; i++) {
ctx.fillText(lines[i].text, 410, lines[i].height);
}
});
one more question how i can set padding top for textarea text on canvas?
Related
I have been struggling with a problem for some time now. I am trying to create a image onto a canvas, and when the user clicks on a div outside the canvas the background of the canvas should be changed to that image. This works, but the problem is that when I click a image nothing happens, if I click on a different image the image I clicked before gets loaded. This is making me so frustrated, and I really could need a set of new eyes on this.
The code that generates the image is very complex, it is a angular app and it depends on many directives to do some heavy lifting. The code that is generating the image is below:
var canvas = document.getElementById("image");
var context = canvas.getContext("2d");
// Setup the margin box
context.fillStyle = '#000000';
context.globalAlpha = 0;
context.fillRect(53, 53, 319, 107);
context.globalAlpha = 1;
$scope.resultTempImage = false;
$scope.generate = {
price: 1
};
$scope.color = "#ffffff";
$scope.product = {
bg: 'img/bg/bg-green-pattern.png',
icon: '',
color: "#000000",
font: 'Acme',
text1: "",
text2: "",
text3: "",
quantity: 120,
price: {
id: 1,
value: 17900
}
};
// Generates the first image
var firstLoadBg = false;
var bgImage = new Image();
var loadBg = function(image, callback) {
bgImage.src = '';
bgImage.onload = function() {
firstLoadBg = true;
callback(this);
};
bgImage.src = image;
}
var iconImage = new Image();
iconImage.onload = function() {
console.log('icon image loaded');
context.drawImage(this, 53, 53);
};
$scope.generateImage = function(product) {
/**
* Generates the image in the canvas
*/
// Generate a bg image
loadBg(product.bg, function(image) {
if (firstLoadBg == true) {
context.drawImage(image, 0, 0);
console.log('bg drawn');
}
if (product.icon) {
iconImage.src = '';
iconImage.src = product.icon;
}
// Add lines to the lines array
var lines = [];
if (product.text1 != "") {
lines.push(product.text1);
}
if (product.text2 != "") {
lines.push(product.text2);
}
if (product.text3 != "") {
lines.push(product.text3);
}
calculatePaint(lines, product.icon, product.font, function(Paint) {
/**
* Response from the Paint function, holds the position of the generated text.
*/
context.font = Paint.textSize;
context.fillStyle = product.color;
context.textAlign = "center";
context.textBaseline = "middle";
// Draw text lines
var y = Paint.bleedHeight;
angular.forEach(lines, function(value, key) {
if (lines.length == 2) {
context.textBaseline = "top";
if (key == 0) {
y = y - Paint.topPadding;
}
if (key == 1) {
y = y + Paint.padding;
}
}
if (lines.length == 3) {
context.textBaseline = "top";
if (key == 0) {
y = y - Paint.topPadding;
}
if (key == 1) {
y = y + Paint.padding;
}
if (key == 2) {
y = y + Paint.padding;
}
}
context.fillText(lines[key], Paint.bleedWidth, y, Paint.maxWidth);
});
var dataURL = canvas.toDataURL();
$scope.resultTempImage = dataURL;
console.log('done painting');
});
});
}
function calculatePaint(lines, icon, font, callback) {
/**
* #function for fitting text to the margin box
* #param line1 String
* #param line2 String
* #param line3 String
* #return obj
*/
// Margin of icon
var iconMargin = 90;
// Initial fontSize
var fontSize = 80;
// Bleed
var marginWidth = 53;
var marginHeight = 53;
// Inner box width
var w = 319;
var iconw = 319 - 100;
// Inner box height
var h = 107;
var Paint = {
bleedWidth: marginWidth + (w / 2),
bleedHeight: marginHeight + (h / 2),
maxWidth: w,
padding: 2,
topPadding: 53
};
if (icon) {
Paint.bleedWidth = marginWidth + 100 + (iconw / 2);
Paint.textSize = fontSize + 'px ' + font;
Paint.maxWidth = iconw;
fontSize = 60;
}
// Check lines array
if (lines.length == 1) {
// only one line is present
Paint.textSize = fontSize + 'px ' + font;
}
if (lines.length == 2) {
Paint.textSize = (Paint.bleedHeight / 2) - 8 + 'px ' + font;
Paint.padding = Paint.padding + (Paint.bleedHeight / 2) - 2;
}
if (lines.length == 3) {
Paint.textSize = (Paint.bleedHeight / 3) - 8 + 'px ' + font;
Paint.padding = Paint.padding + (Paint.bleedHeight / 3) - 2;
}
callback(Paint);
}
I found a solution to my own problem. What i did was preload all the images when the page load. Like this:
Data.get( 'assets/images' ).then( function( res ) {
if( res.success == true ) {
$scope.items = res.message;
angular.forEach( res.message, function( item, key ) {
preload( item.data, function(image) {
item.image = image;
} )
} );
}
} );
var preload = function( src, callback ) {
var image = new Image();
image.onload = function() {
callback( this );
}
image.src = src;
}
This preloads all my images and when im ready to draw my canvas I have all the image obj ready to go.
Goal
The stripes in the background remain fixed while the cones rotate about the center.
Current State
live demo:
https://codepen.io/WallyNally/pen/yamGYB
/*
The loop function is around line 79.
Uncomment it to start the animation.
*/
var c = document.getElementById('canv');
var ctx = c.getContext('2d');
var W = c.width = window.innerWidth;
var H = c.height = window.innerHeight;
var Line = function() {
this.ctx = ctx;
this.startX = 0;
this.startY = 0;
this.endX = 0;
this.endY = 0;
this.direction = 0;
this.color = 'blue';
this.draw = function() {
this.ctx.beginPath();
this.ctx.lineWidth = .1;
this.ctx.strokeStlye = this.color;
this.ctx.moveTo(this.startX, this.startY);
this.ctx.lineTo(this.endX, this.endY);
this.ctx.closePath();
this.ctx.stroke();
}
this.update = function() {
//for fun
if (this.direction == 1) {
this.ctx.translate(W/2, H/2);
this.ctx.rotate(-Math.PI/(180));
}
}//this.update()
}//Line();
objects=[];
function initLines() {
for (var i =0; i < 200; i++) {
var line = new Line();
line.direction = (i % 2);
if (line.direction == 0) {
line.startX = 0;
line.startY = -H + i * H/100;
line.endX = W + line.startX;
line.endY = H + line.startY;
}
if (line.direction == 1) {
line.startX = 0;
line.startY = H - i * H/100;
line.endX = W - line.startX;
line.endY = H - line.startY;
}
objects.push(line);
line.draw();
}
}
initLines();
function render(c) {
c.clearRect(0, 0, W, H);
for (var i = 0; i < objects.length; i++)
{
objects[i].update();
objects[i].draw();
}
}
function loop() {
render(ctx);
window.requestAnimationFrame(loop);
}
//loop();
What I have tried
The translate(W/2, H/2) should place the context at the center of the page, then this.ctx.rotate(-Math.PI/(180)) should rotate it one degree at a time. This is the part that is not working.
Using save()and restore() is the proper way to keep some parts of an animation static while others move. I placed the save and restore in different parts of the code to no avail. There are one of two types of result : Either a new entirely static image is produced, or some erratic animation happens (in the same vein of where it is now).
Here is the changed pen: http://codepen.io/samcarlinone/pen/LRwqNg
You needed a couple of changes:
var c = document.getElementById('canv');
var ctx = c.getContext('2d');
var W = c.width = window.innerWidth;
var H = c.height = window.innerHeight;
var angle = 0;
var Line = function() {
this.ctx = ctx;
this.startX = 0;
this.startY = 0;
this.endX = 0;
this.endY = 0;
this.direction = 0;
this.color = 'blue';
this.draw = function() {
this.ctx.beginPath();
this.ctx.lineWidth = .1;
this.ctx.strokeStlye = this.color;
this.ctx.moveTo(this.startX, this.startY);
this.ctx.lineTo(this.endX, this.endY);
this.ctx.closePath();
this.ctx.stroke();
}
this.update = function() {
//for fun
if (this.direction == 1) {
this.ctx.translate(W/2, H/2);
this.ctx.rotate(angle);
this.ctx.translate(-W/2, -H/2);
}
}//this.update()
}//Line();
objects=[];
function initLines() {
for (var i =0; i < 200; i++) {
var line = new Line();
line.direction = (i % 2);
if (line.direction == 0) {
line.startX = 0;
line.startY = -H + i * H/100;
line.endX = W + line.startX;
line.endY = H + line.startY;
}
if (line.direction == 1) {
line.startX = 0;
line.startY = H - i * H/100;
line.endX = W - line.startX;
line.endY = H - line.startY;
}
objects.push(line);
line.draw();
}
}
initLines();
function render(c) {
c.clearRect(0, 0, W, H);
for (var i = 0; i < objects.length; i++)
{
ctx.save();
objects[i].update();
objects[i].draw();
ctx.restore();
}
}
function loop() {
render(ctx);
window.requestAnimationFrame(loop);
angle += Math.PI/360;
}
loop();
First I added a variable to keep track of rotation and increment it in the loop
Second I save and restore for each individual line, alternatively if all lines were going to perform the same transformation you could move that code before and after the drawing loop
Third to get the desired affect I translate so the center point is in the middle of the screen, then I rotate so that the lines are rotated, then I translate back because all the lines have coordinates on the interval [0, H]. Instead of translating back before drawing another option would be to use coordinates on the interval [-(H/2), (H/2)] etc.
Im using clusterfck.js to analyze and return colors. I wan't to grap images from search-results, but it seems to be a problem with the way clusterfck does the data reading. Here's the code from clusterfck:
$(".kmeans-button").click(function() {
if (!colors) {
colors = getImageColors();
}
var k = $(this).attr("data-k"); // $.data() returned undefined
$("#clusters").empty().append("<div>calculating distances...</div>");
kmeans.clusterColors(colors, k); // Threaded analyzing (worker)
})
function getImageColors() {
var img = $("#test-image");
var width = img.width();
var height = img.height();
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
context.drawImage(img.get(0), 0, 0);
var data = context.getImageData(0, 0, width, height).data;
var colors = [];
for(var x = 0; x < width-12; x += 3) {
for(var y = 0; y < height-12; y += 3) { // sample image, every 3rd row and column
var offs = x*4 + y*4*width;
var color = [data[offs + 0], data[offs + 1], data[offs + 2]];
colors.push(color);
}
}
return colors;
}
I tried to put in img.crossOrigin = "Anonymous"; with no luck. I guess it needs to be loaded in af function and somehow callback colors. This is the best i could come up with, but it's not working.
$(".kmeans-button").click(function() {
if (!colors) {
getImageColors2(function(colors2) {
colors=colors2.slice(0);
var k = $(this).attr("data-k-e"); // $.data() returned undefined
$("#clusters").empty().append("<div>calculating colors...</div>");
kmeans.clusterColors(colors, k);
})
}
})
function getImageColors2(callback) {
var img2= document.getElementById("test-image");
var img = new Image();
img.onload = function () {
var width = img.width();
var height = img.height();
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
context.drawImage(img.get(0), 0, 0);
var data = context.getImageData(0, 0, width, height).data;
var colors = [];
for(var x = 0; x < width-12; x += 3) {
for(var y = 0; y < height-12; y += 3) { // sample image, every 3rd row and column
var offs = x*4 + y*4*width;
var color = [data[offs + 0], data[offs + 1], data[offs + 2]];
colors.push(color);
}
}
callback(colors);
}
img.src=img2.src;
}
I assume that it can't access the data without loading into a image when on a external server. What am i missing her ? Maybe i got it all wrong ?
Thanks for your comments! I got it working now with the callback function :-)
function getpalette(colors) {
$("#clusters").empty().append("<div>calculating colors...</div>");
kmeans_paint.clusterColors(colors, k);
}
$(".kmeans-error-button").click(function() {
k = $(this).attr("data-k-e");
if (!colors) {
getImageColors(getpalette);
}
})
function getImageColors(callback) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function () {
var width = img.width;
var height = img.height;
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
context.drawImage(img, 0, 0);
var data = context.getImageData(0, 0, width, height).data;
var colors = [];
for(var x = 0; x < width-12; x += 3) {
for(var y = 0; y < height-12; y += 3) { // sample image, every 3rd row and column
var offs = x*4 + y*4*width;
var color = [data[offs + 0], data[offs + 1], data[offs + 2]];
colors.push(color);
}
}
callback(colors);
}
img.src=document.getElementById("test-image").src;
}
I have a dom editor which a user can insert textbox and images. One of my requirements involve saving a snapshot of what is in the editor into an image. I did some research and there are some solutions, but they don't seem 100% foolproof. I've tried implementing a solution myself, clobbering code here and there:
function measureText(text, size, font) {
var lDiv = document.createElement('lDiv');
document.body.appendChild(lDiv);
lDiv.style.fontSize = size;
lDiv.style.fontFamily = font;
lDiv.style.position = "absolute";
lDiv.style.left = -1000;
lDiv.style.top = -1000;
lDiv.innerHTML = text;
var metrics = font.measureText(text, size.slice(0, size.length - 2));
var lResult = {
width: lDiv.clientWidth,
height: metrics.height + lDiv.clientHeight
};
document.body.removeChild(lDiv);
lDiv = null;
return lResult;
}
function wrapText(context, item) {
var words = item.text.split(' ');
var line = '';
var x = parseInt(item.x);
var y = parseInt(item.y);
var width = parseInt(item.width.slice(0, item.width.length - 2));
var height = parseInt(item.height.slice(0, item.height.length - 2));
var fontsize = parseInt(item.size.slice(0, item.size.length - 2));
var font = new Font();
font.onload = function () {
context.save();
context.beginPath();
context.rect(x, y, width, height);
context.clip();
context.font = item.size + " " + item.font;
context.textBaseline = "top";
for (var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = measureText(testLine, item.size, font);
var testWidth = metrics.width;
if (testWidth > width && n > 0) {
console.log("Drawing '" + line + "' to " + x + " " + y);
context.fillText(line, x, y);
line = words[n] + ' ';
y += metrics.height
}
else {
line = testLine;
}
}
context.fillText(line, x, y);
context.restore();
}
font.fontFamily = item.font;
font.src = font.fontFamily;
}
this.toImage = function () {
console.log("testing");
var canvas = document.getElementById("testcanvas");
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext("2d");
var imageObj = new Image();
var thisService = this;
imageObj.onload = function () {
ctx.drawImage(imageObj, 0, 0, 400, 400);
for (var i = 0; i < thisService.canvasItems.length; i++) {
var component = thisService.canvasItems[i];
if (component.type == "textbox") {
var x = component.x.slice(0, component.x.length - 2);
var y = component.y.slice(0, component.y.length - 2);
var w = component.width.slice(0, component.width.length - 2);
var h = component.height.slice(0, component.height.length - 2);
wrapText(ctx, component);
}
}
};
imageObj.src = this.base.front_image;
}
Somehow I believe I almost made it, however from the ,
There seems to be some positioning/font placement issues, just a few pixels lower. The top 1 a div with no padding, (its model can be seem on the panel on the left), while the bottom one is the canvas.
I wish to have a 1 to 1 accurate mapping here, can anyone enlighten what might be the problem?
As far as I know its not possible to draw HTML into a canvas with 100% accuracy due to the obvious "security" reasons.
You can still get pretty close using the rasterizeHTML.js library.
It uses a SVG image containing the content you want to render. To draw HTML content, you'd use a element containing the HTML, then draw that SVG image into your canvas.
Given (1) a font-family and (2) a unicode character code.
Is it possible to, within JavaScript, produce an image that looks like:
http://www.freetype.org/freetype2/docs/tutorial/metrics.png
Basically, I want to:
display the character itself (enlarged)
get the various font metrics
draw a bunch of light grey lines
Now, drawing the light grey lines is simple -- I just use SVG. However, how do I extract the font-metrics of the character?
Based on the library mentioned above I made this codepen
http://codepen.io/sebilasse/pen/gPBQqm?editors=1010
[edit: capHeight is now based on the letter H as suggested by #sebdesign below]
HTML
<h4>
Change font name
<input value="Maven Pro"></input>
<small>[local or google]</small>
and font size
<input value="40px" size=8></input>
and
<button onclick="getMetrics()">
<strong>get metrics</strong>
</button>
</h4>
<div id="illustrationContainer"></div>
<pre id="log"></pre>
<canvas id="cvs" width="220" height="200"></canvas>
JS
(getMetrics());
function getMetrics() {
var testtext = "Sixty Handgloves ABC";
// 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
*/
CanvasRenderingContext2D.prototype.measureText = function(textstring) {
var metrics = this.measureTextWidth(textstring),
fontFamily = getCSSValue(this.canvas,"font-family"),
fontSize = getCSSValue(this.canvas,"font-size").replace("px",""),
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 = 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;
var ctx = canvas.getContext("2d");
ctx.font = 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);
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;
};
//callback();
var fontName = document.getElementsByTagName('input')[0].value;
var fontSize = document.getElementsByTagName('input')[1].value;
var WebFontConfig = {
google: {
families: [ [encodeURIComponent(fontName),'::latin'].join('') ]
}
};
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
document.body.style.fontFamily = ['"'+fontName+'"', "Arial sans"].join(' ')
var canvas = document.getElementById('cvs'),
context = canvas.getContext("2d");
var w=220, h=200;
canvas.style.font = [fontSize, fontName].join(' ');
context.font = [fontSize, fontName].join(' ');
context.clearRect(0, 0, canvas.width, canvas.height);
// draw bounding box and text
var xHeight = context.measureText("x").height;
var capHeight = context.measureText("H").height;
var metrics = context.measureText("Sxy");
var xStart = (w - metrics.width)/2;
context.fontFamily = fontName;
context.fillStyle = "#FFAF00";
context.fillRect(xStart, h/2-metrics.ascent, metrics.bounds.maxx-metrics.bounds.minx, 1+metrics.bounds.maxy-metrics.bounds.miny);
context.fillStyle = "#333333";
context.fillText(testtext, xStart, h/2);
metrics.fontsize = parseInt(metrics.fontsize);
metrics.offset = Math.ceil((metrics.leading - metrics.height) / 2);
metrics.width = JSON.parse(JSON.stringify(metrics.width));
metrics.capHeight = capHeight;
metrics.xHeight = xHeight - 1;
metrics.ascender = metrics.capHeight - metrics.xHeight;
metrics.descender = metrics.descent;
var myMetrics = {
px: JSON.parse(JSON.stringify(metrics)),
relative: {
fontsize: 1,
offset: (metrics.offset / metrics.fontsize),
height: (metrics.height / metrics.fontsize),
capHeight: (metrics.capHeight / metrics.fontsize),
ascender: (metrics.ascender / metrics.fontsize),
xHeight: (metrics.xHeight / metrics.fontsize),
descender: (metrics.descender / metrics.fontsize)
},
descriptions: {
ascent: 'distance above baseline',
descent: 'distance below baseline',
height: 'ascent + 1 for the baseline + descent',
leading: 'distance between consecutive baselines',
bounds: {
minx: 'can be negative',
miny: 'can also be negative',
maxx: 'not necessarily the same as metrics.width',
maxy: 'not necessarily the same as metrics.height'
},
capHeight: 'height of the letter H',
ascender: 'distance above the letter x',
xHeight: 'height of the letter x (1ex)',
descender: 'distance below the letter x'
}
}
Array.prototype.slice.call(
document.getElementsByTagName('canvas'), 0
).forEach(function(c, i){
if (i > 0) document.body.removeChild(c);
});
document.getElementById('illustrationContainer').innerHTML = [
'<div style="margin:0; padding:0; position: relative; font-size:',fontSize,'; line-height: 1em; outline:1px solid black;">',
testtext,
'<div class="__ascender" style="position: absolute; width:100%; top:',myMetrics.relative.offset,'em; height:',myMetrics.relative.ascender,'em; background:rgba(220,0,5,.5);"></div>',
'<div class="__xHeight" style="position: absolute; width:100%; top:',myMetrics.relative.offset + myMetrics.relative.ascender,'em; height:',myMetrics.relative.xHeight,'em; background:rgba(149,204,13,.5);"></div>',
'<div class="__xHeight" style="position: absolute; width:100%; top:',myMetrics.relative.offset + myMetrics.relative.ascender + myMetrics.relative.xHeight,'em; height:',myMetrics.relative.descender,'em; background:rgba(13,126,204,.5);"></div>',
'</div>'
].join('');
myMetrics.illustrationMarkup = document.getElementById('illustrationContainer').innerHTML;
var logstring = ["/* metrics for", fontName,
"*/\nvar metrics =",
JSON.stringify(myMetrics, null, ' ')].join(' ');
document.getElementById('log').textContent = logstring;
}