tags:

views:

89

answers:

4

i have a blue container that is the canvas for my text. what i'm trying to do is resize the font so that the entire text fills as much of the blue container as it can.

i have access to the height of the text content, which is both paragraphs and the line in between them. also, i have the height of the blue container, the height of each line, the number of lines and the number of words.

i tried using the height of the line, based on a default font size, with the amount of lines to determine how much i need to increase the font so it would fill the blue container. however, doing so also changes the amount of lines, ruining my chances for success while making my head swim.

what formula should i use so the font size fills the blue container?

here are my numbers:

Content height:  342.46
Content/Container Width:  400
Font Size:  11
Line Height:  11 * 1.2 //120% of the font size gives the line height
Number of Lines:  24
Number of Words:  2055

also, it's possible for the text content to be long and to extend well past the container, so ideally a formula (or suggestions) for both an increase or reduction of the font size is what i'm trying to achieve.

alt text

+1  A: 

Things get a lot simpler, if you can use a fixed-width font. Then factors like: kerning, different character-widths, etc., disappear.

Assuming you can't -- or won't -- use a monospaced font, then the problem looks a lot like duplicating the rendering that a browser does -- or the "Print Preview" functionality of a printer driver. You might investigate that approach.

A quick and dirty scheme that should be good enough might be:

  1. Append a <span id="CanYouSeeMe">.</span> to the end of the text.

  2. Measure the span's offset (SpanOffset) relative to the container height (ContainerHeight).

  3. Set the font size (FS) to: FS *= ContainerHeight / SpanOffset;

  4. Measure the new SpanOffset.

  5. If SpanOffset is still less than the ContainerHeight, Increase FS a tiny increment, in a loop, until SpanOffset just leaves the container, then backup 1 increment.

  6. Likewise if SpanOffset was outside the ContainerHeight, Decrease FS a tiny increment, in a loop, until SpanOffset just enters the container.

Brock Adams
+1  A: 

I don't think there is a perfect solution to this problem.

Basically, the amount of "wasted" space at the end of each line and paragraph depends very much on the text in question. Character widths and kerning cause minor differences on average (but major differences in the worst case).

So you have to go by some heuristic. If possible, have you software "guess" a font size, test if the text fits or not (how this is done depends on what language/API you're using), and if not, adjust up or down slightly.

Here's a possible heuristic:

#numBreaks is the number of line breaks in the text
#numChars is the number of chars (excluding breaks) in the text
#avgCharWidth and lineHeight (pixels) are derived from font size

#avgWrapWidth is the width of the container minus a bit to
#account for the wasted space on the right. I would suggest:
#avgWrapWidth = realWidth - avgCharWidth * 6

totalLength = numChars * avgCharWidth
numLines = totalLength / avgWrapWidth + numBreaks
textHeight = numLines * lineHeight

textHeight is the height (in pixels) that the text is expected to take if wrapped to the container. You could binary search through your font sizes to find one with the best estimated height.

Artelius
+1  A: 

This is actually rather a difficult problem, because the lines break at word boundaries and word lengths vary, as indeed do character sizes. Plus, you can have an arbitrary number of explicit linebreaks. So to do it properly requires taking into account a lot of the specifics of the text, and a general analytic solution will be, um, quite complicated.

(In "real" typography, there are also a bunch of tricks that are used to fiddle the layout a bit by adjusting the letter and word spacing so that things fit while maintaining a decent appearance, but we're certainly not going to get into that here.)

However, let's put aside exactness and instead consider a simplified model that might help get you into the right ballpark.

The area available is width * height. Given a piece of text of a given length, we want to determine a suitable fontsize such that the wrapped height of the text is close to height.

For the time being, let's just gloss over all details of units and the vagaries of the text content and imagine that we can calculate the overall size of the text from just length and fontsize:

textWidth = A * fontsize * length
lineHeight = B * fontsize

where A and B are some scaling constants. (B should be easy to calculate, and indeed you have it as 1.2. A is a little trickier and depends on how you choose to measure length, eg as number of characters or number or words. However you do it, A can only ever be an approximation, but if you're dealing with reasonable quantities of reasonably well-behaved text then the approximation can be pretty good.)

Then the area occupied by the text is:

textArea = A * fontsize * length * B * fontsize
    = A * B * length * fontsize ^ 2

Setting this equal to the available area gives us an upper limit for the fontsize, ie:

fontsize <= sqrt(width * height/(A * B * length))

Clearly this is going to overestimate significantly because it assumes the text packs perfectly, which it doesn't -- there will be area lost at the end of lines and at the bottom of the screen, even in the absence of explicit line breaks. However, it's quite likely that by putting an appropriate fudge factor into our scaling constant A (ie, by upping our estimated text width to include the lost areas) we can get a good enough fontsize to be going along with.

We can make the analysis more sophisticated by taking into account more specifics of the text. For example, if you count the number of hard line breaks in the text you can treat those as separate, since they add height without adding width. This gives rise to a slightly more complicated quadratic equation that you'll need the quadratic formula to solve rather just a simple square root. Whether this is worthwhile probably depends mostly on how much variation there is in the rate of explicit linebreaks in the texts you're dealing with.

One other thing to note is that you can often cheat a bit by adjusting only the line spacing, since this allows you to change the vertical span without risking any reflow of the text. You probably don't want to do this by large amounts, because it will look silly. But it's certainly worth considering adding a little bit of vertical padding if you've got the wrapping about right but the text doesn't quite reach the bottom.

walkytalky
Liked the line-spacing idea.
Brock Adams
+1  A: 

In TeX typesetting system this problem is usually solved using a simple bisection algorithm (presented f.e. by Knuth in The TeXbook), here in a pseudo-python:

def findsize:
    maxsize=100pt
    minsize=8pt
    epsilon=0.5pt
    while (maxsize-minsize>epsilon):
        triedsize=(maxsize+minsize)/2
        setfontsize(triedsize)
        if boxisoverfull():
            maxsize=triedsize
        else:
            minsize=triedsize
    return minsize

Note that you probably can't do this without experimenting with every size, as every browser might typeset your text in different way, making different interword, interletter spacing; by having a minimum font size set by user (I've seen lots of webpages displaying improperly because of this one), by using ligatures (probably not now, but in future it is possible).

The problem here is that the webbrowser will probably redraw every try, so this might be slow. You can overcome this by making the box disappear (f.e. display:none) for the time of computation. I remember I did that once and it worked even in IE6.

liori