Wednesday, April 22, 2009

PDF with paging

In my previous post I wrote about how to render the contents of an NSView to a pdf. That method was simple and intuitive. However, it had 1 drawback. It didn't allow me to create paginated PDF's. The way you go about this is much less intuitive and requires you use the NSPrintOperation class. Basically, what you want to do is print to a PDF. You start by creating an NSPrintInfo object using the default printing preferences:


//this will point to our NSPrintInfo object
NSPrintInfo *printInfo;
//this will point to the default printer info object
NSPrintInfo *sharedInfo;
//thi will point to our settings for the NSPrintInfo object
NSMutableDictionary *printInfoDict;
//this will point to the settings for the default NSPrintInfo object
NSMutableDictionary *sharedDict;

sharedInfo = [NSPrintInfo sharedPrintInfo];
sharedDict = [sharedInfo dictionary];
printInfoDict = [NSMutableDictionary dictionaryWithDictionary:
sharedDict];

//below we set the type of printing job to a save job.
[printInfoDict setObject:NSPrintSaveJob 
forKey:NSPrintJobDisposition];

//set the path to the file you want to print to
[printInfoDict setObject:@"/Users/OnCocoa/Desktop/test.pdf" forKey:NSPrintSavePath];

//create our very own NSPrintInfo object with the settings we specified in printInfoDict
printInfo = [[[NSPrintInfo alloc] initWithDictionary: printInfoDict] autorelease];


Once you set up your NSPrintInfo object you have to create an NSPrintOperation object, specifying which view you want to print from and which NSPrintInfo object you want to use.


//create the NSPrintOperation object, specifying docView from the previous post as the NSView to print from.
NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:docView printInfo:printInfo];

//we don't want to show the printing panel
[printOp setShowPanels:NO];

//run the print operation
[printOp runOperation];


Thats all you need to do to print a view to a PDF with paging the vanilla way. If you want to print like Safari does, you have to set up the margins appropriately using the code below:


[printInfo setHorizontalPagination: NSFitPagination];
[printInfo setVerticallyCentered:NO];
[printInfo setHorizontallyCentered:NO];

NSRect imageableBounds = [printInfo imageablePageBounds];
NSSize paperSize = [printInfo paperSize];
if (NSWidth(imageableBounds) > paperSize.width) {
imageableBounds.origin.x = 0;
imageableBounds.size.width = paperSize.width;
}
if (NSHeight(imageableBounds) > paperSize.height) {
imageableBounds.origin.y = 0;
imageableBounds.size.height = paperSize.height;
}

[printInfo setBottomMargin:NSMinY(imageableBounds)];
[printInfo setTopMargin:paperSize.height - NSMinY(imageableBounds) - NSHeight(imageableBounds)];
[printInfo setLeftMargin:NSMinX(imageableBounds)];
[printInfo setRightMargin:paperSize.width - NSMinX(imageableBounds) - NSWidth(imageableBounds)];


One more thing to keep in mind is that if you are generating a PDF from HTML using a WebView (like in my previous post) you have to wait for the contents of the WebView to render. It doesn't matter if you are loading from disk or from the net, you should always set a frame delegate using WebViews -(void)setFrameLoadDelegate:(id)delegate and respond to:


- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame;


This method will get called once the WebView renders the contents of the page you requested. Check out the screenshot below to see what you can accomplish with all this:




Good luck!

1 comment:

  1. This is great info but I wonder if you might know how to do the opposite.
    What I mean is if I load a pdf file into webview by the user clicking on the link how do I get it to print just the pdf not the document view which shows just the frame of the pdf plug-in with a scroll bar ??

    ReplyDelete