1. NodeBox 1
    1. Homepage
    2. NodeBox 3Node-based app for generative design and data visualization
    3. NodeBox OpenGLHardware-accelerated cross-platform graphics library
    4. NodeBox 1Generate 2D visuals using Python code (Mac OS X only)
  2. Gallery
  3. Documentation
  4. Forum
  5. Blog

How to perfectly fit text in a box

Posted by Giorgio O. on Jul 26, 2008

hello everybody,

I'd love some help in trying to find a way to perfectly fit some text in a rectangular box, in order to get as little margin as possible.
'Perfectly' here means to get the real vertical size of the text box.

If I have a string, I can get the estimate of the coordinates of the text bounds in a couple of ways, but neither is really satisfying.
I've tried so far:
a) using textheight & textwidth
b) using the textpath.bounds
they do return different values.

my test code:

size(600, 600)
 
font("Courier")
fontsize(10)
 
myX = 100
myY = -100
myTest = 80 # box width
myString = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."
#myString = "adipisci velit..."
 
tp = textpath(myString, x=myX, y=myY, width= myTest)
(tp_x, tp_y), (tp_w, tp_h) = tp.bounds
#print tp_x, tp_y, tp_w, tp_h
 
# gray rect from textpath.bounds coordinates
fill(0, 0.4)
rect(tp_x, -tp_y, tp_w, tp_h)
 
# red rect from textmetrics coordinates
fill(1, 0, 0, 0.4)
rect(tp_x, -tp_y, textwidth(myString, width=myTest), textheight(myString, width=myTest))
 
fill(0)
#align(RIGHT)
text(myString, x=tp_x, y=-tp_y, width=tp_w, height=tp_h)
as you can see from the output, there's a problem at the end of the text box: one or more lines (depending on font size) are 'lost' drawing the text box.

ciao
G


 
Posted by Giorgio O. on Jul 26, 2008

here's a slightly better version of the fit-text-in-a-rect code.
still, as you wil see, sometimes the last line of the text flows out of the box.
any ideas?

size(600, 600)
grid = ximport("grid") # so that we can use the new text.fit_fontsize method
font("Helvetica")
 
myX = 50
myY = -50
 
var("myW", NUMBER, 135, 10, 600) # desired box width
var("myH", NUMBER, 83, 10, 600)# desired box height
 
 
myString = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit last line etc etc..."
fSize = grid.text.fit_fontsize(myString, myW, myH)
fontsize(fSize)
 
 
tp = textpath(myString, x=myX, y=myY, width=myW, height=myH)
(tpX, tpY), (tpW, tpH) = tp.bounds
#print tpX, tpY, tpW, tpH
 
deltaY = tpY - myY
 
 
# gray rect from textpath.bounds coordinates
nofill()
stroke(0, 0.4)
rect(tpX, -tpY, tpW, tpH)
 
# red rect from textmetrics coordinates
stroke(1, 0, 0, 0.4)
rect(myX, -myY-deltaY, textwidth(myString, width=myW), textheight(myString, width=myW)-deltaY)
 
 
fill(0)
#align(RIGHT)
text(myString, x=tpX, y=-tpY-deltaY, width=tpW, height=tpH)



Posted by Tom De Smedt on Aug 01, 2008

Hi Giorgio,

Some tough questions :-)

First of all, as you mention there is a difference between textmetrics() and path.bounds. The bounds is the box that fits exactly around the given path, whereas texmetrics is the hidden paragraph box - which is more complex do to spacing etc.

Second, the grid.text.fit_fontsize() matches text size to the width of a given box. What you want is vertical fitting as well, which is more difficult. But we can start from fit_fontsize() which is quite a solid algorithm. The most elegant solution I've found so far is to then do additional lineheight() tweaking:

grid = ximport("grid")
 
txt = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."
x = 40
y = 70
var("w", NUMBER, 200, 50, 400)
var("h", NUMBER, 200, 50, 400)
 
f = grid.text.fit_fontsize(txt, w+10, h)
fontsize(f)
 
# Increase line spacing until the last line touches the bottom of the box.
for i in range(100):
    if textheight(txt, w+10) < h:
        lineheight(lineheight()+0.01)
    else:
        break
fill(0)
text(txt, x, y, width=w+10)
rect(x, y-fontsize(), w, h, fill=None, stroke=0)
Last, notice that sometimes I use width+10 instead of just width to get the dimensions exactly right. This is workaround for a bug in NodeBox that needs fixing. We've only recently discovered it when writing the Grid library.



Posted by Giorgio O. on Aug 04, 2008

Hello Tom,

thanks for the answers! I'll posto soon the result of the experiment I'm working on.



Posted by Tom De Smedt on Aug 04, 2008

Hey Giorgio,

I've added a text.fit_lineheight() to the latest release of the grid library, based on the code above.