Printing Off-screen PDFViews
I have a situation where I want to print a multi-page PDF. While I could use the PDFKit utility classes and/or quartz functions to get the information to manually write drawing/pagination code for a NSView subclass, I had thought that quicker alternative would be to create an off-screen PDFView and tell it to print itself. When I tried this solution, the print dialog didn't go away, all of the print settings controls on the right half of the print dialog disappeared, and the application froze.
I then wrote a tiny test application with the following method that illustrates the problem. When the test program is compiled without the USE_PDF_VIEW preprocessor macro defined, the blank view displays fine. If USE_PDF_VIEW is defined, the document doesn't print, most of the print dialog controls disappear, and the app freezes. While I have other ways of accomplishing my goal, I'm curious as to why this shortcut doesn't work. Is there something a开发者_如何转开发bout Cocoa drawing I still don't understand? Am I banging into Apple Voodoo Magic(tm) behind the scenes that makes PDFView behave in a completely different way than other NSViews?
- (void)printMyStuff:(id)sender {
NSPrintInfo *currInfo = [NSPrintInfo sharedPrintInfo];
#ifdef USE_PDF_VIEW
PDFView *pdfView = [[PDFView alloc] init];
PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:@"/Users/wls/Documents/my_document.pdf"]];
[pdfView setDocument: pdfDoc];
[pdfView printWithInfo:currInfo autoRotate:YES];
#else
NSView *myView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 500, 500)];
NSPrintOperation *myop = [NSPrintOperation printOperationWithView:myView printInfo:currInfo];
[myop runOperation];
#endif
}
Had the exact same problem.
The PDFView
needs to be added to a NSWindow
in order for printWithInfo:autoRotate:
to work (atleast in my case), otherwise the printing controls go blank or won't work.
Here's the complete code:
PDFView *vDoc = [[PDFView alloc] init];
[vDoc setDocument:pdfDoc];
[vDoc setAutoScales: YES];
[vDoc setDisplaysPageBreaks: NO];
NSWindow *wnd = [[NSWindow alloc] init];
[wnd setContentSize:vDoc.frame.size];
[wnd setContentView:vDoc];
[vDoc printWithInfo:printInfo autoRotate:YES];
[wnd release];
[vDoc release];
Building on alex-i's excellent answer, I added the following lines so the print dialog showed up in a user-friendly location:
NSRect windowRect = self.window.frame;
NSPoint printTopLeftPoint = NSMakePoint(CGRectGetMidX(windowRect), CGRectGetMaxY(windowRect));
[wnd setFrameTopLeftPoint:printTopLeftPoint];
My self.window
is for my current window controller, not the temporary window.
I like alex-i's answer because it does not use private APIs. But in my case, I already have a window (and I suppose in most cases you would!), so I figured I would use that window instead of creating one. Here is what I ended up doing, using swift:
func print(_ pdfDocument: PDFDocument, using window: NSWindow) {
// create a hidden pdf view with the document
let pdfView = PDFView()
pdfView.document = pdfDocument
pdfView.autoScales = true
pdfView.displaysPageBreaks = false
pdfView.frame = NSMakeRect(0.0, 0.0, 50.0, 50.0)
pdfView.isHidden = true
// add the view to the window and print
window.contentView.addSubview(pdfView)
pdfView.print(nil)
pdfView.removeFromSuperview()
}
PDFView is a subclass of NSView. The designated initializer for NSView is -initWithFrame:
... if you don't use -initWithFrame:
strange things can happen. Since PDFView has no other designated initializers, -initWithFrame:
is it. I'm guessing that's at least part of your problem.
Another part may be memory related. Are you using garbage collection or not? If you are, you're not keeping a reference to your PDFView anywhere, so may be getting deallocated. If you aren't using garbage collection, you're leaking your PDFView (also because you keep no reference to it, so you can release it when you're done). Same with your myView NSView instance ... you're leaking it if you're not using GC.
精彩评论