Configure player

Close

WWDC Index does not host video files

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

URL pattern

preview

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

$id
ID of session: wwdc2008-701
$eventId
ID of event: wwdc2008
$eventContentId
ID of session without event part: 701
$eventShortId
Shortened ID of event: wwdc08
$year
Year of session: 2008
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2008] [Session 701] Handling PD...

WWDC08 • Session 701

Handling PDF Content in Your Application

Media • 56:06

PDF Kit makes it easy for your Mac OS X application to display and navigate even the most complex PDF documents. Learn the simple steps for adding PDF viewing, page thumbnails and outlines to your Cocoa application. Understand the structure and content of PDF documents and see how to perform editing and annotation. A great session for any Cocoa developer utilizing PDF.

Speaker: John Calhoun

Unlisted on Apple Developer site

Downloads from Apple

SD Video (601.7 MB)

Transcript

This transcript was generated using Whisper, it may have transcription errors.

So what I'm going to cover today is I'm going to give you kind of an overview of the PDFKit framework, and then I'm going to spend the last three parts of the session talking about how you would use PDFKit. First, I'll show you how you would use PDFKit to display a PDF, which is what probably most people would like to do with PDFKit. And then I'll show you kind of, I guess, a little more esoteric uses of PDFKit, how to create your own PDF content, and then how to use annotations. And annotations are kind of fun. I think if you came up with a list of fun things in PDFs, I think annotations would be on your short list.

It's an attempt at humor. OK, so let's get started. PDFKit framework. So what is it? PDFKit is a suite of Cocoa classes. So if your application uses Cocoa or AppKit already, and you're familiar with NSWindow and NSView, PDFKit adds a whole bunch of more Objective-C classes that obviously are tailored towards PDF. And they work really well with the rest of AppKit. It sits on top of Quartz 2D. So if you're familiar at all with the sort of PDF kind of underpinnings of Mac OS X, then you know that PDFKit sort of sits on top and leverages and extends that. If you've opened a PDF in Preview or opened up a PDF in Safari with the built-in PDF plug-in, then you've seen PDFKit. All of the PDF displaying applications on the Mac OS X are using PDFKit. So it's actually a subframework part of the Quartz framework. So if you go into system library frameworks, you'll have to add Quartz framework to your application. And then Quartz framework is sort of an umbrella. There's several subframeworks within it. PDFKit is one of them. It's been around since Tiger, and since we're, you know, talking about Snow Leopard now, that it seems like PDFKit has been around for quite a while.

So here are the classes. I'm not going to go into detail on all of them, but I just kind of wanted to throw them on one slide. I actually couldn't get them all on one slide, because PDF annotation, the fifth one down, it has a whole number of subclasses. And I gave you a couple of them kind of to be representative, but I couldn't show them all. And PDF Action also has subclasses.

But here's basically the classes that you get with PDFKit. But to sort of kind of-- uh... kind of categorize these are making a little easier to understand probably best to kind of pull the first two out uh... p_d_f_ you and p_d_f_ thumbnail view and call these sort of the view classes and that's obvious they have the name they have the word view in their name but they're also both subclass of in s_p_u_ so all the kinds of things if you're familiar with in s_p_u_ you know it takes events mouse clicks uh... has drawn routine you you add these things into windows sorts of kind of properties that PDF view and PDF thumbnail view have. PDF view, in fact, if you do open up a PDF in preview, the sort of main content area where the PDF is displayed is, in fact, a PDF view. And you probably guessed that the thumbnail view is sort of an accessory view for displaying thumbnails for that document, and the two view classes really integrate well together. And I'll show you an example of that in a few minutes.

The rest of the classes I'm just calling base classes. They're not subclass of NSView. They're subclass of NSObject. So in that way, they are sort of more basic or more low level. And in fact, the PDFView and ThumbnailView classes are really built on top of these sort of foundation-like classes. That's not to say, though, that-- I mean, you could just use PDFView, but it actually turns out there are a lot of interesting things that your application can do by directly, not indirectly, calling some of these base classes. And I want to just kind of highlight four of these base classes.

And I'll call these the core base classes. They're PDF document, PDF page, PDF annotation, and PDF outline. PDF document is going to be the object that represents a PDF file, whether it's a file on disk or a file in memory. PDF document is the representation of that. Now, PDFs are comprised of pages, usually more than one. And so for each page within that PDF document, there's going to be a PDF page object. And PDF document's going to create these page objects and retain them for each page in that PDF file. Some pages have outlines on them. Here's a sample that looks like there's a yellow highlight on the text at the top of that page, and then there's a red ellipse at the bottom. These might be annotations, and for each annotation that there is on a page, PDF page will create a PDF annotation object. And then finally, if a PDF has an outline, then there will be PDF outline objects to represent that outline tree. Now an outline, this is what Adobe Reader calls bookmarks, and I believe Preview calls a table of contents. It's not all PDFs have the outline. When they do, it's nice, it's kind of this tree structure that, you know, might list chapters or have some kind of way that allows the user to easily navigate the PDF file. So if it wasn't clear, I want to kind of summarize on this one slide that there is a kind of hierarchy here.

