Laying Out Text Regions
The other half of working out how to lay out a paragraph is knowing the shape available for drawing it. This is where the NSTextContainer class comes in. This class encapsulates a region of the drawing canvas. The default implementation defines a rectangle, but you can define other shapes, as shown in the TextWheel example in my book Cocoa Programming Developer's Handbook. (Click the Downloads tab on the book page, download the examples.zip file, and find the TextWheel folder in the zip file.)
This class defines two methods. The first, -isSimpleRectangularTextContainer, is trivial. This little hack is used to optimize layout. If the region is a simple rectangle, the typesetter can avoid a lot of calls to the container when checking for valid regions. This means that it can be faster to define a group of rectangular regions than to define a single, more complex region.
The other method is more complex, doing the real work of deciding where text should go:
- (NSRect)lineFragmentRectForProposedRect:(NSRect)proposedRect sweepDirection:(NSLineSweepDirection)sweepDirection movementDirection:(NSLineMovementDirection)movementDirection remainingRect:(NSRectPointer)remainingRect
The second and third parameters define the direction in which the text will be drawn. This is very importantthe Cocoa text system is entirely independent of writing-order. Lines can go from left to right or right to left, and they can progress down or up the page. You can use exactly the same text containers that you'd use for English, even if your language contains lines that go from the bottom of the page to the top and start on the right. (The text container even supports text being drawn in a spiral, although that would require some slight modifications to the typesetter.)
This method trims a proposed rectangle. The first argument is the rectangle into which the typesetter proposes drawing text. The return value is the subset that the container allows. The final parameter is used to return a second rectangle, which contains the part of the proposed rectangle that's on the same line and contains regions that may be used, following this region. This parameter is used to draw regions with a hole in the middle.
The TextWheel example defines a circular text region, with an empty circle in the middle, like a cartwheel with a hole for the axle. When the typesetter proposes a region across the middle of the wheel, the text container returns the rectangle on the left of the hole and stores the rectangle on the right of the hole in the location to which the fourth argument points. This region could contain more holes; the typesetter won't use it directly, it will call this method again, proposing the new region.
The typesetter uses this information, combined with the text height, to find out how wide each line can be.