🦿

[iOS swift] PDFのレンダリング

2022/02/05に公開

iOSでPDFを表示する方法がわからなかったので調べた記録です。検索の足掛かりにしていただければ幸いです。

画像としてPDFのページを取得する

  • CoreGraphicsをそのまま叩く
  • PDFKitのPDFThumbnailViewを使用する (PDFの画像だけを欲しい場合はこれが一番手軽)

操作可能なViewとしてレンダリングする

  • PDFKitのPDFViewを使用する

PDFKitは裏でCoreGraphicsを叩いている(色々便利にしてくれるラッパライブラリ)のでどこまで低レベルのAPIを叩くかの違い。

それぞれの特徴

アプローチ 方式 ズームの可否 描画速度
CoreGraphics CGContextに直接描画 不可(描画範囲を指定して再描画) 速い
PDFKit Thumbnail UIImageとして取得 不可(描画範囲を指定して再取得) 速い
PDFView UIViewとして取得 動的(読み込み中は低解像度のサムネイルが差し込まれる)

CoreGraphicsやPDFKit ThumbnailはPDFはあくまでページを画像(ベクターではない)として取得しているだけなので、ズームをすると荒れます。ズームに応じて該当部分のみを取得し直しレンダリングすることで綺麗に表示できますがほとんどの場合PDFViewを使った方が良いでしょう。

アプローチ テキスト選択 ドキュメント iOS Ver
CoreGraphics 不可(自前の実装が必要) https://developer.apple.com/documentation/coregraphics/cgpdfdocument iOS 2.0+
PDFKit Thumbnail 不可(ただのUIImage) https://developer.apple.com/documentation/pdfkit/pdfview iOS 11.0+
PDFView 可(PDFKitが管理) https://developer.apple.com/documentation/pdfkit/pdfthumbnailview iOS 11.0+

テキストを選択させたい場合はPDFViewを使うと良いと思います。
PDFKit搭乗前は下記のようなライブラリが利用されていた...ぽいです。
https://github.com/vfr/UXReader-iOS

また、i文庫HDなどのような初期からあるPDFリーダーアプリではテキストの選択を自前で処理していたようです。

  • SafariのようにPDFをScrollViewで表示したいだけ
    • PDFKitを利用
  • PDFのページ画像が欲しいだけ(サムネイルを取得したいときなど)
    • PDFKitのPDFThumbnailViewで欲しいサイズのサムネイルをUIImageとして取得する
  • テキストを選択できるようにしたい
    • PDFKitを利用

動作するサンプルコード (Playground)

CoreGraphics

https://qiita.com/Nonchalant/items/b94ee09e9bac5c938c62

PDFThumbnailView

import PlaygroundSupport
import UIKit
import PDFKit

guard let url = URL(string: "https://www.ci.seikei.ac.jp/yamamoto/lecture/automaton/text.pdf") else { fatalError() }
guard let doc = PDFDocument(url: url) else { fatalError() }

guard let page = doc.page(at: 0) else { fatalError() }
let image = page.thumbnail(of: CGSize(width: 1000, height: 1000), for: PDFDisplayBox.trimBox)

let view = UIImageView(image: image)
view.backgroundColor = UIColor.systemGray
view.contentMode = .scaleAspectFit
PlaygroundPage.current.liveView = view

PDFView

import PlaygroundSupport
import UIKit
import PDFKit

guard let url = URL(string: "https://www.ci.seikei.ac.jp/yamamoto/lecture/automaton/text.pdf") else { fatalError() }
guard let doc = PDFDocument(url: url) else { fatalError() }

let view = PDFView()
view.document = doc
PlaygroundPage.current.liveView = view

Discussion