LoginSignup
0
2

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

Last updated at Posted at 2023-09-09

前回の記事: SwiftUIのレイアウトでPDFを作成する

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

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

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に結合
let document1 = PDFDocument(data: page1)
let document2 = PDFDocument(data: page2)
let page = document2?.page(at: 0)
document1?.insert(page!, at: 1)

結果画像

評価コード

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ファイルに保存します。


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サイズ指定)

References

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

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2