Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2003-427
$eventId
ID of event: wwdc2003
$eventContentId
ID of session without event part: 427
$eventShortId
Shortened ID of event: wwdc03
$year
Year of session: 2003
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC03 • Session 427

Cocoa Text

Application Frameworks • 48:26

The text system has always been one of the central components of Cocoa, but this year it's getting some exciting new features that will make it more useful and powerful than ever. We briefly review the architecture of the Cocoa text system, then dive in and show you how to start making use of its new capabilities.

Speaker: Doug Davidson

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

You guys are hardcore. Excellent. It's been a long, long week, but we got somebody here who's going to make it all worthwhile. Doug Davidson is going to talk to you about some of the really cool stuff in Cocoa text. So help me welcome to the stage Doug Davidson.

Okay, thanks Jason. I'd like to thank you all for coming. I like to think we've saved the best for the last. As I said, I'm Doug Davidson, and I'm here to talk to you about the Cocoa text system. Now, anyone who's worked with Cocoa at all has seen the text system in action, because in almost any case where a Cocoa application puts up text on the screen, the text system is responsible for it in one way or another.

So the Cocoa text system is at the heart of a lot of the things that we do in Cocoa. Now, what I'm going to do in this session is first of all, I want to go briefly through some of the features of the Cocoa text system, then we're going to dive in and explain how to start using them.

But the primary emphasis that I want to have is on what I think is the most interesting part of the text system, and that is customizing it for your particular application, because one of the principal design points of the Cocoa text system is that it should be highly customizable throughout.

Now, as far as features go, if you've used TextEdit, you're probably familiar with a lot of the features of the Cocoa text system already, because most of what you get with TextEdit are things that you will get for free whenever you use the text system, provided that you check the appropriate boxes in Interface Builder. So showing is better than telling, so let's move on over to the demo and let's take a look.

Now, what I have here is my version that I just wrote of a very simple editor that you've probably seen demoed over and over. Very little code, but it has all the features that you would expect, features that we know and love. But now, this is running on Panther, so we might expect something more.

For example, In Panther, the Find panel is now built into the text system. It took me zero lines of code to add this to my app, and it's fully functional and actually quite full-featured. Also for Panther, we have completely redone the text ruler. The old confusing line spacing controls that nobody can understand are gone by popular request.

In their place, let me We have this neat little pop-up here that by default allows you to pick single spacing or double spacing. Or in fact, just about any kind of spacing you might like. Now, we certainly didn't have user interface in Jaguar for most of these features, and in fact some of them are completely new for Panther. So, for example, the line height multiple. Let's try picking one and a half line spacing. And you'll notice that when we go back, that's now showing up on the pop-up so I can get to it easily again.

We also have this other little pop-up over on the other side. This is the styles pop-up. And what this allows you to do is to save any styles that you might like or use frequently so you can have them right at your fingertips whenever you want them. Now, for demo purposes, I've already preloaded this with a number of styles because I want to show off some of the new attribute features that we have in Panther. For example, underlines. In Jaguar, I think we had two different underline styles. In Panther, we have, I think it's 30. For example, you could get a double underline or a dotted underline or colored underline, basically just about anything you'd expect.

Also, we have taken the strikethroughs and separated them from the underlines and given them the same sort of options. So you can get double strikethrough, co-ed strikethrough, and so forth. What else? We now have outlines, common request, but we don't just have a single outline style. We have a fully adjustable parameter so that you could get, for example, A heavier outline, if you like, or a lighter outline. But that's not all. You also get to set independently the colors of the outline and the interior of the text so that you can get some effects, say, like this.

If you went to the Cocoa Update talk, you heard about the new class NSShadow that allows the setting of a drop shadow on drawing, but it's also available as a text attribute. So you could have a configurable shadow, maybe a little tiny shadow, I'm not sure if you can see it on this big monitor, or perhaps a somewhat larger, somewhat blurred shadow, or essentially any shadow you want.

Now, what I'd really been intending to type here was something more spectacular, but I forgot how to spell spectacular. But that's okay in Panther, because I just start typing and complete. And then, oh, there it is. Now, this text completion is fully customizable, and I'll be showing a demo of that later on.

