Skip to content

Instantly share code, notes, and snippets.

@otmb
Last active September 10, 2023 08:58
Show Gist options
  • Save otmb/81cfeadfe8219d108602041a3c5bd822 to your computer and use it in GitHub Desktop.
Save otmb/81cfeadfe8219d108602041a3c5bd822 to your computer and use it in GitHub Desktop.
Create and combine PDFs of SwiftUI layouts
import SwiftUI
import PDFKit
let pdfDocumentOptions = [
kCGPDFContextCreator: "@mbotsu",
kCGPDFContextAuthor: "@mbotsu",
kCGPDFContextTitle: "Create and combine PDFs with SwiftUI layouts",
kCGPDFContextSubject: "Give up ImageRenderer and create PDFs with UIGraphicsPDFRenderer",
] as [PDFDocumentWriteOption : Any]
struct PDFCombineView: View {
@State var pdfDocument: PDFDocument?
var body: some View {
VStack {
if let pdfDocument = pdfDocument {
PdfView(pdfDocument: pdfDocument)
}
}
.onAppear {
do {
let pdfData1 = Page1().toPdfData()
let pdfData2 = Page2().toPdfData()
let pdfData3 = Page3().toPdfData()
if let document = combinePdf(pdfData1, pdfData2, pdfData3){
pdfDocument = document
let url = try write("sample.pdf", pdfDocument: document, options: pdfDocumentOptions)
print(url.path)
} else {
throw NSError(domain: "combine failed", code: -1)
}
} catch {
print(error.localizedDescription)
}
}
}
func write(_ fileName: String, pdfDocument: PDFDocument, options: [PDFDocumentWriteOption : Any]) throws -> URL {
let url = try createUrl(fileName: fileName)
pdfDocument.write(to: url, withOptions: options)
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
}
}
struct Page1: View {
var body: some View {
Text("Page1")
}
}
struct Page2: View {
var body: some View {
Text("Page2")
}
}
struct Page3: View {
var body: some View {
Text("Page3")
}
}
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) {}
}
func combinePdf(_ pdfData: Data,_ pdfDataList: Data...) -> PDFDocument? {
if let basePdfDocument = PDFDocument(data: pdfData){
pdfDataList.forEach { data in
let pageNo = basePdfDocument.pageCount
if let pdfPage = PDFDocument(data: data)?.page(at: 0){
basePdfDocument.insert(pdfPage, at: pageNo)
}
}
return basePdfDocument
}
return nil
}
extension View {
func toPdfData(width:CGFloat=595.2, height:CGFloat=841.8) -> 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
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment