tags:

views:

516

answers:

4

I know it's a simple concept but I'm struggling with the font metrics. Centering horizontally isn't too hard but vertically seems a bit difficult.

I've tried using the FontMetrics getAscent, getLeading, getXXXX methods in various combinations but no matter what I've tried the text is always off by a few pixels. Is there a way to measure the exact height of the text so that it is exactly centered.

+2  A: 

I found a recipe here.

The crucial methods seem to be getStringBounds() and getAscent()

// Find the size of string s in font f in the current Graphics context g.
FontMetrics fm   = g.getFontMetrics(f);
java.awt.geom.Rectangle2D rect = fm.getStringBounds(s, g);

int textHeight = (int)(rect.getHeight()); 
int textWidth  = (int)(rect.getWidth());
int panelHeight= this.getHeight();
int panelWidth = this.getWidth();

// Center text horizontally and vertically
int x = (panelWidth  - textWidth)  / 2;
int y = (panelHeight - textHeight) / 2  + fm.getAscent();

g.drawString(s, x, y);  // Draw the string.

(note: above code is covered by the MIT License as noted on the page.)

Otto Allmendinger
Yeah...I'm familiar with this concept...but it's wrong. The fm.getAscent() method is the problem. It doesn't report the real pixel ascent of the font and results in text that is closer to the bottom than the top.
Paul Alexander
+3  A: 

Note, you do need to consider precisely what you mean by vertical centering.

Fonts are rendered on a baseline, running along the bottom of the text. The vertical space is allocated as follows:

---
 ^
 |  leading
 v
 -- 
 ^              Y     Y
 |               Y   Y
 |                Y Y
 |  ascent         Y     y     y 
 |                 Y      y   y
 v                 Y       y y
 __ baseline ______Y________y_________
 |                         y                
 v  descent              yy
 --

The leading is simply the font's recommended space between lines. For the sake of centering vertically between two points, you should ignore leading (it's leding, BTW, not leeding; in typography it was the lead inserted between lines in a printing plate).

So for centering the text ascenders and descenders, you want the

baseline=(top+((bottom+1-top)/2) - ((ascent + descent)/2) + ascent;

Without the final "+ ascent", you have the position for the top of the font; therefore adding the ascent goes from the top to the baseline.

Also, note that the font height should include leading, but some fonts don't include it, and the due to rounding differences, the font height may not exactly equal (leading + ascent + descent).

Software Monkey
A: 

Not sure this helps, but drawString(s, x, y) sets the baseline of the text at y.

I was working with doing some vertical centering and couldn't get the text to look right until I noticed that behavior mentioned in the docs. I was assuming the bottom of the font was at y.

For me, the fix was to subtract fm.getDescent() from the y-coordinate.

Joe

A: 

Paul, did you ever find a solution to this?

Best regards,

Gath

Gath