创建分页PDF——Mac OS X

13

我正在使用Swift 3和Xcode 8,Beta 5来制作一个Mac应用程序,用户可以创建长注释并将其导出为PDF。

为了创建这个PDF,我正在使用Cocoa的dataWithPDF:方法,以下是代码:

do {
   // define bounds of PDF as the note text view
   let rect: NSRect = self.noteTextView.bounds
   // create the file path for the PDF
   if let dir = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true).first {
        // add the note title to path
        let path = NSURL(fileURLWithPath: dir).appendingPathComponent("ExportedNote.pdf")
        // Create a PDF of the noteTextView and write it to the created filepath
        try self.noteTextView.dataWithPDF(inside: rect).write(to: path!)  
   } else {
        print("Path format incorrect.") // never happens to me
   }

} catch _ {
    print("something went wrong.") // never happens to me
}

这个方法完全可行,但有一个问题:PDF只会放在一页上,这意味着当笔记中有很多文字时,页面会变得非常长。我应该如何强制将PDF放在它所需的许多信纸大小的页面上,无论是在我的应用程序导出PDF时还是导出后立即操作?


3
Cocoa PDF page splitting 的翻译:PDF页面拆分的Cocoa实现方法。 - Willeke
2
@Willeke 不是重复问题,因为我特别寻求Swift的答案,而不是Objective C。 - owlswipe
@JohnRamos CGPDF Api和Swift - Pacu
可能是如何使用Cocoa(Mac)在Swift中创建PDF的重复问题。 - halfer
1个回答

12

在经过数周的挫折后,我总结出了一种在Swift应用程序中创建分页PDF的终极方法。它并不像看起来那么复杂(以下是基于Swift 2):


注意:在阅读更多内容之前,您应该知道我在Github上制作了一个简单的工具,让您可以在较少的步骤(以及Swift 3中,不需要任何Objective-C)中完成此操作。 请点击此处查看


第1步:正确设置您的应用程序沙箱。每个提交的Mac应用程序都需要正确的应用程序沙箱,因此最好现在花30秒钟正确设置它。如果还没有打开,请转到Target设置,然后到Capabilities标题。在顶部启用应用程序沙箱。您应该看到许多子功能,启用您的应用程序需要的任何一个,并确保将User Selected File设置为Read/Write

第2步:将此函数添加到您的代码中,它将创建一个不可见的Web视图并将您的文本添加到其中。

func createPDF(fromHTMLString: String) {
            self.webView.mainFrame.loadHTMLString(htmlString, baseURL: nil)
            self.delay(1) {
            MyCreatePDFFile(self.webView)
    }
}

第三步: 创建delay()函数,让Xcode等待一秒钟以便HTML加载到Web视图中。

 func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

(注:对于Swift 3版本的此函数,请前往此处。)

步骤4:在您在第2步添加的函数上方添加我们WebView的声明。

var webView = WebView()

第五步: 你可能会注意到我们还没有创建 MyCreatePDFFile() 函数。事实证明,在Swift中无法将此WebView转换为PDF,所以我们将不得不转向Objective-C (叹气)。是的,你必须在你的Swift应用程序中运行一些Objective-C代码。

第六步:通过转到 File -> New -> File (或按下CMD + N)然后双击选择 Objective-C File 来创建一个 .m 文件。将其命名为 CreatePDF.m 并将其保留为空文件。

第七步:在添加您的 .m 文件时,您应该会收到提示添加桥接头文件:

enter image description here

点击 Yes

如果您没有看到提示,或者意外删除了您的桥接头文件,请添加一个新的 .h 文件到您的项目中,并将其命名为 <#YourProjectName#>-Bridging-Header.h

在某些情况下,特别是在使用ObjC框架时,您不会显式地添加Objective-C类,而Xcode无法找到链接器。在这种情况下,创建以上述方式命名的Bridging Header .h文件,然后确保将其路径链接到您的目标项目设置中,如下所示:

enter image description here

第八步:通过转到 File -> New -> File (或按下CMD + N)然后双击选择 Header File 来创建一个 .h 文件。将其命名为 CreatePDF.h

第九步:在您的 CreatePDF.h 文件中,添加以下代码,导入Cocoa和WebKit,并设置PDF创建函数:

#ifndef CreatePDF_h
#define CreatePDF_h

#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>

@interface Thing : NSObject

void MyCreatePDFFile(WebView *thewebview);

@end


#endif /* CreatePDF_h */

步骤10:现在是设置用于创建PDF的.m文件的时候了。首先将以下内容添加到空的.m文件中:

#import "CreatePDF.h"
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>

@implementation Thing

void MyCreatePDFFile(WebView *thewebview) {

}
@end

第11步:现在您可以将代码添加到MyCreatePDFFile(..)函数中。这是函数本身(它放在当前空的void函数内):

NSDictionary *printOpts = @{
    NSPrintJobDisposition: NSPrintSaveJob // sets the print job to save the PDF instead of print it.
};
NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printOpts];    
[printInfo setPaperSize:NSMakeSize(595.22, 841.85)]; // sets the paper size to a nice size that works, you can mess around with it
[printInfo setTopMargin:10.0];
[printInfo setLeftMargin:10.0];
[printInfo setRightMargin:10.0];
[printInfo setBottomMargin:10.0];
NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:[[[thewebview mainFrame] frameView] documentView] printInfo:printInfo]; // sets the print operation to printing the WebView as a document with the print info set above
printOp.showsPrintPanel = NO; // skip the print question and go straight to saving pdf
printOp.showsProgressPanel = NO; 
[printOp runOperation];

第12步:现在将此添加到您的桥接头文件中,以使您的Swift文件能够查看您刚刚创建的Objective-C函数。

#import "CreatePDF.h"

步骤 13: 这段代码不应该有任何错误,但如果还有,请在下面评论区留言。

步骤 14: 要在你的 Swift 文件中的任何位置调用 createPDF 函数,请按照以下步骤进行:

createPDF("<h1>Title!</h1><p>\(textview.text!)</p>")

createPDF内部看到的字符串是HTML,可以编辑成任何HTML格式。如果您不了解HTML,可以在这里查看一些基础知识 right here

就这样! 您现在应该能够从Cocoa/Mac应用程序中直接创建分页PDF文件了。


鸣谢

注意:此答案已经测试可在Xcode 7、Swift 2和OS X El Captian上运行。如果您在使用Xcode 8/Swift 3/macOS Sierra时遇到任何问题,请告诉我。


为什么 MyCreatePDFFile 不能被翻译成 Swift? - Willeke
@Willeke,我无法将printOpts的NSDictionary翻译成Swift,也无法翻译NSPrintOperation PrintOperationWithView。我可能是错的,但这就是我做的方式,而且对我有效! - owlswipe
3
我不认为我喜欢这一部分:"Create the delay() function to allow Xcode to wait a second for the HTML to be loaded into the web view." 如果HTML加载时间超过1秒,这是否会引入竞争条件呢? - jcmiller11
@jcmiller11 你提出了一个很好的观点;根据我的经验,这个方法是可行的(我在我的非常慢的老款Macbook上测试过,每次都能正常工作)。我相当确定你不能使用完成处理程序将本地HTML加载到Webview中,但如果你有其他正确延迟的方法,我会非常感激。你确实需要这个延迟;没有它,PDF文件始终为空白。 - owlswipe
这是唯一对我有效的方法,谢谢 :)WKWebView 还没有实现这个。 - Patrick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接