Application Technologies • 1:10:34
The new CoreText font and text layout APIs enable high-performance advanced font handling and fast Unicode layout while providing close integration with system frameworks. If you are using deprecated APIs such as Quickdraw or simply want to learn how to take advantage of Apple's next generation text engine, you will want to attend this session.
Speakers: Julio González, Ned Holbrook
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning. My name is Julio González. I manage the type group at Apple. I am very excited to be here with you today. I'm excited for two reasons. I get to introduce to you a new framework, and believe it or not, I'm excited because I got to finally tell you about it.
See, we've been listening to your feedback during this past few years. You want to have a faster way to layout text. You want to have an easier way to do so as well. You want to have a font framework that is unified and it's not all over the place. Well, we've done so.
I am an active member of the Carbon Developer List. I usually answer questions regarding text and fonts. And a lot of the time, I'm not the only one. I'm the only one. I'm the only one. I don't like the answers that I give you. Some of those are, stick to Quikdraw, even though I know that Quikdraw is a deprecated API. Or, at times, I give you an answer that says, here's a bunch of Cocoa APIs that you can use to do so, even though I know that you can't or it may not be possible for you to apply this Cocoa solution to your application.
So, You know, I sit back and I say, boy, I really want to let those people know about CoreText, but I can't and I'm just, that's why I'm so excited to be here today and introduce to you CoreText. So, today, I'll give you a little rundown of what CoreText is.
Then I'd like to take a step back and take a look at the whole text system in Mac OS X, how things fit together and where CoreText fits in. Then we'll go and dive deep down into CoreText and what it is, look at some of its concepts, the APIs, give you a demo, look at sample code. Then leave you with some thoughts and hopefully we'll have some time for some Q&A.
So what is CoreText? CoreText is a brand new text layout engine for Mac OS X. It also comprises a comprehensive set of font API. So I say it's new. Well, it's not really new. CoreText has been there since Tiger. Some of you have noticed it. I know so because you've told me so on the developer list. You asked me, what is it? I said, well, wait.
During the Tiger timeframe, the Cocoa and Carbon frameworks made use of CoreText. They started moving away from using Atsui and using CoreText instead. So it is a proven technology. One of our goals when we set out to design CoreText was we really wanted fast layout. We wanted to have really fast text rendered on the screen.
So in the Tiger timeframe, as I said, Cocoa and HIToolbox started using it. And for example, internal benchmarks for HIToolbox show that draw theme text box is twice as fast once they adopted CoreText. So hopefully we can do the same for your applications. When we set out to do CoreText, We had that performance in mind, so we just took a step back and looked at what we had to offer in the past and the new things that we had in Cocoa, plus a few other things that we did internally.
And we created this unified framework for you to use so you don't have to jump back and forth between one API to the next. And finally, one of the main goals of CoreText was to help out our Carbon developers. A lot of them still use Quickdraw. In fact, as of last week, I'm answering questions of Carbon developers that are starting a brand new app and they're using Quickdraw. So hopefully with CoreText, we've solved some of these problems, given you an avenue to move from Quickdraw into something that is more modern.
So let's take a look at the text subsystem. So at the very bottom, we have what we call the core frameworks. First one is ATS, Apple Type Services. You can't use it to draw text or layout text. But it's a... A base framework that allows or vents all the font data to the system. Without it, the rest of the system, you can't do, you can't draw any text. Next one is Quartz.
It's our core rendering engine. It is the bottleneck for all text that is drawn in the system. As such, it is the fastest way to draw text. So you say at this point in time, "Well, it is the fastest way to draw text. Why should I be doing anything else? I should just use Quartz." Well, Quartz is just a rendering engine. It really cares little about text. It has no idea about text.
If you have the glyphs that you want to display, it will put those on the screen. It will get those pixels on the screen really fast. But if you have any issues such as text issues such as reordering, kerning, ligatures, it doesn't know anything about that. For that, you need a text system. So here's what the text system looks as of the Panther timeframe. Oldest of all is QD Text. I love QD Text, Quickdraw Text. Being the oldest, it has the most handicaps. It does not support all fonts. It has no support for UniCode.
In OS X, it has a more limited support of languages and scripts than it did in the classic time frame, and it's also the slowest to render. If that's not enough, as of Leopard, it will be the second release where Quickdraw is deprecated. And new to Leopard, 64-bit, Quickdraw is not supported at all in 64-bit.
Next is Atsui, Apple type services for Unicode imaging. This is a very powerful layout engine, a very complete layout engine, so much so that Cocoa uses Atsui to do some of its text operations. However, it's a technology that predates Mac OS X. As such, it has some really bad, or I shouldn't say bad, it carries some old technologies with it such as Quickdraw. So it creates some clashes, if you will, when you try to use these with modern APIs.
Aside from that, folks that program with Atsui Generally run into all these obscure performance pitfalls. And tasks that should be simple to use in Atsui or simple to do in Atsui are often complex. If you take one of these boxes, the box that says they're Atsui and you want to draw that text Atsui, it takes you half a page of code to do so. So these are some of the issues that we've solved with CoreText and hope to show you a little later on.
On top of ATSUI, we have Cocoa and MLTE. MLTE, Multilingual Text Engine, is a powerful framework. However, this framework is also old technology. It predates Mac OS X, and believe it or not, it's even older than ATSUI. As such, it has more baggage that it carries around, if you will. Quickdraw text, Script Manager, a lot more. However, it's a very easy-to-use framework. And to draw the same text, I'll give you the same example. To draw MLTE there, it probably takes you one or two lines of code to do so. So it is very nice in that respect.
Now, one note of caution for MLTE that I want to bring up at this time. For 64-bit and Leopard, MLTE is not going to be fully supported. The only APIs that will be supported in 64-bit for MLTE are those that make HI TextView work. That's why in previous, I think in the last WWDC, we told you to start moving away from MLTE and start using HI TextView instead. Finally, there is Cocoa. Cocoa is the best way and the easiest way to draw text in Mac OS X.
It is the simplest and most powerful way to do so in Waco S10. And if at times, if you don't find what you need in Cocoa, you can possibly subclass one of its classes and do what you need to do. However, we know full well that for a lot of you, Cocoa is not an answer. It doesn't fit into your architecture.
Or you need to be at a much more lower level that Cocoa doesn't fit your needs. For those, a lot of you folks have picked at ZUI and that's great. But now we have CoreText, which is what we think a much better solution, a better performing solution, easier solution for you to use.
Now, here's the text architecture in Tiger. As you can see, the Cocoa arrow that was pointing to Atsui is now gone, indicating the first transition from Cocoa to using CoreText. And also notice the arrow going from Cocoa down to the base frameworks, ATS and Quartz. In the Leopard timeframe, that arrow is gone. Now, what do I mean about that? It doesn't mean that Cocoa no longer uses the base frameworks to do its things. But what it means is that it's using much less and relying much more so on CoreText in the Leopard timeframe.
Also notice here the new arrow from Carbon to CoreText indicating that finally, Carbon developers can make use of the new technology. And finally, the coloring for MLT and QD text indicating a warning that they're not supported or in the case of MLT, there's little support in 64-bit. So, now you can see CoreText is sort of living in between those two layers, in between the Carbon Cocoa layer and the Frame layer, being the layer where most of the core layout operations are going to go through.
So what is CoreText? Now you need to know what is it that it really does for you. So I told you before there is two facets to it. There is the layout and the font API. I will talk to you about the font API, but before I do that, I want to introduce Ned Holbrook, who will tell you all about the layout capabilities of CoreText.
Thank you Julio. Good morning. I'm very excited to be here with you and tell you about performing text layout with CoreText. So, as Julio mentioned, CoreText is newly public in the Leopard timeframe and as such, if you have taken the opportunity to look at your developer seed, you'll see the full range of CoreText headers that you have available to you. Has anyone taken a look at those yet? Okay, that's good.
Because there's, it may look daunting at first and so what I'd like to do is walk you through sort of the higher level objects that we have available to you in CoreText and how you might use them appropriately to do what you need. So, in order to do so, We should first answer the question, well, why should I even care? Well, we've tried to give you several different reasons why you will be interested in CoreText, and so I'd like to discuss some of those now.
The first is that CoreText is a modern API design. Not only does CoreText make extensive use of system services internally, but it also makes use of the very same conventions used elsewhere in frameworks for Mac OS X. So this means that a lot of the rules of thumb that you've garnered for yourself over the years working with OS X technologies can be put to the same use with CoreText, which means that you can spend less of your time fighting the API and more of the time getting stuff accomplished with CoreText. Cortext is fast. This was a big concern for a lot of you with some previous architectures rendering engines on Mac OS X. And as Julio mentioned, we found internally that Cortext is up to twice as fast than Atsui, so we hope you'll be very excited about that.
CoreText makes use of core foundation objects. Not only does it use core foundation objects as parameters to various functions, but CoreText itself is a collection of core foundation objects. So I'll be talking about these objects in the next 25 minutes or so, but what you should know is that once you have a CoreText object in your possession, then you can go ahead and follow the same create rule that you've learned when dealing with core foundation, and so you can retain and release those objects as necessary, which is great.
CoreText has both Quartz and Cocoa integration. Now, I suppose integration isn't exactly the right word to use. I think I was looking for a word closer to synergy. But basically what this means is that, as Julio mentioned, Quartz is the only way to get text drawn on Mac OS X at the lowest level.
And so obviously, Quartz is making use of Quartz. But in addition, as you'll see, for those of you who need access to the lowest level of information, we've provided that to you in a form that is directly usable by Quartz. So there aren't any expensive type conversions that you'll need to do or that need to be done internally even in order to make use of that information. And for Cocoa, of course, since Quartz is a core file, a file that's not necessarily a core file, it's a core file that's not necessarily a core file. So we've provided that to you in a form that is directly usable by Quartz.
So there aren't any expensive type conversions that you'll need to do or that need to be done internally even in order to make use of that information. And for Cocoa, of course, since Quartz is a core file, a very common file that's not necessarily a core file, but it's a core file, a core framework, it doesn't require the use of any higher level framework, be it Carbon or Cocoa. But if you are making use of Cocoa, there are a couple of instances where we've been able to streamline the process of doing so. And so if you are programming in a Cocoa application, it'll be really easy to get information into Quartz, and I'll show you how.
The most important thing on this slide and the thing I want you to remember is, like Julio said, simple tasks are simple. And frequently these are the most common operations you're going to need to do. If you want to draw a simple paragraph of text on screen, you shouldn't have to write a bunch of code in order to do so because, hey, everybody needs to do it. So with CoreText, we've made those simple tasks, those common operations, very easy to do. And I'll be showing you several examples of how to do so.
And finally, CoreText is easy to optimize. Well, what exactly do I mean by this? Well, with ATSUI, there are cases where calling one function could cause a performance degradation elsewhere in a different ATSUI function, and it wasn't always very obvious, even to us Apple engineers, when those cases were going to be.
And so with CoreText, we've tried to make the processing that we've done more transparent, so you'll be able to look at a piece of code using CoreText and make a very accurate prediction as to where the time in your application is going to be spent for large amounts of text. And so we think that this will make you a lot more interested in using CoreText since you won't have to worry about those issues since you'll be able to see it all up front.
And since it's so fast to start, then, well, you're off on a good foot already. So to start, I figured it wouldn't be fair to let you get through a session without having some sort of demo. And so I'm going to show you what the operation that we call frame setting, but you probably know as simple multi-line text layout.
Okay, so with this example, I have a couple of text documents on screen, and let me bring those up now. So for those of you who can read this text, you'll see that they're not exactly the world's most engrossing documents, but they do have some interesting characteristics that I just want to point out quickly.
Both of these documents are regular RTF files, and I'm showing them in TextEdit right now. And so you'll see this first one with WWDC at the top has a couple of interesting characteristics. It's set in two different languages. I've got the same text on here in Japanese. And you'll note that there are tab stops around these bullet points here.
In this other document, which is the text of the French Constitution, you'll see that there's a large amount of text in this document, and that it also makes use of some various fonts and styles here for simple formatting. And so we have two documents that are looking great in TextEdit, and so I've written a demo application that I'd like to show you CoreText with now.
So this demo example that I've written is basically a very, very simple Cocoa application, but really what I'm making use of Cocoa here is for its RTF reading capabilities. And so there's nothing in this demo that's inherently tied to the use of Cocoa with the exception of the RTF reading, but basically what I've done is created a custom view using CoreText to do text layout. So anything you can do with Cocoa using CoreText, of course, Carbon is available also, and we'll be making samples available to you using both of those frameworks.
So let's go ahead and open up both of these documents in my application here. Now, as I noted, I simply made a custom view here that uses CoreText to do the layout of this text. And the first thing you'll notice is that both these documents have their formatting preserved, which is great, because all I did was just ask for the RTF to be parsed, and now I'm using CoreText to render it, and it looks great out of the box.
One of the other features that I added to this view is that when I click on it, I can change the number of columns that the text is displaying in. And so in this example, I have that large document, and I'm displaying it in three columns. And one thing you'll notice is that besides the formatting, which is the same as it was before, it's still smooth and responsive even for three columns of text.
Thank you. Could we switch back to the slides? So really, at this point, this demo is just smoke and mirrors. And so I thought what I'd do is show you the code that it took to actually draw that view using CoreText. And so you can think of what needed to be done there.
Basically, we have the bounding box of the view. And in that last example, we went ahead and determined what the boxes for the three columns in that view were. And so once we have those boxes determined for each column, I'll show you the code that was needed in order to draw the text using CoreText.
Well, that's it. You'll see at the top there's a for loop for each of the columns that I'm going to be drawing. Now, at this point, I don't expect you to know what the classes are that we're dealing with here. What I do want to point out is how simple this was. Simply for each path for the box that we're drawing text into, we're going to create a frame, we're going to draw it, and if there's any more, we're going to move on to the next.
And that's all there is to it. So you can imagine for a simple box of text, all you need are those first two calls, which is fantastic because I need to set text in boxes quite frequently, and so we've tried to make it as easy as possible. So in the next few slides, I'm going to explain to you exactly what these classes are that I'm dealing with, so don't worry about that now, but again, fast and easy with CoreText.
So now I'm going to kind of give you the roadmap to the CoreText headers that you have available in your Leopard Seed. But to do so, first I just want to have one aside to talk about some terminology. And that is that since CoreText is a UniCode text layout engine, we do have to touch on UniCode terminology for this process. And that is that there's a distinction between characters and glyphs. Now, characters are the numeric input, the actual values of the characters that we're going to layout.
And glyphs, those are the graphical entities that actually are used to display the text on screen. Now, as Julio pointed out, Quartz doesn't really know text. All it knows are glyphs. And so what people need is a way to get from the text that they have, along with any style information they might have, such as font sizes or colors or whatever, and convert them into the appropriate glyphs for drawing that text on screen. And so that is the process that CoreText provides to you, is that text layout process. And so when I, if I refer to text as having been laid out, then what we are dealing with are the glyphs that are appropriate for use by Quartz.
So to start with, to give you this roadmap to find out how to dive into CoreText and make use of it, we should start by talking about a fundamental class, which is the attributed string. Now, when I say fundamental, I don't mean that CoreText implements an attributed string class, but rather it is vital to CoreText because this is the input type for all of the layout operations performed by CoreText.
So you have two choices for classes to use for an attributed string. There's CF attributed string and NS attributed string. As the names imply, they're very similar, and in fact, they're what we call toll-free bridged, which means that any time you see a function that takes one of these classes as a parameter type, then the other can be used interchangeably.
So really then it comes down to how you choose to construct these objects and/or where you may have gotten them from. But both of these objects are the same in their function, and that is to store the text and the style information that's associated with that text. And so if you look in the CoreText headers, you'll see that we provide a list of various attribute names for styles to which CoreText responds.
And so basically each of these classes, for those of you who aren't familiar with it, and I'm sure many of you have seen these before, for each range of text it has associated with it a dictionary of keys and values. And those keys are the attribute names and the values are, for instance, for a font attribute name, it would be the font object itself corresponding to that text.
So one note on bridging here. So in that example that I showed you, what I got from the RTF conversion was an NS attributed string that contained all the style information for the text I was drawing. CoreText does make available to you its list of attributes that it responds to. But of course, it would be rather a nuisance if you had an NS attributed string and you needed to convert from, say, NS font attribute name to our version, the KCT font attribute name.
And so we have taken the opportunity to bridge the most commonly used Cocoa attributes. And so for those of you who are using CoreText within a Cocoa application, this is one of those steps we've taken in order to make that integration simple and painless for you. So let's get on to what is actually in CoreText now. And the first object or class that I want to talk to you about is the frame setter.
[Transcript missing]
Well, the Framesteader internally makes use of a class referred to as the typesetter, and this is really the heart of CoreText. The typesetter is the class that is responsible for performing the layout operation itself. So once you've supplied CoreText with an attribute of string, you've given it the text and the styles that it needs in order to determine which glyphs are appropriate for drawing that text.
And so the typesetter is the class that actually does that for you. And so internally, the typesetter is the Framesteader, excuse me. The Framesteader is making use of a typesetter in order to determine the actual text to be drawn, but you also have the opportunity to use it directly, which is fantastic because, well, it comes in handy.
The typesetter has a couple of other functions besides its core function, and those other functions are to suggest line breaks and to create lines. Now, the process of suggesting a line break is simple. It says, for a given amount of space, how much text can I fit on a single line within that space? And so the typesetter is responsible for determining what the appropriate length of -- what the appropriate length for a line is given a width, and it can do so by the most common word break, word wrapping, or there's also an opportunity to get finer-grained, cluster breaks, but the default is, of course, to do simple word wrapping.
And finally, the typesetter creates lines. Now, I alluded to this when I was talking about line breaking, but what is a line? Well, a line is the object of core currency in CoreText. It is the object that holds the glyphs that are the result of the text layout process.
And so any individual line can correspond to a range of characters within an attributed string. And so you might imagine that a line can encompass the entire string that you provided it with, which could be miles long, really, or it can correspond to individual lines within a paragraph, as you've seen.
And so these line objects can be created from several different sources. As I mentioned, the frame setter, which will create lines for you, has an array of lines corresponding to the text in the box that you asked it to fill. The line can also be created from the typesetter.
So, for instance, in a line breaking example, and I've got a slide on this in just a few seconds, you'll ask a typesetter to create a line from a known range of characters. And in addition, we realize that multi-line text layout is not something that you're always going to need to do. And there are cases when you need just a simple label of text or a single line that you know.
And so in that case, there's a convenience method offered by the line for creating what we call a freestanding line. The result is the same, but the upside is that you don't need to manage creating the typesetter yourself. And so this is another way where we've tried to make your life a little bit easier.
Once you have a line, there are several operations that you can perform with that line. One of the most common operations is the process of determining the flushness of that line, which you might also know of as alignment. So given a particular line, then if I know that I'm going to draw it into a certain space, I might want it to be, say, right flush or aligned right. And so a line can give you the information that you need in order to draw it in the correct position for that flushness. A line can also create a justified or a truncated version of itself, which is great when you need to perform either of those operations.
Most commonly, though, you'll probably be asking the line for various measurements. And there are two major types of measurements that we provide from the line object using CoreText. The first is something we call the typographic bounds of the line, and the second is the image bounds of the line.
Now the typographic bounds is what you'll be using when you're dealing with lines of text. And those are the common measurements that you'll need to know how to place those lines relative to each other. And those are measurements, the ascent and descent of the line as a whole, and the width, the typographic width of that line.
So for text, those are the most appropriate measurements to be using. But there are others of you who are using text as sort of a graphical element, really. And so what you need to know is, you know, I'm going to be using this text as an object in a graphic that I'm drawing, and so what is it exactly? And so the image bounds for a given line are defined as the rectangle that -- the smallest possible rectangle that encloses all of the glyphs in that particular line. And so if you don't need to deal with the ascent and descent of the text and you want to know the actual coverage of that line, then the image bounds are for you.
And finally, the line object, like the frame object, is ready to draw. So since we designed CoreText to be an immediate image, and we're going to be using CG adjunct to Quartz, we use the same placement strategy as Quartz for text. And that is that the origin of a line of text is on the baseline of the text. And so as you would in Quartz, to figure out where to put this line, you simply set the text position in a CG context and then have the line draw itself into that context. So it's very simple.
So as I mentioned at the start of this talk, we tried to make common operations very easy to do. And so I'd like to spend a few lines now talking about those common examples I gave you and how you can actually do them with CoreText. So we're going to take a look at some code now, but I don't think it's too much.
The first of these operations is the process of drawing a simple paragraph. And this is essentially the heart of that first slide of code I gave you earlier on as part of my demo. And so in all these examples, I'm going to assume that you've already figured out some things that are important to the particular application of this process.
And so in this case, we'll have an attributed string. We'll have a path that corresponds to the box in which we're going to want to draw this text. And we have the CG context itself. So I'll assume that we've set those up. Now once we have those, we go ahead and create a framesetter object, CTFramesetterCreateWithAttributedString. And this will give you that object then that is responsible for doling out those frame objects that I talked to you about.
And so in this example, we're simply going to fill a single box with text. And so with that framesetter, we simply ask it to create us a frame. And then once we have that frame, we can draw that frame into the CG context. So I will point out that in the examples that I'm giving, you should note that I've, in addition to leaving out the initialization of those variables, I've also left out the very necessary calls to CF release. But it shouldn't be difficult to figure out where you need to put those because we followed the example. And so what I've done is left off the calls to CF release in these examples. But don't forget.
So that's all it takes for a paragraph. The other common example I gave is using just text as a label within a graphic or perhaps you know that you have a limited amount of text. And this next one is a little bit more complicated, but that's only because I actually show you how to create the attributed string itself.
So again at the top we're going to start with some things. We've got a string. This is just a plain old CFString. And we've got a font, which we're going to talk about in just a second. And we've got our CG context again. So then in order to create the attributed string, for those of you who are unfamiliar with it, what we're going to do is we're going to make the dictionary of those key value pairs that define the style attributes. And so in this case there's only one style that I'm going to apply to my string.
So in this case I know that I have a particular font that I want to draw the string from. And of course being a good programmer I got this string from a localized string. So I'm going to call this strings file or nib or however you've chosen to localize your application.
And so I'm going to apply the font to it by creating an attributed string with those attributes. And then once I've done that, now we can actually get into the core text part of things. And that is we're going to create, as I mentioned, this freestanding line, which is this convenience. So CT line create with attributed string.
Once we have that line we can use either of those measurement capabilities I talked about a couple slides back. Either the typographic bounds or the image bounds to figure out where I'm going to put this in my CG context. And once I've done so I set my text position and I draw the line. So again, very easy. This example is a bit cluttered, but that's only because of this stuff up here. But this should be old hat to most of you.
So now there's one more example that I'm going to walk you through, and that's the process of line breaking. Now, most of you, or I should say in many cases, you won't need to go through this process yourself because we've already provided the frame object for dealing with simple boxes of text.
But you may find that in your application you have more advanced line breaking needs. Maybe you have a special hyphenation mechanism or some other sort of process that you need to do when breaking lines. And so what I've done here is I'm going to replicate the process of filling a frame and show you what's necessary so that if you have one of those more advanced needs, then you can use this as a framework for doing what you need to do.
So to start with, again, we're going to have several variables that are set up as appropriate. In this case, we have the width, which is, we'll assume it's the width of the box in which we're going to set this text. And then we have our CG context, of course.
And then we have our text position. It's likely that you might not have figured this out up front. Maybe we'll figure it out later on. But again, I'll leave it as an exercise to the reader to figure out what the text position is appropriate for this particular need.
Once you've initialized those variables, then we're going to start again with the heart of the process by creating a typesetter. So the typesetter then is going to take this attributed string and it's going to do some initial processing on that to determine which glyphs are going to be used to draw that string. And so then the process of filling this box with text then is going to be simply determining what each of those lines are in that box. And we can do that by asking the typesetter to create or to suggest a line break for us.
And so in this example, I'm going to start with the very start of the text, the very first character in my attributed string. And so that's my start variable, which is the offset into the string itself. And I've got a width. I know the width of the box that I'm trying to fill. At this point, I'm trying to figure out what the first line is. And so I'm going to ask the typesetter to suggest a line break for me.
And what the typesetter is going to do is it's going to return to us the number of characters that are appropriate for fitting in that -- on that first line of text. And so this is taking into account things like hard breaks. And in this case, suggest line break, of course, is the default method of word wrapping.
So it will give you the number of characters. It'll work out just great. And then once you've gotten that information, then you can use the starting offset, which is zero for our first line, and then the number of characters in that first line to actually create the line.
And so this is a pretty simple process at this point. And so we've determined -- we've determined what the text is going to fit on the first line of this particular paragraph is. Now, I've decided to spice things up just a little bit. And I'm going to draw this first line centered.
Okay, slow down. Okay. So we're going to determine the flushness of this line. In CoreText, we indicate flushness or alignment, as I mentioned, as a floating point value between 0 being left flush and 1 being right flush. And so in this case, of course, 0.5 seems just right for centered text.
And so what I'm going to do is I'm going to ask the line to tell me where I need to move, how much I need to move my pen position by, basically, in order to draw it perfectly centered within my known width. And so once I have that offset, then I can go ahead and update my text position in my CG context and then draw my line.
So, one thing I want to point out is that once I've asked the typesetter to suggest a line break, this is a point in the process where you have some flexibility. So, for instance, if you were dealing in a situation where you needed to prohibit a line break between certain words in your text, then that's the point where you don't necessarily have to take the suggestion right off the bat.
You could confer with your own information and adjust that as necessary for your needs and then go ahead and create the line based on the suggestion but also some additional input. So, you have a lot of flexibility in this process. So, at this point, we've drawn our first line.
In this case, we've drawn it centered. And so, in order to move on and find out what the next line in the box we're going to be drawing, we simply increment the starting position by the number of characters that we had in our first line. And then we'll go back up and ask the typesetter to suggest a line break for the next line in the box.
And so, that is, in a nutshell, the process of line breaking text with CoreText. Now, again, this is going to be very helpful to those of you who have specific needs when dealing with line breaking. But in many cases, we expect that the framesetter will be all that's necessary in order to deal with multiple lines of text. So, you have, in addition to being very easy, you have a lot of flexibility in this process.
So now there's one more class I'm going to talk to you about today. And this is another instance where many of you, or for many of your needs, you won't need to make use of this last class because we think that in general dealing with lines and dealing with frames are much easier than dealing with the nitty-gritty details of the actual glyphs when dealing with UniCode layout. But there are those of you who do need access to that information.
Perhaps you're going to perform some graphical operation, some sort of, you know, mask or fill or whatever on particular glyphs. Basically, you have need to read the information about the glyphs themselves from the layout process. And so for those of you, we make available to you individual glyph runs.
So the glyph run is the smallest possible storage container for a run of glyphs. And so it contains the actual glyphs, their positions. And for each run, you also have access to a dictionary of the style attributes that pertain to the glyphs in that run. Now, each run is the result of having grouped glyphs by their attributes.
And so in addition to those style attributes that you can get from the dictionary for each run that is uniform for all the glyphs in that run, there are also occasionally attributes that are not. And so you can see that the glyphs are actually grouped by their attributes that are intrinsic to the text itself.
So for instance, if you're dealing with right-to-left text, of course, the glyphs for a range of right-to-left text need to be swapped in turn. And so we may have made a particular grouping of glyphs based on directionality, for instance. So there are various reasons why that might happen. But you can be assured that the attributes for each run correspond equally to all of the glyphs in that particular run.
As with the line, you can use the run to obtain measurements. Again, we offer both the typographic bounds, which is ascent, descent, and also the image bounds if you need to make use of that.
[Transcript missing]
Those are the basic objects that we expect you to be dealing with in CoreText. And there are a number of other functionality groupings available to you in CoreText. And so I haven't given you an overview of every class, but like Core Foundation, we've organized things by class in our headers.
And so you know where to look now to find out how to get started. But before you do so, I do want to leave you some parting thoughts here for this section of the talk. And The first of these points that I want to mention is that we hope that you'll choose the right tool for the job.
Now, in general, this means that you can take advantage of these higher-level classes, in particular the frame setter, for instance, if you want to deal with paragraphs. And that way it's less hassle to you because it's much less code than delving down into the lower levels and so much less opportunity for mistakes. And I know I make a lot, so I try to go as high as possible.
In addition, if you're using a higher-level class in CoreText, it does take any opportunity it can to optimize operations based on knowledge that it has. And so in some cases, asking a frame setter to do something might be slightly faster than doing it yourself, but we're just trying to help.
But once you have picked which class you're going to deal with, as I mentioned, Framesetter for paragraphs, Typesetter if you want to do things on individual lines, and Align if you're just dealing with a known amount of text, we encourage you to reuse those objects. We expect that you've become used to reusing your core foundation objects, and that's one of the reasons we've made it easy to reuse CoreText objects as well. But of course, anytime you create an object, there's a certain amount of work associated with that.
And so, so long as you know that you can make use of that same work over and over again, you can go ahead and retain that or stick it in a CF collection class if you need to. But hang on to it for as long as you can make use of it, and then when you're done with it, CF release it, and it'll be great.
There is sort of one bugaboo that I have to talk about, and that is that you may find yourself using CoreText within a larger application framework. And in particular, you may find yourself drawing into a CG context that isn't one that you've created. So it's kind of like the dollar you found on the street. It spends like real money, but you're not sure exactly where it's been.
And so the one thing I want to suggest to you is that unless you've created the context yourself, or unless you know that everyone in your application is coordinating the use of this context, it's a shared resource, and so you should be defensive when using it. And so in particular, if you set the text matrix properly, in general that corresponds to the identity transform for the text matrix, then you're assured that the measurements that you took from CoreText are going to correspond to what actually gets drawn by Quartz in the end. So it's very easy to do. It's one call. But it's just good defensive programming.
And finally, if you are making use of the glyph run, individual run objects when dealing with glyphs, don't assume that there's a one-to-one mapping of characters to glyphs. I know this is preaching to the choir for many of you, but the result is not always a one-to-one mapping.
Even in English text, for instance, when dealing with ligatures, we have a case where there's not always exactly one glyph for each character. And so when dealing with a run, you should just be careful when you're dealing with it to ensure that you're not assuming that there is that mapping, and things will get along just great.
So with this overview of CoreText, I think you have the bulk of the knowledge that you need in order to go off and try using it in your applications. And in fact, we encourage you to just give it a try. See if you like it. Maybe there's a problem that you have a bunch of code for and you have some extra time in your schedule, and you can try it out with CoreText and see if things are easier or faster for you.
And so we encourage you to do so. We've tried to make something for you that is fun to use, frankly, because it's so easy and so fast. We think you'll have the opportunity to be a lot more playful in what you do with text in your application. And we've had fun making it, and we certainly hope that you enjoy taking a look at it. And now that we've made it public to you, we encourage your feedback, and we'd like to know what else you need from us in order to make a great application.
So the astute among you may have noticed that there was one hole I left in the application. And that was where to get that font that I stuck in my attributed string earlier on. And so in order to talk about the font objects available in CoreText and the unified font architecture in CoreText, I'd like to invite Julio back up on stage. Thank you.
Thank you, Ned. That was great. Hopefully you can see why we're so excited about CoreText layout and especially how simple it is to use. So before I talk to you about CT font, I want to give you a little rundown of all the font references that we have in Mac OS X.
Yeah, a little overwhelming, isn't it? But anyhow, it isn't that bad. At the lower level, we have ATS font, and that is your primitive font reference to access any font data. Then on top of that, we have CG font, which is you need one of these references if you need to draw text. It's a primitive font reference. Typically, it encapsulates an ATS font, and new in Leopard, you can also deal with PDF fonts as well.
On this side of the screen, I have what I call the classic references, references that predate Mac OS X, the FM font and the font family. An FM font is, you can think of it pretty much the same as an ATS font reference, whereas the FM font family is a collection of these fonts as defined by the FOND resource.
Now, we have an ATS font family, which is also, think of it the same as an FM font family. I want to point this out here at this point because being that Quickdraw is not available in 64-bit, and architecture allows both 64-bit and 32-bit applications to be running at once. So, it is very likely that you're going to, if you're a 64-bit app, that you're going to be dealing with a 32-bit app that has the old font references.
So, I said Quickdraw is dead for 64-bit, and it is, but there's a few APIs that still live. And those APIs are those that I'll talk about in a minute. Julio González, Ned Holbrook On this side of the screen, I have what I call the classic references, references that predate Mac OS X, the FM font family. An FM font family, which is also, think of it the same as an ATS font family.
And those APIs are those that allow you to convert from the old FM font references to the new font references, or to the ATS font references, so that you can still operate with them. Finally, there is the ATSU font reference, and the ATSU font reference is essentially the same as an FM font. They can be used interchangeably.
On this side of the screen we have the new font references native to Mac OS X. So you see the NS font and then the NS font descriptor and sitting right below it you see the CT font and CD font descriptor. I'm not going to get too much into it right now. I will talk to them as I talk about CT font.
One of the features of these font references, when you compare them with the old font references, is that they do encapsulate a lot of information. They're not just in the old font references, they're just basically the same as the primitive font references. So the new font reference, you get a lot of that comes along with them. And there are some things that you need to be aware of as you use them. So let's go into CoreText font.
As I said at the beginning of the talk, one of the feedback that we received from you is that font references were complex, you needed something more unified. So that's what we set out to do with CoreText font. We took a look at the old FM font APIs, the ATS font APIs, what Cocoa had to offer, but also we looked internally at what we did for our layout engines and took into consideration feedback that we received from you. And we created a unified API for you to use, an API where you can feel that everything that you need to do for text drawing, you can stick within this API. No need to go from one API to the other.
And the same with the font types. If you have a CT font, there should be no reason for you to make a call to get an ATS font reference or an FM font reference to get your job done. Everything that you need to do, you should be able to do with CT font.
And finally, one of the great things about CoreText font is that it's a very easy to use font. One of the goals that I set at the beginning of the talk was to help our Carvin developers move away from Quickdraw into the world of Mac OS X or the way things have looked in Mac OS X. All with the old way of looking at font families and the way Quickdraw looks at fonts.
And now you get the same view, they can get the same view as the Cocoa developers have in the past. So as I talked about CoreText font, you will notice all the similarities with the font Cocoa API. So to start with, we have the font reference, CT font reference.
That, you can think of a font reference as a very specific font instance, much more so than an ATS font ref, which is a pointer into a, or a reference to a face or a strike in the font. With this one, you can specify more things, such as a point size.
If you're dealing with fonts that, multiple master fonts that have variation information, you can specify more things. You can specify that information into the font reference. Also, if you want to create a font reference for a particular font that has a font feature, you can also do so. So it is quite specific. Think of it as the reference that you will use to draw text or to find very specific information about the text at that point size.
So there are many or we have many ways to create this font references. Here's just a sample. There's more than these. At the top, CT font create with font descriptor is the preferred way to create font references. As I haven't talked about font descriptors, I'll talk about them in a little bit and we'll see why they're the preferred way. Now, the next three APIs that I have for you are what I call conversion APIs.
It is inevitable that as you deal with all the layers in the system, you're going to be handed font references that are not CT font or an NS font reference. So, for example, if you get a Post-Crit name, you can use CT font create with name. If you're given an ATS font ref, you can call CT font create with platform font.
Or if you're dealing with an old Carbon API, you likely will be provided with a Quickdraw font reference of some sort. Be it the Quickdraw font family name or a font family and a style. So you can create a font reference using those as well. But the last API is a sample of an API that I want to point out. It gives added value to the font API, the new font API. CT font create UI font for language.
With this API, when you call it, you will get a font right away that has the UI. The UI font for the application you're using and the localization that you're using. This is something that in the past, I know Carbon developers couldn't do in any of the font APIs. They would have to query HIToolbox to get what the appropriate font was. So now you can see a sample of an API that's really putting everything into one spot. So you don't have to go to different layers to get things done.
Also, CT font provides a brand new set of functionality. As I told you before, we looked at what we do internally for our layout engines and things that you had asked us to make available to you. These APIs, we have a set of APIs such as APIs for getting line metrics, getting encoding information, language information, glyph information, character to glyph mapping. All these APIs were, a lot of them were private or not available in any of the other API sets. And what these APIs are, you can think of them as font abstractions.
In the past, what you would have had to do was go to the low-level access APIs, query the font tables, or even worse, if you know how to deal with PostScript fonts, you would go dig through the PostScript font to find the information that you had. So now we have a nice set of APIs that give you all this functionality.
Now, another nice feature of CT font is its font cascading mechanism. For those of you that know Atsui, think of font cascading as font substitution. It is basically a better font substitution mechanism. It is more trade-aware. What do we mean by that? It pays a lot more attention to font characteristics. At the bottom, we have a sample here of what Atsui and CoreText do. You see the first line, both Atsui and CoreText do the same font substitution. For the Japanese text.
Whereas in the second one, I don't know if you can see the difference, but CoreText picks a Japanese font that matches the Serfs in the base Roman font. And Atsui doesn't do that. In the last line, we have a sample where Atsui does not know that the base Roman font is not bold and CoreText does.
So it tries a bit harder than Atsui to figure out what the right font to substitute is. The nice thing about this is that you get this for free. As long as you use one of the APIs that Ned talked about, the CT line draw or the CT frame draw, you'll get this functionality for free.
You have an attributed string that is just basically a bunch of Unicodes, it'll do the right thing for you. It'll do the substitution. Now, for those of you that need to go deeper down, as Ned said, there's the glyph run functionality in CoreText font layout. If you need to go at that level, you probably will need to get the same algorithm for font substitution that we use for CT line draw and CT frame draw. So for those, you can use CT font create for string and you will get the same functionality.
Next, the font descriptors. So now you can start to see the parallels that we have with the Cocoa APIs. Font descriptors are great. This is something that Carbon developers did not have in their arsenal at all. You can think of them as a query into the font system. And this query, you can have, it's basically a dictionary of attributes. And here's a sample of some of the attributes you can pass to create a font descriptor.
So it can be very specific or very loose. So some of the samples of the attributes that you can pass are postscript name, family name, style name, such as bold, or traits. So you can say, give me a font descriptor of all the monospace fonts in the system. It will do that for you.
Now, one thing I want to point out, if you notice when we were talking about the font references, in the modern references, we don't have a family reference. Well, there is no need for a family reference in Mac OS X, in the CT font world. You have a font descriptor.
You can basically create a font descriptor that says, "Give me a font descriptor that matches a family name with this name." And boom, you have your family built for you. Lastly, there is persistent storage with font descriptors, and I will talk to you about some sample code. Here is some sample code on how to go about creating a font descriptor.
First, you assume that we have a font name, a CFString with a font name, but likely a PostScript name, and a size. You call ctfontDescriptor, create with name and size, and you're given a font descriptor. In order to obtain a font reference, the next thing you do is you just call ctfontCreate with font descriptor, and there's two optional parameters that you can pass here. I decided to pass this point size and a matrix.
The point size, I passed zero because when I created the font descriptor, I already specified a size, so I choose to ignore it. And then I also choose to ignore the matrix. I'm not going to touch that. And I did include a CF release, which Ned didn't include in his slides. I did have some more space. And here's where we go about creating or storing font references.
Remember, I told you that the preferred way to create a... font descriptors or font references was using font descriptors. And the reason for that is because we also recommend that you use font descriptors as persistent storage. So let's assume that you're working with a document and you were using a particular font reference, one of your fonts. So you want to now save out your document out to disk. How do you go about storing that font reference? Well, the first thing you do is you obtain a descriptor for that reference. So you call ctfontDescriptor. You get the descriptor.
The next, you call ctfontdescriptor copy attributes. What this does for you, it gives you a dictionary of attributes, and it's the minimal set of attributes in this dictionary that it takes to create this font descriptor. So that's what you go ahead... You can then go ahead, serialize, and put in your document.
Finally, in the CT font API, we have font collections, much like in the Cocoa APIs. You can think of font collections as just unions or font descriptors. So you can create a font collection for all your bold fonts, all your italic fonts, whatever. Font collections fit into the realm more of font management.
So it's not a lot of use to most of you folks here, but font collections do offer something that you all need in your applications, and that is enumeration. There is an API in font collections to obtain all the fonts that are available in the system. And as part of that, you can specify that you don't want to have any duplicates returned to you in this font collection.
Also, a neat feature of this is that when you extract the font descriptor from the font collection, you can get a callback or pass a callback that lets you sort the way the descriptors are returned to you. So, it gives you a lot of flexibility. You can get it whatever way you want. If you want to have a list of font families, you can have your sorting algorithm that lists them by font family.
So, as you can see, the new CT font API parallels a lot what is in Cocoa, and that is very intentional. We wanted to give Carbon developers the same access that the Cocoa developers had in their APIs. We did take a look at what was in the old APIs, what was in new APIs, took a look at what was best from those APIs, and gave you a little extra in those private APIs that we had been using in the past that were not available. available to you. Before I go, I want to, just like Ned did, I want to give you some pointers about CT fonts.
Like I said, create fonts from font descriptors. The reason is once you create a font descriptor, think of the font descriptor as the result of a query. So you've done some work already and it is quite fast to go from a font descriptor to a font. And then the other thing is, like I said, the preferred way to store your font reference is using font descriptors.
So go ahead, extract the attributes out of the font descriptor and serialize those out to this. Finally, like Ned mentioned with the framesetters and the typesetter objects, with the CT font reference, it's something that you want to hang on to. Like I said, these references encapsulate a lot of information.
For sure you can imagine there is a CG font ref. There is a lot of information that it carries around so you never have to go to another API set or API type to get what you need done. So hold on to it. It's good for performance. And once you're done, yeah, go ahead and release it.
Finally, use our font cascading mechanism. I say this for two reasons. One, font substitution is really expensive to do, and we've taken great -- gone through great lengths to optimize it so that it performs well with a small number of fonts that we have and with large amount of fonts installed.
But more importantly, we want to provide a consistent experience for our users. Now, you can tell me at this point, "Well, you didn't do that when you showed me the Atsui example." Well, that's going to change. It is certainly our goal for Leopard that every layer in the system will have or experience the same font substitution. And finally, something that we've said in previous WWDCs, and that is rely on our font APIs.
This goes for both the CT font or any other APIs that we have. And I say this again because it is expensive to go looking through font data, and it might seem fine to you when you try to do something on your own, and you're working with the standard set of fonts that would ship with the system. But the moment somebody drops two, three, four, 8,000 fonts into the system, it's going to look very ugly.
So, just want to point out some more information here. Two things from this slide that I want to point out. We've created a new mailing list, CoreText Dev. I checked two hours ago and it's still not up and running, but it should be up and running fairly soon. And it's a great place for you to give us feedback regarding CoreText. Next is documentation. We have no online documentation of CoreText yet. It's been written as we speak.
However, the headers are very well documented. All the APIs, all the parameters, all the attributes that you need to use are very well documented. And also, they're very well organized. All the classes, if you will, that we had, the typesetter, the framesetter, CT font, the font reference, it's all in its own header. So, it should be fairly easy for you to go dig through information there.
Labs, following this session at noon, we will have a Cortex lab. So if you want to get your hands dirty, you want to talk more about it, we'll be there to assist you. Following the Cortex lab, there will be the AppKit lab. And if you have questions regarding Tex and Cocoa, please attend that. There will be somebody there to help you. Tomorrow, we have the Tex Internationalization Lab. So if you have questions regarding Unicode, input methods, localization of some sort, If you have further questions about Cortex, some of us will be there to assist you as well.
Finally, I want to drive some points before you leave. was developed with a lot of Carbon developers in mind. We really wanted to get you out of using Quickdraw, but we knew that you didn't have a really good avenue to do so. We feel that with Cortex we have done so.
So it's really, really time. You know, we've been telling you for the past few years to get rid of Quickdraw. I mean, now you've seen the right end of the wall. It's not even in 64-bit. There'll be less and less support for Quickdraw as time goes along. So go take a serious look at Cortex if you're a Carbon developer. Next.
CoreText is the low-level framework. And if you remember the first slides that I had on text architecture, we had the base frameworks, ATS and Quartz. And on top, we had Cocoa and Carbon. CoreText is sitting right there in the middle. All our text system depends on it. Performance for our text system depends on it.
We've done a great job creating a framework that is really, really good, works well, and performs really well. So if you are thinking about moving your code to CoreText, we're pretty confident that we can accomplish the same things for your code as well. Finally, CoreText is brand new.
It's not set in stone. Go ahead and take a look at the APIs. Give us some feedback. When we designed CoreText, yes, we had performance in mind. We had Carbon developers in mind. And internally, we had internal needs that we needed to fulfill. So we built the API based on all that information, but we know that we're going to miss stuff. We need to hear from you. There's stuff missing? Let us know. There's stuff that quite doesn't work for you? Let us know. Make use of that list as soon as it's up. Or talk to your developer relations person and get that information for us. We're all ears.