0

I have created a simple PDF here to explain the issue I am facing

import com.itextpdf.io.font.FontConstants;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfViewerPreferences;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.*;
import com.itextpdf.layout.property.TextAlignment;

import java.io.File;
import java.io.IOException;

public class PDFGenerator {

    public static final String DEST = "C:\\Users\\skesha01\\temp\\output.pdf";

    public static void main(String[] args) throws IOException {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new PDFGenerator().createPdf(DEST);
    }

    public void createPdf(String dest) throws IOException {
        PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
        pdfDoc.setTagged();
        pdfDoc.getCatalog().setViewerPreferences(new PdfViewerPreferences().setDisplayDocTitle(true));
        Document doc = new Document(pdfDoc, PageSize.A4);
        pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, new FooterEventHandler(doc));

        addContent(doc);
        doc.close();
    }

    public void addContent(Document doc) throws IOException {
        doc.add(new Paragraph("Sample PDF with footer, page numbers, and table fields")
                .setFont(PdfFontFactory.createFont(FontConstants.HELVETICA))
                .setFontSize(12)
                .setTextAlignment(TextAlignment.CENTER));
  
        Table table = new Table(3).useAllAvailableWidth();
        Cell headerCell1 = new Cell().add(new Paragraph("Header 1"));
        Cell headerCell2 = new Cell().add(new Paragraph("Header 2"));
        Cell headerCell3 = new Cell().add(new Paragraph("Header 3"));
        table.addHeaderCell(headerCell1).getAccessibilityProperties().setRole(StandardRoles.TH);
        table.addHeaderCell(headerCell2).getAccessibilityProperties().setRole(StandardRoles.TH);
        table.addHeaderCell(headerCell3).getAccessibilityProperties().setRole(StandardRoles.TH);
        for (int i = 0; i < 1; i++) {
            table.addCell("Row " + (i + 1) + ", Column 1").getAccessibilityProperties().setRole(StandardRoles.TD);
            table.addCell("Row " + (i + 1) + ", Column 2").getAccessibilityProperties().setRole(StandardRoles.TD);
            table.addCell("Row " + (i + 1) + ", Column 3").getAccessibilityProperties().setRole(StandardRoles.TD);
        }
        table.getAccessibilityProperties().setRole(StandardRoles.TABLE);
        table.getAccessibilityProperties().setAlternateDescription("I am reading this table alternate text");
        doc.add(table);
    }
}

and the footer event handler responsible for adding the footer to each page is

import com.itextpdf.io.font.FontConstants;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.tagging.StandardRoles;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Text;

import java.io.IOException;

public class FooterEventHandler implements IEventHandler {
    protected PdfFont font;

    private Document document;


    public FooterEventHandler(Document document) {
        try {
            font = PdfFontFactory.createFont(FontConstants.HELVETICA);
            this.document = document;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        PdfCanvas canvas = new PdfCanvas(pdfDoc.getLastPage());

        canvas.setStrokeColorRgb(0, 0, 0).setLineWidth(0.5f);
        canvas.moveTo(36, 20).lineTo(pdfDoc.getDefaultPageSize().getRight() - 36, 20).stroke();

        addFooter(canvas, pdfDoc, docEvent.getPage());
    }
    private void addFooter(PdfCanvas canvas, PdfDocument pdfDoc, PdfPage page) {

        canvas.setStrokeColorRgb(0, 0, 0).setLineWidth(0.5f);
        canvas.moveTo(36, 20).lineTo(pdfDoc.getDefaultPageSize().getRight() - 36, 20).stroke();
        Text footerText = new Text("Footer Text - Page " + pdfDoc.getPageNumber(page));
        footerText.getAccessibilityProperties().setRole(StandardRoles.ARTIFACT);
        canvas.beginText().beginMarkedContent(PdfName.Artifact)
                .setFontAndSize(font, 10)
                .moveText((pdfDoc.getDefaultPageSize().getLeft() + pdfDoc.getDefaultPageSize().getRight()) / 2, 10)
                .showText(footerText.getText())
                .endText();
    }
}

A similar problem faced by another user but for Header is PDF created by iText7 with Header is not accessible

I am using JAWS / Adobe Reader to check the accessibility of this generated PDF and both of them do not read the footer.

Upon investigation, I have found that the moment we use "setTagged" on the document, footer is no longer being included in the logical structure of the document. From what I read a footer is not a "real element" and it is our responsibility to tag them appropriately. I could not get it read no matter whatever tag and role I used. Also, I found articles which suggest that headers and footers should not be part of the structure as they will hinder the flow of the document when being read to the user.

Note: Removing setTagged reads the entire document along with footer but it brings out several other problems in images and tables.

I would like to know if there is a workaround to my problem. If there is no workaround, is there a standard that tells header and footer should not be read to the user.

Footer should be accessible and read by the screen reader

1 Answer 1

1

iTextPDF's behaviour is correct here. Headers and footers are normally marked as "pagination artefacts". See failure condition 18-001 in the PDF Association's Matterhorn Protocol:

Headers and footers are not marked as pagination artifacts.

This matches requirement 7.8-1 in the PDF/UA standard (ISO 14289).

If you try to override iTextPDF's behaviour in this regard, you will be violating part of the PDF/UA standard.

See also WCAG Technique PDF14: Providing running headers and footers in PDF documents.

Not the answer you're looking for? Browse other questions tagged or ask your own question.