SuperSub
Volume Number: 5
Issue Number: 6
Column Tag: C Workshop
Related Info: TextEdit
Sub and Superscripting with TE
By Robert McKenzie, Santa Ana, CA
SUPERSCRIPTING AND SUBSCRIPTING IN TE
Robert McKenzie formerly of Mountain Lake Software, Inc. as the project
manager of network, file, and multitasking programming tools for ClassKit™, a C++
object-oriented application framework. His career experience of 15 years ranges
from programming parallel processor mainframes to desktop computers. For the past
two and one half years he has been programming on the Macintosh with emphasis at the
network and driver level.
Introduction
This article describes how to encrypt a super/subscripting scheme into the data
structures of the new Text Edit manager routines. Adding this scheme requires that one
or two trap hooks be intercepted. This is also shown.
It was good to see Apple take a quantum leap from the original TE manager to the
new TE manager. But with all the capabilities now built into TE, including color
support and style runs, it’s hard to understand why they left out super/subscripting.
An application which finally provoked me to look for a way to add this
functionality to TE was a test generation program for teachers called MAKETEST®. In
particular, teachers who need to indicate power/base notation need
super/subscripting, unless of course they were to be limited to some Fortran-like
notation, such as:
x^3 + y = 5
Data Structures in TE
The method I will describe depends on the new TE manager routines. These
routines provide the run styles which are needed to keep the necessary style
information on a character by character basis. Trying to manage character by
character style information in the old TE manager routines would require a completely
different approach.
{1}
STElement:
stCount: INTEGER;
stHeight: INTEGER;
stAscent: INTEGER;
stFont: INTEGER;
stFace: Style;
stSize: INTEGER;
stColor: RGBColor;
The data structure of interest is called an STElement (V-262). A style element in
TE is the collection of individual styles common to a sequence of characters in the TE
text buffer. Any sequence of characters that share the same combination of styles,
whether the sequences are adjacent or not, share the same style element entry in the
TE manager’s data structures.
As the TE manager updates the screen it checks where one run ends and another
begins. Each time the styles of a character sequence change, the grafport’s font and text
information are set to the combination of fonts and styles found in the style element
entry for the new style run.
The TE manager does not draw characters one at a time. Once it has set the
grafport’s text parameters to the information contained in the style run, it calls the
QuickDraw routine ’DrawText’ to draw all the characters within the style run at one
time.
Now we have a couple of pieces of information ga thered which begin to suggest an
approach for supporting super/subscripting within TE:
1) STElements is where style combinations are held. This is where we need to find a
place to store the super/subscripting flags.
2) TE always uses ’DrawText’ for displaying one or many characters. This can be
verified by using the trap intercept functionality of TMON. By trying to intercept
various traps while forcing TE to output to the screen in all the ways it can, you
can determine that only DrawText is used.
3) The grafport must be set with all the correct font and style information if the
characters are to be drawn correctly with DrawText.
If we are going to use the style element structure to store the super/subscripting
flags, we must find some unused bits within one of the fields: stCount, stHeight,
stAscent, stFont, stFace, stSize, or stColor. Super/subscripting requires a 3 state
representation: super/sub/normal. Therefore, we need 2 bits which can represent up
to 4 states.
The stStyle field seems like a likely place to look, e specially since it is the field
used to represent the standard styles. However, being a 7-bit set type it only leaves 1
unused bit. Not enough.
The stCount, stHeight, stAscent, and stSize fields contain numeric values used to
compute character spacing for any given combination of styles. If we tried to encode
super/subscripting in one of these fields it would alter the values computed in the
character spacing calculations. The unpredictability of when one of these fields might
be used makes these fields particularly messy to use.
It might be possible to use the stColor field if we imposed some constraints on the
color schemes that could be used. For example, if we used bits 0 and 1 within the
stColor field, that would mean that the other entries in the color table would have to be
organized such that if 0-2 were added to it we would still get the same color.
Otherwise, for example, if a user was typing with green characters and chose only
super/subscripting from the menu, the super/subscripted character might come out
as, say, red instead of green. Of course you’d have to be very familiar with the Color
manager in order to create a color table to avoid this problem. (See the Color manager
of Inside Mac-V for more information on the Color manager.)
This leaves only the stFont field as a place for these super/subscripting bits to
live. To know if this is possible we have to know how the font number is used. The most
important point is how the font number is used to look up the character characteristics
of the various fonts. When QuickDraw is called to output text, as when DrawText is
called, it passes the font number to the Font manager to get the font description
information. The font number is checked against fonts that are already loaded. If the
font is loaded, a handle to the font description information is given back to QuickDraw
for its use. If it isn’t loaded, then the font number is given to the Resource manager
and, again, a handle is returned to QuickDraw. Great. With this knowledge we now have
a well defined event in which an attempt to use the field we have altered can be
intercepted, the super/subscripting bits temporarily masked, and the normal TE
character processing sequence can then go on. Of course using 2 bits within the stFont
field also causes constraints on the range of usable font values. Without tabulating all
the existing font numbers in the world this constraint seems quite reasonable.
Choosing the Super/Subscripting Bits
The font number is a 16-bit integer which allows for approximately 32K fonts.
That’s a lot of fonts for people to choose from. Most of the fonts that I know of are
assigned increasing values starting at 0 (0 is the systemfont), so obviously we don’t
want to use any of the low ordered bits within the font field. It’s also the case that many
programmers assign a field to be a negative value when its meaning is to be
temporarily or permanently changed. Therefore, I chose not to use bit 15 either. The
two best bits to use are bits 13 and 14 of the integer field.
The corresponding value for each bit is:
13: 8192
14: 16384
This means that no fonts with numbers between 8192 and 16384 can be used if
we reserve these bits for super/subscripting. Does anybody know of such fonts? (My
knowledge of font number assignment is limited.) Of course, giving the user a choice of
over 8K fonts to choose from is still plenty.
Setting the Super/Subscripting Bits
Setting the super/subscripting bits is done quite easily, and in a straightforward
manner using the normal TE manager style setting function. An example is shown in
the ‘doEditing’ function in the source listing given.
First I defined some C constants which define the 3 states of super/subscripting:
SUPER 0x4000 /* THE 14th BIT */
SUB 0x2000 /* THE 13th BIT */
REGULAR 0x1FFF /* BITS 0 - 12 */
When the user chooses one of these states, the existing font information is
retrieved by using the function:
/* 2 */
TEGetStyle((**TEH).selStart-1,
&Styl,&lhgt,&ascent,TEH)
In each case the old super/subscripting information is cleared with the bitwise
AND operator:
Styl.tsFont &= REGULAR;
In the case of normal scripting this is all that needs to be done before resetting
the style. If super or subscripting is chosen, the appropriate bit is set with an OR
operator:
Styl.tsFont |= SUPER;
or
Styl.tsFont |= SUB;
Then all that needs to be done is to set the new style:
/* 3 */
TESetStyle(doFont,&Style,1,TEH);
Shifting characters up or down
To make use of the bits that we just set we need to hook into the QuickDraw
DrawText function. It is when this function is called that we will make our move and
fool the TE manager into shifting the characters appropriately. Hooking into the
DrawText trap will be explained in the next section.
But once our intercept routine has been called what do we do? We make use of
point 3 from the ‘Data Structures in TE’ section which states that the grafport is set
correctly just before the text is about to be drawn. When set, the grafport’s font field
will contain the font value set in the Styl.tsFont field above including our
super/subscripting bits. Therefore, the hook routine can get the font value from the
grafport, test it for having either the super/subscripting bit set, and adjust the
grafport’s pen location appropriately. (See Figure 2) The complete routine is then:
/* 4 */
DrawTextHook(...)
...
{
register int fnt,offset;
GrafPtr gptr;
GetPort(&gptr);
fnt = gptr->txFont;
offset = ((fnt & SUPER)
? -3
: ((fnt & SUB)
? 3
: 0));
if (offset)
{ Move(0,offset);
TextFont(fnt & REGULAR);
}
DrawText(text,pos,cnt);
if (offset)
{ Move(0,-offset);
TextFont(fnt);
}
}
Notice the first TextFont call resets the grafport’s font to the expected font by
stripping the subscripting bits. The text is drawn and, if needed, the pen loc and
grafport font is restored to the state previously set by the TE manager.
Intercepting the DrawText/GetResource traps
Setting up trap hooks requires knowledge of assembly and stack management,
particularly when mixing languages like I have done in this project. My programming
was done under MPW using assembly and the GreenHill C compiler. Trap intercepting
is itself a topic of interest. Here I will explain the important points as related to
super/subscripting.
The DrawText hook is used to call our DrawTextHook function described above.
The GetResource hook is used to keep QuickDraw from being confused by a strange font
value found in a style run. The GetResource hook will be called as the TE manager tries
to setup the grafport with the character attributes of a style run before it calls
DrawText. Trying to set up the grafport with an unknown font number is the same
well-defined event as described earlier.
When GetDrawTrapInfo and GetResTrapInfo are called at application startup time
they merely calculate trap information, such as the amount of data space the trap
needs, its code size, and its beginning location. These values are used to set up the hook
functions in the system heap as required.
As seen in the assembly source listing, the data space definitions only force the
assembler to output the correct code. Actual allocation of data space for the hook
routine must be done by the SetupHook function. The actual trap code is between the
TrapBeg and TrapEnd labels. This is the code copied down into the system heap.
Neither of these trap hooks is reentrant. Therefore, a call-level variable, ‘CLvl’,
must be maintained in order to keep track of how deeply any calls to these routines are
nested. If the CLvl becomes greater than one, then it should do nothing except act like
the standard trap.
When the toolbox calls the DrawText trap, we have to take care of transforming
the Pascal-oriented parameters on the stack to MPW-C parameters. The Pascal call to
DrawText has the form: