I'm creating an SVG text box using the Raphael library, and filling it with a dynamic string which is extracted from an XML document.
Sometimes, this string is longer than the canvas I am placing the text box on, so I need to either limit the width of the box which will itself force the line breaks (I can't find any evidence of this being possible) OR ensure that a '\n' line break is inserted after a certain amount of characters.
So (1) is this the best option? And (2) how would I do this?
There isn't an attribute for text wrapping, but there is a simple trick you can use. Add one word at a time to a text object and when it gets too wide, add a line feed. You can use the getBBox() function to determine the width. Basically, you emulate an old fashioned typewriter. Here is a sample of some code that will do this for you. You could easily turn this into a simple function that takes the text and a width.
var r = Raphael(500, 500);
var t = r.text(100, 100).attr('text-anchor', 'start');
var maxWidth = 100;
var content = "Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. ";
var words = content.split(" ");
var tempText = "";
for (var i=0; i<words.length; i++) {
t.attr("text", tempText + " " + words[i]);
if (t.getBBox().width > maxWidth) {
tempText += "\n" + words[i];
} else {
tempText += " " + words[i];
}
}
t.attr("text", tempText.substring(1));
thanks for the answer. However, I found that I needed a few adjustments to work for me:
function textWrap(t, width) {
var content = t.attr("text");
var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
t.attr({
'text-anchor' : 'start',
"text" : abc
});
var letterWidth = t.getBBox().width / abc.length;
t.attr({
"text" : content
});
var words = content.split(" ");
var x = 0, s = [];
for ( var i = 0; i < words.length; i++) {
var l = words[i].length;
if (x + (l * letterWidth) > width) {
s.push("\n");
x = 0;
}
x += l * letterWidth;
s.push(words[i] + " ");
}
t.attr({
"text" : s.join("")
});
}
The changes were:
- the comparison needed to use (l * letterwidth) ... not just l
- the if/else changed to just an if - so that a line break will always set X to 0
- and always add the new l * letterwidth to the x value
hope this helps.
mark's solution is slow for large amounts of text (firefox 11). I think that is because the text is re rendered several times for getting BBOX. the following function is more efficient for large amounts of text but perhaps less exact (code from raphaelmarkup project):
/**
* @param t a raphael text shape
* @param width - pixels to wrapp text width
* modify t text adding new lines characters for wrapping it to given width.
*/
rm._textWrapp = function(t, width) {
var content = t.attr("text");
var abc="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
t.attr({'text-anchor': 'start', "text": abc});
var letterWidth=t.getBBox().width / abc.length;
t.attr({"text": content});
var words = content.split(" "), x=0, s=[];
for ( var i = 0; i < words.length; i++) {
var l = words[i].length;
if(x+l>width) {
s.push("\n")
x=0;
}
else {
x+=l*letterWidth;
}
s.push(words[i]+" ");
}
t.attr({"text": s.join("")});
};
Well, i solved it tweaking it a little bit
var words = server.split( " " );
var length = words.length;
var temp_text = "";
for( var i = 0; i < length; i++ ) {
temp_text = temp_text + ' ' + words[i];
t.attr( "text", temp_text );
if( t.getBBox().width > width ) {
temp_text = temp_text.replace(/( *)(\w+)$/, "\n$2");
}
}
t.attr( "text", temp_text.trim() );
I know it's a little belated now, but you might be interested in my Raphael-paragraph project which does this automatically.
Raphael-paragraph allows you to create auto-wrapped multiline text with maximum width and height constraints, line height and text style configuration. It can hyphenate long words and truncate them if they exceed vertical bounds. 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.