如何使用 Cocoa (Mac) 在 Swift 中创建 PDF

Posted

技术标签:

【中文标题】如何使用 Cocoa (Mac) 在 Swift 中创建 PDF【英文标题】:How to create a PDF in Swift with Cocoa (Mac) 【发布时间】:2016-08-15 00:16:06 【问题描述】:

Xcode 7.3.2、Swift 2、Cocoa (Mac)。

我的应用程序涉及用户输入一些文本,这些文本可以导出为 PDF。

在我的应用程序的 ios 版本中,我可以使用 CoreText 框架相对轻松地创建 PDF:

let html = "<font face=\'Futura\' color=\"SlateGray\"><h2>\(title)</h2></font><font face=\"Avenir\" color=\"SlateGray\"><h4>\(string)</h4></font>"

    let fmt = UIMarkupTextPrintFormatter(markupText: html)

    // 2. Assign print formatter to UIPrintPageRenderer

    let render = UIPrintPageRenderer()
    render.addPrintFormatter(fmt, startingAtPageAt: 0)

    // 3. Assign paperRect and printableRect

    let page = CGRect(x: 10, y: 10, width: 595.2, height: 841.8) // A4, 72 dpi, margin of 10 from top and left.
    let printable = page.insetBy(dx: 0, dy: 0)

    render.setValue(NSValue(cgRect: page), forKey: "paperRect")
    render.setValue(NSValue(cgRect: printable), forKey: "printableRect")

    // 4. Create PDF context and draw

    let pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)

    for i in 1...render.numberOfPages 

        UIGraphicsBeginPDFPage();
        let bounds = UIGraphicsGetPDFContextBounds()
        render.drawPage(at: i - 1, in: bounds)
    

    UIGraphicsEndPDFContext();

    // 5. Save PDF file

    path = "\(NSTemporaryDirectory())\(title).pdf"
    pdfData.write(toFile: path, atomically: true)

但是,UIMarkupTextPrintFormatterUIPrintPageRendererUIGraphicsBeginPDFContextToDataUIGraphicsEndPDFContext 在 OS X 上都不存在。我怎样才能执行与此 iOS 代码完全相同的操作(创建一个基本的 PDF使用 Mac 和 Cocoa 从一些 HTML 写入到某个文件路径作为分页 PDF)?

编辑:这个问题的答案在这里:Create a paginated PDF—Mac OS X。

【问题讨论】:

请参阅developer.apple.com/library/mac/documentation/GraphicsImaging/…,“创建 PDF 文件”作为起点。您可以用 HTML 制作一个 NSAttributedString 并绘制它。 不在我的电脑前,但您需要查看如何在 NSGraphicsContext 中设置 CGContextRef,然后您将能够使用 NSAttributedString 的绘制方法。 如果您遇到更具体的问题,欢迎您编辑您的问题。不过,我在工作的 Windows 机器上。 @zneak 我在将所有 Objective-C 转换为 Swift 时遇到问题,我不断收到错误消息“无法将 CG[whatever] 转换为 UnsafePointer&lt;void&gt; 用什么方法调用? 【参考方案1】:

这是一个从纯 HTML 生成 PDF 的函数。

func makePDF(markup: String) 
    let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let printOpts: [NSPrintInfo.AttributeKey: Any] = [NSPrintInfo.AttributeKey.jobDisposition: NSPrintInfo.JobDisposition.save, NSPrintInfo.AttributeKey.jobSavingURL: directoryURL]
    let printInfo = NSPrintInfo(dictionary: printOpts)
    printInfo.horizontalPagination = NSPrintingPaginationMode.AutoPagination
    printInfo.verticalPagination = NSPrintingPaginationMode.AutoPagination
    printInfo.topMargin = 20.0
    printInfo.leftMargin = 20.0
    printInfo.rightMargin = 20.0
    printInfo.bottomMargin = 20.0

    let view = NSView(frame: NSRect(x: 0, y: 0, width: 570, height: 740))

    if let htmlData = markup.dataUsingEncoding(NSUTF8StringEncoding) 
        if let attrStr = NSAttributedString(HTML: htmlData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) 
            let frameRect = NSRect(x: 0, y: 0, width: 570, height: 740)
            let textField = NSTextField(frame: frameRect)
            textField.attributedStringValue = attrStr
            view.addSubview(textField)

            let printOperation = NSPrintOperation(view: view, printInfo: printInfo)
            printOperation.showsPrintPanel = false
            printOperation.showsProgressPanel = false
            printOperation.run()
        
    

发生了什么:

    将 HTML 放入 NSAttributedString。 将 NSAttributedString 渲染到 NSTextField。 将 NSTextField 渲染到 NSView。 使用该 NSView 创建一个 NSPrintOperation。 设置打印参数以另存为 PDF。 运行打印操作(实际上会打开一个对话框来保存 PDF) 大家都很开心。

这不是一个完美的解决方案。注意硬编码的整数值。

【讨论】:

非常感谢您的回答,真的很接近了!不幸的是,它只生成一页,溢出的文本被截断。如何让文本进入新页面? 嗯。没有简单的方法来分页吗? This code gist 让我在 iOS 上通过简单的自动分页来执行 HTML->PDF 的事情,有没有办法我们可以将该 iOS 代码的分页部分调整为适用于 OS X 的内容? quemeful,我已经开始在这个问题上悬赏。如果你能给我建议如何对我创建的 PDF 进行分页(而不是像在你的代码中那样被截断,它应该进入一个新页面!),我很乐意奖励你一些互联网积分。 如何在没有对话框的情况下将其保存在指定的文件夹中?谢谢

以上是关于如何使用 Cocoa (Mac) 在 Swift 中创建 PDF的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift、Cocoa、Mac OSX 中将 EXIF 数据设置为 NSImage

如何在cocoa中模拟mac媒体密钥

Cocoa Xcode:使用 Swift 4 for Mac 以编程方式从 AppDelegate 打开窗口控制器?

菜鸡的swift学习:Mac中语言设定,no such module Cocoa的,可选类型的声明

在 Cocoa App Swift [Mac OS] 中显示在 containerView 内的 NSViewController 不会与 Container 的边界对齐

如何使用 Storyboards/Cocoa 在 Swift 3.x 中引用视图的窗口