PDFKit 是苹果在 iOS11 后推出的框架,它提供一系列 API 方便我们展示和操作 PDF 文档。
PDFKit 常用于展示和操作 PDF 文档,我们可以从这两方面展开探索:
- View:视图,展示能力
- Model:模型,操作能力
一、视图
1. PDFView
PDFKit 提供了一个叫 PDFView 的 UIView 子类,它可以用于展示 PDF 文档内容,而且基本包含了所有基本的 PDF 浏览器的功能:查看、缩放、选择文本等等。当然它还提供多种属性用于自定义页面的布局等。
// 创建 PDFViewlet 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 配置 PDFViewthumbnail.pdfView = pdfViewthumbnail.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"])
页面操作
// 从文件中获取 PDFPagelet myPage = document.page(at: 0)// 添加 Pagedocument.insert(newPage, at: document.pageCount)// 交换 Page 位置document.exchangePage(at: 0, withPageAt: 1)// 删除 Pagedocument.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?.stringpageSelection?.attributedString
自定义 PDFPage
我们可以通过继承 PDFPage,并重写其 draw(with box: PDFDisplayBox, to context: CGContext) 方法来绘制一些自定义的内容,比如给 PDF 文件添加上 水印 :
// 1. 配置 PDFDocumentDelegatepdfDocument.delegate = self// 2. 实现 PDFDocumentDelegate 的 classForPage 方法func classForPage() -> AnyClass {// 这里返回自定义的 PDFPage:WatermarkPagereturn 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 = .closedArrowline.endLineStyle = .openArrowline.color = UIColor.redlet border = PDFBorder()border.lineWidth = 5line.border = borderpdfPage.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 = .buttoncheckbox.widgetControlType = .checkBoxControlpage.addAnnotation(checkbox)// 按钮let button = PDFAnnotation(bounds: bouds, forType: .widget, withProperties: nil)button.widgetFieldType = .buttonbutton.widgetControlType = .pushButtonControlbutton.caption = "按钮"page.addAnnotation(button)// 文本输入框let textField = PDFAnnotation(bounds: bounds, forType: .widget, withProperties: nil)textField.widgetFieldType = .textpage.addAnnotation(textField)/*....*/
三、小结
总的来说,PDFKit 给我们提供了非常易用且方便扩展的处理 PDF 的能力。PDFKit 更多的是用于展示和在原有的 PDF 文档上进行标注等,至于从零创建并简单排版 PDF 文档,它提供的能力有限,关于这块的探索,我会在下一个篇文章讲解。
