Search code examples
macoscocoacore-graphicsquartz-2d

Mac OS X: How to draw multiple NSViews into a new, multi-page PDF?


I have a graphics-related Document-based Mac App. My app's Documents may have multiple "pages". Each page has an NSView "canvas" object.

My app has several export options which are implemented as methods which return an NSData object (which is then written to disk).

I would like to implement a PDF export option in a method which:

  1. Creates an in-memory PDF
  2. loops thru my document's canvas views and renders each in a new page in the in-memory PDF
  3. returns the in-memory, multi-page PDF as an NSData

The code below is what I am currently trying.

Each page in my document is 800 x 600 pixels.

When I write the resulting NSData object to disk, the write operation succeeds, but the file on disk is corrupted somehow. The file cannot be opened in Preview or any other app I've tried.

What am I doing wrong?

NSMutableData *data = [NSMutableData data];
CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)data);

CGRect mediaBox = CGRectMake(0.0, 0.0, 800.0, 600.0);
CGContextRef ctx = CGPDFContextCreate(consumer, &mediaBox, NULL);
CFRelease(consumer);

NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:NO];

for (NSView *canvas in myCanvases) {
    CGContextBeginPage(ctx, &mediaBox);
    CGContextSaveGState(ctx);

    [canvas displayRectIgnoringOpacity:mediaBox inContext:gc];

    CGContextRestoreGState(ctx);
    CGContextEndPage(ctx);
}

CGPDFContextClose(ctx); // UPDATE: this line was originally missing. see answer below
CGContextRelease(ctx);

...

NSError *err = nil;
if (![data writeToFile:s options:NSDataWritingAtomic error:&err]) {
    if (err) {
        NSLog(@"%@", err);
    }
}

Solution

  • OP here. I have solved the problem. I was missing a final call to CGPDFContextClose().

    So before releasing the context...

    CGPDFContextClose(ctx);
    CGContextRelease(ctx);