There's a kind of nestedness. You have a single PDF document object, and that's the green box here. And for each page on that -- and that's going to represent this PDF file. And each page in that file, there's going to be a PDF page object. for each annotation on the page. The page will create annotation objects.

And then sort of parallel to that, like page array, there's the outline tree. And the document is going to own the root object. And then each of those outline elements, if they have children, will own and retain those. So I've got kind of an example here of the outline root, which is the larger purple box.

Has two children, the first of which also has a child, just to give you an example. So anyway, that's more or less the framework kind of summary. So let's go ahead and start how we would use PDFKit. So let's imagine that we want to write a kind of a lightweight PDF viewer. And obviously we want to be able to display PDF files, but we want to be able to -- we want the user to be able to interact with these files. They should be able to select text. If there's a link or something in the document, they should be able to click on that link and it works.

If there's an outline for the PDF file, we should be able to display that as well, especially for the larger documents that tend to have the outline. I mean, that helps the user navigate, so we'll want to support that. And while we're, you know, making it easier to find things in the PDF, let's go ahead and allow text searching as well. So these are kind of the criteria, or rather the things we want to cover in our application. So how would we do that? Well, first we want to display a PDF. And the way we're going to do that is I'm going to assume that you fire up Xcode and you create a new project. And if you use one of the Xcode templates, you'll probably want to use the document-based Cocoa application template. And so Xcode will create, you know, a nib file for you and a few classes. And so what we'll probably do then is go into Interface Builder with that nib file and add some of these view classes that I mentioned, these PDF views. And so basically for displaying the PDF, we're using PDFKit. So let me go ahead and show you how you would use Interface Builder.

Just making sure I was on the right machine here. So here's Interface Builder, and I'm going to create an application template here. Generally, like I said, if you've used Xcode to create your template, you'll already have this interface. And the important thing is that is this window, this NSWindow object that is created for you. So here's our window.

And let me go into the-- let me close the inspector here and bring up the library. And here are all your classes. Now, I could scroll through and try to find the PDF classes, but I'll just type PDF, and it narrows the field down. And here, in fact, here are the view classes. Here's the PDF view, the PDF thumbnail view. So I'll start by dragging a PDF view in to the window. And you'll notice that even as I drag it in and start to resize it, that there's already PDF content there. This is kind of just a trick of an interface builder.

Obviously, your application will have to load your own PDF content. And I'll drag thumbnail view over here and size it. Let me hold down, I'm going to hold the control key down, and I'm going to drag over from the thumbnail view to the PDF view. Now, in Interface Builder, this is how you can wire up outlets. And you'll see that one of the outlets, the second one here, is PDF view. So by selecting that, I've essentially wired the PDF view outlet from the thumbnail view to the PDF view. So now they kind of have a relationship. The thumbnail view knows from which PDF view to get its thumbnails, basically. So I've got my view classes added. Let me set some springs. While I'm at it, if I go into the inspector here, you can see, for the example for the PDF view, I can turn on-- I can set some attributes just from Interface Builder. I can turn on automatic scaling. I can have it default to two-up mode or single page mode, or I'll just leave it in single page continuous. Thumbnail view, I've got some attributes I can set from within Interface Builder as well. I'll just leave the defaults, though. So I've got the thumbnail view, the PDF view. I'm going to select both of those and then embed these inside a split view and set the springs on that. That way, when the window resizes, the split view will also resize. And now I've got everything kind of wired up. Let me go ahead and try it out. If I go into the file menu and run simulate interface, Interface Builder launches this simulator, and here's our window. It's opened up, and this time, the thumbnail view has been told that there's a new document, and so the thumbnail view has gone off and fetched all the thumbnails here.

And as I'm scrolling the PDF view-- I'm using the scroll wheel, by the way, on the mouse-- as I'm scrolling the PDF view here, what it's doing is it's sending out page change notifications. And because we wired the outlet from the PDF thumbnail view to this PDF view, the thumbnail view knows which PDF view to listen to for these page change notifications. And as the page is changing, the thumbnail view is selecting the current page. And then similarly, I can click on pages inside the thumbnail view, and it does the hit detection and knows which PDF view to say, you know, go to page three or go to page four or page five. I can select text in the PDF view. I could copy and paste into text edit if I wanted. If there's a link, for example, here's a link annotation. The cursor turns into a hand, and I get a tool tip. I can click on that link, let go, and it takes me to that destination in the PDF view. I can even do some kind of crude editing. There's some drag-and-drop functionality built into the thumbnail view. Let's see. If I want to, for example, rearrange page one and two, I can just drag it like that. And now I've reordered the PDF document. I can even drag in content. Here's an image. And the thumbnail view accepts image dragging, so dragging images in. I could even drag pages from one PDF document over to another PDF document. That's about it. I'll switch back to my slides.

