Graphics and Media • 1:13:58
Understanding how to take advantage of Mac OS X's PDF capabilities is important for all application developers. Learn how Quartz 2D and PDF Kit benefit your application by giving easy access to the robust graphics and final form document capabilities of the PDF format. Also learn how Mac OS X's PDF-based printing architecture can increase the features and capabilities of your application via its PDF Workflow feature. Don't miss the opportunity to harness the power of PDF in your application.
Speakers: Paul Danbold, Richard Blanchard
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it may have transcription errors.
Good morning, and welcome to the PDF session. Paul Danbold, one of Apple's evangelists. I'm going to start this session with a little bit of an overview of all the PDF services in the OS. Thanks. The goal here is to make sure you get the big picture, what we've done in the OS to support PDF, why we designed the APIs the way we did that, the various tools and things we built upon those APIs. And you can see what we've done manifested in many of our applications, like Preview and other PDF-savvy apps. Most important, of course, is we want you to get the message that you should support PDF in your applications and software products and try to make sure you understand you can tap into the OS at what level in order to provide PDF functionality.
Does anybody in the room not know what PDF is? Stand up and wave your hands if you don't. I was like, we'll take care of you later. OK, so we're not going to talk about the details of the PDF specification, but we'll take just one slide, basically to remind you why PDF exists. I'll let you read this quote from John Warnock, and then just make the simple point that when we designed our APIs to support PDF, the model we had in mind was true to the original spirit of PDF. basically as a container for text, graphics, images that's efficient, high fidelity, device independent, et cetera. And so when you look and evaluate our APIs, that's what you should bear in mind.
Uh, so I'm not here talking about T-Raw for some other file format. We're talking about PDF today, and we picked PDF because it met our requirements. And just got a couple of things to say about the ways in which PDF meets our requirements, and I think it obviously, hopefully, meets yours. Uh, one thing that almost goes without saying is that PDF is a proven technology. Uh, if anybody here is from the Acrobat team, thank you very much.
Uh, your product is, uh, coming up for 12 years old. I think actually sometime next week. And the PDF spec itself is a little older than that. It's been around for about 13 years. So it's well established. Pretty much anybody on the planet knows that if you want to create a document that may be viewed on other computers and printed, et cetera, and appearance matters, then PDF is the choice. It's definitely the best choice.
Another thing we like about PDF is that the spec is public. Go to adobe.com any day and download the specification. You don't have to sign a license agreement or, you know, do any NDA stuff. You can read about PDF in detail and go write products that use PDF. Not quite sure the same is true of Metro, but we'll see. And talking about that, or sort of tying into that, another thing we really like is that PDF is cross-platform. You can create a PDF on any computer system, and that PDF might be viewed on any other computer, and it might end up on paper, and modulo the capabilities of the target device, it's going to look the same.
Another thing we like about PDF, because there's lots of PDF documents in the world, is that it's a compact and efficient file format. You know, if you have an image appearing multiple places in a document, only one instance of that image can be stored in the file. And the same goes for other things that tend to take up a lot of space, like, you know, fonts and color spaces. So it's an efficient file format. And then if you're security conscious, a nice thing about PDF is that you can encrypt and password protect PDF files.
So all those things are good for us and good for you, good for customers. That quote that I had a couple of slides back from John Warnock had the word paper in it. And we like to think of PDF as digital paper. And there's some interesting aspects to that. Unlike, say, HTML, when you create a PDF file, you as a creator control what that PDF file is going to look like.
It's formatting. So regardless where that PDF is viewed, on somebody else's screen, on paper, the format should be preserved. All that information is stored inside the PDF file itself. And then when it's drawn on screen or goes to paper, then it can be drawn with the highest fidelity and the best color accuracy possible for that target device.
And it is page-oriented, which means you as the creator of the PDF file, you have some saying where the page breaks are, and for every page size, you control the margins, et cetera. And it's random access. So kind of unlike PostScript, the viewer of a PDF file can go to any page in any order and display it, print it, et cetera.
Um, as Spotlight shows, and as you can do in applications like Preview, etc., you can search PDF files. That's pretty easy. Um... I mentioned last slide, it's encryptable. So, you know, you can password protect a PDF file. If you open it up with the owner password, you've got unlimited privileges to go as far as changing the content of the PDF. If you open it up with a user password, you have limited privileges. And so you can usually view it, but you may not be able to copy the file or print it.
And last, you know, PDF was never designed for inline editing, like you can edit an RTF file, etc. But if you want to circulate a PDF document for review, those other people can circle things they don't like, add little text stickers, etc. to the document. They can't fix your spelling mistakes, but it's an excellent format for groupware and things like that. So put all that stuff together. Basically, PDF is great for a lot of purposes that are important to us. That's why for printing, it's our default spool file format. That's why it's a native file format for our graphics stack. And it's why we make it so easy to create PDF on the platform. You can do it from the print dialog for any application.
So here's where you come into the picture. What can you do with PDF? And I guess the answer ranges from nothing, in which case I don't know why you're in this session, but to everything that you can do in, you know, apps like the ones we provide, like Preview. Safari's a good PDF client as well. Generally speaking, the more your application deals with text, vector graphics, images, et cetera, the more PDF functionality you should build into your application.
I'll let you read this list. I just want to make a couple of points. The first one is if you provide the functionality to open up a PDF from the file system or somewhere on the network, we really think you should be able to open up PDF from the pasteboard. So that's a great solution for getting PDF from one app to another. Another thing is although we provide PDF generation from the print dialog, something you should think about is whether your users are going to think to go there if they want to create PDF. So you should think maybe about adding a Save As or an Export item under the File menu. If your application is, let's say, a music application or deals with a lot of numbers or video, for example, then maybe Export makes more sense than Save As. Anyway, something to think about.
I mentioned the fact that PDF isn't designed for editing, interactive editing, but there's nothing to stop you writing a software product that can modify PDF files for very useful purposes. I mean, for example, a printed position program is going to relay out the pages in a PDF document for maybe printing a booklet. So we'll get into all the other things mentioned on this slide later on in this session.
I've got a diagram here, PDF services, just to give you kind of the big picture for what you can do with PDF. And, you know, the gist of this diagram is that Quartz 2D is the mothership. If you want to go down for the finest brain control over PDF processing, the Quartz APIs provide that for you.
And you may well want to go down there to implement the functionality you want, but something we're going to focus on in this session are the things that are on the second layer. I'll get into those a little bit later, and then we'll really go into depth on things like PDFKit. The important thing for you coming out of this session is to know that if you want to add PDF functionality, yes, Quartz may be the place to go, but then there are the higher-level services that may make your life easier.
So we'll start with Quartz, Quartz 2D. It contains basically a comprehensive set of APIs for working with PDF, PDF creation, opening up, drawing PDF, et cetera. The good thing is that we've done the heavy lifting for you. All the APIs, easy to use, very well documented, lots of sample code. So if you want to add PDF functionality, you're very welcome to read the PDF specifications. It's about 1,200 pages long. But you don't really need to do that. Look at our documentation, start using our APIs, and you'll be off to the races pretty quickly.
Definitely not in this session gonna give you, you know, the full set of PDF APIs out of Quartz 2D. I just got two slides here with a, you know, a small sampling of the PDF APIs. Just wanna make a couple of points. We'll repeat the point about the pasteboard.
Down on that third bullet item, I want to make the important point. You know, we enable you to open up protected PDF files, but it's an honor system. So if you open up a password-protected PDF file, it's your duty to honor the privileges that are in that PDF file. So you should call the APIs that we provide, and I haven't listed them here, but, you know, we provide a function to check if it's okay to print the PDF file. And in fact, even if the password string is blank in the PDF, You should always check the privileges before you enable any type of functionality on the PDF document that's been opened up.
Um, and I'm going pretty quickly through this slide, but just looking down at the bottom, uh, bullet item, we have--especially in Tiger, we've provided full functionality for PDF. You can get at everything. And there are lots of APIs for getting information out of a PDF document once you've opened it up. There's document-oriented APIs, page-oriented APIs. So we're really giving you full access to the PDF.
When it comes to creating PDF, that's also extremely easy. If you already know how to use our drawing APIs to draw into a window context, you can use that same code and draw the same content into a PDF context, and you've got a PDF file. So it's really easy. You should also enable writing PDF to the pasteboard, like I keep mentioning. In Tiger, we've added a couple more things just to sort of keep up with the latest capabilities of PDF. you can create links from the document you're creating. I haven't shown the API to do this, but you can create a link to a web page. The two functions I've listed here enable you to create a link between, you know, one rectangular area on a page to somewhere else to point somewhere else in the document.
The last thing we did in-- the last thing mentioned on this slide that we put into Tiger was really to give you 100% access to the content of the PDF. So you can now get into the page content stream. This is the point, if you need to do this, where you really do need to start reading the PDF specification. Once you know about PDF operators and the whole imaging model, if you need to-- and again, this is only for very specialized purposes-- you can get down into the contents of every page.
And we can never talk about PDF without somebody getting up and saying, where are we with respect to the latest specification? And the simple answer is, we're up to date. Generally speaking, any PDF document you find should be rendered correctly by Quartz 2D. It should look good in preview and any other application that uses it. If you run into a document that doesn't render correctly, send it to us. File a bug report. We've got a couple of known exceptions, knockout groups and some oddball shaders. So there's a few things that we may fill in in the future, but basically we're up to date with the latest spec. When it comes to writing out PDF, you don't want to assume that everybody who reads your PDF supports the latest spec, so compatibility is the important thing.
We start off with 1.3, the 1.3 spec. That's our baseline, and then only when we need it do we start using things out of 1.4 or 1.5. So we use some 1.4 stuff for transparency, et cetera. Thank you. OK, just want to make one more point before I move off from Quartz, and that is to say, to remind you, all the features are there, easy to use, well designed, well documented. The other thing you shouldn't forget is it's free. You've got the Quartz framework, or rather the Core Graphics framework. All those APIs are there for you.
Now it's these other things that we're going to cover in more detail in this session. So I'm not going to steal the thunder from the folks who are going to take over after me. Just to take you through the layers quickly. PDFKit, if you've got a Cocoa app running on Tiger or later, PDFKit is probably your answer. Very easy to use, and just look at what you can do in preview, mail, even Xcode, definitely Safari. If that kind of functionality for PDF meets your needs, then PDFKit is your answer.
You know, we've got all the APIs to generate PDF, but sometimes you want to repurpose PDF. You might want to generate PDFX3 for sort of print, for sort of pre-press print applications. PDFX3 is a subset of PDF where you want to control the type of PDF generated, you know, just to make sure you don't have nasty surprises when the PDF goes to the press. And you can do that with a Quartz filter. You can also do things like repurpose PDF for viewing on the web where you don't want very high resolution images. So the place to go to create those Quartz filters is the ColorSync utility. You can go there, create Quartz filters, look at the ones that Apple provides pre-canned for you, test them out, and apply them. So you can repurpose your PDF files with Quartz filters. And they're accessible from Preview and the Print dialog.
To state the obvious, the printing system knows all about PDF. We have code that will turn PDF into PostScript, PDF into Raster data. We have code to turn PostScript into PDF. And what we'll get into, and what Rich Blanchard touched on in the previous session, was we allow you to hijack the PDF coming out of the print dialog and send it to your code, your applications, tools, or scripts, whatever, and use this mechanism to create PDF workflows.
Uh, something else we'll cover later on in this session, uh, Automator, which I assume you all know about. Uh, it has a set of PDF actions, and we'll be showing you how to create your own PDF actions, you know, to extend what you can do with PDF in automated workflows.
And since Panther, there's been course bindings for Python, so you can now generate, modify, and do other things with PDF in Python scripts. I just got one last thing before I hand over to talk about, and that is that PDF is a great solution for legacy formats. So if your software is used in places where people insist on continuing to use EPS graphics and things like that, then PDF is a great solution. Of course, PDF shares the same imaging model with PostScript. So when you're handling EPS, for example, using our PDF APIs, you don't lose any of the information in the graphic Text stays as text, lines stay as lines, et cetera. If you use the API, some of them listed here, then what you can do is you can render the EPS with full fidelity onto the window and print it correctly. You don't have to see the jaggy preview images either on screen or print. So for Carbon, Cocoa, and even down at the BSD layer, we let you handle PostScript-based, what I call legacy file formats using PDF. And the same thing really applies for Quick Draw, which also is deprecated. So if you want to bring in Quick Draw data, picked files, et cetera, to screen or out to print, you can do that as well, both from Cocoa or Carbon.
So that's the whistle-stop tour of everything we've done with PDF in the OS. Just a quick summary, and then I'm going to hand over. So if you're in any doubt, think about the PDF functionality you need to put into your application or software product. If you need the fine-grained control, Court Studio is your answer. All the APIs are well-designed, documented, et cetera. Lots of good sample code. But if you want to use PDF and you're running Tiger or later your Cocoa, then pay attention to the next part of this session, because PDFKit is just great. It gives you almost all the PDF functionality you're likely to want. And for the last part of this session, we're going to get into PDF in the workflow. There's a lot of things you can leverage that's sitting on top of Quartz 2D that will make your lives a lot easier. So with that, I'm going to hand over to John Calhoun. Let's talk about PDFKit. Take it away, John. JOHN LUTHER: Thanks. Hi.
Okay, so PDFKit is a new framework in Tiger. There's actually a new framework called Quartz Framework in System Library Frameworks, and that's kind of an umbrella framework. PDFKit's one of the frameworks inside Quartz. And I mention that because if you want to write a Cocoa application today, you're going to have to manually add the Quartz Framework to your application to get the PDFKit framework support. And also, while I mention it, if you're using Interface Builder, One of the things you might want to use from PDFKit is a PDF view, and that's inside a PDFKit palette, which is under Developer Extras Palette, so you'll have to add that to Interface Builder, and then that'll give you the PDF view that you can add to your application.
So PDFKit is new to Tiger, and it's being used currently by Preview. It's also being used in Safari. If you don't have a PDF plug-in installed, it'll use PDFKit by default. And ostensibly, it's a suite of Cocoa classes that are built on top of, as you saw, Quartz 2D. So all the same sort of functionality that Quartz 2D has percolates up into PDFKit. And if you're familiar with the Quartz 2D model and their API, then I think you'll see a lot of parallels. They have Core Graphics, or Quartz 2D has a CG PDF document, and there's a corresponding PDF kit, PDF document object, same with pages and that sort of thing. I give you kind of a little, I guess, architectural diagram here. But last year, I gave more of an architectural talk. This session, I'm gonna try to do-- my little segment of the session here-- is I'm gonna try to do much more of a code-driven session. But I will try to sneak in a little bit of architectural comments from time to time. So the first thing I want to show is-- over on the demo machine-- is Safari using PDFKit. And I'll be brief, but... I'll show you that in a second. I'll just turn off voiceover. So the reason I wanted to show you Safari initially is because Safari is more or less just using a PDF view, and I'll get more into that later, but, I mean, it literally took the Safari team maybe two or three lines of code to get this kind of PDF support into Safari. So if I find a PDF on a website somewhere, and let me go to some tech specs.
Okay, here's a Spotlight PDF. So I don't have any native plug-in, or I don't have a PDF plug-in installed, so Safari just basically replaces their web view, essentially, with a PDF view. So what you're looking at, for the most part, is just this PDF view. And you see that you can display the PDF. You can scroll through it, select text. If I copy and go into text edit and paste, we're trying to preserve as best we can the font characteristics and that sort of thing. And this is something that you get for free with PDF view and PDF kit.
The Safari team didn't have to write any of this code. Even without UI, even without buttons and things, you get a contextual menu. So I can control click in the view and I can turn on facing pages. I can turn on auto size if I want so that the document will be scaled dynamically as the view is resized.
So you get all this kind of functionality for free. And if I can have the sound on again, I'll turn on voiceover. http://images.apple.com/macquest and/pdf/macquest and spotlight pd.pdf window back button. If you saw Scott's demo, I guess it was Monday, you may have seen he showed VoiceOver. And I'm not going to go over the whole thing again, but I wanted to kind of point out that because PDFKit is on Tiger and VoiceOver is new in Tiger, we took advantage of VoiceOver so that any application, and in this case Safari Preview has this functionality as well, gets voiceover support for free. So you see the little black box around the back button? That's voiceover choosing that UI element to have focus by default.
But I can... Let's see if I can remember how to do this. If I can switch the focus to the PDF view... Spotlight, find anything on your map... Okay, so now it's reading through the text. And there are ways, and I'm not an expert at this, But now let's see. There we go. So I can navigate through the PDF document and have the text read to me by voiceover. And so that's one of the things that you get for free with PDFKit. So if I can go back to the slides.
Okay, so here's the first sort of little architectural slide. PDFKit is essentially a suite of these PDF class, NS object or AppKit Cocoa classes. You can see that the one in yellow, the PDF view, is going to be the more complicated one. It's subclassed off NS view, so all the sort of things that NS view has and inherits, like a view, hit testing, events, a draw method, PDF view has as well. And most of the talk that I give will sort of focus around PDF view, but I don't want you to think that you have to have a view-based app to use PDFKit. The other classes that you see in blue are-- I call them kind of utility classes-- PDF border, destination. They don't require a view, and you could just use these if you wanted to search for text in a PDF or get the position of each character on a page, that sort of thing. I'll talk more about these classes later as they come up. The only thing I wanted to point out, though, was the other sort of utility classes are all just subclasses of NS objects, so they're simpler, I guess. And then the one class at the bottom, PDF annotation, is a virtual class. So all the real work, for the most part, it happens with those subclasses. So a PDF annotation could be a circle or free text or a link. And so that's where some of that functionality is going to come in. So I already explained this, that there's the high level view and then the utility classes. So let me just show you then a little app I wrote. and back on demo machine one. I guess I didn't get that turned off.
So this is an application I wrote-- PDF linker. PDF linker voice. Let's see if it turns off this time. It's supposed to be Command-F5. So this is PDF Linker. And this is available. All the sources for this are available. You'll see the URL at the end. It's available from DTS. Should be up on the website now. But I'm just going to first thing just open up a PDF. And you'll notice that we borrowed a lot from preview here.
There's a toolbar at the top. At the bottom, I don't know how well you can read that, especially in the back, but there's some UI. There's some radio buttons that are grayed out. A drawer has opened on the right side. But the main part of the window is the PDF view, so you see the PDF content here. So I'll switch back to the slides and show you.
how you first get the document to come up. And this is only, this is, like I said, this is the two or three lines of code that Safari really had to do in order to get a PDF to display in the PDF view. This is where the first utility class comes in. A PDF view needs to have a PDF document associated with it. So what the code does in PDF Linker is it allocates a PDF document, and then there's a method called initWithURL. So usually your application has a URL, like if you're in this document-based app, you can ask the document for its file name, and that'll give you a path. Or if the user has just brought up an open panel, you can ask for the URL. But there's another method in PDFKit for creating a document from data itself. So once you have this PDF document object, you tell the PDF view, "Set document." And then once you do that, the view is gonna retain the document, so the last line of code just releases it.
So that's all there is to associate in a document with a view. So I'll show you just a few of the attributes, then, of the PDF document. You can ask the document for the number of pages. Oh, I should point out, once you call set document on the view and release it, you can always ask the view for its document back. So the reason you might want to do this is for some of these attributes.
You may want to query the document and find out how many pages are in the document. The way that you get back the pages is to ask the document for one of these PDF page objects. And I'll talk more about that later. Certain document information, like the author of the document, the title, subject, keywords, those sorts of things you'll get from the PDF document object. Searching actually happens within the document itself. That allows the document to kind of search across pages. if the PDF document has an outline associated with it, it's from the PDF document that you'll ask for that outline, and I'll show you that in a minute. And then finally, if you want to save the document, the--save the PDF, the PDF document class has the save methods in it.
Okay, so back to the demo. So how about navigation? So within the PDF view, I've got a toolbar at the top, and you see the next button. There's a previous button that's disabled. If I click on the next button, it obviously tells the PDF view go to the next page, and you see now that the previous button is enabled. I can click next, next, previous, previous. Let me turn something off here.
I can--and you also see that within the PDF, when there are links, like if I click on this link here, it takes me to page 24. That happens for free. The PDF view is handling that. But the PDF view is also maintaining a history. So as the user navigates and goes to the next page, previous page, or traverses links, the PDF view is maintaining a history so that I'm able to very trivially put a back and forward button in the toolbar and have the PDF view navigate through the history. Also, another way to navigate is to type in an explicit page number, like I typed in page 20, and then the PDF view will go to page 20. Something I'll just show you is you may have noticed that as I've been kind of moving through this document, this page number field here has been updating. So what I'll do is show you in code how that's done, and show you frankly how easy that is to do. Back to the slides.
So in order to go to the next page, you just tell the PDF view, go to next page. Go to previous page. I told you that the PDF view is maintaining a history, so you can just say go back, go forward. And then there are sort of helper methods like can go back and can go forward that return boolings so that you're able to enable or disable UI elements in your app. Then the last method of navigating where I typed in a page number, there's a method on the PDF view called go to page, and it takes a page object. And remember I said that you get back page objects from a PDF document. So you would ask the PDF document for the page that corresponds to an index. And since we're zero-based internally, if the user typed in page 20, you'd ask for the page corresponding to index 19. So I subtract 1. I ask the document to give me back that page object. And then the view has a method goToPage. I pass it that page object. And the right thing just happens. The view updates, goes to page 20, and that's added to the history.
And then as far as how your application can show what current page the user is on, there's a notification in the PDF view. So every time the view changes page, either as a result of you calling go to next page, go back, go forward, or the user actually clicking on a link internally within the view, a page change notification comes up. There's other notifications at the document level. PDF view has other notifications as well. So I'm just kind of going over the surface of it in my little You're going to always check the documentation and find out a lot more than I'm going over here. So back to the demo app.
So I'm going to be brief here, because Preview does a much better job of this, but there are a number of display options, and I already showed you a number of those in Safari, that you always have this contextual menu. And one of the things I added to this PDF Linker app is I put in a little toolbar item that allows a user to select between-- I just picked three various display modes. So here we're in, you know, sort of the single-page continuous mode where I can scroll through the entire PDF, or I can switch on-- by clicking this toolbar item, I can switch to the one single-page mode here without the scroll bar, and the user has to navigate this way. Or the two-up, facing pages mode like that. And if you look at-- I'll switch back to my slides.
In order to control that, to control how the view displays the PDF, there's just a real simple call, PDF view set display mode, and we have a number of defines for one up, one up continuous, facing pages, facing pages continuous. So it's real trivial for me in the toolbar item to just call one of those-- set one of those display modes in the PDF view. But like I said, I'm not gonna go over all the other display modes. You can play with preview to get kind of a sense them are or look at the documentation. But you can specify what box to use for the PDF to display, whether to use its crop box or its media box. You can turn on and off page breaks. You saw that you can change the scale that the PDF is displayed at. For more esoteric things, you can set the text-greeking threshold, turn on and off anti-aliasing, that sort of So back to the demo again. Outlines.
So this document I happened to open up has an outline. Not all PDF documents have them. But you know it has an outline because the PDF linker application checked that there was one and opened up the drawer automatically. And inside the drawer here, you see that there are four items-- there's contents, three other items. And one of them, the third one here, Quartz 2D reference, has a disclosure triangle. If I twist that down, I see that that item has four sub-items and then some of those have sub-items, etc. And clicking on any of these items, the PDF view is updating. The view itself that's in the drawer is just an NSOutline view, so there's nothing, you know, PDF kit-specific here. But I'll show you then how that's all accomplished, and this does take a little bit more code. So back to the slides.
Um, so... the PDF outline is one of these new utility classes. And like I said, not all documents have it, but if the PDF document has an outline, you just ask the PDF document, "Give me the outline root." And if it returns an object, then your PDF has an outline. And that root object is a PDF outline object. So I'll show you then the next utility class, PDF outline.
Here are some of the attributes. The outline item can have children, and as you saw, the root item is the one. We actually never display the PDF outline root, but that root for that PDF that I showed you had four children. So when I ask that outline root for the number of children, it returns four. Then four, I can index over each of those and ask and get the array of, I can get back the PDF outline children, and then each of those children, as you saw, could have children as well. So an NS outline view just kind of allows you to kind of traverse that tree, and it'll automatically take care of the disclosure triangles for you. So that, like I say, is not part of PDFKit. What PDFKit gives you are the PDF outline objects to, you know, populate that outline view. An outline object that has a label associated with it, that's what's displayed in the outline view. So contents, for example, was the label for the first child. And a PDF outline object has a destination associated with it. So when the user actually clicks in the outline view and picks one of these destinations, there's a method-- and again, I didn't show you every method-- but there's a PDF view method called go to destination, where you pass it this destination, and the view just goes to that destination and again maintains it in its history, its navigation history.
There we go. Okay, searching. So as kind of we go on here, back to the demo machine, kind of as we go in, what's happening is the amount of code that would be required to write is increasing. We started out with one line of code here, one line of code there. Searching is a little more complicated. So what I can do is, for example, and Safari actually, they did have to write quite a bit of code to get this working, or I shouldn't say quite a bit. They had to write more than two or three lines of code to get this working.
But let's say I bring up a find panel. Now, I had to provide-- my application had to provide that find panel. And the user types in a word, like "quart" say. And I've got a checkbox here I can turn on and off, whether or not to ignore case. When I click on the next button, the view that you provide has-- tells the PDF view to find this word. And I can click next, next, next, next, and you see that the PDF view finds the next instance, next instance, previous, previous. So this is kind of the standard find method, I guess.
I call it the Safari style because they implemented it with PDFKit. But there's another way to do a search, and this is more like, I guess, the preview style or maybe the Google style, that I type in a search term, And now it goes out and you see that the window, I've kind of--it's turned into a split view, and the top half of the split view is dropped down to show an NS table view. And in this table view, you see all the instances of the word "quartz" that were found in this document. I've got the page number over here. I've got the section that it corresponds to, and that would be the outline item over in the drawer. And then I have a little bit of--I want to call it-- sample text that surrounds where that instance of the word was found in the third column of the table. So go back to the slides.
So I'll show you how you do this, and it's actually not that difficult. So I've shown you there's two actual methods of searching a PDF inside PDFKit. There's actually a third, but it's kind of trivial, so I won't waste your time with that. But the first method is a little more complicated. So there's a method on the PDF document called find string from selection with options.
The string is obviously the string that the user typed in. The example I gave, you type in the word "quartz," the fine string will be the word "quartz." From selection is a little more complicated, and I'll get back to that in a second. The with options, the options are trivial. AppKit has search options like NSBackwardsSearch. And you saw I had a check box for case sensitivity. There's an option for that as well. So that's where you provide those. To do the previous, when the user clicked on the previous button, I was using that NSBackwardsSearch option and and passing that to the PDF document.
So the from selection is the kind of complicated part. So when it goes out and finds the instance of the word, what it returns to you is a PDF selection object. And you can take that selection and make that the current selection in the view. And you look at the last couple lines of code here. That's what I'm doing. If I get back a selection, I'm telling the view, make this the current selection. And then I'm telling the view to scroll that selection to visible. Well, you see that back in that first search method, I'm asking the view for its current selection. And I'm using that as the parameter that I pass to the find method. So what I'm essentially saying is start finding this word "quartz" starting from the current selection. So it starts from that point and then continues on through the rest of the document. So if I sit here every time the user hits next, next, next, and I just run this code, essentially we step our way down through the PDF document. At some point, it's going to return null when the word isn't found or when it's not found from that selection. So that's why I test to make sure that I was actually returned a selection.
The second method, the kind of preview style of searching, in some ways might be simpler because there's a method on the document just called begin find string with options. It doesn't return anything at all. You pass it the string again like quarts, and you pass it the same options, but you don't say, you don't specify from where because it's going to go off and search the entire PDF document. But it doesn't do it synchronously. It does it asynchronously. So it goes off, starts doing the search, and the way you're told about when an instance is found, if it's found, is by way of a PDF notification. Or if your PDF document has a delegate and your delegate implements the did match string method, then your did match string method gets called. And again, as per the first case, a PDF selection is the object that's passed to your search method. So I should tell you a little bit about PDF selections. That's one of the new utility classes.
So you think of a selection as actually a selection on a page, but it's a little richer than that. It could actually span multiple pages. So one of the attributes you can get back from a PDF selection are the pages, and I say that in plural because it can return you back an array of potentially multiple pages if the selection spans multiple pages. And they'll be sorted so the first page will be the first page logically that the selection covers. You can ask for a given page to get the bounds in that page's coordinate system, get the bounds that are covered by that selection. You can also find the nearest outline item that corresponds to that selection. And that's how in the second column in my PDF linker app that I showed you, that's how I was able to populate that second field. In fact, the first field where I indicated what page the selection was on was using that first attribute. Finally, you can add selections together kind of in a Boolean fashion, which I don't really demonstrate here.
But you can extend the selection as well. I can extend it by essentially growing the selection by an arbitrary number of characters preceding or following the selection. And then I can always ask the selection for the string, which is the text that the selection covers. So it's a combination of those last two, extending the selection and asking for its string, that allowed me to populate the third column in the table view and show you some of the sample text that was covered by that selection.
having problems with this clicker. OK, so now it gets more complicated. Back to the-- demo machine here. So this is probably -- you saw that even the searching was, you know, maybe a handful of lines of code to do. This is a little more complicated. For this application, PDF linker, I wanted to show you how you could do something sort of beyond just viewing a PDF. So I've got this item up in the toolbar you may have seen called test links and edit links. And if I switch to edit links, you'll see that -- okay, here we are. I don't know well this shows in the back. All these link selections-- I'll just toggle it on and off-- all these link selections, when I'm in Edit mode, I can draw a little gray border around these annotations. And the way I did this was by subclassing the PDF view. And I'll show you how that's done in code. Back to the slides.
So I subclassed the PDF view, and there's a method on the PDF view called drawPage. Now you don't usually call drawPage, or you shouldn't call drawPage from your application, but it's called internally so that every time a new page is displayed and needs to be drawn, this method inside PDF view gets called per page. So if you subclass a PDF view, you can override this drawPage method and do your own drawing. If you're lazy, like I was in this app, and you want the to handle most of the PDF drawing, you just call super, so I call super. PDF view draws the PDF page, any current selection, that sort of thing. But then I can now come in after it's finished, and I can do post-drawing. In this case, I want to draw gray rectangles around all the link annotations. So how do I do that?
Well, I should take a break and point out some of the attributes of a PDF page. I've talked about it a few times already. So a PDF page is what's passed into this draw page method. Some of the attributes you can get at the PDF page level are you can find out the document that owns the page. You can find out the bounds of the page. And this is going to depend on whether you want to know. You can ask for the media box, the crop box, that sort of thing. You can also find out if the page is rotated and what its rotation is. And you can also get back, if the page has annotations, you can get back an array of these annotations. And I'll be calling this method, of course. You can also get back the text for the page. And the page has a draw method. And that's what the PDF view is actually calling in order to display the page in its own draw page method. So I was interested in the annotations. So when draw page gets called, I call super, let the view do the drawing. But then I want to come in afterwards and see if there are any annotations on the page. So the page was passed to my method. All I need to do is say, ask that page for its annotations. And it'll return me-- if it has any annotations, it'll return me an array of these. So then I can, in my app, walk through each of these PDF annotation objects and see if any of them are links.
So what are some of the attributes of a PDF annotation? PDF annotation has a type associated with it. So since I'm only interested in link annotations, I can ask for the annotation type and see if it matches type link. But you can also find out for any given arbitrary PDF annotation object the page that it's associated with. You can find out its bounds in that page's coordinate system.
It has a draw method as well. So when a page is being drawn by PDF view, the page also draws its annotations associated with it. And then the various subtypes, the subclasses of the PDF annotation have their own attributes, like a PDF annotation link obviously is going to have a destination associated with it. So what my code does then is walks through each of those annotations that's in that array and calls type and finds out if the type is equal to string link. So I'm only interested in the link annotations.
So, oh, and I should point out then I call the bounds method on the annotation, and I can draw just a gray rectangle using those bounds. And that's how I get the annotation to show the bounds. And when I actually click on one of the annotations, you'll see I kind of have a notion of-- in PDF Linker, I have a notion of what the current annotation is. And so I draw it differently. I draw it with red text, and I draw a grow box so that we could resize. And I allow hit testing. I have to enable some hit testing in order to determine if the user has clicked on one of the links. And this is something that actually might be difficult for you to see in the back. But as I'm clicking on a link annotation, you'll see at the bottom of PDF Linker, I'm asking that link annotation for some of the attributes that I just talked about. I'm asking for, in this case, its destination. And if it's a URL, I'll display its URL. If it has a destination somewhere within the PDF view or somewhere within the PDF document associated with it, I get that destination. And I can find out the page that that destination links to as well as the point on that page. So this is all using some of those methods in PDFKit. But let's say that I select some text here. And I've got this method. I've got a menu item in PDF Linker called New Link. So the user selects New Link. And what I've done is-- and this is why it's kind of nice using PDFKit-- I could have just put a rectangle in some arbitrary position on the page. But since I saw there was a selection, I could ask the view for the selection. I can ask the selection for the pages that are covered. I can take the first page, because we're not going to handle the case where the user might try to make a selection across multiple pages. And I can get the bounds for that selection on that page.
and I can use those bounds to initialize my link annotation. So it's kind of nice for the user that I'm able to kind of initialize this link's position based on the selection. And then this isn't a proper link yet because I don't have a URL or a destination associated with it, and I could click on the URL radio button down here and associate it with a web page, but let's just say I'll show you the more complicated case that I want for whatever reason to have that be a link that takes you to this table on pixel format on page 11. So I've got a button that is enabled down at the bottom called Set Destination. And as soon as I click Set Destination, you'll see that right above it now, again, this may be hard to see in the back, but the UI is updated, and I say that this now is a link that points to page 11, and it points to, I use the top left corner of the view here, 0.0, 532.5. So that's the location that corresponds to this top left corner on this page. And if I go back to test links and I go up to the first page here, you'll see that it is a link. If I click and let go, in fact, it does take me to page 11. And it takes me to that same point on that page. So I think that's it for the demo. I'll show you just how that's done in code before I hand it off. So the way I created the link up front when the user selected "new" from the tool menu is, I found--again, as I said, I found the selection, the current selection for the PDF view. I got the pages covered, and since I'm only interested in the first page, I got page at--the object at index 0. That gave me the first page. So then I asked that selection, "Give me the bounds," and this will be in page space, corresponding to that--to that selection for that page. And so I got back the bounds. So I call PDF annotation link alloc, and then that has a method called initWithBounds. And so I pass it in those bounds, and I get back, like I say, not a properly formed link because I don't have a destination associated with it, but I get back nonetheless a PDF annotation link object. So I can tell then the page that corresponds to that selection. I can say, "Add this annotation." And so now, as far as the page is concerned, there's an additional annotation on this page. So I'm gonna take a minute just to talk about the difference between page space and view space. The PDF documents have their own coordinate system. Each page has its own page space. PDF defines their coordinate system as the 0, 0. The origin is in the lower left. They use points, so 72 points per inch. So all the coordinates are in that space. But with a PDF view, obviously the user could be zoomed in, there could be two pages displayed, and so there needs to be a mechanism whereby you can go between, like, a user clicking the mouse somewhere in the view and then determining internally which page that corresponds to and then specifically what coordinate within that page's view space or page's page space that that point corresponds to. So all the utility classes, when they take rectangles, points, that sort of thing, all the coordinates are all in page space. But at the view level, you need the methods in order to map between those two. So... That's how, I guess, the point of that, I guess when I was doing the hit testing, I needed to convert from the view space and determine if you were actually over an annotation. So I had to convert that into the pages space and then walk through the annotations, ask for their bounds, and see if the point corresponded to any of those bounds. So the next step in my demo is after I created the link annotation is I needed to make it a proper annotation by giving it a destination. So to create a PDF destination-- and this is another one of those utility classes-- I needed to use some of that-- use some of the transformation methods in the PDF view in order to figure out where the top left corner of the view was. So I can get the top left corner of the view in view space just by asking for its frame and creating a point that corresponds to the top left corner. But to figure out what page that's on, I ask the view "page for point." I pass in the point, and it'll return me a PDF page object that corresponds to-- if you pass true for nearest-- it'll return the page that most closely is nearest to that point. So now I know what page corresponds-- or I know rather what the top left corner of the view-- I know what page the top left corner of the view corresponds to. But now I want it in page space, so I call convert point to page on the PDF view. I pass in that page, and I pass in that point, and now I get back in that page's coordinate system an actual coordinate in page space. So the destination utility class has a method called initWithPageAtPoint. So I know the page. I've got the point now in page coordinates, so I can create this destination object, and then the last line just says to the PDF annotation link-- and I mentioned that I had a notion, kind of, of what the current selected link is. I call setDestination on that, and I pass it that PDF destination.
I guess we're done. So there's a lot more documentation online. There's the sample code, and there's stuff in here that I didn't cover. You can play with preview. So -- and then I guess from about noon on today, I'll be in the lab. So thanks. I'll pass it to Rich.
Thank you, John. John's PDF kit is incredibly exciting. You really shouldn't lose sight of that. Every day I use Preview, and people give rave reviews to Preview for its speed and its power. And what PDFKit lets you do is take all of that capability and put it in your applications. And that's just amazing. It lets you make-- and if you'll excuse me for the baseball analogy that I always use, or one of them I always use-- it lets you make a home run out of your application. Makes your applications into these great PDF handling applications. And below PDFKit, you can use Quartz or Cocoa to do the exact same thing. So there's all these frameworks in the system to really help you make great applications. So of course, that's not what I'm going to talk about at all. So the ideas, my ideas, you don't always need to write an application. You can handle PDF in ways that are maybe more suited to your smaller tasks. Command line tools, writing Automator plugins. And again, going back to the baseball analogy, these are the bunts and the sacrifices of PDF. So we're not going to do the PDF home runs. we're going to do little things, little base hits. So to start with, for a couple of releases now, Mac OS X has had Python bindings for Quartz. So you can write Python scripts to create PDF, which is very powerful. Starting with Tiger, you can now write automator actions built on these scripts or written in Cocoa to analyze and also to create PDF. Print dialog supports PDF workflow, so now you have a place once you've written these actions and these tools to host them where the user can get at them easily. And we have PDF workflow API so your applications, if you insist on writing these home runs, can actually execute some of these smaller pieces. So that's what we're going to look at here, ways you can create PDF, ways you can analyze PDF, and do it simply and not really outside of an application or to be pulled into an application later. So again, the bindings have been there for a while so that you can use Quartz directly from Python. They're pretty much direct mappings. If you want to learn Quartz, one of the great ways to do it is to start to use the Python wrappings.
You can do it interactively. You can make direct calls. And the calls are pretty much the same. So that's terrific. The Python bindings also wrap up some higher level functionality, which is exciting. They let you render HTML into a Quartz context directly. That's not something Quartz does. It's one of the higher frameworks. Same with RTF. And you can also handle a variety of image formats through these Python bindings. So there's a lot of power there through the Python scripts. So what do you want to use these scripts for? Experimenting is great. Writing tools are terrific. You can write great command line tools to do it. And then once you've written these scripts, you can wrap them up inside of Automator and make them generally useful to people.
If you want to know where the API is for the Python bindings, it's hidden. It's in developer examples, Quartz, Python. I never remember where that is. And it's in a file called API summary, all uppercase, for some reason. But that's where you can find it. You can go in there and you can see actually what the calls are in Python. And you can sit down, open up a Python interactive session, and just start typing them.
This is my sample that I always show when I talk about Quartz bindings. And I like it because it's small and it's very powerful. And it's a PostScript interpreter in Python. The idea here in this sample-- it's two slides-- this script's going to take in a PostScript file. It's going to convert it to PDF. And from PDF, it's then going to render each page of the PDF into a raster bitmap and save that out as TIFF. So the first step-- that's what this slide is-- is being able to take that PostScript and create PDF. And all you have to do is create an image provider from the file that comes in on the command line.
create a consumer, some place to write it. And for this PDF, we're actually going to use this TIFF file base, or TIFF base name. And then you create a converter, and you call convert. You've just written something that converts PostScript to PDF in a few lines of code. That's not what our goal is. Our goal is to go to the next step and actually go to raster. And so what you end up with is the kind of loop that most of your scripts will have if you're writing using these Python bindings on top of Quartz, which is a loop that counts over the number of pages in the PDF document. And in this particular case, for each page, we create a raster context, an RGB raster context in this case. We render into it.
Once it's rendered, we then say, OK, now I want you to write out those bits as TIFF. And now you get one TIFF file for every page that was in the original postscript. It's very short. And this is the kind of powerful things you can write with these Python bindings.
But it gets more exciting than that. Because of these Python bindings, you can actually go look out there and find libraries, Python libraries, other people have written. And you can then integrate them into Mac OS X and in with Quartz. I usually go to Vaults or Parnassus. This is a great place for finding Python libraries and applications that have been written.
And I browse around, I find something interesting, and I ask myself, well, how can I integrate that now in with Quartz and with Mac OS X? And there are a bunch of different things that are interesting. One is website creation, content creation, with something like Zope or Plone, which are Python servers.
And maybe you want to use Quartz to create a PDF document that you then stream down to the user, which, oh, by the way, Safari will handle in line, thanks to PDFKit. Or maybe you find a useful graphics library. And the example I'm going to use today is PyChart, which is a GPL charting program. It's written in Python. And what it knows how to do is take data, or take some Python code, and generate charts, and PyCharts, and scatter charts, and all the fancy scientific charts. And what it can do is write these into a PDF file or even a PostScript file. But that's not exactly what I wanted. I wanted it to take this library and for it to be able to render into a Quartz context so I could mix into that same Quartz context other rendering done with Quartz. So I can generate a page that has not only a chart on it, but maybe some HTML, in the case we're going to show, or maybe some RTF.
So PyChart's great because it renders vector drawings. Well, that's what we want with Python. I didn't want to be generating GIF charts or any of that. I wanted something that was high quality vector art. So we're going to extend PyChart. They have a concept of a canvas, and they have different canvases inside of PyChart. And one of them is a PDF canvas, one's a PostScript canvas. And the interesting thing about that is both PDF and PostScript share the same imaging model, which, oh, by the way, is shared by Quartz. So conceptually, this is going to be very trivial to move over on top of Quartz. First thing we had to do is create this Quartz canvas. And again, PyChart's all built around writing to a file. We needed to change that. So we take the normal init that's part of the canvas inside of PyChart. And we say, OK, hey, we might actually pass you, instead of a file name, a CG context, a Quartz context. And we're going to render into that. And all this code does is remember that Quartz context.
Then we go implement all the methods inside of PyChart's canvas. And again, this was all written before Quartz and just something completely different. But because of the shared imaging model, you'll see the mapping's pretty much one to one. Move to becomes move to point. Line to, add line to point. strokes, it's that imaging model. So boom, all of a sudden you've got this powerful graphics library ported over, drawing into a Quartz context. So now can we mix Quartz and pie chart? So can we render both of them together? And the idea was I wanted this output. Can I generate a PDF file with Quartz that had a pie chart output, vector output on top, and on the bottom, an HTML table?
And the answer, of course, is yes. I signed up for the session before, and the answer was yes, but luckily it's yes. And the main driver looks something like this. There's some standard initialization. There's a call to one method that's going to draw the bar chart, and then there's another one that's going to draw the table. And we're just going to look at that briefly. The Python isn't particularly interesting, but the bottom yellow lines are, which is after you set up the chart and you set up PyChart, hey, these are the kind of bar graphs I want, at the end, all you have to do is say, hey, draw it into this context. drawn into this court's contest.
And similarly, on the other side of it, I want to draw the table. We're going to generate the HTML for a table based on some comma-separated values that were coming into the script. And then at the end, we're going to say, hey, render this HTML. And the result is that combination I showed you. It's everything put together under one context.
So you can go look for other Python libraries and integrate them the same way. The common imaging model is going to make it very easy to bring some very interesting things onto Quartz and Mac OS X. So that's just a script. That's creating a script in Python, maybe moving over a library on top of Quartz, and make it useful to you. So it seems like we're going to step away from it, but we're not really going that far. In Tiger now, we have Automator actions. And we ship a bunch of useful PDF Automator actions. But you can create your own. If you haven't used Automator much, this is an example of something a user can do just using the existing Automator actions. So this particular workflow lets the user take an album out of iPhoto, and then renders all the pages, and then generates a contact sheet and opens up the PDF. So it's very simple for a user to do this. But it would be nice if there were some more actions. So what you can do is take a Python script you've written, for example, the one I was showing earlier, the one based on PyChart, which takes these comma-separated values in, generates a page with a chart on the top and a table on the bottom, and you can wrap that very easily into an automator action. And all you have to do to do it is go into Xcode 2.1-- make sure you have 2.1-- select the appropriate skeleton that says, hey, I want to build an action that's based around a script.
Go in, put your script into that template, change the description so it properly describes what you have, tweak the nib, in this case actually remove a lot of stuff. I'm not a UI designer, so sorry. It's not very exciting. But now, boom, you've got an action that anybody can use to take comma-separated values like you might export out of Excel and generate PDF output. Thank you.
But you can be a little more ambitious if you want. These actions can also be written in Cocoa. And again, if you go use them in Cocoa, then you're going to use Quartz directly. And it's very simple if you want to write them in Cocoa. You implement this one method, runWithInputFromActionError. You'll get your input file, which generally is going to be whatever you want. Maybe it's going to be comma separated values. Maybe it'll be something else. In the example we're going to look at, it's going to be a PDF file. And then you use Quartz to manipulate the PDF file. And now you have an action that can do specific things. In our case, it's going to mean scanning a PDF and trying to come up with some analysis of what's inside of it. So that's what I wanted to do. It turns out Tiger has this new scanning API inside of it. And just to give you an idea how that scanning API works that we're going to use, you start with a PDF document. You create the PDF document from a file you get.
Once you have the PDF document, you have that loop that we looked at that loops over each page in the document. You get the PDF page representation for each page. You get the content stream for each page. Then what you need to do is tell the Scanning API what operators, what PDF operators, you're interested in. And you do that by creating this operator table. You create the operator table and you start putting things in it. In this case, we're looking for images. To find images, we need to implement, or bottleneck if you will, the DO operator inside of PDF. So we put that into this operator table. We create a scanner. And then we say scan. And what Quartz is then going to do is it plays back your PDF whenever it sees one of these DO operators. It's going to call back into your code. And the code I have just says, oh, OK, you found an image. What page was I on? And it keeps a tally. Keeps a tally of that inside of it. And kind of coincidentally, once it has a tally, it generates comma-separated values of those tallies. So now here we have an action that will do exactly that. It generates comma-separated values.
with how many images there are on each page. So can we put all these pieces together? And the answer, of course, is yes. So I'm going to show you a quick demo. But the idea here is we're going to take the automator action that we have that scans the PDF file, generates the comma-separated values, and spits them out of an action. We're going to hook into that another action, which reads the comma-separated values, creates that PDF file. We're going to do that in Automator. We're then going to use Automator's ability to create a print workflow item, which gets saved directly into the proper place, that the print dialog can find it. We're then gonna open up something in Safari. We're gonna go to the print dialog, and we're gonna invoke this action. Again, this is something that you can write these little actions, and then the user can put them together. So we're going to try this demo. Let's see how this works. This will be demo two. Wow. It's a good thing I explained how this really works.
Well, see, that was good, because it happened before my demo. We're going to go back to the slides. That's actually good. It gives us more time for Q&A, which is what you really wanted. So anyway, the important part of the demo was how to properly handle a kernel panic.
The important part of the demo was take these automator actions that you, the developers, can make. A user then strings them together in some useful way. The user can then easily save them where the print dialogue can pick them up. And then whatever application can drop a print dialogue, be it Safari or TextEdit or Pages, that workflow can then be run. The workflow the user created can then be run to analyze or to create content based on that. So it's a very powerful way of taking these little bunts and singles that we talked about, letting the user hook them them together and then putting them right into the user's hand inside of the print dialog.
So that would have been the PDF workflow, is what I was going to show, the way that print dialog works. And then the next obvious point is, well, how can my application invoke those PDF workflow items directly? And it turns out there's an API for it, luckily. If you want to actually invoke these workflows directly, rather than from the print dialog, first you want to enumerate them. That's just called PM workflow copy items. That'll give you an array of URLs where each URL represents one of these workflow items.
And then when you want to execute it, PM Workflow submit PDF with options. So you'll submit the workflow you want to execute along with the PDF and maybe a title, and it will execute. And the important thing about a workflow, PDF workflow, is it takes PDF in and it does something with it on the outside. And you have no idea what happens to it. It might email it. It might turn it into TIFFs. It might turn it into comma separated values. You don't know. So that's what happens with workflow.
There's lots of different workflow items that are actually supported in the system. We support folders inside of your PDF services folder, which is where these things live, if you want to organize them hierarchically. We support folder aliases. If there's a folder alias workflow item, we just move the PDF right into there. Unix tools, Apple scripts, Automator Workflow was the big one for Tiger. You can put an application right in there, and the application will get an open event on the PDF document. And we support Quartz filters as well.
So what I really wanted was an application that would demo this, and I wasn't going to demo it apparently now. Luckily, I have the screenshot. So this is FlyMeet Software. These are the makers of VoodooPad. I'm a big fan of VoodooPad. And they have this thing called FlySketch, which is a screen capture utility. It'll let you draw the window over a portion of the screen. You can capture it, and then you can annotate the result. And what you have then is really a bitmap, but it's in a PDF form. And FlySketch had the ability to then do something with it, mail it or whatever. So I sent an email to Gus and I said, hey, you know, it'd be kind of cool. We have these APIs that are workflows. And if you could then use that workflow API to add anything that the print dialog can do, your application can now do with your PDF, that would be really great. I sent that mail and some other mail and answered some mail, and all of a sudden, it came back to me like this. And he had done it. I don't know what it took him, an hour, 30 minutes. It was great. So thank you, Gus, if you're out there. It was terrific. And it's a great example of how you can bring all this power right into your application.
So high level PDF, this is the takeaway. You can use Quartz directly to write Automator actions. But you can also use Python to write them even faster. And whether you write them in Cocoa or write them in Python, you can then wrap them in Automator to give to your users, to do useful little nuggets of information. The user can then wrap them into more useful workflows, and they can then invoke them from the print dialog. Or if you're a great developer like Gus, you can invoke them directly from your application.
All right, so this is actually, I've got a couple minutes. This is actually the highlight for me of the conference. We have a new Quartz book coming out. It's coming from Morgan Kaufman. This is a terrific book. The author is somebody I've worked with for 16 years. He's a good friend of mine. He's known for his attention to detail, and this book shows it. If you need to learn Quartz, this is the book you're going to want to get. Now, Apple's publications group has done a great job of revving all the Quartz documentations for Tiger, so you can go out and see all this great reference material, and it really is terrific. But if you're the kind of guy like me down with a book that just says, hey, look, here's how you do this, and has great examples, this is going to be really terrific. So it's coming from Morgan Kaufman, and if you don't believe me about the level of detail in this, there's a sample chapter that you can go get and go read that's available, and I think you'll be incredibly impressed. And it actually came to my attention this week, there's another very respected, impressive Macintosh developer who's also working on a Quartz book. So I think those two books are going to mean very good things for Quartz here as we go forward.
So for more information, here's where the sample code is. Related sessions coming up. These are the Friday sessions. Moving from Quick Draw to Quartz. Definitely go to this if you have a lot of Quick Draw codes still sitting around. We want to get Quick Draw gone. Optimal 2D application graphics. Obviously, if you're going to use Quartz, you get a lot of power and a lot of speed for free, but you can still go down the wrong path if you're not careful. This will tell you how to do that. And then the graphics and media feedback form, which is always pretty exciting. You should make sure you make that and find all the hard questions. Contact Travis Brown if you have any questions.