The MathPaper windows that we built in the last chapter didn’t quite live up to their initial billing. We promised what you see on the left side of Figure 12-1 but gave what you see on the right. The difference between the two windows is a matter of fonts and formatting. Although the NSTextView object that we used allows a great deal of control over fonts and formatting, when we invoked the replaceCharactersInRange:withString: method in PaperController we were simply pasting plain ASCII text into the selection, which comes up as left-justified monofont text — not very interesting. To get the promised fonts and formatting, we’ll have to learn about Rich Text Format (RTF). Most of today’s word processors (e.g., TextEdit, Microsoft Word) support RTF, and it has been used as a cross-platform format for years, so you probably know a little bit about it already. In this chapter, we’ll show you how to code RTF right into your applications.
Suppose you want to amaze your friends by showing them how easy it is to create text with different font sizes in a window. You might want the final window to look something like that shown in Figure 12-2.
One way that you can do this is by using the NSText class as if it were a simple text editor, sending an instance of NSText commands to insert text, select the text, and then change the text to the desired size. Although this is an inefficient way to manipulate the NSText class, it is conceptually easy.
The NSText class (the superclass of our friend, NSTextView) provides
lots of commands for selecting, modifying, and altering the text it
contains. The NSFont class allows us to create a font
with any name and point size, and the NSMakeRange( )
function returns an
NSRange
structure with a requested starting point and length. With the NSFont
and NSRange, we need to send only a few messages to create the text
in Figure 12-2.
Assume that theText
is an object of the NSText
class. The first message we’ll need is:
[theText replaceCharactersInRange:aRange withString:aString]
which replaces a substring of text in our NSText object with the
string “aString”. The substring
being replaced starts at position aRange.start
and
has length aRange.length
. This is conceptually the
same as selecting the substring in an editor and then typing some new
text to replace it.
The next message we’ll need is:
[theText setFont:aFont range:aRange]
which sets the text in theText
(an NSText object)
in the range aRange
to have the font
aFont
.
The last message we’ll need is:
[NSFont fontWithName:aName size:aSize]
which invokes an NSFont class (or factory) method to create a new
font object with the name aName
and point size
aSize
.
Following is the method we used to create the Helvetica text in the window in Figure 12-2:
- (IBAction)fontDemo:(id)sender { float s; for (s = 10.0 ; s < 36.0 ; s += 5.0) { NSString *str = [NSString stringWithFormat:@"This is Helvetica in size %g ", s]; int length = [ [theText textStorage] length]; NSRange range; [theText replaceCharactersInRange:NSMakeRange(length,0) withString:str]; range = NSMakeRange(length,[str length]); [theText setFont:[NSFont fontWithName:@"Helvetica" size:s] range:range]; } }
If you want to see the fontDemo:
method’s output, create
a new project and insert the method in a new subclass of NSObject.
The subclass should also contain an outlet called
theText
that connects to an NSScrollView in a
window. To invoke the method, instantiate an object of your new
subclass, make it the delegate of the File’s Owner,
and invoke fontDemo: in the
applicationDidBecomeActive:
notification method. (Note that this is not the only way to do
this.[33])
When we wrote our original NeXTSTEP book back in 1992, we called this method slowFontDemo: because it was very slow. Running on a 25-Mhz NeXTstation (CPU speed has certainly gotten better, thanks to Moore’s law), you could actually watch each line of text being inserted into the text object and being reformatted to the specified size. It actually looked like somebody was sitting down at the computer’s keyboard, inserting the string “This is Helvetica in size nn” into the text editor, selecting it with the mouse, and changing its size. When the method ran, it was just plain ugly. But that was 10 years ago. If you build a “quickie” application that invokes the fontDemo: method and run it today, the text will appear immediately. This is a testament both to the significantly faster speed of today’s computers and to the improved algorithms used for rastering fonts.
Indeed, this short demonstration shows some of the significant advantages of Cocoa for handling text:
The fonts look great, because they’re automatically scaled by Quartz to whatever size you request.
The text automatically wraps when you resize the window.
The second time you run this demo it will run even faster, because the Window Server will have cached bitmaps for the sizes of the fonts that you have specified.
Nevertheless, it’s somewhat awkward to drive the NSText object like a word processor. For our purposes, it’s far better to simply load a file into the NSText object and have it display all of the fonts at the same time. To do that, we need to learn about Rich Text.
The other way to manipulate text is by constructing the text you want ahead of time — with all of the fonts and formatting commands already in place — and then reading it into the NSText object in a single operation. One format for this data stream, called Rich Text , was developed by Microsoft in the 1980s.
Rich Text looks a little like the codes used by TeX (pronounced “tech,” as in the word “technique”), the document typesetting system developed by Donald Knuth. Here’s the “raw” Rich Text code that will display the same fonts and text as the above demonstration:
{ tf1macansicpg10000cocoartf100 {fonttblf0fswissfcharset77 Helvetica;} {colortbl; ed255green255lue255;} margl1440margr1440vieww8320viewh3920viewkind0 pard x1440 x2880 x4320 x5760 x7200qlqnatural f0fs20 cf0 This is Helvetica in size 10 fs30 This is Helvetica in size 15 fs40 This is Helvetica in size 20 fs50 This is Helvetica in size 25 fs60 This is Helvetica in size 30 fs70 This is Helvetica in size 35 fs20 }
An RTF file consists of unformatted text (e.g.,
“This is Helvetica”), control words
(e.g., fonttbl
), control symbols (e.g.,
~
), and groups enclosed by curly braces
({}
). Each control word
begins with a backslash
(
) and consists of a string of
letters followed by an optional numeric argument. Each
control symbol
(none in our previous example) begins
with a backslash and is followed by exactly one nonalphanumeric
character (e.g.,
~
represents a nonbreaking
space).
Curly braces have a special meaning: they
define groups
that support Rich Text graphics states. If you
change the state of a font within a graphics state, the change is
lost when the state is closed.
Don’t be alarmed if Rich Text seems a little complicated! There are really only a few Rich Text controls that you need to be concerned about, and later in this chapter, we’ll introduce an RTF object that handles them for you automatically. Many of the RTF controls that are generated by the previous examples can safely be ignored.
The Mac OS X editor TextEdit is a Rich Text editor. If you open a new file with TextEdit, it will be either a plain text file or an RTF file, depending on your TextEdit preference settings. If it’s plain text, choose TextEdit’s Format → Make Rich Text menu command to make it RTF. Then save the “empty” file, and you’ll get a file that contains the following information:
{ tf1macansicpg10000cocoartf100 {fonttbl} {colortbl; ed255green255lue255;} margl1440margr1440vieww9260viewh7500viewkind0 }
Try this in TextEdit, save your file, and then use the Unix
cat
(or
vi
, pico
, etc.) command to
list the file’s contents in a Terminal window. (The
contents of your file may differ slightly, depending on your
defaults.) If you add three lines of text in TextEdit in Helvetica
(probably your default font), as shown in the window on the left in
Figure 12-3, you’ll end up with
something like this:
{ tf1macansicpg10000cocoartf100 {fonttblf0fswissfcharset77 Helvetica;} {colortbl; ed255green255lue255;} margl1440margr1440vieww9000viewh7820viewkind0 pard x1440 x2880 x4320 x5760 x7200qlqnatural f0fs24 cf0 This is line 1 This is line 2 This is line 3 }
When TextEdit reads a file, it checks the first five characters to see if they are “{ tf”. If they are, TextEdit assumes that the file is in Rich Text Format.
Next, let’s experiment with text weight and angle changes. The window on the right in Figure 12-3 produces the following RTF code:
{ tf1macansicpg10000cocoartf100 {fonttblf0fswissfcharset77 Helvetica;f1fswissfcharset77 Helvetica-Bold;f2fswissfcharset77 Helvetica-Oblique; f3fswissfcharset77 Helvetica-BoldOblique;} {colortbl; ed255green255lue255;} margl1440margr1440vieww9620viewh7500viewkind0 pard x1440 x2880 x4320 x5760 x7200qlqnatural f0fs24 cf0 This line is plain f1 This line is bold f2i0 This line is italic f0i0 ul This line is underlined f3i ulnone This line is bold italic }
Now let’s try changing the font size. The window on the left in Figure 12-4 produces this RTF file:
{ tf1macansicpg10000cocoartf100 {fonttblf0fswissfcharset77 Helvetica;} {colortbl; ed255green255lue255;} margl1440margr1440vieww3560viewh1820viewkind0 pard x1440 x2880 x4320 x5760 x7200qlqnatural f0fs20 cf0 This line is 10 point size fs24 This line is 12 point size fs28 This line is 14 point size fs31 This line is 15.5 point size }
Finally, let’s try changing fonts . The window on the right in Figure 12-4 produces this RTF file:
{ tf1macansicpg10000cocoartf100 {fonttblf0fswissfcharset77 Helvetica;f1fnilfcharset80 AppleGothic;f2fmodernfcharset77 CourierNewPSMT; f3fromanfcharset77 TimesNewRomanPSMT;f4fscriptfcharset77 BrushScriptMT;} {colortbl; ed255green255lue255;} margl1440margr1440vieww3500viewh2020viewkind0 pard x1440 x2880 x4320 x5760 x7200qlqnatural f0fs28 cf0 This is Helvetica f1 This is AppleGothic f2 This is Courier New f3 This is Time New Roman f4i This is Brush Script MT}
Rich Text Format is a system for encoding various kinds of font information into a printable ASCII character stream (only 7-bit ASCII characters are used, for portability). Using Rich Text control words, you can encode font, size, and even margin changes in an application-independent fashion. There are many Rich Text control words; Apple implements only a subset of them. This brief discussion should be enough to get you going.
An RTF document begins with the character string
“{
tf0”
or “{
tf1” and ends with a closing
brace, “}”. Inside the RTF
document, you can have control words, which begin with a backslash
(), and text. Control symbols are interpreted as
commands, while text is displayed or printed.
You can have additional pairs of braces within an RTF file. Any formatting controls that you issue within a pair of braces will be used but will not be printed when the Rich Text is printed. For example, the following sequence in an RTF file:
This is { a test} of Rich Text.
prints like this:
This is a test
of Rich Text.
Controls can appear anywhere in the text. For example, the strings:
This is a test plain of Rich Text.
and:
This is a test 0 of Rich Text.
print like this (the same as the string with braces above):
This is a test
of Rich Text.
Normally, Rich Text ignores carriage returns. If you
want a carriage return, precede it with a
backslash (). If you
want a backslash, type a double backslash (
\
).
These are examples of control symbols.
You can define any number of fonts within an RTF document. Fonts are
given numbers; you usually define them within a set of braces at the
beginning of the document. In our earlier example, the following
string defined a single font table, with the font
f0
being Helvetica:
{fonttblf0fswissfcharset77 Helvetica;}
Table 12-1 summarizes some common RTF controls.
Table 12-1. Common RTF controls
Control word |
Meaning |
---|---|
|
Declares a file to be a Rich Text file — you should use
|
Font control word |
Meaning |
|
Begins definition of a font table |
|
Selects font 0 |
|
Selects a sans-serif font fontname |
|
Same as |
|
Selects a serif font fontname |
|
Selects another kind of font fontname |
|
Selects a font size — nn is in half points |
Font control word |
Meaning |
|
Plain |
|
Bold |
|
No bold |
|
Italic |
|
No italic |
|
Gray; nnn= 0 for black, 1000 for white |
|
Underline |
|
No underline |
|
Superscript nn half points |
|
Subscript nn half points |
Formatting word |
Meaning |
|
Left-justify text (quad left) |
|
Center-justify text (quad center) |
|
Right-justify text (quad right) |
|
Tabstop |
|
Paper width in twips[a] |
|
Paper height in twips |
|
Left margin |
|
Right margin |
|
First-line indent in twips |
|
Left indent in twips |
|
Word underline |
|
Dotted underline |
[a] There are 1,440 twips in an inch. |
The RTF controls in Table 12-2, while useful, are not currently implemented by Apple.
Table 12-2. Unimplemented RTF controls
RTF control |
Meaning |
---|---|
|
Shadow |
|
Small caps |
|
All caps |
|
Invisible text |
|
Double underline |
If you specify a font that isn’t available on the machine you’re using, you are likely to get Courier.
[33] Another way to create an application to invoke
fontDemo: is to use Project Builder
and Interface Builder to create a project with an NSTextView object
in a window and a menu item called Demo. Then subclass NSObject to
get the MyObject class, and add an outlet called
theText
and an action called fontDemo: to it. Instantiate the subclass to
get a MyObject instance, connect the theText
outlet to the NSTextView
outlet, and make the Demo
menu cell send the action message fontDemo: to the MyObject instance. Finally,
unparse MyObject, type in thefontDemo: code above, build and run the application, and
choose the Demo menu command.
18.118.144.248