How can I determine accurate height of Raphael text()? - javascript

Using Raphael JS 2.1.2, the following code:
var distance = 250;
var sizes = [ 14, 18, 24, 48, 72, 96 ];
for(var i = 0; i < sizes.length; i++)
{
var size = sizes[i];
var text = me.paper.text(distance + (size * 5), me.top + 200, size).attr({ 'font-size': size });
var rect = text.getBBox();
text.paper.rect(rect.x, rect.y, rect.width, rect.height).attr({ stroke: '#FF0000' });
}
produces this output:
How can I accurately measure the height of the text, as you can see the bounding box includes vertical padding which is relative to the font size?
Also $(text.node).height() returns the same value as rect.height. I am trying to align the top and/or bottom of text with other elements so I need some way to determine the padding or text height per font-size.
I could maintain a collection of { size: [font size], padding: [padding] }, but it would be good if I could generate this at runtime.

You can't. Actually this is not a "padding". getBBox returns dimentions given from a font metric, not individual glyphs. BBox height for text element includes font ascent and descent.
In most fonts, ascent reserves a gap above cap-height for glyphs such as "Ä". Discenders are reserved for lowercase characters with "tails" such as "g", "j", "q" and etc. For example, draw a rect around text "Äg".
For more detais see:
http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Text
http://www.w3.org/TR/SVG11/text.html#BaselineAlignmentProperties
http://en.wikipedia.org/wiki/Baseline_(typography)

It's possible but it's messy. You can do this by using OpenType.js, then drawing a path using same text/size as Raphael with OpenType getPath and measuring the path. You can measure the outputted path with Raphael.pathBBox(path.toPathData()) or (preferable as its faster though more limited browser support) with creating a temp SVG as outlined here.
The size of the path will be the exact size of your text as you desired.
Along the same lines you may also encounter a position challenge i.e. trying to determine how the svg box fits over the raphael box. You can use the ascender/descender values provided by the font object returned from OpenType load alongside with the y/y2 values of your SVG path. All of these will allow you to figure out the baseline of OpenType text and you can match this to your Raphael box.

Related

JavaScript convert text to pixel coordinates

In short, given a font (with size, weight, etc.) and a string, is there a way I extract an array of pixel coordinates relative to the top left of the first letter using JavaScript?
E.g. I might become
[
[0,0],
[0,1],
...
]
Edit: I am not looking for the bounding box, rather I am looking for the coordinates of each and every individual pixel that makes up the text. I need this information to essentially recreate the text later using squares.
Yes, you can use the canvas element to obtain text metrics:
// setup canvas
var c = document.createElement("canvas"),
ctx = c.getContext("2d");
// define (pre-loaded) font:
ctx.font = "bold 16px sans-serif";
// get metrics
var m = ctx.measureText("Hello");
The metrics object will give you the width and height etc. But - only width is currently supported in most browsers. There exists a poly-fill that will cover things like ascend and descend which you would need.
An alternative is to scan the bitmap to detect the rectangle the text coverts. I have given an answer here which covers this technique:
Javascript Method to detect area of a PNG that is not transparent
Also, if you only need the bounding box of the text (not pixel-accurate start point of the text itself) you can use a DOM element to get the width and height for that box. Here is one approach for that:
Use Javascript to get Maximum font size in canvas
Updated after clarification: For extracting pixels from the bitmap and convert those into points:
https://stackoverflow.com/a/30204783/1693593

Which algorithm to use for finding out whether a coordinate is in a specific area?

I want to know whether a point is in the "black" area of some image like the one below.
For the time being I created a large array like this (generated outside JavaScript):
area = [ [0,200], [0,201], [0,202], ..., [1,199], [1,200], ...]
to indicate which coordinates are black. Since this is getting very memory heavy for larger areas (I'm talking of image sizes of about 2000x2000 pixels), which kind of algorithm would you choose that is fast and not too memory hungry for finding out whether a specific coordinate is inside the black area?
You can draw the image to a canvas with same width and height as the image and then retrieve the pixelColor from the canvas at the specific point(x|y).
Here is a thread on how to retrieve the pixcel color:
Get pixel color from an image
This is how i retrieve the pixel color from the mouseposition and return a colorcode('#rrggbb'):
var pixelData = canvas.getContext('2d').getImageData(event.offsetX, event.offsetY, 1, 1).data;
var hex= '#' + valToHex(pixelData[0]) + valToHex(pixelData[1]) + valToHex(pixelData[2]);

Positioning an SVG element using javascript

I am doing a modification on svg-edit. I am using a function to make a path element bigger or smaller based on width and height inputs by the user. The user selects an element and clicks on a button to fire up the function which takes the last known width and heght measurements and then asks from the user the new width and height values. It then creates a divisor which it uses to create a TRANSFORM MATRIX operation on the element to make it as big as the user wants.
The problem is that when transforming matrices, the elements also changes position.
I want when the user is asked for a width and height also to be asked for x,y position on canvas and then move the selected element to that position.
Is their a way of repositioning an svg element?
function changeDimensions()
{
svgNode = svgCanvas.getSelectedElems()[0];
var dims = Raphael.pathBBox(svgNode.getAttribute('d'));
lasth = parseInt(dims.height);
lastw= parseInt(dims.width);
var transformw=prompt("Enter your new width");
var transformh=prompt("Enter your new height");
newW=transformw/lastw;
newH=transformh/lasth;
svgCanvas.changeSelectedAttribute("transform", "matrix(" + newW + ", 0, 0, " + newH + ", 0, 0)");
svgCanvas.recalculateAllSelectedDimensions();
}
Different svg elements have different attributes that they use to position themselves. For example rect's have x and y attributes but circles have cx, and cy. Path's do not have separate attributes.
However you can probably get what you need from a transform! Most svg elements will accept a transform attribute where you can assign a translation. E.g.
<path d="M10,10L20,100" transform="translate(30,40)"/>
In fact you can probably scale your path with the same transform attribute.

wrap text to fit into a rectangle : raphael

Anyone know of function that can break text at word boundaries to fit into rectangle
Following is code for rectangle and text
window.onload = function () {
var outsideRectX1=30, outsideRectY1=30,outsideRectX2=220, outsideRectY2=480, outsideRectR=10;
var group = paper.set();
var rect1=paper.rect(outsideRectX1+40, outsideRectY1+70, 80, 40,10);
var text3=paper.text(outsideRectX1+75, outsideRectY1+85,"Test code for wrap text").attr({fill: '#000000', 'font-family':'calibri', 'font-size':'14px'});
group.push(rect1);
group.push(text3);
};
When text is greater than rectangle width it automatically wrap so that it always display into rectangle boundaries.
I'm not sure whether there is any direct way to wrap the text according to the size of the rectangle. May be you can specify line breaks or a "\n". Or you can try to resize the rectangle as and when the text length increases.
Here is a sample code where the rectangle resize as the text length increases.
var recttext = paper.set();
el = paper.rect(0, 0, 300, 200);
text = paper.text(0,10, "Hi... This is a test to check whether the rectangle dynamically changes its size.").attr({"text-anchor":"start",fill:'#ff0000',"font-size": 14});
text1=paper.text(0,30,"hi").attr({"text-anchor":"start",fill: '#ff0000',"font-size": 14});
//el.setSize(495,200);
recttext.push(el);
recttext.push(text);
recttext.push(text1);
alert(recttext.getBBox().width);
alert(recttext.getBBox().height);
var att = {width:recttext.getBBox().width,height:recttext.getBBox().height};
el.attr(att);
recttext.translate(700,400);
I know it's a little belated now, but you might be interested in my [Raphael-paragraph][1] project.
It's a small library that allows you to create auto-wrapped multiline text with maximum width and height constraints, line height and text style configuration. It's still quite beta-ish and requires a lot of optimization, but it should work for your purposes.
Usage examples and documentation are provided on the GitHub page.

Raphael svg invert y-axis coordinates

I am using the Raphael library from http://raphaeljs.com/ and work on a chart library. For this library it is useful when the Y-axis are inverted. Now 0,0 is at the top left but I want it to be at the bottom left.
There is a possibility to apply a scale matrix to an element but I want the coordinates to be inverted for whatever I draw. Any clues?
The only way I could figure out to do this was to apply a negative scaling to the svg element using CSS (see this fiddle). (I used jQuery to add the styles).
This is not without problems, though. For example, text is going to be mirrored, unless you do something to un-mirror it (like applying the invert() method I added to elements using Raphael.el):
Raphael.el.invert = function() {
this.transform('s1,-1');
};
Also, if you are going to be interacting with the elements using your mouse, you will have to tweak things. Note that the black circle uses a pretty standard mouseMove function, but it doesn't work - it moves in the wrong direction in y. So you have to do something like I did with the other circles:
function cMove(dx, dy, x,y) {
this.attr('cx', x);
this.attr('cy', paperHeight - y);
};
In short, this is not at all elegant, and no other things I tried were really any better. I know this isn't what you want to hear, but I would recommend getting used to the coordinate system as it is, unless you just plan on displaying static charts.
One small issue with Mike C's resolution is that you have to know if the text is going to be inverted in the end when you create the text. If you want to ensure right-side-up text at the end (after applying other transformations) I found it works well to alter the text element's .transform() to flip the scale of text to right side up at the end.
function InvertText(ObjSet){
// This function resets the inversion of text such that it is always right side up
// ObjSet is a raphael paper.set() object
for (var i=0; i<ObjSet.items.length; i++){
var ThisObj = ObjSet.items[i];
if (ThisObj.type == 'text'){
var tArr = ThisObj.transform();
// Find the scaling factor
for (var j=0; j<tArr.length; j++){
if (tArr[j][0] == 's'){
tArr[0][2] = 1;
break;
}
}
ThisObj.transform(tArr);
}
}
}
You can use like this:
var ObjSet = paper.set().push(
paper.text(0,10,'FirstText'),
paper.path('M,0,0,v,100,h,20,v,-100,h,-20'),
paper.circle(0,0,5)
);
//Flip everything on the y-axis
ObjSet.transform('s,1,-1, T,100,100');
// Make the text right-side-up
InvertText(ObjSet);
Here's how to do it just with RaphaelJS transforms, no CSS transforms.
var SCALE = 2;
var paper = Raphael(0, 0, 400, 700);
// box notched at bottom-center and right-center
var p = paper.path("M0,0 L100,0 L100,40 L90,50 L100,60 L100,100 L60,100 L50,90 L40,100 L0,100 Z");
var bounds = p.getBBox();
p.attr({
stroke: 'none',
fill: [90, '#578A6E', '#34573E'].join("-")
})
.transform("t"+ (-bounds.width/2) +","+ (-bounds.height/2) +
"s"+ SCALE +","+ (-SCALE) +
"t"+ (bounds.width/2) +","+ (-bounds.height/2));
Raphael applies scale transforms from the center of the element's bounding box, rather than its origin. To invert the y-axis, offset before scaling, then offset again after.

Categories

Resources