But for this particular application, I decided I wanted to write zero lines of code for it. And so all I got is the default built-in functionality, which uses the system spelling dictionary plus anything I happen to have in my document already. So let's put it all together. I have a somewhat more complicated style here. So a little something more spectacular for Panther.

Oh yes, there's one more thing. When I go to save this document, I can save it as rich text or plain text, just as you'd expect, but there's one more option here. I can also save this as a Word document. So this simple 50-line application can read and write Word documents. As a matter of fact, in Panther, any Cocoa application can now read Word documents without changes of code, provided it was using the standard init with path or init with URL APIs. Writing Word documents is a little more difficult.

You need to have one line of code for that. As far as reading Word documents, we go back as far as Word 6, I think it is. For writing, we use the most recent format, which is roughly Word 97 and later. You may have heard that Word has a new XML file format called WordML, and we read that, too.

Now, I have to caution you, of course. There is going to be some loss of fidelity because the Cocoa text system does not support all the features that Word does and vice versa. Most notably, for example, the text system doesn't have support for tables yet. Whereas before, what you have to do is open up the document in Word, save it as RTF, then open it in a Cocoa application. Now we've cut out that step entirely. You can just open it directly in Cocoa application and get the same effect. Okay. So now let's go back to the slides.

What I want to do is review briefly the new features that we've added for Panther. We have the new attributes and attribute features that I've showed you, the outlines, shadows, underlines, strikethroughs, and so forth. We also have a couple of new attributes, the tooltip and cursor attribute that allow you to set a tooltip or a custom cursor and associate it with a particular region of text.

And we have a little something called the link attributes that I'll be showing you later on. We have new paragraph style features, such as the new line spacing options that I showed you, and there are also some new tab stop features that I won't have time to go into right now. We have some whole new API sets. We have NSLift generator and NSATS typesetter, I'll be discussing more later on.

And then we have the most obvious and visible new features, like the new font panel you've probably seen, the new text ruler I showed you, the find panel completion, reading and writing Word documents. And there are quite a few other individual APIs that I may not be able to go into in detail here, but I wrote a number of pages of release notes about them.

So I urge you to take a look at the release notes. And take a look at the headers, because all the new things for Panther are clearly marked in the headers. And I think the release notes should go into quite a bit of detail describing what these things are and what they do.

One other thing that's new for Panther is WebKit. And some of you may be wondering, what's the relationship between text system, text views, and WebKit and web views. Well, the short answer is that they're different in much the same way that a web browser is different from a word processor. The fact of the matter is that most web pages today don't look very much like text pages. And WebKit is -- they're full of frames and plug-ins and JavaScript and so forth.

And WebKit is aimed squarely at the display of the most complex HTML. So if what you want is to display a chunk of HTML in your application, probably what you want is a web view. On the other hand, if you want -- what you want to show is really text, or in particular, if you want editability, then you want a text view.

So, if you want to show a text view, you want to show a text view. And if you want a text view, you want to show a text view. And if you want to show a text view, you want a text view. And if you want to show a text view, you want to show a text view. And if you want to show a text view, you want to show a text view.

And if you want to show a text view, you want to show a text view, you want to show a text view. And there's a sort of a bridge between them, and that is that the Cocoa Text System does have an HTML import feature, which is necessarily lossy, but it does at least allow you to get the text, rich text content of HTML into the text system.

So now it's time to dive in and start showing you how to use all these features and new features and so forth. But before I do that, I want to put some structure and organization on my discussion of the text system. And the paradigm I like to use is model, view, controller. Yes, you've heard it many times. Within the context of the text system, the model is the text document, the characters and their attributes. The view is the text view you've seen that handles the user input and display. And the controller classes handle everything in between.

You can do quite a lot in the text system just by dealing with NSTextView, as we'll see. The TextView is the visible face of the text system for most purposes. What a TextView is, is a representation of a single region within which text is laid out, such as a page, and it handles all of the display and all of the user interaction for the text on that page. If you have, say, multiple pages, then you would have multiple TextViews, and those sibling TextViews would know about each other and work together and have a certain amount of shared state.

