Mac OS X Essentials • 1:04:10
Learn how quickly and easily you can render text on Leopard using the Core Text Framework. Walk through the complete development of a fully functional Carbon application using Core Text and the Font Panel, and find out how to access Core Text from an NSTextView in a Cocoa application. Bring your laptop.
Speakers: Ned Holbrook, Nathan Taylor
Unlisted on Apple Developer site
Transcript
This transcript has potential transcription errors. We are working on an improved version.
My name is Ned Holbrook and I'm a software engineer working on text layout at Apple, so let's get started. So this morning we are talking about several things. We decided to plan this session with sort of a variety of the groups in mind. So, if you're coming to the Mac OS X platform from say Windows, then you'll see that we've interspersed some sort of signposts throughout so you can kind of get your bearings with Core Text relative to what you've been working with on Windows.
Also, if you have come to our Core Text session last year, then you'll -- we've tried to beef things up a bit by showing you a bit more about how Core Text interacts in a real world application with other frameworks and so that's kind of what we're going to be doing this morning. I would start off by saying that, if you haven't done so already, I highly recommend taking a look at our Core Text session from last year.
We're going to start off with an overview of Core Text but it's nowhere near as detailed as the presentation we gave at WWDC06 but those slides and that talk are available online so, if you haven't done so, go ahead and look that up at some point and you'll see a very detailed overview of Core Text in Leopard.
But we're going to start off by giving you a quick overview so we can remind ourselves of some of the terms that we're going to be using. Then we're going to talk more generally about text and fonts on Mac OS X and best practices for dealing with those.
We're going to spend particular attention with drawing, inputting and some minutia about saving text and font information in documents and, of course, this is a hands on session so, if you haven't done so already, please go ahead and access the session website for Session 127 and there's a download called Core Text Arc Hands On and we are actually going to be walking through that sample application as we go.
So -- so if -- if you are looking for something else, I'll point out that this afternoon there's a Cocoa text session that goes into some amount of detail in extending the Cocoa text system and in particular if you're Mac OS X application programmer who's dealt with QuickDraw or ATSUI and are interested in how you might convert Core Text in your application, then there's also a session this afternoon following the Cocoa session about making the switch, but I'll assume that we have all of our hands on projects ready and let's go ahead and get started by taking a look at the application that we're going to be working with and so to do so I'd like to bring Nathan Taylor on stage, Nathan.
All right. So this is where we're going to start. I'm going to show you a quick look at this application that we've put together for the hands on. If you've downloaded the hands on material you'll see that I have it broken down into six steps, and I'm just going to give you a start by letting you see what the finished product looks like here.
So let's run this guy and as you can see we have an application and we're using Core Text to draw text along an arc in the application, you know, the drawing and the rotations are all graphical operations and you'll see that when you look at the code. This is also a live application with all the niceties you'd expect for the user interaction, so we have live update of the text.
If I type here, we update live. I have interactions with font control, so I have these bold and italic check boxes and you notice we update them based on the status of the font, and I also have integration with the font panel so the user can change the font and even support multiple Windows with live updates of fonts and, if we have time, I'll show you these options that I have hooked up here at the end that use some of the Core Text details to, you know, detect when we substitute fonts, you know, draw glyph metrics and draw line metrics. With that I hope you've downloaded the demonstration and I'll hand the session back over to Ned to give you the Core Text overview.
Great. So like I mentioned, we're going to start off by giving a quick rundown of the talk we gave at our session last year. So this is one of the slides that I pulled up, this is kind of the tech marketing speak about Core Text but what I wanted to do instead of going through these points again because you have access to those slides, is to go ahead and just -- just refresh your mind as to what Core Text actually is.
For Windows programers in the audience you can think of Core Text as being similar to Uniscribe and that is what it does is Unicode Text Layout and some font management. So Unicode Text Layout essentially means that in Unicode rather than having a one-to-one mapping from characters to glyphs, we have a many-to-many relationship and so the process of Unicode Text Layout then is the process of converting from characters, the numeric representation of text, into positioned glyphs. Glyphs, of course, being the graphical atoms in a font that are used to render text and so when we talk about text layout it's that process of conversion from text and some style information to glyphs and their positions relative to one another.
And Core Text also includes a great set of utilities for working with fonts. So let's go ahead and talk about very briefly the major classes that you're going to deal with in Core Text. The highest level one and the one that for many tasks is sufficient is the Framesetter and the Framesetter is responsible for filling frames with text.
So essentially you have a box and you want to put some text in it. Usually this could be multiple paragraphs of text. You could have multiple styles, anything you want. You can even flow text from one frame to the next, the frame being the object that is created to hold these text boxes and so in order to do so the Framesetter makes use of a class called the Typesetter. Now primarily the Typesetter is responsible for the actual text layout process, that conversion to glyphs I was talking about, but another crucial feature that it offers and one that's essential for something like the Framesetter or yourself is determining where line breaks are in text.
So you can think of text as having been laid out into a giant line of text and for each line that you're going to draw you're going to see how much text is going to fit in a certain width and you're going to then use the Typesetter after it's suggested a line break to you to create a line, and a line is pretty much self-explanatory it's a line of text.
It can be of multiple styles and directions of text and the line is comprised of a series of glyph runs and each one of these glyph runs contains those positioned glyphs grouped by style. So when a run is drawn either by yourself or by Core Text, you can be assured that the graphic style that you're going to use in order to render those glyphs is the same for every glyph in a particular run.
So now that we have our overview of Core Text in hand, a good refresh from last year, let's jump straight to drawing. Now the reason I want to do this is because Core Text, of course, is tied intimately to Quartz which is the graphics rendering mechanism for Mac OS X. It's used pervasively and so I thought especially for people who were coming from a Window perspective we might as well just give a quick overview of how to think about text with regards to Quartz.
So the major differences from other graphic systems are, of course, primarily coordinate spaces. Quartz offers freely transformable coordinate spaces which is a great convenience to have since it's a resolution independent graphics mechanism and so you can think of those as being GDI mapping modes in that you have your pick of, you know, the mapping from your coordinate system to the actual display.
Now there's two common orientations that we use on Mac OS X, we call them normal and flipped. Normal, of course, is positive "y" going up and flipped is positive "y" going down. So, if you've dealt with other raster graphic packages such as GDI on Windows or QuickDraw on the Mac you may think that the flipped coordinate system is the most natural but in Quartz we tend to use the normal coordinate system which is the default coordinate system unlike the flipped which corresponds roughly to MM text used by GDI but keep in mind that the coordinate spaces are remappable; you have your choice.
So we tend to think of things in normal but you can do whatever you want but just keep this in mind because you may run into situations where things go the other way and, of course, within that same frame of measurement all of your measurements are floating point all the time.
This corresponds quite closely to GDI+ on Windows which also uses real coordinates for measurement. This is incredibly handy when you're dealing with resolution independence you can, of course, go straight to PDF or print or whatever and you have the same coordinate system that you are dealing with, but I will just point out that being floating point there are some bugaboos that may apply that don't necessarily apply when dealing with integer map on a computer so keep that in mind.
And then I also wanted to give a quick overview of the line and glyph metrics that we tend to use in Core Text and Quartz and also the Cocoa text system and other parts of the system. You'll see in this diagram that we have on the slide here there are two different types of metrics; we have line metrics and glyph metrics. The line metrics are indicated in that orangish yellow color and the glyph metrics are in the white color.
And the -- the -- along the left side of the diagram here these are the primary line measurements that we talk about when dealing with individual lines of text, the ascent, the descent and the baseline. The baseline typically doesn't get a measurement of its own but the relationship between the baseline and the ascent, of course, is the ascent measurement and then the difference between the baseline and the descent is the descent value. When you're dealing with multiple lines of text and trying to figure out how they are placed relative to one another other, you quite frequently encounter the leading as well that is in the lower right and that's an additional amount of space that's added typically between lines.
Then common glyph metrics we refer to the advance width -- I say the advance width although sometimes we do shorten it to advance, but I say advance width here because technically advance is a two dimensional measurement in that you could have a shift vertically between adjacent glyphs but this advance width is the distance you would conceptually move to get from one glyph to the next in a sequence. And then along the right we have a couple other information data points, cap height and x height, these are less frequently used but especially in a graphics context when you might be looking at doing some sort of vertical positioning of text these can come in handy.
The reason I've separated them into line and glyph metrics all of the line metrics correspond to individual fonts but in Core Text we group those so that, when you request those measurements from a line, we give you an aggregate value so it's appropriate for all of the different fonts in the line and all of the rest of the information, of course, can be gotten from fonts directly and typically don't correspond to a line as a whole. And so with that admittedly brief overview, let's go ahead and actually start drawing some text so, Nathan.
Thanks, Ned. So what we have here is the start of the Core Text Arc application and I'm starting with a Carbon application template from Xcode and we're going to add things to it. As you see I already have some stubs for some implementation files and headers and I also have a nib laid out at the beginning of step one.
So let's take a look at this nib, it defines our user interface and as you can see this is the basic layout of the window. We've got the HIView subclass that's going to do our ArcView drawing and we have the check boxes and text field for user controls. So to implement this we need to implement the ArcView subclasses and HIView subclass and so I'm going to open up ArcView.h here and declare some things.
So let me drop in the declarations we need. Now this is the basic HIView subclassing stuff. We have our class registration and everything else. What we're going to want to pay key attention to, when I implement it, is the ArcViewDraw method here. This is what's going to do the drawing and this is where we're going to use Core Text heavily. So we save that. I'm going to switch to the implementation file with the counterpart button here and drop in our initial implementation. All right.
Now look at the implementation and let's take a look at some of the functions we have here. Basically, we have ArcViewCreate which is the constructor for our HIView subclass and this is just setting up a structure that we have defined and of key interest is, you know, we need a default font to get started so ArcViewCreateDefaultFont is our first call we make to Core Text in here. We use CTFont, create with name give him the default name and size and this will create a font that we'll use for our initial rendering.
Below that we see ArcViewCreateWithString or CreateAttributedString, sorry let me jump to that here and this is what's setting up the string that we're going to draw and what we're doing is we're going to build up an AttributedString from a default string and we're going to add our font to it. Those of you that are a student and are looking at the code notice we have two attributes that we're using here.
We also have the ligature attribute and we're going to intentionally going to specify that to be zero to disable all ligature processing because drawing text on an arc with ligatures isn't going to flow very well, so this is one of the tricks you might want to take out from this talk.
So we're going to create a dictionary with our attributes and then given our string, we'll use CFAttributedStringCreate with our string from the ArcView and these attributes and now that we have our AttributedString let's go to where we do our drawing. I'll scroll this up a little bit so you can see.
So now we'll putting this drawing together like you would in any application. We're going to get someone to draw first and then we'll worry about making into an arc. So the first thing we need to do is we're going to set-up our context and as Ned mentioned we have flipped and normal transforms on our context.
HIView deals in the flipped context and for our purposes here we want a normal context, so I'm going to translate and scale the CTM appropriately so we can draw and then I'm going to use our method to create our AttributedString and then the next we'll use CTLineCreateWithAttributedString with our AttributedString to create a line object.
Now for this first step that's all we're going to need to draw is our CTLine, as you'll see we'll call CTLineDraw after setting the text position. It's important to note that Core Text will always draw the line at the text position and as Ned may have mentioned that corresponds to the baseline of the font metrics line metrics.
So before we can actually get this to run, we need to do one more thing and that's to actually register the class, so let's drop in this one line to register our subclass here before we do anything else very imperative and we will build this and go and there we go we're drawing our default text with the default font Dito we specified and it doesn't do much else yet.
So let's now take the next step, step two if you're following along and get this thing drawing along the arc and all we're really going to do for this one is open up ArcView.c and we're going to replace the implementation for ArcViewDDraw with an updated one -- and I love how this jumps around -- and there we go we'll save that off and I'll take a quick look at it and walk you through again.
So pretty much what we're doing here is the same. We still create the line the way we did, but now we have two nested four loops if you'll notice. The first one is going to walk the runs in the line so that there is CT run so, if there are multiple runs as you saw in the fully featured app we had two different fonts split up into three runs, so this will iterate the runs and then as we go through the runs we're going to iterate the glyphs and create a glyph range. We're going to draw glyphs one glyph at a time applying a graphical transform to do the rotation of the glyphs, get it to draw on our arc and then we'll just call CTRunDraw with the single glyph, glyph range to draw in our context here.
So, if we build and run this now, we should draw on our arc and so for now the app is drawing how we want it to do but now we need to get feedback from the user and with that I'm going to let Ned tell you how to do text input. Switch to slides, please.
- Thanks, Nathan. Things are really starting to perk up here. So, Nathan brought up a good point and I just want to touch on that before I move on to my next slide. He mentioned that the origin of text is on the baseline and that's a very good point and one that I forgot to mention in my previous slide about metrics. That's a common convention using the baseline as the origin for both Core Text and Quartz, of course, Cocoa which uses -
- makes use of both technologies internally often refers to the rectangle of a line of text or fragment of text and so in that case they would actually place the origin at the ascent of the line of text but otherwise metrics should be consistent throughout the system. So that was just the one point I wanted to make.
So -- so you've got something drawing now, but you probably noticed it was pretty obvious -- he spelled default text out there on the screen that we don't really have it hooked up to anything yet. We're going to need to get some text in and so there's two types of text we don't want to deal with, first of all we're going to want to replace that default string with something that's a bit more localizable that is we're probably going to want to import this fantastic application to other markets throughout the world or whatever and so we might want to be able to specify something for our different markets something in Japanese, something in German whatever, and we're also going to want to eventually take a user input and, of course, for both these tasks we're going to need a quick overview of strings and so it goes without saying, but I'll say it anyway Mac OS X uses Unicode.
This is a far cry from having to worry about the A and W variance of Windows where you're dealing with code pages in the A and Unicode in the W versions. There used to be a long, long time ago pre Mac OS X the requirement of using the Script Manager which is kind of a code page system of its own.
Code pages are incredibly unwieldy, they're completely impractical for dealing with real text, Japanese for instance couldn't really make use of a script for modern Japanese and so the Script Manager is fairly, pretty, completely deprecated and so which is great because we have Unicode to solve all of our needs for us. So it does make things a lot simpler especially when you keep in mind that we have a handful of system wide classes available to you for dealing with Unicode strings. There is, of course, CFString and NSString.
You'll note on the slide I have that double headed arrow there between the two that's my way of indicating that these classes are toll-free bridged so CFString, of course, lives in Core Foundation which is a "C" procedural API for this functionality but there's also the NSString class in Foundation which is a different syntax for using strings but both of these classes can be used interchangeably with the methods or functions on the other, so it's largely a syntactic difference but all of this functionality is available to you both at a very low level and at a more convenient level and then built on top of those string classes are the AttributedStrings and so Nathan already made use of one of these in our sample code and you can think of an AttributedString as being the array of characters in your string and then parallel to that we have an array of attributes that specifies the style corresponding to a particular characters in that string and so in Nathan's example he set a font and another attribute for disabling ligatures but there are other attributes available to you both through Core Text and a wide variety available to you through Cocoa.
So these strings -- these AttributedStrings are the primary input to Core Text as they carry both the text and the style that we're required to render it. The CF and NSString classes also offer a wide variety of utilities including converting to and from other well known encodings, so I encourage you to make use of these extensively as does Mac OS X.
So the first type of string I mentioned is that localizable text and so there's two different primary types of localizable text, the first most common one that you would think about and one that we have been using throughout already is in resources either attached to view or control of some sort and so, when Nathan had check boxes and everything, that text was actually stored in a nib file which, of course, makes is easy to localize so for text that is localizable text that is attached to a view in your user interface definition then a nib file is a preferred mechanism for keeping track of that.
Also we have strings resource files this is appropriate when you're dealing with text-only resources that is not necessarily attached to a particular view but this is something that we're going to make use of in the very next demo. In order to show you how we can use some text that we haven't created in Interface Builder itself in a nib we're going to want to be able to use in our application and localize at some future point.
So but the most exciting type of text input, of course, is going to be the text that you get from the user and so there's a wide variety of functionality that's offered by system-defined control and view classes the primary one being, of course, Unicode. You don't have to worry about, you know, what language or whatever your user is interacting with in order to get text in through one of these controls or views into your application.
In particular the system defined classes and views offer support for input methods. This is incredibly useful as there are many languages used on Mac OS X that the keyboard isn't really appropriate by itself. You might want to have some graphical or typing base mechanism for constructing the text that you're going to input in a view and the system defined classes take care of the input method integration for you which is great.
And finally the system defined classes also offer user interface consistency. We're very concerned about consistency of usability throughout the system and so by using one of these system classes, of course, you're guaranteed that the most appropriate mechanisms are used for interacting with text the most appropriate display and paradigms. So you should definitely make one of these classes wherever possible not just because it makes your life a whole lot easier but also because it's what your users expect.
So there's a variety of classes and views that are available, the most common ones I've indicated on the slide are in Carbon, HITextView and MLTE, the later being appropriate for large amounts of text such as in a Text Editor of some sort and HITextView offering either static or that's a static text or text controls and then there's also Cocoa which offers its own set of views, the most commonly used are NSTextField for user interface interaction at the control level and NSTextView which is appropriate for large amounts of text.
So we encourage you to make use of these, of course, we're going to not be able to use one of these for drawing our text, but we're certainly going to make use of one of these for getting the text from the user and that's exactly what Nathan is going to do over on the demo machine right now.
Thank you.
All right. So this is where we left off, we are basically drawing static text on an arc and that's great but this is an application that the user is going to run so I'm going to show you in this next step -- step three if you're following along how we're going to get default text in and then live text input and for the sake of having some code to demonstrate I'm going on get the default text from the strings file. It's still just a little bit of code rather than from a nib file because that's no code.
But, so let's take a look at this, and I'll get started here so I'm going to open up control.h here and I am going to drop in some of the text field utilities, the declarations for these guys, so as you can see I put in two functions here that I'm going to define, InitializeTextField, this is going to be called when we open up a window and it's going to grab the string from the strings file and send it to our text field and then Update Text which is going to be responsible for extracting the string from the text field and applying it to our ArcView.
Now jump to the implementation file of this and let's drop in the implementations of these guys, and we'll take a look at what we got. So initializeTextField here is really quite simple. All we're going to do is call the CFCopyLocalizeString macro and having this in there handles all the interactions with the appropriate strings files. There's several of these macros that allow you a little bit more control over how your strings are organized and, if you're writing a Cocoa application, there are equivalent NS localize string macros.
Now just using this is not enough to get the strings file in your code. You need to use one of developer tools called genstrings let me jump to the project and I'll show you what the string looks like. genstrings will when you pass your source files will parse those and create appropriate strings files and then, once you have the strings files, you can as necessary, edit the strings and your localizers can create copies of these for other localizations. As you see our now default string will have some Unicode text with some Japanese text interspersed with the English text as you saw in the original application.
So that's the strings file and that's how we're going to get the strings file out and all we're going to simply do is set it to our text field. Now update text is the one responsible for grabbing the text from control and there really isn't any Core Text specific -- anything really specific here this is standard HIView stuff we're just going to grab the text and assign it to our ArcView. So with that in mind let's hook up the application event handlers -- or the Window Event Handler in this case for these guys.
First thing I'm going to do is drop in a new implementation of HandleNew which is the event handler for creating a new window and just quickly walk you through this -- see if we can jump to the top here. What I've added are the control events that we're interested in handling and then I am going to set-up a ControlEventHandler for those events and I'm going on use my Window Event Handler for convenience and before we show the window I'll initialize the text field and update the text. That should be there.
You know what let me do that again because I dragged the wrong one in. I want this to compile for you guys, so now we'll go to the Window Event Handler and put in the new Window Event Handler and then we'll take a quick look at what we're doing for the TextField Event Case.
So this is where we're handling the events from our text field control and, you know, we're going to respond to two of them, the TextAccepted and the TextDidChange event. The TextEventAccepted is going to be good for getting the input at the end when the user hits the carriage return, but we want live input and live feedback here, so we're going to listen for the text to change event also and to handle this properly -- and it's actually fairly well documented, we want to look for the unconfirmed range parameter. If that parameter is present, that means there's an unconfirmed range of text usually from an input method and in that case we're going to bypass update the text and wait for that to be confirmed.
So let's take a look and see what this does, I'll build it. Now you can see we have our default text pulled in from the strings file assigned to our text input field and the ArcView and live update I was telling you about is now working and so let's show you what would happen -- what happens with the input method.
So I'll switch to the Hirigana input method -- and I apologize if there are Japanese speakers here, if I mess anything up, I'm not. So I'm going to try and type something and notice I'm not updating yet because technically there's still an unconfirmed range and once I confirm that, we'll update the view of the text. And this is what we sort of desire for live update behavior.
So now that we're getting text into the application both from a strings file and from user input, the next thing we're going to want to do is move on to step four and, you know, hook up the fonts window, but before I do that let Ned tell you about fonts. Slides.
So we're going to need to talk about fonts. As Nathan said we've already been making use of our default font, but now it's time to actually get real and let our user select some fonts for using -- for use in their document. So first let's get an overview of fonts on Mac OS X, so there's two primary classes that you're going to want to make use of when dealing with fonts, there's the font which is a specific graphic instance that's used to render in a particular case and then there's a descriptor and a descriptor you can think of as being a loose match to a font or kind of like a search query. So this is something that can be used to either uniquely identify a particular font by its attributes or it can be something that you can use to basically find the most appropriate font on a particular system.
Now Windows programers have already made this distinction -- are used to making this distinction by both having a font instance in the form of an HFONT and a font descriptor of sorts being a LOGFONT structure and so it's -- it's a very common distinction to be made so a descriptor allows you to say something like I want a serif font instead of saying well I want this particular serif font, you can say well I want a font that has these particular attributes and handles that mapping for you.
So, of course, as with strings we also have toll-free bridged classes for dealing with these classes as well. What you see on the slide is a rough overview of both CTFont and CTFontDescriptor being in Core Text and available at a very low level to you and NSFont and NSFontDescriptor their bridged counterparts which are available through the applicant framework and so is available with a different syntax but, of course, those classes are toll-free bridged so you can use the two interchangeably and then at the -- at the very bottom of this diagram you can also see the implementation of these classes which, of course, are based on CGFont which is the Quartz font instance and under the hood ATSFont which roughly corresponds to a particular font file or font container on disk.
These are, of course, the implementation you typically don't want to use one of these classes. It's much easier to make use of the higher level classes but if for some reason you do need to access one of those lower level classes Core Text allows you access to both of those. So that's how fonts work.
Now we're going to need to figure out in some cases what font in particular we're going to be dealing with from a user perspective, of course, as an application you should prefer to use the font panel sometimes called the fonts window in user interface parlance for letting a user select fonts.
It used to be the -- the normal mode to have a big menu with all of your fonts and everything well, it's not really what users expect these days on OS X. The font panel offers a lot more flexibility when dealing with fonts other than just that single long listing and lets you access many of the attributes in a particular font and so, in particular, in Leopard we've enhanced the capabilities of the font panel interface for Carbon to allow you to access a font descriptor for the user font selections, this is a great boon for anyone who has had to deal with integrating font panel into a Carbon application because the font descriptor makes it virtually painless to apply the newly updated attributes from the user selection to the font panel to the particular font that you're using in a document.
So that's a great new addition in Leopard and one that Nathan is going to make use of and then, if you do need to enumerate all of the fonts on a particular system, of course, there is also a CTFontCollection class, and we gave an example of using that in our slides last year and so that is also available to you. So with that in mind, let's go ahead and actually hook up to the font panel as Nathan is going to show you on the demo machine. Thanks.
Thanks, Ned. So, hooking up to the font panel is really fairly simple, you know, it could be you can think it's fairly complex especially if you've tried to do it in other Carbon applications using ATSUI or even QuickDraw. We've added some functionality to the font panel to support Core Text font descriptors and you'll find that using those with the font panel a lot easier, and I'm going to show you that in a few minutes.
First thing is I want to show you how to display a font panel for your application and our preferred way is for you to add a format menu for your application where you'll format fonts and format text in your application. In this case all we really care about are fonts. As you can see in the nib I have a format menu defined and I have a show fonts menu item.
What I've done to the menu item is the menu item has the command SHFP and that corresponds to the standard constant KHI show/hide fonts panel and, if you define this with a menu item or a control to send that command and let that command filter through your application, the standard event handler will show or hide the fonts panel depending on the current state.
You may want to make note of that event when it is issued so that you can do certain things such as update the font panel which we do in this application or update your menu which we also do, and I'll show you in one of the later -- in the next step. So that's a look at the code. We'll get out of Interface Builder here and go back to Xcode.
So to hook up to the fonts panel I'm mostly going to be interacting with the fonts.h and .c files here, so we'll open fonts.h and as we've been doing I'm going to drop in some declarations for the font panel support and all we really have right here is the ChangedFont function.
This function is going to be where we're going to handle events from the font panel, so we're going to take an event as input and the WindowRef. We'll respond to this event by setting the font from the font panel to the ArcView, so let's go ahead and implement that, jump to the implementation file and drop in the implementation here.
Now, if those of you familiar with Cocoa you will note that you use a method very similar to this when responding to the font panel you'll get a new font from the font panel and you'll be asked to convert your current font to that font. What we're doing here for Core Text in ChangeFont is, first thing we're going to do is get the current font out of the ArcView. Once we have that we will copy the font descriptor with CTFontCopyFontDescriptor from the current font and also extract the point size so we can use it when recreating the font if that hasn't changed.
The next we're going to do is extract the descriptor, the CT font descriptor from the event -- from the font panel so we'll get event parameter and the parameter kEventParamCTFontDescriptor, we will get the font description from the font panel. Now it's important to note that the font panel has a lot of different modes and it's possible that the user only interacted with one of these modes and possibly only changed one aspect of the font.
So this font descriptor you get from the font panel could be partially complete that is the user manipulated the size slider you may only have a point size in the font descriptor from the font panel so what you need to do is merge this -- these new attributes from this new font descriptor with your current font that's why we got the current font to begin with.
So the first thing we're going to do is call CTFontDescriptorCopyAttributes with this new descriptor from the font panel, that will give us the dictionary of attributes and Core Text has a very convenient method, CTFontDescriptorCreateCopyWithAttributes that takes a basic -- base descriptor, the original descriptor and a set of a new attributes and will do the proper merge of those attributes with the existing descriptor. It will handle the tough work of merging the more complex attributes like variation settings and feature settings from the typography panel.
Once we have this new font descriptor we're pretty much home free. We will create a new font, CTFontCreateWithFontDescriptor with this new one and then we will -- we'll specify zero point size and null for the matrix parameter, basically so that we will preserve the values that are in the descriptor and so the descriptor came from a font originally that was merged with the font panel, so it has everything we need inside of it at this point we don't want to override any of those because we want to respect the user selection in the font panel and last we will set the font to our ArcView and release everything.
The next part of interacting with the font panel is you need to tell the font panel what the current font is and it's important to do this so that the font panel when it's displayed or when the user switches windows is always displaying, you know, the correct font. So I'm going to go to controls.h again and here we're going to go to the font panel utilities and I'm going to declare a utility function here, UpdateFontPanel, this is what we'll call to update the font panel with the font for the current ArcView.
Now, if we go to the implementation file and drop in the implementation, we can see -- take a look through this and see what UpdateFontPanel actually does. It's really quite simple, like change font we're going to get the current font and then the descriptor from the current font CTFontCopyFontDescriptor again and then we're going to call SetFontInfoForSelection. This is the primary mechanism for accessing the font panel from the Carbon interface.
We'll specify that our selection is a Core Text type and pass in our descriptor. Those of you that may have tried this before with the ATSUI styles and having to build up these fairly large structures for all the information might be relieved moving to Core text that it's as easy as passing a descriptor around.
Now the last piece of information that I want you to note is that the last parameter to SetFontInfoForSelection is the window event target of our current window. This could be the window or could be a control since we're sort of Windows-based here I'm going to use the window. It's important to specify this because that tells the font panel where to send its next selection event to.
If you don't do that, you may not get the selection event that you need when the user interacts with the font panel. Last things we're going to hook the events up in our event handlers, so first I'll drop a new implementation of HandleNew, all this new implementation really does and we'll take a quick look is add another two events that we want to handle here, the WindowActivated and the FontSelection event and then also we'll update the status of the font panel so that when we have a new window it gets -- the font panel gets updated. So we go to the Window Event Handler and we'll drop in a new implementation here with the code to handle these events, save our file and let's take a quick look at what we're doing with the font events.
So when we get the show/hide font panel event, all I'm going to do is call update the font panel so that it knows what Windows sent the event to and what the current font is, and I'm not going to set the status code or anything because I don't actually handle the event. I want the standard event handler to do that, and we get the selection event, all I'm going to do is call my change font routine and that's going to apply the current font.
So if we build and run this now our application is up and I can bring up the font panel -- excellent. And I should be able to change the font here, so let's pick a different font let's try Cochin you see that the font change is not very clear, maybe Copperplate, yeah, there we go.
It's always, you know, I can change the styles and the sizes. It all updates live, responds to the font events and, if I open up a new window, you know, it sets the current font in the new window, switch back, we're good to go. So before we're done with fonts Ned has a little bit more to tell you about determining font capabilities and this is what we're going to use for hooking up these bold and italic check boxes you've been looking at for the past few demos, Ned.
- Nathan, that was riveting.
- Slides, please. Thank you.
- All right. So what we've done so far, of course, we've gotten some drawing in, we've gotten some strings in, we've gotten some font panel action in, this has been fantastic. So I want to go back to rendering for just a couple of slides here. So, of course, as I mentioned before Mac OS X uses Quartz or Core Graphics as its fundamental graphics mechanism and Quartz brings a lot to the table. It's really a great mechanism to work for.
It's resolution independent, it has great PDF, all sorts of cool shadings and all sorts of stuff I couldn't even begin to tell you about but there's, and of course, CGFont is the drawing primitive for dealing with text and fonts within a Quartz context and -- but there's a catch. The catch is that if you've been dealing with raster graphics package or bitmap graphics package for most of your life as most of us have, there's a bit of a sea change that you're going to have to make when you think about fonts in your application.
And that is the notion that not all styles apply in any particular instance for a font. So what I mean by this is that when you're dealing with a raster graphics package like QuickDraw or GDI, it's really easy to take any font that a user has on their system and say, oh hey, I want an italic version of that font and there it goes or hey, I want a bold version and get this chunky double struck bold, but Quartz is really founded in the sensibility of PDF which holds text in very high regard and so we tend to take the font designer into account when dealing with fonts.
In particular not all font designers have designed a particular family with bold and italic instance, sometimes they might be, you know, in a completely different font family or maybe there just isn't one, maybe the design isn't suitable for either of those styles. So it's important to note both for yourself and to your users that they can't just arbitrarily go and apply these styles to just any font they have on their system.
So as we saw in the last example the font panel makes this really easy because it lists all of the styles that are available for a particular font even beyond simple bold and italic if the font happens to have one, but you may find there's an instance where you have kind of this sort of old notion of dealing with the font, and so you'll find out that you can almost synthesize an italic for any particular face and you can do that graphically by applying a shear transform which is where you turn the rectangle into a parallelogram and so it doesn't really give you real italic, you know, it won't give you any sort of change to the design of the font but it will give you an obliqued graphics effect for that particular font instance but bold on the other hand is a different story.
It -- there's no real way to synthesize a bold for a font that didn't have one in a way that's going to work in every context that that particular font instance is going to be used. That old notion of doing the literal double strike in order to get the bolder text isn't going to work and so in order to handle this -- this new world where fonts look great because they were designed that way rather than just being kind of mangled by your graphics, we're going to need to determine what particular fonts have particular styles or perhaps other traits in some cases and in this case I'm going to turn it over to Nathan to show you how to do so.
All right.
So back to the demo we're moving on to step five. We're going to hook up those check boxes and menu items to make text switch the font to bold and italic faces. What's important to note is we're going to be analyzing the capabilities of the font using Core Text to figure out if these check boxes even apply for the font we've selected. So if a font doesn't have an italic face, you know, moving from the current face, we're going to disable that check box.
So let's get started here and we're going to be working in the fonts.h and c and controls.h and c files. So in fonts.h I want to drop in a couple more utilities for probing font traits and setting font traits. So we'll drop those in, two simple functions here that we're going to be implementing, ToggleFontTrait and CanToggleFontTrait.
Essentially ToggleFontTrait is just going to be take the current font and take the font -- the trait that is specified and switch the meaning of it from the current font. So, if the font is bold and we want to toggle bold trait, we're going to make it nonbold and CanToggleFontTrait is very similar but all it's going to do is determine whether the font can actually do that. So let's drop in the implementation for these.
All right. And we'll take a look at what ToggleFontTrait does here or right there, first thing we're going to do as we've done before is get the current font and then we are going to call CTFont and get symbolic traits. That will give us the traits that we determine for the font as a short-bit field, you know, using bitmask for each trait and this represents things like bold, italic, monospace and various other things and then we're going to do a quick set of code here and we had it broken down into if-else for readability there are other ways we can do this but we're going to call CTFontCreateWithSymbolicTraits, give it our original font and default values of zero and null for size and matrix.
We don't want to change those and then we're going to pass in the trait value as the fourth parameter and the trait mask as the fifth parameter and, again, like I said these are bitmasks and so the fifth parameter is important that actually tells Core Text which trait you desire to modify and it's possible to modify multiple traits at a time and the value for those traits will be set by the values you pass in for the fourth parameter.
So, you know, the first line here that I've highlighted we're going to be trying to turn on a given trait and the other call we're trying to turn off that given trait and so, once we've done that we will set the font to our ArcView. If you take a look at CanToggleFontTrait, the code is pretty much identical.
All it does is take a font as input instead of getting it from our current ArcView and then it checks to see if we can actually make a converted font, and it will toss out the result but it tells us whether the font can do this or not.
So we move on to controls let's quickly drop in the controls stuff here, the control utilities let's put in the declaration. I just have two utilities here to update the font controls those are the check boxes and update the format menu which is the update the menu items, and I'm going to jump to our implementation file and put the implementations in the right place.
I'm not really going to walk you through these because this is standard control functions calls for HIToolbox but suffice it to say these are calling those two functions that I provided the ToggleFontTraits and the CanToggleFontTraits in response to being called and then update the menu items and essentially we're just changing a few attributes on those controls.
All right. So before this will run we need to, again, update our event handler, so let's replace update new and all we're really doing here is making sure we update the font controls as well as the font panel, and we drop in our new Window Event Handler, and now I will show you what we're doing in the Window Event Handler is when we get the font control commands which we have defined in the controls.h file for bold and italic, we're going to toggle the font trait and then reupdate the controls with the current font because we've changed the font and that's all we really need to do. If we build and run this, you can see the effect right away -- hide Xcode. We're starting to look like that app I first showed you.
We got our text drawn in Arc, we got the font panel integration and if I change the font -- let me bring up the font panel so you can actually see how things are going here, if I change it to bold, you can see that since dedo our current font does not have a bold italic typeface, we disabled the italic check box as well as the italic menu item.
Now if I disable bold and switch to italic, you can see that the reverse happens, switch to a more complete face say Cochin and now I actually have access to a bold italic face and once you've got that and you're giving the proper feedback to the users for the capabilities of the font. We're not giving them misconceptions of what fonts can, you know, that they can do things with fonts that the fonts actually don't support and so before I wrap up and show you some of the option hookup let's have Ned talk about saving your text into your document.
Slides, please.
Thanks. Nathan. Truly that's better than Cats. So this is just kind of a quick run down on saving because by and large we hope that you as you're designing your document format will consider some of the existing mechanisms for dealing with saving text but, if you're implementing a file format of your own, we certainly strongly recommend that you make use of Unicode.
CF has some conveniences for dealing -- for converting your text to a version that's appropriate for being saved in a document, the external representation, of course, is UTF-16 with a byte order marker that means that, if you save your document on an Intel machine and open on a PowerPC that byte order marker will, of course, be a signal to CFString as to what byte ordering to use when reading that text back in.
So we do recommend that, if you have the choice, you make use of this convenience although you may find that if you're dealing with, you know, documents from the internet or some other protocol you might need to also use something like UTF-8 which is also available to you but do keep that in mind as a consideration when you're designing document format.
Also and sometimes you may need to use this not just for documents but perhaps for other purposes as well, when you are dealing with the style for a particular run of text, you should make note that we strongly encourage you to serialize the font descriptor and not the font.
Remember as I said the font descriptor contains the attributes that describe a font rather than information about a particular instance of a font and so by serializing the font descriptor, there's some amount of flexibility that you have when you're reading that document back in -- in hopes of providing some sort of reasonable behavior when a user opens document on a machine that doesn't have a font that the document was created with for instance and also it's a lot easier to identify the information you're going to need to represent a font because it's all just in the font descriptor attributes so that's fantastic.
And then as I mentioned at the beginning of this slide we really do have a ton of functionality available to you in Mac OS X, in particular available through Cocoa which allows you to save your text documents as not only simple formats like RTF but even formats such as the Open Office format and Office XML and others.
So it's really fabulous technology for dealing with a single line of code, to save a single line of code, to restore, it's really great stuff and we encourage you to make use of that if that fits your needs. So that's just a quick rundown, there's a lot more to go into detail here, but we just want to touch on it being good citizens in Mac OS X. So now we come to the bonus material. We thought about director's commentary but we realized there probably wouldn't be as much there as we might have liked, so instead we're going to show you how to hookup those option check boxes. So no Quentin Tarantino but Nathan will do.
Demo one, please.
All right. So you've been looking at these option check boxes for a while. They do some interesting things with Core Text and since we're a little short on time I'm going to run through this very quickly. If you're following along it's step six. So the first thing I'm going to do declare some options and some accessors, and I will define them as well with the option accessors, and then let's, actually, get to the nuts and bolts.
We're going to look at the drawing here, so let's first look at what we need to do to get and draw glyph bounds, so I'll drop in the code for this. If the option is set, we'll get the image bounds from the run for the current glyph range, and we'll simply stroke a rect and that's pretty straightforward. All of our graphic operations apply.
Line metrics are almost as easy to do with our run. We'll call CTRunGetTypographicBounds, and we're going to get the ascent and descent to use to construct the height of the box and then again all we have to do is stroke the rect. Determining when we have substituted fonts is a little tricker but not that bad.
If we drop in some code to determine when the substitution occurs, we will set-up a Boolean we'll use for drawn and all we're really doing here is extracting attributes from the run and we want the font attribute and then we're going to compare with CFEqual our current ArcView font to this font from the run. If they're not, well we know we need to draw -- do some special drawing to implement this option.
So to do that I'm going to get rid of the simple run drawn and implement or substitute glyph drawing and so, if we're not, we're going to do a simple run drawing and, if we are, we have to draw manually and we're going to get the graphics font, the CGFontRef, from -- from our current font and then simply going to get the glyphs and the glyph positions from the run and pass those on to CG to show our glyphs at these positions. So now the last bit is to hook up the Event Handler, so I'm going to do that real quick and drop in the new implementation here.
All this is doing is putting a handler for the commands from our check boxes and then setting the correct options to the ArcView and, if I build and run let's hide Xcode real quick and you can see now we can dim the substituted glyphs as I showed before draw glyph bounds and draw line metrics. All right. Ned, time to wrap up.
All right. That was great, so, of course, for more information you can contact Deric Horn the Application Frameworks Evangelist who can put you in touch with the right people to resolve your issues. Under documentation, sample code, and other resources I do point out the second URL there is the address of the webpage where you can subscribe to the coretext-dev public mailing list so if you want to hang out with other Core Text aficionados that is the proper venue to do so and, of course, on the session page for this particular session there is doc links to the documentation for Core Text and this sample code and some other stuff. So that's great, if you want even more in depth hands on discussions with Nathan, myself or other Core Text engineers, you can come to Lab C on Friday afternoon.
There's going to be a lab on Core Text. I will also point out as I did at the beginning of this talk there's a talk on Extending the Cocoa Text System this afternoon, followed by another Core Text talk geared towards people who are converting from existing Mac QuickDraw and ATSUI situations. So that is fantastic, in summary Unicode is awesome. We use it throughout the system, you should definitely too.
The way to do so is, of course, to make use of it and Core Text allows you to make use of that in a very low level context and, of course, built on top of this mechanism you have the full flexibility of the Cocoa text system, so we really are trying to get you to use Unicode by making it easy to do so in your application.