在 macOS 上使用 Swift 在 PDF 上绘图

Posted

技术标签:

【中文标题】在 macOS 上使用 Swift 在 PDF 上绘图【英文标题】:Draw on a PDF using Swift on macOS 【发布时间】:2017-06-19 21:11:05 【问题描述】:

我的目标是在 PDF 上编写文本,例如注释。

我实现了将 PDFPage 转换为 NSImage,我在 NSImage 上绘图,然后保存了由图像形成的 PDF。

let image = NSImage(size: pageImage.size)        
image.lockFocus()

let rect: NSRect = NSRect(x: 50, y: 50, width: 60, height: 20)
"Write it on the page!".draw(in: rect, withAttributes: someAttributes)

image.unlockFocus()

let out = PDFPage(image: image)

问题显然是out(输出 PDF 的新页面)是图像的 PDFPage,而不是常规图像。所以输出的 PDF 非常大,你不能在上面复制和粘贴任何东西。这只是一系列图像。

我的问题是,是否有一种方法可以在不使用 NSImage 的情况下以编程方式在 PDF 页面上添加简单文本。有什么想法吗?

注意:ios 编程中有这个课程UIGraphicsBeginPDFPageWithInfo,对我来说可能非常有用。但是我找不到用于 macOS 开发的类似类。

【问题讨论】:

是的...您可以使用 html 来做到这一点。 @ClaudioCastro 没有别的办法吗?以下是如何在 iOS 上解决我的问题,但我在 macOS 中找不到等效的方法:***.com/questions/32113468/… 对不起,你说的是 macOS... 在新的 iOS 11 和 xCode 9 中,有一些关于 pdf api 的消息。 【参考方案1】:

您可以在 macOS 上创建 PDF 图形上下文并在其中绘制 PDFPage。然后,您可以使用 Core Graphics 或 AppKit 图形将更多对象绘制到上下文中。

这是我通过打印您的问题创建的测试 PDF:

这是将该页面绘制到 PDF 上下文中的结果,然后在其上绘制更多文本:

这是我编写的将第一个 PDF 转换为第二个 PDF 的代码:

import Cocoa
import Quartz

let inUrl: URL = URL(fileURLWithPath: "/Users/mayoff/Desktop/test.pdf")
let outUrl: CFURL = URL(fileURLWithPath: "/Users/mayoff/Desktop/testout.pdf") as CFURL

let doc: PDFDocument = PDFDocument(url: inUrl)!
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = page.bounds(for: .mediaBox)

let gc = CGContext(outUrl, mediaBox: &mediaBox, nil)!
let nsgc = NSGraphicsContext(cgContext: gc, flipped: false)
NSGraphicsContext.current = nsgc
gc.beginPDFPage(nil); do 
    page.draw(with: .mediaBox, to: gc)

    let style = NSMutableParagraphStyle()
    style.alignment = .center

    let richText = NSAttributedString(string: "Hello, world!", attributes: [
        NSFontAttributeName: NSFont.systemFont(ofSize: 64),
        NSForegroundColorAttributeName: NSColor.red,
        NSParagraphStyleAttributeName: style
        ])

    let richTextBounds = richText.size()
    let point = CGPoint(x: mediaBox.midX - richTextBounds.width / 2, y: mediaBox.midY - richTextBounds.height / 2)
    gc.saveGState(); do 
        gc.translateBy(x: point.x, y: point.y)
        gc.rotate(by: .pi / 5)
        richText.draw(at: .zero)
    ; gc.restoreGState()

; gc.endPDFPage()
NSGraphicsContext.current = nil
gc.closePDF()

【讨论】:

完美答案!我现在明白了。 嘿,罗伯!如果我需要使用新绘制的页面怎么办?使用您的代码,新页面将保存在 outUrl 路径中,但无法以编程方式访问它。我唯一能做的就是将 PDFPage 保存到 outUrl 然后再次打开它。我查看了 CGContext 的文档页面,但没有找到任何可以获取已修改的 PDFPage 的内容。知道如何在 gc.closePDF() 调用后立即获取它吗?这不重要,实际上只是为了好奇。 使用带有CGDataConsumer 参数的CGContext 初始化器。在closePDF 之后,您可以从数据中创建PDFDocument 的新实例,而无需通过文件。 这是一个很好的答案,虽然我在 Swift 4 中尝试使用 NSGraphicsContext.setCurrent() 时遇到错误。它说“类型 'NSGraphicsContext' 没有成员 'setCurrent'”。它仍在文档中,所以我不确定。 NSGraphicsContext 现在有一个属性,current,而不是一个 setter 方法。我已经更新了我的答案。

以上是关于在 macOS 上使用 Swift 在 PDF 上绘图的主要内容,如果未能解决你的问题,请参考以下文章

Cocoa swift macOS 打印 html 到 pdf

在 Swift 中在 iOS 上显示 PDF [重复]

使用 swift 代码将 web 视图上的 pdf 共享到其他设备

如何在 Swift 中的 PDF 上绘制一些东西?

Swift ui macOS 在特定 url 上打开一个新的 google chrome 窗口隐身

在 MacOS 上使用 pyuno 进行文件转换