PDFView drawPage: (PDFPage *) page toContext not called in 10.12

In my subclass of PDFView

- (void) drawPage: (PDFPage *) page toContext:(CGContextRef)context


is not called after


[self setNeedsDisplay:YES];


Any ideas why this could be happening ?

Replies

Sample class to recreate the problem.



import Foundation
import Quartz
//MARK:- Custom PDFPage
public class MyPDFPage : PDFPage {

    private var currentContext : CGContext? {
        get {
            if #available(OSX 10.10, *) {
                return NSGraphicsContext.currentContext()?.CGContext
            } else if let contextPointer = NSGraphicsContext.currentContext()?.graphicsPort {
                let context: CGContextRef = Unmanaged.fromOpaque(COpaquePointer(contextPointer)).takeUnretainedValue()
                return context
            }
     
            return nil
        }
    }


    override init() {
        super.init()
    }

    //Called on macOS <10.12
    public override func drawWithBox(box: PDFDisplayBox) {
        print(#function)
        NSColor.redColor().setFill()
        saveAndRestoreGState { (ctx) in
            NSRectFill(self.boundsForBox(box))
        }
    }

    //Not called on macOS = 10.12
    public override func drawWithBox(box: PDFDisplayBox, toContext context: CGContext) {
        print(#function)
        NSColor.greenColor().setFill()
        CGContextSaveGState (context)
        NSRectFill(self.boundsForBox(box))
        CGContextRestoreGState (context)
    }


    private func saveAndRestoreGState(drawingClosure: (ctx:CGContextRef) -> ()) -> () {
        if let context = self.currentContext {
            CGContextSaveGState (context)
            drawingClosure(ctx: context)
            CGContextRestoreGState (context)
        }
    }
}
//MARK:- Custom PDFDocument
public class MyPDFDocument : PDFDocument {

    override init(){
        super.init()
        //Create 4 pages
        for pageItr in 0...3 {
            let newPage = MyPDFPage()
            self.insertPage(newPage, atIndex: pageItr)
        }
    }
}

//MARK:- Custom PDFView
public class MyPDFView : PDFView {

    public var myDocument : MyPDFDocument? {
        set{
            self.document = newValue
        }
        get{
            return self.document as? MyPDFDocument
        }
    }

    //called on macOS < 10.12
    public override func drawPage(page: PDFPage) {
        super.drawPage(page)
    }

    //Not called on macOS = 10.12
    @available(OSX 10.12, *)
    public override func drawPage(page: PDFPage, toContext context: CGContext){
        super.drawPage(page, toContext: context)
    }

}

Both PDFView and PDFPage subclasses appear to be broken under Sierra. From my experiments, if you subclass PDFPage and programmatically create and add it to a PDFDocument, no draw routines are ever called (i.e. your app holds and draws any images and text destined to eventually be saved to a PDF file disk - PDFPage gets called during saving to draw the data into its context created for saving). Similarly, if you do NOT subclass PDF page, but instead initialize PDFPage with an NSImage (one of the init variants), it also will not call the PDFView drawpage:toContext: method (i.e. "scan to PDF" is borken for all apps that support scanning. ONLY when a pdf document is read from disk have I had any success in having my PDFView subclass drawPage:toContext: method called - and THAT is after the PDF page has already been drawn from elsewhere. The PDFKit implementation in Sierra appears to create a PDFViewController (an Apple open source PDFViewController.mm from 2011 is available here http://opensource.apple.com//source/WebKit2/WebKit2-7536.30.1/UIProcess/API/mac/PDFViewController.mm

I suspect that the 2016 version of Apples PDFViewController could be the culprit, designed explicitly to handle drawing in Preview - in the open source PDFViewController, you can see where it actively filters some mouse events (and that is one of my problems). Would love to have aome runtime config options would allow this controller to be turned OFF or never created. I am actively working all aspects of the PDFKit on Sierra problem - our company has supported scanning and document management on Apple platforms since 1988, and happily supported very high speed document scanning to PDF when OS X came out with PDFKit, which has worked like a champ for over a decade. A number of other Mac App developers created utility apps around PDFKit and all now fail under Sierra. Until the developers can come up with a workaround or Apple steps in and brings PDFKit under Sierra in line with PDFKit under all previous versions of Mac OS X, we are in a real bind.


I am happy to discuss my findings outside the forums. craigl [at] mindwrap.com

I got the same problem. [PDFView setNeedsDisplay:YES] won't redraw the page unless I zoom in or zoom out... It seems a big bug here.

Did [self setNeedsDisplay:YES]; work for you? the setNeedsDisplay in my code doesn't even work.. The PDFView won't redraw on MacOS Sierra even I use [self setNeedsDisplay:YES];. But it worked well on 10.9 - 10.11 ...


Maybe the drawPage:toContext: won't be called because the [self setNeedsDisplay:YES] didn't work?

I have been having big issues too.


I have discovered that drawPage:toContext: is not called reliably.

The private drawPage:inContext: is however called.

Looking at the disassembly drawPage:toContext: seems like a cover for drawPage:inContext:


I do have persistent redrawing issues though.

The 10.12.1 beta seems to improve things but resizing is still flakey.

The current state of play is here

https://github.com/ThesaurusSoftware/PDFPageBuilder/tree/Sierra/PDFPageBuilder


One of the things I find is that when drawPage:inContext: is called [NSGraphicsContext currentContext] can be nil.


This suggests that the receiver is not being called from within a drawRect:.

Is use NSLayoutManager for my text layout and this dies if [NSGraphicsContext currentContext] is nil.


I try to handle the context as below.


Reworking the exact sequence of state save/restore modified how the PDF gremlins appear but I am not sure if my context handling is flawed or merely aggravating the PDFKit.

- (void)drawPageItemsToContext:(CGContextRef)context
{
// On 10.12 this method can be called with [NSGraphicsContext currentContext] = nil.
// Perhaps the call is made outside of the drawRect: call chain.
BOOL hasInitialNSGraphicsContext = [NSGraphicsContext currentContext] ? YES : NO;
if (hasInitialNSGraphicsContext) {
[NSGraphicsContext saveGraphicsState];
}
else {
CGContextSaveGState(context);
}


// life is much easier if we use a flipped co-ordinate system.
// NSLayoutManager expects a flipped NSGraphicsContext to be defined -
// without it layout fails.
NSGraphicsContext *flippedGC = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES];
[NSGraphicsContext setCurrentContext:flippedGC];


// define the flip transform
NSAffineTransform* xform = [NSAffineTransform transform];
[xform translateXBy:0.0 yBy:self.mediaBoxRect.size.height];
[xform scaleXBy:1.0 yBy:-1.0];
[xform concat];

// draw all map items
for (TSPageItem *mapItem in self.pageItems) {
[mapItem draw];
}

// highlight all container rects
if (self.highlightPageItemContainerRects) {
for (TSPageItem *mapItem in self.pageItems) {
[mapItem drawContainerRect];
}
}


if (hasInitialNSGraphicsContext) {
[NSGraphicsContext restoreGraphicsState];
}
else {
[NSGraphicsContext setCurrentContext:nil];
CGContextRestoreGState(context);
}

}

I'm having problems too and found that this from WWDC is directly on point for rendering a pdf and capturing an image

The new UIGraphicsRenderer has a subclass for PDF and manages the context lifetime.

Looks like the problem code is hitting the context lifetime management changes.


I'll be working on migrating to use of the new class UIGraphicsPDFRendererContext.. The documentation is sparse so we'll see how it goes.


In WWDC2016 session 2015 "What's New in Cocoa Touch". The transcript says "

So, something that you're writing with, the graphics,begin image context, API, would look like that. And I don't know how many of you already made the mistakeof trying to get the image after the begin image context. I'm definitely one. But now, we have a new class,the new UI graphics render class. And what this is giving you is, first, it's fully color managed. It's going to do the right thing by default.

If you're on a 9.7 inch iPad, you're goingto get a wide color context. If you're not, you will get the classic context.

The other thing, it's block based, easier to use. And, it's an object based API so we haveto increase our classes for images and PDFs.

Also, and that's quite important,that class will manage the lifetime of your context. Which means that we can do some memory optimizationunder the hood. Let me give you an example. For that the equivalent of the one before,you just create your renderer. You try to generate an image, it's just a matterof passing the block, and having your drawing code here."


From the presentation slides 87 & 94

"UIGraphicsBeginImageContext and UIGraphicsEndImageContext

32 bits and sRGB only Error prone

Not extensible

• • •

Example of OBSOLETE code

func createDrawing(size: CGSize) -> UIImage {
let renderer = UIGraphicsBeginImageContext(size)
/ let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image

}"


In slide 94 new form is UIGraphicsRender class and example is

func createDrawing(size: CGSize) -> UIImage {

let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { rendererContext in

/ /drawing code here

} }

Will, this thread is about PDFKit in MacOS Sierra, your reply references Cocoa Touch and iOS.

Hi guys,


Any developments on pdfkit rendering issues ? I note that the apple sample code @ PDF Editor


https://developer.apple.com/library/content/samplecode/PDFAnnotationEditor/Introduction/Intro.html


Is pretty buggy, specifically when dragging annotations around pdf renders poorly