There are a few other classes that come in and work with the text system at the view level. For example, the standard Cocoa Ruler classes are used in displaying the ruler for text. And then there's a class called NSTextAttachmentCell. It's a special kind of cell that is used for displaying the icons or images corresponding to inline attached files. And the most visible new features for Panther come in at the view level. The updated font panel I mentioned, the new text ruler, the find panel completion, and the cursors and tool tips that I've talked about.

[Transcript missing]

So what are these attributes? The standard ones that are recognized and understood by the text system are listed in an AppKit header called NSDistributedString.h in the AppKit. And they include most of what you'd expect: fonts, colors, underlines, strikethroughs, and so on and so forth. There are a few things that don't appear as individual separate attributes. For example, bold and italic are not separate attributes. They're treated as traits of the font.

And we use NSFontManager to add these traits or remove these traits from the font. Also, anything that is part of a paragraph style, like margins, tab stops, line spacing, and so forth, is not a separate attribute, but instead it's a feature of an NSParagraphStyle object that encapsulates the text. It encapsulates all that information.

And then there are some special attributes that deal with view and display and interaction, like the cursor and tooltip attributes that I mentioned before. And then there's a link attribute that's used whenever text has a hyperlink associated to it, and the attachment attribute that's used when there's an inline attached file.

We have quite a lot of new features in Panther. The attribute level, we have the stroke width and stroke color that I demonstrated when we used for outlines, the new underline styles and colors, strike through styles and colors, the shadows, a couple I haven't mentioned though, bleakness and expansion, you can look those up, and the cursor and tooltip attributes.

So now we have enough to talk about more or less the first level of customization of the text system, where you deal with the view and the model layers only and you don't worry about what comes in between. So the first thing you should think about when trying to customize at this level is receiving notifications from the text view or acting as a text view's delegate.

The text view's delegate is actually very powerful. For example, the text view's delegate is notified whenever the user makes any change to the text, and the delegate has the opportunity at that point to accept or deny or modify that change. So the text view's delegate can control whatever the user does in the text view.

The text view's delegate is also notified if the user makes a change to the selection in the text view, and again has the opportunity to accept or deny or modify that change. And there are many other text view delegate methods that give the delegate control over a lot of things that are going on with the text view and its user interaction.

But in some cases you'll find you want to do some kind of customization that you can't do as a text view's delegate. At that point, then it's time to start thinking of subclassing NSTextView. If you went to the tips and tricks session earlier today, I gave an example of a custom subclass of NSTextView. That was intended to do some additional drawing.

It was drawing bubbles around the text, somewhat the sort of way that iChat does, for example. And you can also subclass to customize the user interaction in the text view a number of other ways. I'm going to give an example in a minute that will, I think, show clearly the distinction between delegation and subclassing and working with NSTextView.

Now, when you go to customize the text system, it will often happen that you'll want to make modifications to the text that you're working on. And the way this typically works in the text system is that user-initiated changes to the text go first to the text view and then through it to the text storage, whereas programmatic changes will typically go directly to the text storage. But they all end up with the text storage in the end. And the text storage, as I've said, is a mutable attributed string. When you want to change it, you just use mutable attributed string methods to alter it.

But, as I also said, it has this additional feature that it notifies the rest of the text system whenever it changes. So if you're going to be making multiple changes to the same text storage in a row, what you'll want to do is wrap those in a call to begin editing and end editing.

And what that will do is cause those notifications to be coalesced into a single one at the end for efficiency's sake. You can also subclass in as text storage, but that's a little more difficult, and I'll be talking about that later on. So, let's go over to the demo again.

I want to show an example of customizing TextView, both as delegate and subclass. So we have here, we're going back to text completion, and you'll recall I told you that text completion was fully customizable. So here's a custom completion that you might use if you were working in, say, financial reporting. Because what this will do, if you type in the name of a company and try to complete, it will complete the name of the company. It will also add the stock ticker symbol.