So the first step there, kind of laying out the nib we did. And that turned out to be fairly straightforward. We added the PDF view, the thumbnail view. We wired up that outlet. And you saw all the text selection, the hit detection, the link traversal, that kind of stuff. We can check both of the first two items off of our list. So I showed you PDF view. And I did want to spend one slide kind of giving you a rundown of some of the methods that you'll find for PDF view. I mean, you can look in pdfview.h, and I think the header is fairly well documented. There's at least a line or two, a comment for each method, and I think it's fairly self-explanatory. But just to kind of give you just an overview of the kinds of methods you'll see in pdfview, there's methods for navigation. So go to page, in, go to previous page, go to next page. In fact, when I clicked on a thumbnail in the thumbnail view, the thumbnail view itself was calling one of these methods on the pdfview. There are methods for changing the way the PDF view displays the PDF content. And you saw in Interface Builder, I changed it from one up to up. There's a lot of other methods where you can turn on and off anti-aliasing, things like that. There are methods for zooming in and zooming out if you want to put some toolbar items in your application for zooming. There are methods for select-- you saw that I could select text. for programmatically selecting text as well or getting the current selection. And then there's print methods if you want to wire up printing to your PDF view. There's conversion routines for mapping between, like, a mouse click in the view and determining which page that mouse click was on and where sort of in the page's coordinate system that mouse click happened. And there are notifications as well, and I showed you the one notification, or I told you about it, the page changed notification, and your application can listen to that as well, and then there's like, I don't know, a whole host of other notifications as well. And there are other methods as well in the header, but I just kind of wanted to summarize, you know, the bulk of them here. So let's get back to our application.

Like I said, in Interface Builder, we cheat. I kind of, you know, conjure up some PDF content just to make Interface Builder more interesting so that you can see the effect of changing some of these attributes, but in your application, obviously, you've got to provide your own PDF content for your PDF view. Now, this is the line of code that you do that with, um, or the couple lines of code. Essentially, you're gonna create a PDF document object, and I'm gonna assume that, uh, you've already brought up the NSOpenPanel, for example, and the user has selected a PDF file, so you've got a URL for that PDF file. So to create a PDF document from that URL, you call this, uh, initializer on PDF document. So I've got document equals PDF document alloc init with URL, and I pass in that URL that the user has presumably selected. And if it is a legitimate PDF file, you'll get back a PDF document object. So then the next line of code, myPDFView-- I'm going to assume that you've got an outlet to that PDF view in your nib called myPDFView. So myPDFView is just an instance of PDFView. So the method on PDFView we're going to call is setDocument, and we pass it that PDF document, and just that one line of code there kind of triggers off this whole kind of cascade of events. The PDF view now has a document. It actually sends out a document change notification.

That's what the thumbnail view listens to. So the thumbnail view starts getting busy fetching thumbnails. The PDF view gets busy laying out its subviews and displaying the PDF content. And so the other thing that the PDF view does is it retains the document. So that last line of code, I can go ahead and just release that PDF document, and if at any point in my application I need access to that document, I can just ask the PDF view for its document. Just as there's a set document, there's a getter as well. And I'll show you an example of that.

So PDF document, that's kind of the big base class. That's kind of the wellspring of all the other classes in PDF Kit, so I'll spend a slide on PDF document. If you look at pdfdocument.h and look at what kinds of methods there are in that class, they kind of fall into these categories. You can find out the number of pages of a document. And for example, the thumbnail view calls that method to determine how many thumbnails to lay out. You can, given that now you know how many pages there are, you can ask for a specific page. So you can say, give me the page at index 0, for example, to get the first page.

And then you'll get back that PDF page object that the document has created for that page. There are methods for adding, removing, reordering pages in the document, and obviously the PDF thumbnail view is calling these methods. There are, there's like metadata information about the document. So modification date, author, creator, you can get that kind of information from the PDF document class as well. Searching, since we're going to do searching in our application, I'll be coming back to this one, but this is where you would do the perform searching is at the PDF document level. The outline, I kind of hinted at that earlier. That's where the outline root is, and again, we'll be using this in our application. I'll show you this API specifically. And then finally, you know, if there have been editing, if pages have been added, removed, reordered, or if pages have been cropped or annotations added or removed, there's methods in PDF document for saving out a new PDF file that represents all those edits and those changes. Thank you.

So back to our application, we wanted to get the outline if the PDF document-- if the PDF file has an outline. And the method we're going to call is outlineRoot, and that's a PDF document method. So to get back that PDF document from the PDF view, there's myPDFView. Again, that's the outlet to the PDF view in your nib. MyPDFViewDocument returns that PDF document object that we just set a few slides ago. We asked for the outlineRoot, and I just kind of want to that since not all PDF files have an outline, your application should expect to get null back, because if there's no outline, you'll get back null, and you should probably display to the user somehow, you know, that this PDF file doesn't have an outline. Once you--I guess I should have pointed out earlier, in order to display the outline tree, there is no PDF outline view.

