views:

67

answers:

3

First problem: You have 400 pixels width to go on, and need to fit some text within that constraint as large as possible (thus, the text shall use that amount of space).

Throw in a new constraint: If the text is just "A", then it shall not zoom this above 100 pixels height (or some specific font size).

Then, a final situation: Linebreaks. Fit some text in the largest possible way within e.g. 400 x 150 pixels.

An obvious way is to simply start with point 1, and then increase until you can't fit it anymore. This would work for all three problems, but would be very crude. The fitting of a single line within bounds could be done by writing it with some fixed point size, check the resulting pixel bounds of the text, and then simply scale it with a transform (the text scales properly too then, check out TransformUI).

Any ideas of other ways to attack this would be greatly appreciated!

+2  A: 

I would do the following:

Assume you want W pixels wide text.

Pick an arbitrary size, say 10pt, and see what bounding box the text-string gets for that size. Lets say it gets N pixels wide.

Set the new size to 10pt * W/N, and repeat from step one, until you get within a reasonable threshold. (Hopefully it would work within one iteration.)

This relies on the fact that the width of the string, is roughly proportional to the size of the font.

aioobe
Interesting insight! But it doesn't take into account line breaks!
stolsvik
sure, you can do it vertically too?
aioobe
+2  A: 

As what you are modelling is complex, especially with line breaks, then your initial proposal of trying all sizes is along the right lines, especially if it needs to be accurate.

However, rather than testing each value, you can use a binary search to find the appropriate font size. You know the size is somewhere between 1 and 100 (your upper range). using a binary search, each test sets the font size and checks the resulting layout. If the text is too large, then we search the lower half of the current range of possible values. If the font size fits, then we search the upper half. Your search will use at most 7 attempts (100 log base 2 rounded up), it will be exact, finding the largest size without going over, and it will be flexible if you need to add more requirements later, such as a mix of fonts or more stringent constraints on the layout.

I'm assuming you are using a text component that does line wrapping, and that you can set the maximum width to 400. So, you set the font size and it does the layout giving you back the required height, laying out text within the given width.

You can use hints to try to guide the algorithm to the result quicker, such as making your first guess close to the expected size, but text rendering is fast, that the performance increase may not be worth the implementation effort.

See Wikipedia - Binary Search Algorithm

mdma
This is what I would do. I would however make sensible guesses as to minimum and maximum sizes. Pick a font size, get the height and width of the String and scale by the min of boxheight/stringheight and boxwidth/stringwidth. Round down, and that is your minimum font size (i.e. you know that will fit in). Similar calculation for the maximum size.
DJClayworth
+2  A: 

I'd instantiate the Font at the largest desired size: say 72 for one inch glyphs at 72 dpi. Use TextLayout to get the bounds and scale using AffineTransform (direct) or AffineTransformOp (offscreen), while preserving the aspect ratio. Suitable RenderingHints help, too.

trashgod
Thanks for the idea! This is however nearly the same as I also suggested, only going in the opposite direction. And it doesn't take into account line wraps.In fact, I don't think you need "more RenderingHints" for the scaling, as the AffineTransform apparently can work directly on the font sizes, and thus the actual drawing of the fonts takes this into account (so you can have a 10 point glyph fill the entire screen and not become jagged, given that you shove a AffineTransform into the Graphics object before paint). Amazing example in the TransformUI link I gave.
stolsvik
Aha! I see what you mean: `AffineTransform` scales a given outline font smoothly, but `TextLayout` needs a `FontRenderContext` to do metrics. I (incorrectly) thought that instantiating `Font` for multiple sizes would be sluggish.
trashgod