Winter 92 - THE TEXTBOX YOU'VE ALWAYS WANTED
THE TEXTBOX YOU'VE ALWAYS WANTED
BRYAN K. ("BEAKER") RESSLER
NeoTextBox is an alternative to the TextEdit utility routine TextBox. NeoTextBox
provides full-justification capability and the option to use TrueType features while
retaining all the advantages of TextBox. The three routines that comprise NeoTextBox
compile to fewer than 900 bytes yet offer a 40% performance increase over TextBox
in common cases.
In the deepest, darkest corner of the TextEdit chapter inInside Macintosh Volume I,
there's an extremely useful routine called TextBox.
pascal void TextBox(void *text, long length, Rect *box, short just)
Given a rectangle and some text, TextBox word wraps the text inside the rectangle,
drawing in the font, style, and size specified in the current grafPort.
Anyone who's tried to word wrap text knows that it's not as easy as it first appears.
Perhaps that's why TextBox takes the approach it does: to perform its task, TextBox
creates a new TERec with TENew, sets up the rectangles in the record, and calls
TESetText to create a temporary handle to a copy of the text you provided to TextBox.
TextBox then calls TEUpdate to wrap and draw the text, and finally TEDispose to
dispose of the TERec. By calling TextEdit to do the text wrapping and drawing, TextBox
avoids doing any hard work. Unfortunately, it also incurs quite a bit of overhead.
Despite its pass-the-buck implementation, TextBox's use of TextEdit has several
advantages. Perhaps most important, TextBox works correctly with non-Roman script
systems like Japanese and Arabic without the need for any extra programming.
Another handy side effect is that updates in TextEdit degenerate into calls to DrawText,
and can therefore be recorded into QuickDraw pictures. TextBox was designed
specifically for drawing static text items in dialog boxes and performs this function
well.
So TextBox is great--if you're drawing dialog boxes. But you want more. You want
better performance. You want more flexibility. You want to control line height. You
want full justification (instead of only left, center, and right alignment). You want to
use whizzy TrueType calls when they're available. You want to control the text drawing
mode. You can't stand the way TextBox always erases (and therefore isn't too useful
when you're drawing to printers--it slows printing way down). Yeah, and you don't
like that 32K text limitation either. You want to word wrapWar and Peace in a single
call to TextBox. And you'd like some useful information back, too, like the line height it
used, and where the last line of text was drawn, so that you can draw something below
the text. And, of course, you want to retain the advantages of TextBox.
Well, this is your lucky day.
ENTER NEOTEXTBOX
NeoTextBox is the TextBox you've always wanted (and didn't even have to ask for).
NeoTextBox is on the average 33% faster than an equivalent call to TextBox. Plus, it's
considerably more flexible:
• NeoTextBox allows a line height specification. You can ask for the default
(same behavior as TextBox); use variable line height, which adjusts for
characters that extend beyond the font's standard ascent or descent line; or
specify a line height in points.
• NeoTextBox provides left, center, and right alignment and full
justification.
• NeoTextBox never erases the rectangle it's drawing into. It lets you erase
or, if you wish, draw a colored background.
• NeoTextBox returns the total number of lines in the wrapped text.
• NeoTextBox can return, via VAR parameters, the vertical pen position of
the last line of text and the line height that was used to draw the text.
NeoTextBox gives you all this extra functionality, yet retains the advantages of
TextBox. It is completely language independent and uses the Script Manager heavily
(just like TextEdit). It's easy to call, and if you don't want all the spiffy new features,
it's easy to get TextBox-like behavior with a free performance increase.
Let's take a look at the parameters for NeoTextBox.
short NeoTextBox(unsigned char *theText, unsigned long textLen,
Rect *wrapBox, short align, short lhCode, short *endY,
short *lhUsed)
The first two parameters, theText and textLen, are analogous to TextBox's text and
length parameters: they specify the text to be wrapped. Note that theText isn't a Pascal
string--it's a pointer to the first printable character.
The third and fourth parameters, wrapBox (box in TextBox) and align, also hearken
back to NeoTextBox's ancestor. Just as in TextBox, wrapBox specifies the rectangle
within which you're wrapping text, and the align parameter specifies the alignment. In
addition to the standard TextEdit alignments teFlushLeft, teCenter, and teFlushRight
(see "Text Alignment Constants for System 7"), a new alignment is
defined--ntbJustFull. It performs full justification in whatever manner is
appropriate for the current script.
The fifth parameter, lhCode, specifies how the line height is derived. If lhCode is 0, the
default line height is derived via a call to GetFontInfo. This gives the same behavior as
TextBox. If lhCode is less than 0, the line height is derived by determining which
characters in the text that's being drawn extend the most above and below the baseline
(see "SetPreserveGlyph With TrueType Fonts"). Finally, if lhCode is greater than 0,
the value of lhCode itself specifies the line height. For instance, you can draw
12-point text in 16-point lines.
The last two parameters, endY and lhUsed, are reference parameters that allow you to
retrieve the vertical position of the last line of text and the line height that was used to
draw the text, respectively. The endY parameter can be very useful if you intend to
draw anything below the text, since it tells you exactly where the last line of text was
drawn. To find out what the actual derived line height was if you used a negative lhCode,