Convert Instant JSON to XFDF on iOS

To achieve interoperability between XFDF (XML Forms Data Format) and JSON files that conform to PSPDFKit’s annotation schema, PSPDFKit has APIs for importing JSON data into a PDF and exporting it as XFDF.

Unlike PSPDFKit’s JSON schema, XFDF doesn’t have a concept of differentials for describing changes to a PDF (e.g. deleted annotations, new bookmarks, edited forms). XFDF is instead a representation of all annotation and form data inside a PDF document at a point in time. Since these concepts don’t directly translate to each other, there are some limitations that wouldn’t be represented in the final XFDF.

The resulting XFDF file will include all annotations you specify, as well as all form data from the document. The XFDF file will conform to the ISO Specification, and can be imported by conforming third-party viewers, such as Adobe Acrobat.

You can import conforming JSON files and export to XFDF using the following code:

func export(jsonFile: URL, for document: Document, toXFDFFileAt XFDFFile: URL) throws {
    guard documentProviders.count == 1 else {
        fatalError("This code assumes a Document consisting of a single PDF file")
    }

    // Import the data from the JSON file into the PDF document.
    let documentProvider = document.documentProviders[0]
    let jsonProvider = FileDataProvider(fileURL: jsonURL)
    try document.applyInstantJSON(fromDataProvider: jsonProvider, to: documentProvider, lenient: false)

    // Create a data sink into which the XFDF data will be written.
    // The file should contain nothing but the data written to it, so the default options are perfect.
    try let destination = FileDataSink(fileURL: XFDFFile)

    // Writing XFDF happens through a helper object and requires specifying the annotations to be exported.
    let writer = XFDFWriter()
    let allAnnotations = document
        .allAnnotations(of: .all)
        .values
        .flatMap { $0 }

    // Finally, write the data. The data sink will close the file when it goes out of scope.
    try writer.write(allAnnotations, to: destination, documentProvider: documentProvider)
}