Mac OS X:如何将多个NSView绘制成一个新的多页PDF?

时间:2012-05-03 19:27:41

标签: macos cocoa core-graphics quartz-2d

我有一个与图形相关的基于文档的Mac App。我的应用程序的文档可能有多个“页面”。每个页面都有一个NSView“画布”对象。

我的应用程序有几个导出选项,这些选项实现为返回NSData对象(然后写入磁盘)的方法。

我想在以下方法中实现PDF导出选项:

  1. 创建内存中的PDF
  2. 循环浏览文档的画布视图,并在内存PDF中的新页面中呈现每个视图
  3. 将内存中的多页PDF作为NSData
  4. 返回

    以下代码是我目前正在尝试的内容。

    我的文档中的每个页面都是800 x 600像素。

    当我将生成的NSData对象写入磁盘时,写入操作成功,但磁盘上的文件以某种方式损坏。该文件无法在预览或我尝试过的任何其他应用中打开。

    我做错了什么?

    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);
        }
    }
    

2 个答案:

答案 0 :(得分:5)

OP在这里。我已经解决了这个问题。我错过了对CGPDFContextClose()的最后通话。

所以在发布上下文之前......

CGPDFContextClose(ctx);
CGContextRelease(ctx);

答案 1 :(得分:0)

我发现这是一个最有用的答案(并且在问题和答案上都提出了OP!)。有一些打嗝将它翻译成Swift 3,但这就像一个魅力,它可以在iOS上运行......

let data = NSMutableData()
guard let consumer = CGDataConsumer(data: data) else { Swift.print("Aargh - no CGDataConsumer"); return }
var mediaBox = CGRect(x: 0, y: 0, width: 800, height: 600)
guard let ctx = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else { Swift.print("Aargh - no CGContext"); return }

//let gc = NSGraphicsContext(cgContext: ctx, flipped: false) 
//I don't need the NS context, as my drawing routines just use CGContext

for page in self.pages { // Array of page models
    ctx.beginPage(mediaBox: &mediaBox)
    ctx.saveGState()
    myDraw(page, context: ctx, options: []) // My routine
    ctx.restoreGState()
    ctx.endPage()
}
ctx.closePDF()
do {
    try data.write(toFile: "/Users/grimxn/Test.pdf", options: .atomic)
} catch {
    Swift.print("Aargh - failed to write output file")
}