Merge PDF files on iOS

Viewed 15754

Is there a way in iOS to merge PDF files, that is, append the pages of one at the end of another and save it to disk?

7 Answers

Swift 5:

import PDFKit

Merge pdfs like this to keep links, etc...

func mergePdf(data: Data, otherPdfDocumentData: Data) -> PDFDocument {
    // get the pdfData
    let pdfDocument = PDFDocument(data: data)!
    let otherPdfDocument = PDFDocument(data: otherPdfDocumentData)!
    
    // create new PDFDocument
    let newPdfDocument = PDFDocument()

    // insert all pages of first document
    for p in 0..<pdfDocument.pageCount {
        let page = pdfDocument.page(at: p)!
        let copiedPage = page.copy() as! PDFPage // from docs
        newPdfDocument.insert(copiedPage, at: newPdfDocument.pageCount)
    }

    // insert all pages of other document
    for q in 0..<otherPdfDocument.pageCount {
        let page = pdfDocument.page(at: q)!
        let copiedPage = page.copy() as! PDFPage
        newPdfDocument.insert(copiedPage, at: newPdfDocument.pageCount)
    }
    return newPdfDocument
}

The insert function of PDFs can be found in the doc, where it says:

open class PDFDocument : NSObject, NSCopying {
...
// Methods allowing pages to be inserted, removed, and re-ordered. Can throw range exceptions.
// Note: when inserting a PDFPage, you have to be careful if that page came from another PDFDocument. PDFPage's have a 
// notion of a single document that owns them and when you call the methods below the PDFPage passed in is assigned a 
// new owning document.  You'll want to call -[PDFPage copy] first then and pass this copy to the blow methods. This 
// allows the orignal PDFPage to maintain its original document.
open func insert(_ page: PDFPage, at index: Int)

open func removePage(at index: Int)

open func exchangePage(at indexA: Int, withPageAt indexB: Int)
...
}

Create your mergedPdf as variable of your class:

    var mergedPdf: PDFDocument?

On viewDidLoad() call the merge function and show your merged PDF:

    mergedPdf = mergePdf(data: pdfData1, otherPdfDocumentData: pdfData2)
    // show merged pdf in pdfView
    PDFView.document = mergedPdf!
    

Save your pdf.

First convert the newPDF like this:

let documentDataForSaving = mergedPdf.dataRepresentation()

Put the documentDataForSaving in this following function: Use the save function:

let urlWhereTheFileIsSaved = writeDataToTemporaryDirectory(withFilename: "My File Name", inFolder: nil, data: documentDataForSaving)

Probably you want to avoid / in file name and don't make it longer than 256 characters

Save-Function:

// Export PDF to directory, e.g. here for sharing
    func writeDataToTemporaryDirectory(withFilename: String, inFolder: String?, data: Data) -> URL? {
        do {
            // get a directory
            var temporaryDirectory = FileManager.default.temporaryDirectory // for e.g. sharing
            // FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last! // to make it public in user's directory (update plist for user access) 
            // FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).last! // to hide it from any user interactions) 
            // do you want to create subfolder?
            if let inFolder = inFolder {
                temporaryDirectory = temporaryDirectory.appendingPathComponent(inFolder)
                if !FileManager.default.fileExists(atPath: temporaryDirectory.absoluteString) {
                    do {
                        try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
                    } catch {
                        print(error.localizedDescription);
                    }
                }
            }
            // name the file
            let temporaryFileURL = temporaryDirectory.appendingPathComponent(withFilename)
            print("writeDataToTemporaryDirectory at url:\t\(temporaryFileURL)")
            try data.write(to: temporaryFileURL)
            return temporaryFileURL
        } catch {
            print(error)
        }
        return nil
    }
Related