Creating and editing PDF 2.0 Documents

Tags: PDF 2.0

In July 2017 PDF 2.0 was released which added a bunch of useful features to PDFs such as AES-256 encryption, unicode passwords, associated files, and more. Later that year we released 7.1.0 which brought PDF 2.0 support to iText! This page details how to create a PDF 2.0 document using iText, and how to take advantage of the new associated files feature in PDF 2.0.

Creating a PDF 2.0 Document

Creating a PDF 2.0 document with iText is simple. All you have to do is set the PDF version number to be 2.0:

PdfWriter writer = new PdfWriter(OUTPUT_FILE, new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0));
PdfDocument pdfDocument = new PdfDocument(writer);
pdfDocument.setTagged();
Document document = new Document(pdfDocument);
document.add(new Paragraph("Hello world!"));
document.close();
 

Editing a PDF 2.0 Document

iText is more than capable of editing an existing PDF 2.0 document and doing things like adding a signature or adding additional content. Let's create a PDF that documents the evolution of the iText logo. Here are some of the older versions of the iText logo:

Let's create a portfolio that details the evolution of our logo. Each page will have a separate version of the logo, as well as an associated file attachment (new in PDF 2.0!) that maps each page to the embedded image.

String[] logoFilePaths = {
        "C:\iText\logos\itext_logo_v1.png",
        "C:\iText\logos\itext_logo_v2.png",
        "C:\iText\logos\itext_logo_v3.png",
};
 
PdfWriter writer = new PdfWriter(OUTPUT_FILE, new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0));
PdfDocument pdfDocument = new PdfDocument(writer);
Document document = new Document(pdfDocument);
 
 
for (int x = 0; x < logoFilePaths.length; x++) {
    String path = logoFilePaths[x];
    String description = "iText logo version: " + (x + 1);
    document.add(new Paragraph(description));
    Image image = new Image(ImageDataFactory.create(path)).scaleAbsolute(100, 100);
    document.add(image);
 
    PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(pdfDocument, path, "logo_v" + (x + 1) + ".png", PdfName.ApplicationOctetStream);
    pdfDocument.addAssociatedFile(description, fileSpec);
    pdfDocument.getPage(x + 1).addAssociatedFile(fileSpec);
 
    if (x != logoFilePaths.length - 1)
        document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
 
}
document.close();

Something is missing though- the latest and greatest iText logo:

Luckily with iText appending a new page with our logo and embedded images is simple:

PdfReader reader = new PdfReader(INPUT_FILE);
PdfWriter writer = new PdfWriter(OUTPUT_FILE, new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0));
PdfDocument pdfDocument = new PdfDocument(reader, writer);
Document document = new Document(pdfDocument);
 
String logoPath = "C:\iText\logos\itext_logo_recent.png";
 
pdfDocument.addNewPage();
document.add(new AreaBreak(AreaBreakType.LAST_PAGE));
 
int logoNumber = pdfDocument.getNumberOfPages();
 
String description = "iText logo version: " + logoNumber;
document.add(new Paragraph(description));
document.add(new Image(ImageDataFactory.create(logoPath)));
 
PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(pdfDocument, logoPath, "logo_v" + logoNumber + ".png", PdfName.ApplicationOctetStream);
pdfDocument.addAssociatedFile(description, fileSpec);
pdfDocument.getPage(logoNumber).addAssociatedFile(fileSpec);
 
document.close();

Consuming a PDF 2.0 Document

We can read in a PDF 2.0 document just like any PDF in iText through the PdfReader object. Since the document we just created above has files associated each page we can take advantage of the new getAssociatedFiles() function of a PdfPage object to get all the files associated with that page. From there we can easily loop each attachment to extract the files that are associated with each page.

PdfReader reader = new PdfReader(INPUT_FILE);
PdfWriter writer = new PdfWriter(OUTPUT_FILE, new WriterProperties().setPdfVersion(PdfVersion.PDF_2_0));
PdfDocument pdfDocument = new PdfDocument(reader, writer);
 
for (int x = 1; x <= pdfDocument.getNumberOfPages(); x++) {
    PdfPage page = pdfDocument.getPage(x);
    PdfArray associatedFiles = page.getAssociatedFiles(false);
    for (int i = 0; i < associatedFiles.size(); i++) {
        PdfDictionary attachment = associatedFiles.getAsDictionary(i);
        String fileName = attachment.getAsString(PdfName.F).getValue();
        byte[] fileBytes = attachment.getAsDictionary(PdfName.EF).getAsStream(PdfName.F).getBytes();
        Files.write(new File("C:\iTextLogos\" + fileName).toPath(), fileBytes);
    }
}

If we look at the contents of the C:/iTextLogos directory we see that our new attachments were properly extracted!