Precise Bounding Rects of Strings

Written by Caylan Larson

Using Cocoa, it is very easy to draw a text string of any sized font. There are so many calculations happening behind the scenes - line height, descent, ascent, line gap and a variety of bounding rectangles (rects). For the most part, developers don't need to worry about it. Apple's engineers have done the heavy lifting. Unfortunately, when a developer wants to precisely align text inside of a view, the bounding rects provided by Apple's NSString APIs are not immediately clear or usable.

An image from Apple's Core Text Architecture Guide.

For example, when I draw a simple 4 letter Helvetica string and use NSString's -sizeWithAttributes:, you'll see that the rect (in purple) includes a bottom margin (line gap), as well as another gap on the top, and character margins on both the left and right.

A string drawn in helvetica. The bottom-left corner is the origin.

If you're using one font, you can get away with compensating. However, fonts differ wildly. Take "Adobe Hebrew" for example.

A string drawn in Adobe Hebrew. This font has a much larger line gap.

Not only does Adobe Hebrew have a large line gap (bottom), it has an almost nonexistent top, left and right margin. Font characteristics like this can become a challenge when designing an application to work with all fonts.

Luckily, there's a simple fix. I call it SemiGlyph and it's a NSString category that uses the -boundingRectWithSize:options:attributes: method from NSString's AppKit Addition. It took days of trial and error, but it works well for my use case.

The same string drawn using SemiGlyph's -drawGlyphsAtPoint:withAttributes:.

The trick to -drawGlyphsAtPoint:withAttributes: is in the calculation of the bounding rect using the option value NSStringDrawingUsesDeviceMetrics, which according to Apple's documentation "uses image glyph bounds instead of typographic bounds." Perfect.

There are likely other ways to calculate this same rect. Fellow developers pointed me to NSTextFieldCell and NSLayoutManager, but the same sources indicated that it is difficult to vertically align for arbitrary fonts. I have not tested these solutions. So far, the simple SemiGlyph category works as advertised.

A sample application, including the SemiGlyph category, is available on Github.

comments powered by Disqus