SwiftUIのレイアウトでPDFを作成して結合する

前回の記事: [SwiftUIのレイアウトでPDFを作成する](https://qiita.com/mbotsu/items/1c1a49efed96f8ae145f)

今回はSwiftUIで作成したView同士を結合したPDFを作成してみた。

## ViewからPDFのDataオブジェクトを作成

```swift
let page1 = try! Page1().getPdfData(width: 200, height: 200)

...

let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: width, height: height))
let pdf = pdfRenderer.pdfData(actions: { (context) in
  context.beginPage()
  rootVC?.view.layer.render(in: context.cgContext)
})
```

## PDFを結合する

- PDFKitでPDFのDataオブジェクトをPDFDocumentとして取り込む
- PDFDocumentからPDFのページを抽出して最初のPDFDocumentに結合

```swift
let document1 = PDFDocument(data: page1)
let document2 = PDFDocument(data: page2)
let page = document2?.page(at: 0)
document1?.insert(page!, at: 1)
```

## 結果画像

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35973/1fc481c3-2a59-d085-6bfe-292153cb3a4a.png" width=300>


## 評価コード

```PDFCombineView.swift
import SwiftUI
import PDFKit

struct PDFCombineView: View {
  @State var pdfDocument: PDFDocument?
  
  var body: some View {
    VStack {
      if let pdfDocument = pdfDocument {
        PdfView(pdfDocument: pdfDocument)
      }
    }
    .onAppear {
      let page1 = try! Page1().getPdfData(width: 200, height: 200)
      let page2 = try! Page2().getPdfData(width: 200, height: 200)
      
      let document1 = PDFDocument(data: page1)
      let document2 = PDFDocument(data: page2)
      let page = document2?.page(at: 0)
      document1?.insert(page!, at: 1)
      pdfDocument = document1
    }
  }
}

struct Page1: View {
  var body: some View {
    Text("Page1")
  }
}

struct Page2: View {
  var body: some View {
    Text("Page2")
  }
}

struct PdfView : UIViewRepresentable {
  let pdfDocument: PDFDocument
  
  func makeUIView(context: Context) -> some PDFView {
    let pdfView = PDFView()
    pdfView.document = pdfDocument
    return pdfView
  }
  func updateUIView(_ uiView: UIViewType, context: Context) {}
}

extension View {
  func getPdfData(width:CGFloat=595.2, height:CGFloat=841.8) throws -> Data {
    
    let pdfVC = UIHostingController(rootView: self)
    pdfVC.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
    let scenes = UIApplication.shared.connectedScenes
    let windowScene = scenes.first as? UIWindowScene
    let window = windowScene?.windows.first
    let rootVC = window?.rootViewController
    rootVC?.addChild(pdfVC)
    rootVC?.view.insertSubview(pdfVC.view, at: 0)
    let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: width, height: height))
    let pdf = pdfRenderer.pdfData(actions: { (context) in
      context.beginPage()
      rootVC?.view.layer.render(in: context.cgContext)
    })
    
    pdfVC.removeFromParent()
    pdfVC.view.removeFromSuperview()
    
    return pdf
  }
}
```

## おまけ

作成したdocumentは`document.write(to: url)`でPDFファイルに保存します。

```swift

let url = try write("sample.pdf", pdfDocument: document1)
print(url.path)

...

  func write(_ fileName: String, pdfDocument: PDFDocument) throws -> URL {
    let url = try createUrl(fileName: fileName)
    let options = [
        kCGPDFContextCreator: "@mbotsu",
        kCGPDFContextAuthor: "@mbotsu",
        kCGPDFContextTitle: "SwiftUIのレイアウトでPDFを作成して結合する",
        kCGPDFContextSubject: "ImageRendererは諦めてUIGraphicsPDFRendererでPDFを作成する",
    ]
    pdfDocument.write(to: url,
                      withOptions: options as [PDFDocumentWriteOption : Any])
    return url
  }
  
  func createUrl(fileName: String) throws -> URL {
    let fileManager = FileManager.default
    let url = fileManager.temporaryDirectory.appendingPathComponent(fileName, conformingTo: .pdf)
    if fileManager.fileExists(atPath: url.path) {
      try fileManager.removeItem(at: url)
    }
    return url
  }
```

### Inspectorで見るとこんな感じ(A4サイズ指定)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35973/87b509ac-2939-8447-f184-90c05decbf69.png" width="300">

## References

- [Read Operations](https://developer.apple.com/documentation/pdfkit/pdfdocument/read_operations)

  - documentからのpage、insert

- [Write Operations](https://developer.apple.com/documentation/pdfkit/pdfdocument/write_operations)
  - documentの書き込み write
  

## 実用的に一部書き直したコード

https://gist.github.com/otmb/81cfeadfe8219d108602041a3c5bd822