We didn't need to create a PDF outline view because AppKit has a class called an NS, the NSOutlineView that works perfect for this example. The only thing that the NSOutlineView needs, you have to supply-- your application has to supply three delegate methods to get the NSOutlineView, you know, properly data provided, and that's what PDFKit does is it provides that data. And in fact, the PDFOutline object itself is where you get that data. You know, I'm not gonna kind of really go into detail that gets wired up, you can look at some sample code that I'll point you to in a minute. It'll show you how you can wire up an NSOutlineView and PDFKit to display that outline tree. But since I'm talking about the PDF outline class and it was kind of one of those core classes, I thought I'd go over some of the attributes of the PDF outline.

Remember that the document will -- if there is an outline for the file, the document will that outline root, and each child of that root, each child in this sort of outline tree, is also gonna be a PDF outline object itself. So the method that you would call on the root-- well, let me give you an example.

Let's say that there's a particular PDF that has 10 chapters in it, and the outline consists of chapter 1, chapter 2, up to chapter 10. Then one of the methods you'd call if you got back that outline root is the number of children. And so for the root outline object, you would expect number of children is gonna return the value 10. Now, if each of those children, chapter one, chapter two, you know, if they don't have any subsections or subchapters or anything like that, the number of children on each of those children should return zero, meaning, you know, that they're leaf nodes. Given that you know now the number of children, you can ask for a specific child. So of the root outline, if you wanted to get the first chapter, you would ask for the child at index 0. And then of that child, you could ask for its label. Now, the label is the string that actually gets displayed in the NSOutlineView. The root's not gonna have a label because the root is just kind of a container, but its children and its children's children, of course, should have a label. And they should also have an action. That's what happens when a user clicks on a specific outline item. So, for example, if chapter one begins on page six, there's gonna be an action associated with that outline item that essentially says, "Go to page six." I'm not covering PDF action in this session, but suffice it to say that, you know, there will be an action that more or less means, "Go to page six." And there's a method on PDF view called perform action, and you could pass it this action, and the PDF view will perform it, will go to page six.

It essentially amounts to handing an action over the fence the outline view to the PDF view. So last thing we wanted to do in our app was searching, and I mentioned that PDF document is where you do that, and here's what the line of code would look like. So there again is my PDF view document, so we're asking the view for that PDF document object, and the method on PDF document is begin find string with options. Now, the string that you pass in is an NSString. I just--I've got search string here. This presumably is whatever string that the user typed in that they want to search for. And then I've got NS case insensitive search as one of the search options. AppKit defines these, and you can pass these in as flags to kind of refine, you know, how the search is performed.

This method is asynchronous, though. As soon as you call begin find string, it returns right away. So your application can continue to be live. You know, the user can continue to resize the window or scroll through the pages of the document. The way you get notified that the PDF document has found one of the instances of your word, the search string that you're looking for, is either from a notification-- and I give it to you on the last line there-- the PDF document did find match notification, or it's probably the preferred way, just for performance reasons, you can make some object in your application a delegate to a PDF document. And one of the delegate methods that you can implement And then each time PDF document finds an instance of that word, it's gonna call your did match string method, and it's gonna pass you a PDF selection object that represents that instance. Again, I won't go into the details of PDF selection, but suffice it to say, the object is loaded. There's enough information there that you can direct the user to where within that document that that instance was found. So let me go ahead and switch back to the demo machine and show you the application that kind of combines all those things. It's called PDFKit Viewer, and I'm gonna go ahead and open up-- I'll open up the PDF spec 1.6.

And here's our PDF view and the split view, more or less the same way I laid it out in Interface Builder. But over here on the left side, I kind of, to save screen real estate, I kind of nested thumbnail view and the NS outline view under inside a tab view. But you'll see here that this particular PDF, it has an outline, but the root only has a single child called PDF reference. That's the label. But if I twist down the disclosure triangle, you'll see that it has plenty of children. And as I'm clicking on these NS -- as I'm clicking on these outline items here, you'll see that I'm getting the action and I'm telling the PDF view to perform that action, and then the right thing is happening over here. Here's our search field. I'll type in a word, color, say, for example. And I've got this kind of split view within a split view, kind of Bento application. You'll see that I, I'm able to, from that PDF selection object, I'm able to tell them what page it's on, what section it's under, and as the user clicks on this, I can highlight in the PDF view where that instance is, and I can, you know, scroll to that page as well. So if you want to download the sample code for PDFKit Viewer and kind of comb through it and look at how all this is wired up, feel free to. I think you'll be surprised at how little code there is. Let's go back to my slides. Thank you. So that was the first kind of example of using PDFKit. So now we're gonna get into some slightly more, like I said, kind of esoteric stuff. I'll show you how you would create some new PDF content.

