PDFKit
是苹果在 iOS11 后推出的框架,它提供一系列 API 方便我们展示和操作 PDF 文档。
PDFKit 常用于展示和操作 PDF 文档,我们可以从这两方面展开探索:
- View:视图,展示能力
- Model:模型,操作能力
一、视图
1. PDFView
PDFKit 提供了一个叫 PDFView
的 UIView 子类,它可以用于展示 PDF 文档内容,而且基本包含了所有基本的 PDF 浏览器的功能:查看、缩放、选择文本等等。当然它还提供多种属性用于自定义页面的布局等。
// 创建 PDFView
let pdfView = PDFView(frame: view.bounds)
view.addSubview(pdfView)
// 展示形式:单页,双页等等
pdfView.displayMode = .singlePageContinuous
// 接收 PDFDocument 类型属性,来展示已存在的 PDF 文档。
pdfView.document = PDFDocument(url: pdfDocumentURL)
2. PDFThumbnailView
PDFThumbnailView
顾名思义,它直接提供了一个展示 PDF 内容缩略图的视图。
let thumbnail = PDFThumbnailView(frame: frame)
view.addSubview(thumbnail)
// 为 thumbnail 配置 PDFView
thumbnail.pdfView = pdfView
thumbnail.thumbnailSize = CGSize(width: 50, height: 50)
thumbnail.layoutMode = .horizontal
二、模型
在 PDFKit 中, PDFDocument
就等同于实际的 PDF 文件,它包含 PDF 文件的元信息等。而 PDFPage
则是管理每一页的渲染,添加标注 PDFAnnotation
等。下面这幅图展示了这三个基本模型的关系。
1. PDFDocument
读写操作
// 从文件 URL 中读取
if let documentURL = Bundle.main.url(forResource: "test", withExtension: "pdf"),
let document = PDFDocument(url: documentURL) {
pdfView.document = document
}
// 保存文档
document.write(to: documentURL)
// 保存并加密文档,这里还可以配置上 owner 和 user 两种权限
document.write(to: documentURL,
withOptions: [.ownerPasswordOption: "kiwi",
.userPasswordOption: "kiwi123"])
页面操作
// 从文件中获取 PDFPage
let myPage = document.page(at: 0)
// 添加 Page
document.insert(newPage, at: document.pageCount)
// 交换 Page 位置
document.exchangePage(at: 0, withPageAt: 1)
// 删除 Page
document.removePage(at: document.pageCount - 1)
解密与权限
PDFDocument 可以判断文档是否被加密,并提供解密方法。同时还可以获取到当前文档用户的权限等。
let document = PDFDocument(url: documentURL)
// 判断是否加密文档,并通过密码解锁
if document.isEncrypted && document.unlock(withPassword: "kiwi") {
// 判断文档用户权限
if document.permissionsStatus == .owner {
// Owner 权限
} else {
// User 权限
if document.allowsCopying { /* ... */ }
if document.allowsPrinting { /* ... */ }
}
}
2. PDFPage
获取文本
我们可以直接通过 PDFPage 的 string
属性获取到当前 Page 的文本信息,也可以通过 selection(for: range)
方法获取部分文本信息。还可以通过 attributedString
属性获取到富文本信息。
let page = pdfDocument.page(at: 0)
page?.string // 文本
page?.attributedString // 富文本
// 通过 NSRange 指定选择的文本内容
let pageSelection = page?.selection(for: NSRange(location: 10, length: 5))
pageSelection?.string
pageSelection?.attributedString
自定义 PDFPage
我们可以通过继承 PDFPage,并重写其 draw(with box: PDFDisplayBox, to context: CGContext)
方法来绘制一些自定义的内容,比如给 PDF 文件添加上 水印
:
// 1. 配置 PDFDocumentDelegate
pdfDocument.delegate = self
// 2. 实现 PDFDocumentDelegate 的 classForPage 方法
func classForPage() -> AnyClass {
// 这里返回自定义的 PDFPage:WatermarkPage
return WatermarkPage.self
}
// 3.
class WatermarkPage: PDFPage {
override func draw(with box: PDFDisplayBox, to context: CGContext) {
// 绘制原始内容
super.draw(with: box, to: context)
//绘制水印
/*...*/
}
/*...*/
}
WatermarkPage 的具体绘制实现可以查看苹果提供的官方 Demo-CustomGraphics
3. PDFAnnotation
我们可以在 PDFPage 上添加标注 PDFAnnotation
,创建标注时可以指定内建的标注类型PDFAnnotationSubtype
,比如线段,矩形,下划线等。比如下面的代码表示创建一条红色带箭头的线段:
// 创建 PDFAnnotationSubtype 为 line 的标注
let line = PDFAnnotation(bounds: view.bounds, forType: .line, withProperties: nil)
line.startPoint = CGPoint(x: 0, y: 0)
line.endPoint = CGPoint(x: 100, y: 100)
line.startLineStyle = .closedArrow
line.endLineStyle = .openArrow
line.color = UIColor.red
let border = PDFBorder()
border.lineWidth = 5
line.border = border
pdfPage.addAnnotation(line)
PDFAction
PDFAnnotaion 有一个 PDFAction
类型的属性,PDFAction 有这几种具体类型:
- PDFActionGoTo
- PDFActionNamed
- PDFActionRemoteGoTo
- PDFActionResetForm
- PDFActionURL
PDFActionGoTo
可以跳转到文档指定的位置:
let destination = PDFDestination(page: pdfPage, at: CGPoint(x: 30, y: 200))
let actionGoTo = PDFActionGoTo(destination: destination)
annotation.action = actionGoTo
PDFActionURL
可以打开一个 URL 地址:
let url = URL(string: "http://meshkit.cn")
let actionURL = PDFActionURL(url: url)
annotation.action = actionURL
其余的 PDFAction 这里就不展开描述了,更多的可以查看官方文档: PDFAction
Widgets
PDFAnnotationSubtype
有一种类型是 widget
,它代表一类特殊的 Annotations,比如是按钮、单选按钮,复选框等这些可交互的控件。
创建 PDFAnnotaion 指定 PDFAnnotationSubtype
为 widget 后,还需要指定 PDFAnnotationWidgetSubtype
和 PDFWidgetControlType
才能确定某一类型的 widget:
// 复选框☑️
let checkbox = PDFAnnotation(bounds: bounds, forType: .widget, withProperties: nil)
checkbox.widgetFieldType = .button
checkbox.widgetControlType = .checkBoxControl
page.addAnnotation(checkbox)
// 按钮
let button = PDFAnnotation(bounds: bouds, forType: .widget, withProperties: nil)
button.widgetFieldType = .button
button.widgetControlType = .pushButtonControl
button.caption = "按钮"
page.addAnnotation(button)
// 文本输入框
let textField = PDFAnnotation(bounds: bounds, forType: .widget, withProperties: nil)
textField.widgetFieldType = .text
page.addAnnotation(textField)
/*....*/
三、小结
总的来说,PDFKit 给我们提供了非常易用且方便扩展的处理 PDF 的能力。PDFKit 更多的是用于展示和在原有的 PDF 文档上进行标注等,至于从零创建并简单排版 PDF 文档,它提供的能力有限,关于这块的探索,我会在下一个篇文章讲解。