Skip to main content
Skip table of contents

Creating and editing PDF 2.0 Documents

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 version 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 the PDF 2.0 specification. 

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:

JAVA
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();
C#
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();

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.

JAVA
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();
C#
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:

JAVA
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();
C#
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 with 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.

JAVA
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);
    }
}
C#
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();
        File.WriteAllBytes("C:\\iTextLogos\\" + fileName, fileBytes);
    }
}

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

A view of the iTextLogos directory
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.