Introduction

This series of articles is intended to help developers create accessible PDFs with iText. The articles are based on the WCAG 2.0 PDF Techniques for creating accessible PDFs. I have no formal training in PDFs or iText. The code provided in this guide is only my best guess at how to solve some common accessibility requirements with iText. Using these techniques does not mean your PDFs are necessarily WCAG 2.0 compliant. You will have to test your results for yourself. I don’t have solutions for all of the WCAG 2.0 PDF Techniques. Generally, if there is no article for a given technique, it means I have not needed to create such a PDF or I have yet to figure out how to implement the technique with iText. Please feel free to contribute to improve on my work or fill in the gaps.

iText is probably the most widely used PDF library offered under a non-commercial license. However, it has some shortcomings in the area of accessibility. It provides an API to implement accessible PDFs, but it is no easy task for the uninitiated. Furthermore, most iText developers are likely to be familiar with the “add” API, where you can simply add constructs to the document like this:

    public void createPdf(String filename) throws DocumentException, IOException {
        // step 1
        Document document = new Document();
        // step 2
        PdfWriter.getInstance(document, new FileOutputStream(filename));
        // step 3
        document.open();
        // step 4
        document.add(new Paragraph("Hello World!"));
        // step 5
        document.close();
    }

Source Reference: http://itextpdf.com/examples/iia.php?id=12

The problem is that to tag a PDF for accessibility, you need to use a lower level API. Hence, not only is the developer required to have an understanding of what accessible PDFs entail, but a more complex API must be used. This guide should lessen the learning curve and provide some shortcuts to speed up development and reduce errors.

It should be noted that the iText author blogs that he intends to provide better support for accessibility.

Also note that there are no solutions provided for the PDF techniques targeting forms. I have not researched these techniques and do not have any solutions.

The iText Accessibility API

Let’s not waste any time. Here is what your accessibility code will resemble:

ca.discotek.itext.guide.BasicAccessiblePdf

    try {
      Document document = new Document(PageSize.LETTER);
      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(
          "pdf-output/BasicAccessiblePdf.pdf"));

      Rectangle rect = document.getPageSize();

      writer.setTagged();

      document.open();

      BaseFont bf = BaseFont.createFont(BaseFont.COURIER, "Cp1252", false);

      PdfContentByte cb = writer.getDirectContent();

      PdfStructureTreeRoot rootElement = writer.getStructureTreeRoot();
      PdfStructureElement paragraphElement = new PdfStructureElement(rootElement, PdfName.P);

      cb.beginMarkedContentSequence(paragraphElement);

      cb.beginText();

      cb.setFontAndSize(bf, 12);
      cb.setLeading(18);

      cb.moveText(rect.getLeft() + 50, rect.getTop() - 50);

      cb.showText("iText is awesome!");
      cb.endText();

      cb.endMarkedContentSequence();

      document.close();
    } 
    catch (Exception e) {
      e.printStackTrace();
    }

This is the simplest example of an accessible PDF and it is already fairly ugly. I wanted to dive into some example code immediately to demonstrate the basic building blocks. You should be aware of the following:

  • The writer.setTagged() invocation in line 8 is required to instruct iText that the PDF will be tagged for accessibility. This means that screen reader software will try to interpret the accessible content, not the viewable content. This is important to understand because software such as Adobe Reader will no longer try to infer the accessible content from the viewable content if a PDF is flagged.
  • Lines 16 and 17 establish the “PdfStructure” elements of the PDF. These elements form a tree in which you will nest other PdfStructure elements and your actual content. In this way, its structure is very much like HTML. Furthermore, you need to use the appropriate PdfStructure element types which indicate the current context (e.g. paragraphs, tables, lists, headers. See section 14.8.4.3 of the PDF spec for more info). You should also be aware that there is no mechanism to allow you to arbitrarily insert structure elements into the tree at any given time. This may not seem like a big deal, but when you consider adding “Page X of Y” in the footer of every page, you may start to understand the problem. You may not know the number of pages in the PDF until you have reached the end. How will you insert the Y part of the footer?
  • Line 19 indicates that you are about to write-out some accessible content. Lines 19 through 29 configure and write-out the content. Line 31 indicates that the chunk of accessible content has been completed.

The importance of automating the tracking of the PdfStructure will become more obvious when trying to create complex PDFs. Hence, before we start discussing any of the WCAG PDF “Techniques”, let’s put together the code to automate the PdfStructure tracking. The basic structure of any PDF document will look something like this:

<root>
    <document>  <!-- document -->

        <div>  <!-- page 1 -->
            <div>...</div> <!-- header -->
            <div>...</div> <!-- body -->
            <div>...</div> <!-- footer -->
        </div> <!-- page 1 -->

        <div>  <!-- page 2 -->
            <div>...</div> <!-- header -->
            <div>...</div> <!-- body -->
            <div>...</div> <!-- footer -->
        </div> <!-- page 2 -->

        .
        .
        .

    </document> <!-- document -->
</root>