And if there's more than one, then you get a list and you get to pick. And you'll notice that these things are -- the stock tickers here are blue and underlined. And in fact, when I mouse over them, I get a pointy hand cursor, which is a clue that these are actually links. And I chose to set them up as links to the stock price. I see we're not doing so well. So how do we do this? Let's see if we can take a look at the code.

I've set this up as a TextView that has both a delegate and it's also a custom subclass. What the TextView's delegate can do in the context of completion, the delegate is allowed to have complete control over the list of completions that's presented, their order, and which one is first selected. That's the first thing we're doing.

As a delegate, we implement TextView completions for partial word ranges and so forth. We have a dictionary here whose values are the company names and keys are the ticker symbols. The possible completions are just the whole list of company names, all the keys in the dictionary. All we do is go through and see if the partial word that we're completing has a prefix to one of those. Then we add it to the list of completions, and we're done.

So that's what you could do with completion acting as a TextView delegate. But we also did something more. When the completion was actually accepted and confirmed, we added in the stock ticker symbol and made it a link. Now, there are a number of methods associated with completion that are designed to be overridable by subclasses, and one of them is this method and search completion for partial word rank that handles putting the completion back into the text.

Now, there's one thing I just skipped over that I probably should mention, and that is this. We made a call to set link text attributes. Now, when you set link text attributes on a text view, this is a special set of attributes that the text view will automatically apply to text that has a link on it. So in this case, we're making it blue, and we're giving it an underline, and we're adding a pointing hand cursor to it. So all text that has a link will automatically get those attributes applied to it.

So now our subclasses method for inserting completion, what it's going to do is it gets told whether this is a tentative completion or a final confirmed completion. And if it's a final confirmed completion and we have the stock ticker symbol for it, then what we're going to do is let it be inserted as usual. We call it super method. And then we're going to create an appropriate URL for the link.

Actually, first what we do is modify the string to be inserted, so we add the ticker symbol to it. Then we get a URL and we add this link attribute to the appropriate range corresponding to the sticker symbol. And then the TextView automatically does the rest, applying the blue color outline and cursor to it because we set the link text attributes. You could also have complete control over the list of completions presented as a TextView subclass, but I chose to present it in a delegate in this case, just to make the distinction clear. Okay, so let's go back to the slides.

You could do quite a bit with the Cocoa text system by dealing only with the view and the model layers and never worrying about what happens in between. But at some point you're probably going to get curious and wonder how it is that the Cocoa text system actually goes from the characters and their attributes in the text storage to the pixels you see on the screen.

Before I discuss that, I'm going to need to introduce another concept, and that is the glyph. Now, characters in the text storage are Unicode characters, but a glyph is something different. A glyph is a single displayable element of a particular font. It has no meaning outside of the particular font that it's in. So, in some sense, the job of the text system, at least at the controller level, is to take the characters and attributes in the text storage and to convert them to a sequence of glyphs and their positions that can be displayed.

That's not quite as simple as it might sound. For example, the relationship between characters and glyphs is not one-to-one. It's potentially many-to-many. So, for example, if we look at the character we recognize as an accented A, acute accented A, in Unicode this can be represented either as a single accented A character or as two characters, an A and an accent.

Likewise, depending on the font, it might potentially be represented by one accented A glyph or it might be represented by two glyphs, an A and an accent. And the text system has to keep track of all that and the relationship between the two, and then it has to calculate the appropriate positions for the glyphs so that we end up with the final displayed result that is the appropriate glyphs in the appropriate places with the appropriate attributes.

So what are the classes that are actually involved in doing this? The principal class that's involved here is NSLayoutManager. NSLayoutManager is the heart of the text system. It is the controller class in the Cocoa text system. I said that the text storage notified the rest of the text system when it changes. Well, it's the LayoutManager that gets notified by the text storage. And when that happens, the LayoutManager keeps track and it arranges for all the necessary work to be done. The LayoutManager is the controller class that's involved here. The principal class that's involved here is NSLayoutManager.

NSLayoutManager is the heart of the text system. It is the controller class in the Cocoa text system. I said that the text storage notified the rest of the text system when it changes. Well, it's the LayoutManager that gets notified by the text storage. And when that happens, the LayoutManager keeps track and it arranges for all the necessary work to be done.