So what I want to do for this part is create an application that will do watermarking. I want to be able to take some string and plaster it on every single page in the PDF document. And I want this to-- I guess I want to do it low enough level inside PDFKit that all the sort of higher level things, like the PDF view and the thumbnail view and the printing and the saving and all that stuff, kind of picks up that overlay, that watermark. So the way I'm gonna do that is to go down to one of the base classes in PDF kit and subclass PDF page.

So let me show you first, over on the demo machine here, what the application does, what it looks like, and then I'll explain to you how you would do that. So this also-- PDF Watermarker-- this also is an application that you can download, and this has even less code in it. So it's got a fairly simple window. It's got a string here for the watermark, and I've got a button here for selecting a PDF. So let me find a small PDF.

So we've opened up a PDF, and the PDF view has populated now, and the thumbnail view as well. And you'll see that our string, confidential, has been plastered on every single page. It's probably hard to see. I guess you can kind of make out. The thumbnail's also picked up that string as well.

And if I change the string, say draft, you'll see that the view is told to redraw. And this time, the word draft is superimposed or overlaid. on every single page. And if I save this PDF, I'll call it draft. I get this new PDF file, and this is calling the PDF documents routine to write out that. Let me go ahead and open Adobe Reader, and I'm going to, by the way, launch Adobe Reader a few times today, just, I guess, to show you that, you know, there's no smoke and mirrors here. So here's real PDF content now, and sure enough, there's our watermark on every page. So... Go back to the slides. So the way we did that was subclassing PDF page.

Because if you go back to-- if you go down to the PDF page level and can overlay your content, then, as I say, every sort of class that sits on top of that is going to reflect that overlay. The problem is-- and this kind of goes back to that slide where I showed the sort of hierarchy or the nestedness of the base classes. The problem is that when I initialize a PDF document with a PDF file, it's going to go ahead and start creating these PDF page objects.

And we're trying to subclass PDF page, so we need a way to tell PDF document, don't create PDF page objects, create, let's say, PDF page watermark classes. So the bad news is, in order to get PDF document to use our subclass of PDF page, we've got a subclass PDF document. But the good news is there's really only one method that we have to override in our PDF document subclass, and that's this method, page class. Now, this is the class that PDF document calls when it's creating pages. Now, the default implementation of page class is just to return PDF page class. So we override this one method and instead return our own class, PDF page watermark class. So with this one kind of override, now the PDF document, when we initialize it with a file, will create several PDF page watermark classes. So what do we do?

with the PDFPage Watermark class, we override this one method, drawWithBox. But before I show you that code, PDFPage is kind of an important base class, so I'll give you kind of the one-slide summary of the kinds of methods that you'll see in PDFPage. Because of that sort of nested nature, a page is owned by a document, so there's a method to find the parent or the owning document of that page. You can get the bounds from a PDFPage. Adobe's PDF spec defines all the coordinates in a PDF in points. That's 72 points to the inch. So the width and height, the bounds that you get back from a PDF page are going to be in points, as are, frankly, all the geometry in PDFKit. If the page has a rotation, you can find that out. Generally it doesn't, so you'll get back zero. But you can set that as well. The page creates annotations, so there are methods on PDF page to get those annotations. There's methods for deleting, for adding annotations as well. The page is where you get the text of that PDF page. So you can call a method on the page to get the text, and in fact, it's this method that PDF document calls when you perform a search. It goes through each page of the document, gets its text, and searches through that text.

And then finally, there's draw method for the page. And this is important. This is how the PDF content gets displayed in a PDF view, as each PDF page is told to draw. And this is also important because this is the class-- or rather, this is the method that we're going to override in our subclass, the draw method. And here it is, PDF page draw with box. So here's our override.

And the first line of code inside our draw with box is to call super draw with box. And the reason we call super is because we're PDF page, go ahead and draw the PDF content, the basic PDF content itself. So we'll let PDF page do the heavy lifting. But when this function returns, when this method returns, then we can kind of add any additional content that we want to overlay. So I'm going to assume you have that NSString that the user typed in as an instance called watermark string. And so I'm calling an NSString method there, drawing rect. And I assume that I've already I calculated a destination rectangle.

That'll be end points, by the way. Um, and then an attributes dictionary, the font, the size, the color, that sort of thing. So really a few lines of code, and, um, that's why I say if you go and look at the sample code, there's very little code there. Um, but because we did it at that PDF page level, all the other kind of the view, the thumbnail view, the saving, the printing, we didn't have to touch any of that. It just--it kind of just percolates up. So... Last part-- I'm going through this a lot quicker than I thought I would.

The last part has to do with annotations. Well, we'll have some fun with this section. So here's an interesting screen that shows you, by the way, a bunch of annotations. And, you know, I told you earlier I couldn't fit all of the PDFKit classes on one slide because of all the annotation subclasses. Well, here are those annotation subclasses. And if you look at them, I think most of them, just the name itself is fairly descriptive. I mean, I won't go into all of them, but I think, you know, a line annotation is a line, a circle.

