Download Rendered PDF

I need to render a PDF and then make it downloadable to the mobile device. I can generate the PDF but I'm unsure how to configure the download aspect. I have the following code:

let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: 612, height: 792))
                
let pdf = renderer.pdfData { (context) in
                    context.beginPage()
                    
let text = "Test" as NSString
                    
text.draw(in: CGRect(x: 0, y: 0, width: 100, height: 25))
Answered by DTS Engineer in 821676022

Most of what I wanted to say has already been covered in responses to this forums post above, but here is a summary of my thoughts about this question:

Quartz 2D Programming Guide: Graphics Contexts: Creating a PDF Graphics Context https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-TPXREF118

  • this document talks about the general concepts involved in creating a PDF. As it has been observed above, this document uses Objective-C language. However, you should be able to get the idea of the general flow of the APIs. And, you can look up the Objective-C versions of the APIs on our website and then display the Swift equivalents using the language pop-up menu in the top right of each page.

The APIs described in the "Creating a PDF Graphics Context" section are documented here:

Core Graphics: CGPDFContext https://developer.apple.com/documentation/coregraphics/cgpdfcontext/

  • you can use the language popup at the top of each page to select the language - Objective-C or Swift.

Those are the APIs for creating PDF files. When creating PDF files, you use the Core Graphics drawing APIs for drawing content. Please see, the following documentation for more information about Core Graphics.

Core Graphics https://developer.apple.com/documentation/coregraphics/

Quartz 2D Programming Guide https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html

For drawing text, the current recommendation is to use TextKit (https://developer.apple.com/documentation/appkit/textkit). There is also the lower level Core Text API (https://developer.apple.com/documentation/coretext/), also mentioned above, but you should only consider using that if you cannot do something you want to do using TextKit.

We also offer the PDFKit APIs (https://developer.apple.com/documentation/pdfkit/). They are mainly for displaying PDF files in PDFViews, but the also offer some APIs for editing PDF files. Such as, for example, the ability to adjust the properties of pages and to move pages around in documents.

The PDFKit APIs also offer a way to create PDFPages from UIImages or NSImages (https://developer.apple.com/documentation/pdfkit/pdfpage/init(image:)), but if you want to have scalable vector graphics in your PDF files you should use the Core Graphics APIs to create your PDF files.

If you are interested in creating PDFs from your SwiftUI views, you can use the PDFPage init(image:) method together with SwiftUI's ImageRenderer (https://developer.apple.com/documentation/swiftui/imagerenderer). The idea here is to use the ImageRenderer to create images that you use to initialize PDFPages and then add those pages to a PDFDocument.

Using the PDFPage init(image:) method may seem like a simple and convenient way to go, but I would be remis if I didn't provide the following warnings:

  • creating pages from raster images will create large PDF files containing raster images and will defeat scalable vector graphics quality of PDFs that many users expect when using PDF files. When scaling pages and viewing them at different resolutions, blocky edges in the graphics may result.
  • PDF files containing pages created from raster images will be substantially larger than PDF files created using Core Graphics and TextKit drawing routines.
  • be sure to test in every version of the operating system where you expect your app will be deployed - previously, I have received some bug reports about this method not working as expected.
  • you will need to figure out your own strategies for formatting your SwiftUI views when drawing to an ImageRenderer so that they will be sized appropriately for PDF pages.

For better quality PDF files, I recommend using the Core Graphics CGPDFContext based routines together with Core Graphics and TextKit.

It looks like you’re using UIKit, rather than SwiftUI, for your app. Is that right?

This matters because one part of this problem is presenting the file export UI, and how you do that depends on your UI framework.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi,

Thank you for your response. I did not realize the code I provided was UIKit. I'd prefer to use SwiftUI. Would you be able to provide a solution to do the following:

  • Generate a PDF composed of data from a SwiftData class
  • Allow the user to export the PDF to their file app
I did not realize the code I provided was UIKit.

Well it isn’t definitively, which is why I asked (-: I’ve now retagged your thread to make it clear that SwiftUI is your focus.

As you’ve noted, there are two parts to this:

  1. Generating the PDF data

  2. Exporting that data as a file

I can help you with the second part here on this thread. I’m not really the right person to help you with the first part. If you need help with that, I recommend that you start a new thread that’s focused on PDF generation.

For exporting a file you’ll use one of SwiftUI’s fileExporter(…) modifiers. Pasted in below is a trivial example.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


struct ContentView: View {
    @State var status: String = "Click the Export button."
    @State var isPresented: Bool = false
    let document = TextDocument()
    var body: some View {
        VStack {
            Text(self.status)
            Button("Export") {
                self.isPresented = true
            }
        }
        .fileExporter(isPresented: $isPresented, document: document, contentType: .text) { result in
            switch result {
            case .success(_): status = "Export succeeded."
            case .failure(_): status = "Export failed."
            }
        }
        .padding()
    }
}

struct TextDocument: FileDocument {
    static let readableContentTypes = [UTType.text]

    var text = "Hello Cruel World!"

    init() {
    }

    init(configuration: ReadConfiguration) throws {
        fatalError()
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        FileWrapper(regularFileWithContents: .init(self.text.utf8))
    }
}

Most of what I wanted to say has already been covered in responses to this forums post above, but here is a summary of my thoughts about this question:

Quartz 2D Programming Guide: Graphics Contexts: Creating a PDF Graphics Context https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-TPXREF118

  • this document talks about the general concepts involved in creating a PDF. As it has been observed above, this document uses Objective-C language. However, you should be able to get the idea of the general flow of the APIs. And, you can look up the Objective-C versions of the APIs on our website and then display the Swift equivalents using the language pop-up menu in the top right of each page.

The APIs described in the "Creating a PDF Graphics Context" section are documented here:

Core Graphics: CGPDFContext https://developer.apple.com/documentation/coregraphics/cgpdfcontext/

  • you can use the language popup at the top of each page to select the language - Objective-C or Swift.

Those are the APIs for creating PDF files. When creating PDF files, you use the Core Graphics drawing APIs for drawing content. Please see, the following documentation for more information about Core Graphics.

Core Graphics https://developer.apple.com/documentation/coregraphics/

Quartz 2D Programming Guide https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html

For drawing text, the current recommendation is to use TextKit (https://developer.apple.com/documentation/appkit/textkit). There is also the lower level Core Text API (https://developer.apple.com/documentation/coretext/), also mentioned above, but you should only consider using that if you cannot do something you want to do using TextKit.

We also offer the PDFKit APIs (https://developer.apple.com/documentation/pdfkit/). They are mainly for displaying PDF files in PDFViews, but the also offer some APIs for editing PDF files. Such as, for example, the ability to adjust the properties of pages and to move pages around in documents.

The PDFKit APIs also offer a way to create PDFPages from UIImages or NSImages (https://developer.apple.com/documentation/pdfkit/pdfpage/init(image:)), but if you want to have scalable vector graphics in your PDF files you should use the Core Graphics APIs to create your PDF files.

If you are interested in creating PDFs from your SwiftUI views, you can use the PDFPage init(image:) method together with SwiftUI's ImageRenderer (https://developer.apple.com/documentation/swiftui/imagerenderer). The idea here is to use the ImageRenderer to create images that you use to initialize PDFPages and then add those pages to a PDFDocument.

Using the PDFPage init(image:) method may seem like a simple and convenient way to go, but I would be remis if I didn't provide the following warnings:

  • creating pages from raster images will create large PDF files containing raster images and will defeat scalable vector graphics quality of PDFs that many users expect when using PDF files. When scaling pages and viewing them at different resolutions, blocky edges in the graphics may result.
  • PDF files containing pages created from raster images will be substantially larger than PDF files created using Core Graphics and TextKit drawing routines.
  • be sure to test in every version of the operating system where you expect your app will be deployed - previously, I have received some bug reports about this method not working as expected.
  • you will need to figure out your own strategies for formatting your SwiftUI views when drawing to an ImageRenderer so that they will be sized appropriately for PDF pages.

For better quality PDF files, I recommend using the Core Graphics CGPDFContext based routines together with Core Graphics and TextKit.

Download Rendered PDF
 
 
Q