I'll be discussing these in more detail later on. And then the LayoutManager stores all the information about the sequence of glyphs and where they're positioned. And it's actually the LayoutManager that does the display, arranges for them to be displayed as necessary when requested to do so by the view.

So how is it that text gets from the characters and attributes in the text storage to the pixels you see on the screen? There are four separate steps. I want to go through them in order. First, attribute fixing, where we make sure the attributes are consistent. Second, glyph generation, where we convert the characters and their fonts into glyphs. Third, layout, where these glyphs are broken up into lines. The glyphs are positioned in the line, the lines are positioned in the line, and the lines are positioned in the line.

Fourth, display, where the glyphs are marshaled up and sent down to Quartz to actually be rasterized into bits on the screen or displayed on the printer or PDF or what have you. And in the Cocoa text system, all these processes are usually done lazily and on demand. So what typically happens is that although these occur in this order, they get pulled through from the end. So what will happen is that the text view will be required to redisplay a certain region. So it will call on the layout manager to read the text.

And the layout manager, in order to do that, needs the layout information. So the layout information has to be calculated. But to do the layout, you need to have generated the glyphs. So it causes that to be done. But before you generate the glyphs, you need to have fixed the attributes. So it causes all that to occur in order, pulled through from the end.

Attribute fixing. Now attribute fixing is actually performed by the text storage. There are several different kinds. First of all, there's font fixing, where we make sure that the font that's applied to a particular character is actually a font that can render that character. And we will, the Cocoa text system will substitute fonts as necessary in order to get one that can render the specified character.

And then there's paragraph fixing. You remember I talked about the NS paragraph style attribute that encapsulates all the paragraph style level features. Well, what we need to do is make sure that that attribute is actually constant over the length of an entire paragraph of text. We can't have more than one for a given paragraph. And then there's attachment fixing, which makes sure that the attachment attribute applies only to the attachment character that it's supposed to. And can all these be customized? Yes, they can be customized by subclassing NSText storage and overriding the appropriate methods.

Next, glyph generation. Now, when the Layout Manager is informed by the text storage that things have changed, it will have to invalidate a certain portion of any information that it already has. due to that change. You can also manually cause the layout manager to do this invalidation if you want to force some recalculation. And so then when that information is needed again, the layout manager will call on the glyph generator typesetter to recalculate the information about what the glyphs are and where they were positioned.

And it's glyph generator that actually does a conversion from the characters and fonts to the glyphs. And then the layout manager stores the sequence of glyphs that it has produced. And because the relationship between characters and glyphs is not one to one, the layout manager also has to keep track of the relationship between character indexes and glyph indexes. And if you ever need that information, you go back to the layout manager and ask it.

Then comes layout, and the layout manager calls on the typesetter to perform the layout. The typesetter goes back to the layout manager to find out what the glyphs are, because they've already been generated, and it goes to the text container to determine the geometry of the page to figure out where it can put lines on the page. The typesetter is the class that actually calculates the layout.

It breaks the text up into lines, positions the glyphs in the lines, and it is also allowed at this point to insert or change glyphs in the glyph stream. For example, if it's doing hyphenation, it may need to insert a hyphen glyph. And then the typesetter calls back to the Layout Manager and tells it what it did. It tells it where each line ended up and which glyphs ended up in the line and where the glyphs are positioned in the line, and the Layout Manager stores that information.

Then display. When the text view needs to redisplay a certain region, first thing it does is go to the layout manager and ask it what glyphs are involved, what glyphs are needed to display that particular region of text. And then it calls back on the layout manager again, first to cause the layout manager to display the background appropriate to those glyphs. That's typically things like the selection rectangles.

And finally, to ask the Layout Manager to display the glyphs along with any other additional drawing that's needed like underlines, strikethroughs, attachments, and so forth. Now it's not just NSTextView that can do this. Your custom view can do it too. In fact, there's an example, developer examples appkit circle view that does this.

There are a couple things you have to remember if you're going to do this. When you call on the Layout Manager to do this sort of drawing, you must already have focus locked on a flipped view. If your view is flipped and you're in your drawRank method, that's automatic.