We've seen one of those already. A link annotation, that's when you, you know, those don't actually necessarily draw any content, but when the user clicks on a link, there's some action that happens. So the PDF annotation class, that was that last base class. Let me just give you one slide summary of some of the methods that you'll find on PDF annotations in general. You can ask the annotation for its type. So for example, a circle annotation will tell you its type is circle. You can get the page that the annotation belongs to, get its kind of containing page. You can also get the bounds of the annotation. That's going to be in points, of course. And not only that, but combined with its parent, the page, you can kind of tell, based on the parent's bounds and the annotation's bounds, you can determine where on that page that it's going to be displayed.

and it has a draw method in order to be displayed. So that turns out to be kind of an interesting method because kind of like I did with PDF page in the last example, you can subclass an annotation and override its draw method, and you'll get a very custom, you know, behavior. I mean, you can customize the look of your annotation. In fact, I'll show you one annotation here in a few minutes that more or less requires you to override its draw method. An annotation can have an action associated with it. A link is an obvious example. A link that didn't have an action wouldn't be a very useful annotation. And then there are all these sort of subclass or subtype-specific attributes. So if you go into the various annotation subclasses like the circle, you'll find methods for setting the color of the circle, the line thickness of the circle, whether it's dashed or solid. So there's setters and getters for all those attributes of the various PDF annotation subclasses. So let me spend a couple slides on rendering of PDF annotations because they're kind of strange. There's really two ways that an annotation can draw itself. The one way I'm kind of calling the parametric style of drawing. And that's where there are parameters for each kind of attribute of an annotation, and taken in whole, there's enough information there that we can draw that annotation. And I'll give you an example here. If you actually look at an annotation the way it exists in a PDF file, it kind of looks like that stuff I have in the blue box there. It's kind of a key value pairing.

So the type of this object is annotation. The subtype of this annotation is circle. You know, this is the kind of attribute that we would return when you call the type method on the annotation. The rect, that's gonna be the bounds that you would get back, and that's in points, so it's an array of floats, 10, 10, 80, 32, so this thing's 80 points wide, 32 points tall. And then finally, that last one, C, that's color. So a color parameter describes what color this annotation is going to be, and in this case, it's RGB values, so the first value, 100% red, no green, no blue. So I can tell just from these parameters that this is gonna be a red circle, and I know where it's gonna be drawn. And there are certain defaults. If there's no line width specified, we assume 1. If it's not specified whether it's solid or dashed, we assume solid. So there's enough information here that we can just parametrically render this annotation.

And in fact, when you call PDFKit to create annotations or when you call the PDFDocumentSave method to write out a new PDF file, we write out all these parameters for the annotations. So all the setters that you call to set the color, to set the line width, will ultimately turn into parameters that look something like this when they're written out to disk. But here's kind of the schism. There's another way that annotations can be drawn, and that's with an appearance stream. So Adobe defined an appearance stream. It's basically-- if you're familiar with PostScript, it's kind of like-- I mean, it would look very familiar to you. It's a series of drawing commands. For the circle, it might be, you know, create this path, set the pen color to red, and then stroke the path. It's probably equivalent to calling the parametric style of drawing, but not necessarily, and that's where it can be kind of confusing because the Adobe spec says if there's both an-- both an appearance stream and parameters, that the appearance stream wins. So in PDFKit, that's what we're calling. If the annotation has an appearance stream, we render that appearance stream.

And furthermore, when we're writing out PDF files, we call each annotation's draw method, and we record the drawing from that draw method into an appearance stream, and we flatten that out when we write the PDF to disk. And why that's kind of interesting is-- this is what I was talking about earlier. If you override this drawWithBox method on the PDF annotation, if you subclass an annotation and override this method, you can do your own drawing, and it's gonna get saved into an appearance stream. And then when you open that PDF document in Adobe Reader or any other application that supports PDF, you're gonna see your appearance stream rendered. So like I said, it kind of gives you a way So rather than talk more about annotations, I'll show you them on demo machine.

Annotations are probably one of those things that lend themselves better to kind of showing than talking about. So the application is called PDF Annotation Editor, and once again, you can download the sample code for this. Here's that PDF that I had on my slide that has all those annotations on it. I've just opened that up in a PDF view. If I select the text, by the way, you can see that very little of the content of this PDF is actually, you know, part of the PDF page itself. Most of it is our annotations. And for each annotation, I've got this kind of edit and test mode button down here. And suffice it to say-- I mean, I didn't really talk about this in the session, but you can look at the sample code. What I'm doing is I'm subclassing PDF view in this case. And when we're in edit mode, I draw little gray boxes around all the annotations, and I intercept all the mouse-downs so that if I click on an annotation like this circle over here, I can intercept that mouse-down, and now I note which annotation is selected. And this is kind of the interesting part of the application is over here I've got this annotation inspector, I guess. And depending on what kind of annotation you click on, I populate this palette with some of the attributes, some of the parameters for that annotation. So this is a circle annotation.

