News Code Other Stuff
Font Metrics and Drawing Strings

Note:
Since the time I wrote this article, Apple added a page on font metrics to the developer documentation: Getting Font Metrics

There are several ways to get information about the dimensions of a chunk of text. First there are NSString's sizeWithAttributes: and NSAttributedString's size: methods. Those return the size (NSSize) of the bounding box for the string in question.

If you just want to draw some text somewhere inside a view, that's all you need to know. Just create a rectangle with the correct size and draw the string using drawInRect:.

In my case I wanted to draw the string at a more specific location. To find out where the baseline will be located inside the strings bounding box, we need some information about the font. That's where NSFont comes in. Here we find some helpful methods:

  • ascender
  • descender
  • xHeight
  • capHeight

This picture should explain the return values:

Font Metrics

You'll probably miss the capHeight; from my experiments it seems like this is always the same as the ascender.

Note that the xHeight and the ascender are not quite what one would expect. In fact, for Lucida Grande (the system font) it looks even worse. But since we don't have any better data we are at the mercy of the font designer here.

All values are distances from the baseline. So, naturally, you can't query the baseline position; it's always zero.

Also keep in mind, that the descender is a negative value.


Now, do we have enough information to position the string where we want to?

Not quite.

We still need to know where a) the origin of the drawn rectangle is and b) where the text is positioned inside it's box.

The origin of the rectangle we draw actually depends on the focused view. This is explained in the documentation:

If the focus view is flipped, the origin is set at the upper-left corner of the drawing bounding box; otherwise the origin is set at the lower-left corner.

The origin of the text inside the bounding box on the other hand is always on the upper left. I couldn't find this information anywhere. But you can see that the text get's clipped at the bottom when the bounding box is too small.

OK, another picture:
Text Positioning

The yellow rectangle shows the bounding box of our string.

The red one is the rectangle we used for the drawInRect: message.

Assumed our view is not flipped, the origin for the drawing rectangle is on the lower left (red arrow).

Still, the text is positioned at the top of it with the origin at the upper left (yellow arrow)

Now we have enough data to determine the position of the base line (green). The actual calculation is left as an exercise for the reader.