Another thing you have to keep in mind is that the Layout Manager keeps track of all its positions relative to the origin of the text container. So, the Cocoa text system is a very interesting tool for the positions of lines and so forth, which may not be the same as the origin of your view. In fact, it often isn't. So, you'll have to translate between coordinate systems. And it keeps track of the location of positions of glyphs relative to the origin of the line in which they live.

So, there's a certain amount of translation that has to go on, and we'll see an example of that. So now we have enough to talk about what is more or less the second level of customization, where you are actually getting information about where individual pieces of text are positioned on the screen.

And the source of all such information is the Layout Manager. Layout Manager knows all. The Layout Manager, if you need to go from a character range to a glyph range or vice versa, which you will often need to do, you call on the Layout Manager to get the position of a particular glyph or position and size of a particular line, or to go backwards, given a position to locate a glyph, or given a rectangle to locate the glyphs in it. All that sort of thing, you call on the Layout Manager. There are quite a few methods to do this.

It's also possible to subclass the Layout Manager if you want to customize any of its particular features. For example, if you have a custom attribute that you want to affect drawing at the individual glyph level, one way to do that is by subclassing NSLayoutManager. I'll give an example of that. Let's go on over to the demo again. Let me see if I can bring this up.

So here I have some Chinese text. These characters are beautiful and concise, but one thing that they don't necessarily do, except especially for someone like me who doesn't read very well, is make it obvious how they're intended to be pronounced. So over the years, there have evolved a number of schemes for adding some sort of phonetic annotation to characters. In some cases, the annotation appears above the characters. or in some cases, it appears beside the characters. But either way, it makes it crystal clear how these are intended to be pronounced. For example, this is .

: But fortunately, the dictionary where I got these pronunciations also gave me some clue as to the meanings of these characters. So we can display that, and that will help us to recognize this quotation here from The Art of War, which might be rendered as, "If you know your adversary and you know yourself, you need not fear the result of a hundred battles." So the text system doesn't have any built-in facilities for this sort of thing. This is all customization. How do we do it? Let's take a look.

What I've done is to define some custom attributes, and I have some code that is fairly simple and kind of simplistic, actually, that sets these attributes based on a dictionary that I provided it. What I'm going to show you -- actually, all this code should be available for download from ADC, if not now, then very soon.

What I have done is subclass NSLayoutManager and override the primary draw method, draw griff range at point. And so what we're going to do here is we're given a glyph range. We need to look at some of these attributes. So first we convert it back to a character range.

Now, before we do any of our own drawing, we're going to call on the super method to actually draw the original glyphs. And then the next thing we're going to do is draw all these annotations. And it's actually not that complicated. What we do is go through the text, the characters in the text storage, and we look What we do is here, we look for any characters that have our custom attribute.

We also have calculated what glyph corresponds to this character, and where it's located as far as the point, and where its line lives. And then, you'll remember I talked about all the translation that needs to be done. Well, to get from the coordinates of a glyph, as the layout manager gives them to you, to coordinates in your view, you need to translate by the origin of the line rectangle that it lives in, and by the origin of the container. and all that is available. That is a very standard idiom.

And we also get some information about the glyph and the font that we're going to use in doing our sizing. So the value of this attribute is just the little string that we're going to show as an annotation. And basically all we do is figure out a rect where we want to put it and some attributes to draw it with, and just draw it.

The bottom annotations for the meanings are essentially the same. The right annotations, the are a little more complicated because in that case each glyph has to be positioned individually. But I'll let you look at that once you've downloaded this. That, I think, is all of this. So let us go back to the slides.

Now we come to the most difficult part of the talk, where we discuss NS-TypeSetter. As I said, NS-TypeSetter is a class that actually performs the layout. And that's what mainly makes this topic so complex, because layout is inherently complex. NS-Typesetter itself is a mostly abstract superclass. If you wanted to provide your own complete layout engine and entirely replace the one that the text system uses, then you can subclass NS-Typesetter directly. But most people are probably not going to want to do that. The main concrete subclass of NS-Typesetter is NSATS-Typesetter, which performs the standard system layout.