It's got a border color. It's got an interior color. It's got flags set to both display and also be printed if the user prints. It doesn't have an action associated with it. It does have a border with four points thickness, and it's set to be dashed. This is a square annotation. It has a border color but no interior color. So I'm just calling -- in this application, I'm just calling these getters and setters to get these parameters. And I'm populating this inspector over here with that.

Let me open up a PDF that doesn't have any annotations on it, though. It's that small one again. And I'll show you the other kind of fun thing about this app. I can go into the annotation menu and create any of those annotations. So let's go ahead and create a circle annotation. So if I go into the inspector, I can change. It's got some kind of default values, but I can go and enter in, like, here's the line width for this annotation, and I can bring up the color picker, and I'll make it red. Why not?

Set the red. I can make it dashed. I can give it an interior color or not. So there's our circle annotation. It was all created programmatically, just calling these classes or these methods in PDFKit. I can add... Let me add a stamp annotation. This is that one annotation, by the way, that I said doesn't really have any parameters that describe how to draw it. So a stamp annotation really pretty much requires an appearance stream. If you create a stamp annotation with no appearance stream, even Adobe Reader doesn't really know what to do with it. It'll just kind of give you a box. So what I've done-- and you can see how I've done this if you take a look at the sample code-- is I've subclassed the stamp annotation in PDFKit, and I overrode the draw method. And I'm just drawing the string apple here. Actually, I think it's a PDF. So I'm scaling it to fit the bounds of the annotation. And if I save this-- let me go ahead and place it here and hit Save. If I save this PDF-- and I'll call it annotated-- and now go into Adobe Reader and open it up.

Again, this is kind of my-- showing you that there's my Apple stamp annotation being rendered from its appearance stream, and the circle as well, even though it does have parameters. And in fact, let me just show you that one kind of conundrum, since I do have time here. Let me go ahead and open up that annotated PDF again. See, one of the problems is, since that PDF was written out, now there's an appearance stream, And since the appearance stream wins when there's both parameters and appearance stream, I can go here and uncheck the dashed on the circle, for example, or change its thickness to one point, and you don't see any change because we're still rendering the appearance stream. And this is where you get kind of into a schism where, like I said, the parameters and the appearance stream don't necessarily have to match. In fact, if I resize this, you'll see that what's really happening is just the appearance stream itself is just being scaled to its bounds. There's a method in PDFKit for basically stripping off the appearance stream, and that's the kind of thing in this application I should probably add a button or something like that so that you could click a button and strip off that appearance stream, and then you would see it rendered with its parameters instead.

So let me go back to the slides just for one last little tour through annotations. 'cause I'm just gonna spend-- I was told, by the way, that this-- this WWDC is kind of gonna be geared towards-- or at least for the Mac OS X content-- kind of geared slightly toward power-- power user, power developer. So I thought I'd spend a couple of slides on widget annotations, 'cause these are by far the most kind of loaded annotations that the PDF spec defines. So a widget annotation is what-- what most people think of when they talk about forms.

These are the text fields, check boxes, radio buttons, the kinds of annotations that you see in a tax form, for example. The type is actually widget. And why these kind of complicate things is-- or why they're kind of the most loaded annotation is that they kind of introduce a kind of, like, database-like quality to the PDF document. So if you have a text field and the user types in a name, Now there's kind of a name associated with that text field, so it kind of gets kind of database-like real quick. The annotations that PDFKit exposes for those widget annotations are these three-- the button widget, choice widget, and text widget. And one thing that all three of these have in common is-- and this kind of alludes to that sort of database-like nature-- is they all have a field name associated with them. So, you know, text widget, button widget-- there's gonna be a field name, and that name, even though the user never sees what this field name is, that essentially is the key into or the field of that internal database. So in global to the PDF document, there's going to be a field for whatever that field name is in that widget. And this is also a bit of a schism here as well because, you know, while I kind of was implying that, you know, the document owns the pages, the pages own the annotations, that there's this kind of nestedness, For widget annotations, they kind of break outside their page, and the field name, at least, is document-wide. And I'll show you the importance of that here on the next slide. So here's an example.

Here's a text widget. And I've got a little screenshot there of a tax form, and I've highlighted one of the fields. And what you don't see is that there is a widget annotation that's laid basically just inside that box. I've typed in the string public, for example. Now, internal in this document, I don't know what the field name is unless I looked. I mean, the user actually doesn't see what the actual field name is, but I'm going to assume it's something obvious like last name. So when I typed in the word public, then internal to the PDF document, it kind of goes to the field named last name and assigns it the value public. And now anywhere else in that PDF document, if there's a text widget, doesn't matter what page it's on, you can have multiple on the same pages, if you have a text widget that has the same field name, it's gonna be updated as well. So you change it in one place, it's gonna be changed everywhere.

