在macOS上使用Swift绘制PDF

在macOS上使用Swift绘制PDF,swift,macos,pdf,Swift,Macos,Pdf,我的目标是在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, withAttr

我的目标是在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)
问题很明显,输出PDF的新页面是图像的PDF页面,而不是常规页面。因此,输出PDF的大小非常大,您无法复制和粘贴任何内容。这只是一系列图像

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


注意:iOS编程UIGraphicsBeginPDFPageWithInfo中有一个类,它对我的情况非常有用。但是我找不到用于macOS开发的类似类。

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

以下是我通过打印您的问题创建的测试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()

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

以下是我通过打印您的问题创建的测试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()

是 啊你可以用html来做这件事。@ClaudioCastro没有其他方法吗?以下是如何解决我在iOS上的问题,但我在macOS上找不到类似的方法:对不起,你在谈论macOS。。。我的解决方案是针对iOS的,我使用html生成带有文本和图片的pdf。在新的iOS 11和xCode 9中有一些关于pdf api的新闻。是的。。。你可以用html来做这件事。@ClaudioCastro没有其他方法吗?以下是如何解决我在iOS上的问题,但我在macOS上找不到类似的方法:对不起,你在谈论macOS。。。我的解决方案是针对iOS,我使用html生成带有文本和图片的pdf。在新的iOS 11和xCode 9中,有一些关于pdf api的新闻。完美答案!我现在明白了。嘿,罗伯!如果我需要使用新绘制的页面怎么办?使用您的代码,新页面将保存在outUrl路径,但无法通过编程方式访问它。我唯一能做的就是将PDFPage保存到outUrl,然后再次打开它。我查看了CGContext的文档页面,但没有找到任何东西来获取修改后的PDFPage。知道如何在gc.closePDF调用后立即获取它吗?这并不重要,实际上只是出于好奇。使用接受CGDataConsumer参数的CGContext初始值设定项。关闭PDF后,您可以从数据创建PDFDocument的新实例,而无需通过文件。这是一个很好的答案,尽管我在尝试使用Swift 4中的NSGraphicsContext.setCurrent时遇到了一个错误。它说类型“NSGraphicsContext”没有成员“setCurrent”。它仍然在文档中,所以我不确定.NSGraphicsContext现在有一个属性current,而不是setter方法。我已经更新了我的答案。完美的答案!我现在明白了。嘿,罗伯!如果我需要使用新绘制的页面怎么办?使用您的代码,新页面将保存在outUrl路径,但无法通过编程方式访问它。我唯一能做的就是将PDFPage保存到outUrl,然后再次打开它。我查看了CGContext的文档页面,但没有找到任何东西来获取修改后的PDFPage。知道如何在gc.closePDF调用后立即获取它吗?这并不重要,实际上只是出于好奇。使用接受CGDataConsumer参数的CGContext初始值设定项。关闭PDF后,您可以从数据创建PDFDocument的新实例,而无需通过文件。这是一个很好的答案,尽管我在尝试使用Swift 4中的NSGraphicsContext.setCurrent时遇到了一个错误。它说类型“NSGraphicsContext”没有成员“setCurrent”。它仍然在文档中,所以我不确定.NSGraphicsContext现在有一个属性current,而不是setter方法。我已经更新了我的答案。