PDA

View Full Version : Cocoa/Objective-C: How do you insert pages into an NSView?




mpemburn
Jun 2, 2009, 08:31 AM
Hi,

Still relatively new to Cocoa/Objective-C, I haven't been able to find how to insert pages into an NSView with the goal of printing the view as a multi-page PDF file.

Here's the deal: I'm writing a scanning application and have been able to successfully acquire and display (in an NSImageView) multiple pages from a scanner with an ADF (document feeder). Up to this point, I've simply been storing each image in a NSMutableArray and then flipping through the array to show the pages on the UI. I understand that I should be able to have all of my images in a single NSView -- and that if they are that I can print them one to a page if I use NSPrintOperation with the correct NSPrintInfo setting -- but I'm darned if I can figure out how to get those images into one view. Any help would be greatly appreciated.

Mark



JoshDC
Jun 2, 2009, 09:37 AM
Take a look at PDFKit (http://developer.apple.com/Cocoa/pdfkit.html), particularly PDFPage's initWithImage: and PDFDocument's inserting page methods. A PDFView will be able to display and print the PDFDocument fairly painlessly.

mpemburn
Jun 2, 2009, 01:07 PM
Wow! That was almost too easy -- thanks! The method I was trying to work with was a lot more complex than that.

For the sake of you who have found this in a search and want a shortcut to get you started:

1. Add the Quartz Framework to your project. It contains the PDFKit Framework.

2. In the class file you're using to write the pdf file add:
#import <Quartz/Quartz.h>

3. This code will save to a PDF file the contents of "imageStore", an NSArray of NSImages:


PDFDocument * pdf = [[PDFDocument alloc] init];
PDFPage * page;
int i;

for (i=0; i<[imageStore count]; i++) {
NSImage * thisImage = [imageStore objectAtIndex: i];
page = [[PDFPage alloc] init];
[page initWithImage: (NSImage *) thisImage];
[pdf insertPage: page atIndex: (NSInteger) i];

}

[pdf writeToFile: @"/Users/myname/Desktop/scantest.pdf"];


-- Mark

lee1210
Jun 2, 2009, 01:21 PM
A slightly different version:

PDFDocument *pdf = [[PDFDocument alloc] init];

int i = 0;
for (NSImage *thisImage in imageStore) {
PDFPage *page = [[PDFPage alloc] initWithImage: thisImage];
[pdf insertPage: page atIndex: (NSInteger) i];
i++;
}

[pdf writeToFile: @"/Users/myname/Desktop/scantest.pdf"];


You could really collapse the loop at the cost of readability:

PDFDocument *pdf = [[PDFDocument alloc] init];

for (NSImage *thisImage in imageStore) {
[pdf insertPage: [[PDFPage alloc] initWithImage: thisImage] atIndex: [pdf pageCount]];
}

[pdf writeToFile: @"/Users/myname/Desktop/scantest.pdf"];


If you're not on leopard or higher you can't use for...in, but i always like to encourage its use if you have it available. Also, you shouldn't need to separately init then initWithImage:. The rest one could take or leave.

-Lee

mpemburn
Jun 2, 2009, 02:23 PM
Cool, thanks!

I do like the "for in" syntax more but I haven't really made up my mind whether I'm going to make this app compatible with versions earlier than Leopard. There are a couple of other Leopard-only capabilities that I'd really like to use (such as Preference binding).

One thing I discovered since my previous post: The line:

[pdf writeToFile: @"/Users/myname/Desktop/scantest.pdf"];

. . . generates the error:
malloc: reference count underflow for 0x1754570, break on auto_refcount_underflow_error to debug.

The affected file is "libauto.dylib". A bit of research reveals that this is a 10.5.7 bug and apparently, Apple's on it. It doesn't crash the app (at least not in the brief tests I've done) so I'm not going to worry about it -- unless it persists after the 10.5.8 update.

-- Mark

GorillaPaws
Jun 2, 2009, 02:46 PM
I don't think you're going to loose much in terms of limiting your app to leopard only. I'm not sure if you're making a commercial product or what, but do you really think if someone is not willing to make the leap to Leopard, they will be willing to shell out $ for your app? In some cases the answer may be yes. I'm willing to bet that in the vast majority of situations however, any extra sales you gain by limiting your leopard-only technologies, will be far less than the value of your time had you taken advantage of these tools thereby making your code easier to update/maintain etc.

mpemburn
Jun 2, 2009, 08:47 PM
GorillaPaws -- I tend to agree. When I'm developing web app, I don't mess with coding to older browsers anymore so why should I let myself in for such a headache in this arena -- one that I'm far less conversant in from the git go.

Back to the original question with a variation: My next magical trick will be to save the scanned images in a multi-page TIFF file. Is there perhaps another Quartz class that supports this?

-- Mark

JoshDC
Jun 2, 2009, 09:48 PM
Does the concept of a multi-page tiff file exist? As far as I know it doesn't. NSView's pagination methods are more to do with printing than anything else (as far as I'm aware). Of course, from your PDFDocument, you can take each page's data representation, make that a new image and get that image's TIFFRepresentation. Then you could save those as multiple documents.

mpemburn
Jun 3, 2009, 05:31 AM
Yes, multi-page TIFF does exist. On that other OS (you know the one that begins with W ;-)), multi-page TIFF is offered as an alternative to PDF because somebody . . . didn't think it important to build PDF capability into the system. I'm offering it as an option in my app because it's more-or-less expected now.

The un-manipulated output of my basic scanning routine is, in fact, a TIFF representation so if it's possible to shove more than one of 'em into an NSView, it should be possible to print it as a multi-page document if you set the NSPrintInfo page size to match the scanned page size. Since I've seen multi-page TIFF done by another OS X app, I know it's possible to do.

-- Mark

JoshDC
Jun 3, 2009, 07:20 AM
Sorry about that, turns out TIFF can do a lot more than I thought... Searching around, looks like Ghostscript can do the conversion from a PDF to TIFF and preserve multiple pages.

gs -q -dNOPAUSE -dBATCH -sDEVICE=tiffg3 -sOutputFile="output.tiff" "input.tiff"

Which I got from this (http://ask.slashdot.org/comments.pl?sid=593693&cid=23919915) Slashdot post. It's not particularly elegant, you'd need to look at NSTask to run it on a PDF copy in temporary folder, but it looks like it might be your easiest option. You'll possibly have to bundle Ghostscript with your app as it looks like it doesn't come with OSX; hopefully this doesn't cause any licensing issues...

EDIT: That device type produces a black and white output. You probably want to play around with other parameters as well to get the best possible output.

mpemburn
Jun 4, 2009, 01:21 PM
Thanks for that info GorillaPaws. I'm disinclined to use an external process if there's some way to do it within Objective-C. The basic TIFF format is built into NSImage so it would seem that I wouldn't have to do anything tricky to get the multi-page capability out of that object. This may be wishful thinking on my part though. Guess I'll put this on the back burner for a while.

-- Mark