So that's kind of what I meant by the namespace of the field is global to the document. So buttons kind of take it up a notch. Here's an example of the same PDF file, and there are two boxes, checking and savings, and one of them has the blue check mark in it. Each of these boxes has a button widget within it. And so I'm calling them widget A, widget B. One thing you'll notice that they have in common is they share the same field name. So that kind of intrinsically links these two widget annotations together. If either one of these changes, it's going to affect the same field name in this PDF document database. The way they differ, though, and this is where buttons are kind of different from text fields, is they have a different onState value. And so this is the value that gets assigned to that field if that checkbox or that button is selected. So in this case, since IC Savings has a checkbox in it, we can assume that internal in the PDF document, the field name type-- I just picked type here. I don't know what it is-- is equal to the value Savings. But let's say--I'm just gonna kind of walk through a scenario here. Let's say the user clicks on the button widget on the left. So what happens inside PDFKit? Well, the first thing that happens is we look at widget A's field name, and we see that it's type, and so we look that up in the database, and since the onState value of widget A is checking, we assign the value of type to be equal to that onState value of widget A to checking. So we've changed the value internally in the document.

The next thing that happens is a series of these notifications get fired off, and these are internal notifications that get sent to each widget, and basically it says, "Hey, you know, the value for type has changed, "so any widget that has the same field name type "needs to be reevaluated." And the way they evaluate is they compare their on-state value with the new internal value for that key, for that field. So in the case of widget A, its on-state value is checking.

The document's value is checking, so they match. That means that widget A is on. Widget B, on the other hand, no longer matches. Savings doesn't match checking, so widget B is in its off state. And... there are on and off appearance streams. So depending on whether a button is in its on state or off state, its on or off appearance stream is rendered. So the widgets are told to redraw, and based on their current state, the correct appearance stream is rendered. So again, let me just show you that.

I'm gonna use the same application, PDF annotation editor. Except this time, I prepared a PDF I don't know what application you might be creating here, but here's a PDF that's got some nice rectangles in it. Here's name and a nice little box that's just begging for a text widget. So I'll go into the annotation menu and I'll go down to the text widget and we just created a text widget here and I'll try to place it more or less inside this box. And over here on the inspector now, here's the text widget's field name. So I'll give it something meaningful.

I'll call it name, for example. Here's some other attributes we can set for that widget. I'll just leave them at their default values. But in order to show you kind of the way two widget annotations can share the same field name, I'll go ahead and create another text widget. And I've only got a one-page PDF, so I'll just leave it on this page. And I'll give it the same field name.

This one, just to be different, though, I'll change its alignment. So if I go back to my, like, test mode here, now all the mouse events are being passed on to PDF view, you'll see already that the cursor has turned into an I-beam. So PDF view recognized that there's a text widget and has changed the cursor to an I-beam. If I click, an NS text field is created in the same place where that text widget was. And I can type a value here. I'm going to type Conan. Amen.

And as I'm typing this value, you'll see that internally, again, the database value for the field name is being set to the string I typed, in this case, Conan, the barbarian. And then elsewhere in the document where there's the same field name shared, it's updated there as well. And I can come here and make changes, and it just is reflected in both places.

So I'll show you an example of button widget. Here's gender, male, female. It might be nice to have a button widget there. So I need to go back to my edit mode here. Let me make this turn off the background, make it a radio button, and-- I'll try to size it as best I can. So what do we want for a field name?

Well, we're going to have two buttons, and we want them to share the same field name. We want to link these buttons. So I'll give it the name gender. But what I'm going to set for its on state value is male in this case. And then let me try to-- I'm going to create another button widget, and I'll try to make it more or less look like the other one. And I'll try to position it as best I can.

Something like that. It's close. So this one, we also want the same field name, gender. But obviously-- gender. But obviously, we want a different onState value. So I'll set this one to female. So now when I go to test this-- I can click in the male, and we've just set the field gender to the string male, and female doesn't match, so it is not redrawn with its on appearance until I click on it, and now male is redrawn and doesn't match, so it's drawn with its off appearance. And then here's my kind of last, The proof is in Adobe. I'll call it widget. Save that PDF out. Go into Adobe Reader. Open up widget. And here's our text field. I'll try to type this correctly. And you see that it's reflected in both places. The buttons work as you would expect. So there you go.

So that's it. Allan Schaffer is the man to contact if you want to email someone about anything, any questions. There's a lot of documentation online. Like I said, you can look in the headers, but if you want more of an overview of PDFKit, there are document you can download. Obviously, they're PDFs. There's a bunch of sample code. I showed you three today that you can download, but there's other sample code I've done for previous WWCs you can download that cover different aspects of PDFKit. And I think we've got a lab today at 2 o'clock. So if you have any questions or want to come down and see more of this stuff, I'll be here from about 2 o'clock until the end of the show today. you