Let’s write some code to automate the creation of such a structure. Let’s assume we want to above structure on every page of the PDF, even if we don’t use all of it (e.g. we may not use the headers). Here is a first cut:

package ca.discotek.itext.util;

import java.util.ArrayList;
import java.util.List;

import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfStructureElement;
import com.itextpdf.text.pdf.PdfStructureTreeRoot;

public class DocumentStructure {

  public final PdfStructureTreeRoot root;
  public final PdfStructureElement documentElement;

  List<PageStructure> pageList;

  public DocumentStructure(PdfStructureTreeRoot root) {
    this.root = root;
    documentElement = new PdfStructureElement(root, PdfName.DOCUMENT);
    pageList = new ArrayList<PageStructure>();
  }

  /**
   * warning: pageNumber is zero-indexed. itext api may not be.
   * (you might have to pass in pageNumber-1)
   */
  public PageStructure getPage(int pageNumber) {
    int size = pageList.size();
    if (size < pageNumber) {
      for (int i=size; i

The code is not very complicated. Essentially a PageStructure object will get created for every page, even if you start at the last page. At this point, you may have noticed the use of PdfName.DIV for the page, header, footer, and body elements. And, you may have noticed that iText provides a PdfName.Page constant. However, the PDF specification does not mention a “page” block element. It is my best guess, that just like HTML, there is no concept of pages within the same document in the world of accessibility. A page just breaks a document up into visually manageable chunks, but this probably doesn’t help a blind person. Further, if you use the PAC (PDF Accessibility Checker), it won’t recognize a “page” element. Hence, I believe it is more correct to present an element that is recognized by screen readers and is explicitly mentioned as an element for grouping other elements in the PDF specification.

We may as well discuss now what else our DocumentStructure class can do. WCAG PDF16 Technique and WCAG PDF19 Technique deal with the language of the document. Ideally, we’d discuss the techniques in sequence, but I think its best we finish off with the DocumentStructure class now than have to revisit it later.

A reputable screen reader will support multiple languages and speak with the appropriate accent for the given language. An accessible PDF will flag a PDF as a particular language at the document level (WCAG PDF 16), but will also indicate the language of a given passage if it is not the same language as the rest of the document. It is not clear to me how to flag the language at the document level. However, as of iText 5.3.5 (as documented here), there appears to be a way to set the language at the document level. Unfortunately, only the javadocs for 5.3.5 appear to be published and not a snapshot of the distribution, so I can’t confirm this satisfies PDF19 for certain. Setting the language at the document level enables screen readers to read the title of a document (as seen in the title bar of a window) in the correct language. In no way do I mean to diminish the importance of this feature, but I don’t know what else it does.

In any case, let’s move on to PDF19. Without knowing how to set the language at the document level, the next best approach is to set the language of the document at the PdfStructureRoot level. A screen reader won’t read a title correctly, but it should read the rest of the document correctly. The reason I mention the language features of a document now is that it makes sense to set the language of the content when you initialize the PdfStructure content, so we may as well add that to our DocumentStructure class. Also, since it is possible to have a document which contains multiple languages (e.g. an English document with a French passage), expose the functionality as an API such that it can invoked later if necessary.

To set the language for the PdfStructureTreeRoot or any PdfStructureElement, you simply set the dictionary’s PdfName.LANG attribute to the appropriate language. Both PdfStructureTreeRoot and PdfStructureElement are subclasses of the PdfDictionary class. The code would look something like this:

    element.put(PdfName.LANG, new PdfString("en-ca"));

This would set the language to English/Canadian. Here are the relevant code changes to the DocumentStructure class:

  public DocumentStructure(PdfStructureTreeRoot root, String language) {
    this.root = root;
    setLanguage(root, language);
    documentElement = new PdfStructureElement(root, PdfName.DOCUMENT);
    pageList = new ArrayList<PageStructure>();
  }

  public static void setLanguage(PdfDictionary dictionary, String language) {
    dictionary.put(PdfName.LANG, new PdfString(language));    
  }

We added a String language parameter to the constructor and subsequently call setLanguage(root, language). And we added a the setLanguage method definition. This method is static and can be called from code outside this class in order to change the language for a specific passage if necessary.

I think it is worth mentioning that if you plan on using these examples to build an API for other developers, you should probably provide enum types for the language codes you wish to support, such that there is no mistaking what the proper language codes are. Perhap the supported American types would look something like this:

    public enum Language {
        English("English", "en-us"),
        Mexico("Spanish", "es-mx");

        public final String text;
        public final String code;
        Language(String text, String code) {
            this.text = text;
            this.code = code;
        }
    }

This was an awful lot of preamble, but every solution for the PDF techniques to follow will reference the DocumentStructure class so its important to understand it before moving forward.

You should note that the DocumentStructure class was not designed to support documents with sections. If you require sections, you should avoid using the DocumentStructure class directly, but add its functionality to your code that will support sections. Or contact me and I’ll consider re-working DocumentStructure.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>