4

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 about 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

}

4 Answers4

5

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];
charles
  • 11,212
  • 3
  • 31
  • 46
alex-i
  • 5,406
  • 2
  • 36
  • 56
2

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.

Kenny Wyland
  • 20,844
  • 26
  • 117
  • 229
2

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()

}
charles
  • 11,212
  • 3
  • 31
  • 46
  • This is the best solution for me. Adding it to the subview and making the alpha 0.0 works perfectly for me. Hence, the active window will be used. – davidev Oct 03 '20 at 09:58
1

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.

Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135
  • Nope, I have the same problem, and NSView does have a straight init method... initWithFrame didn't help any. Also, I'm not sure about Waldo, but I'm retaining everything until I no longer need it... – Brian Postow Mar 19 '10 at 18:04