And if you want to use that for the most part, but make some minor modifications to it, then you can subclass NSATS-Typesetter. Now this is new for Panther. A new class, lots of new public API. NSATS-Typesetter actually existed before, but it was completely private. The methods were all different, so you can't use it. before Panther.

Now, what generally happens is that when the layout manager needs a certain portion of text to be laid out, it calls on the typesetter and asks it to lay out a certain number of lines. Then the typesetter goes back to the layout manager to get information about the glyphs that need to be laid out, and it goes to the text container to get information about the geometry, where the lines can be placed.

It does its thing. It does all the layout and it calls back to the layout manager and tells it what it did. It tells it where each line went, which glyphs went in the line, and where all the glyphs are positioned in the line. At this point, the typesetter is allowed to insert or modify glyphs in the glyph stream and it tells the layout manager about that, too. For example, As I said, if it's doing hyphenation, it may need to insert a hyphen. Now, for those of you who have been waiting for it, now in Panther, we have implemented truncation with ellipses.

That is now implemented at the typesetter level, so you can use it. In that case, the typesetter would need to work with the glyph stream to make sure the ellipsis is in there. It may happen in a number of cases that certain glyphs need to be modified if they appear at the end of a line. In that case, the typesetter would have to do that at this stage, because it's only the typesetter that knows where the end of the line is.

And NSATS typesetter has a number of methods and they're divided into several different groups for several different purposes. There is an interface that is intended primarily for subclassers who want to modify layout in the text system and they're allowed to do various things to that, I'll discuss later on. There is also a glyph storage interface to NSATS typesetter that has a slightly different purpose. The purpose of the glyph storage interface, it may be that in some cases you want to use NSATS typesetter by itself without any of the rest of the text system.

And what the glyph storage interface does is to encapsulate all the calls that the layout manager, that the typesetter, excuse me, makes back to the remainder of the text system. So you can override that and replace all this interaction with your own custom method of storing glyphs or storing text or what have you. And what this allows you to do is to make use of the system line layout algorithms without having to use any of the rest of the text system. if that is what you want to do.

But in most cases, if you're going to subclass in ATS typesetter, it's going to be because you want to make some custom modification to the layout that it does. So what you can do with this is that you can be notified as the text is being laid out. You're allowed to modify the position and size of the line fragments as the lines are being laid out. You get some control over line breaking, hyphenation, and so forth. So I have a demo of this. Let's go back to the demo machine.

This is actually the same demo I had before. One thing you'll notice is that as the annotations appear and disappear, the height of the line changes correspondingly. To do that, I actually subclassed NSTypeSetter. and implemented a particular method it has. This is also the means, for example, by which iChat makes sure that the paragraphs of text line up with the little pictures of your buddy on the side.

And what this method does is the typesetter subclass gets called whenever a line has been laid out, and you get passed by reference the rect for the line, the glyph range, and the used rect, that is, the portion of that rectangle that was actually occupied by text and the baseline, and you get a chance to modify them before they get sent off to the layout manager. So what we're doing here is fairly simple.

Again, we're passed in the glyph range. We need to convert it back to a character range. And we go through it, and all we do is we look for our custom attributes and see if we find them, make a note, and we look for the font that they appear in so we can calculate how much space is actually needed.

If we found any and we have some space, then we add it to the line fragment and adjust that.

[Transcript missing]

So the line fragment expands, but only for those lines that actually have the characters with this attribute. All the rest are unaffected. All right, let's go back to the slides.

So now it's time to wrap up. Of course, there's no longer any opportunity to go to any further sessions, but you will presumably be able to view all of these on DVD or by some other means. There have been a number of other sessions on Cocoa. Cocoa Update session discussed briefly some of the changes we made to text for Panther.

The Cocoa Tips and Tricks session, as I mentioned earlier today, had an example, actually several examples that made use of text for various things, for adding, in particular for adding drawing to the text view. So if you're interested in that, you should probably take a look at that session. And you can take a look at the code from that, which is, we have verified, now available for download already. And there are a number of other sessions that may be interesting for those of you who want to deal with text.