How to create a TOC when merging documents?

I need to create a TOC (not bookmarks) at the beginning of this document with clickable links to the first pages of each of the source PDFs. Document document = new Document(); PdfCopy copy = new

I need to create a TOC (not bookmarks) at the beginning of this document with clickable links to the first pages of each of the source PDFs.

Document document = new Document();
PdfCopy copy = new PdfCopy(document, outputStream);
PdfCopy.PageStamp stamp;
document.open();
PdfReader reader;
ListInputStream> pdfs = streamOfPDFFiles;
ListPdfReader> readers = new ArrayListPdfReader>();
IteratorInputStream> iteratorPDFs = pdfs.iterator();
for (; iteratorPDFs.hasNext(); pdfCounter++) {
    InputStream pdf = iteratorPDFs.next();
    reader = new PdfReader(pdf);
    readers.add(reader);
    pdf.close();
}
int currentPageNumber = 0;
IteratorPdfReader> readerIterator = readers.iterator();
PdfImportedPage page;
int count = 1;
while (readerIterator.hasNext()) {
    reader = readerIterator.next();
    count++;
    int number_of_pages = reader.getNumberOfPages();
    for (int pageNum = 0; pageNum  number_of_pages;) {
        currentPageNumber++;
        page = copy.getImportedPage(reader, ++pageNum);
        ColumnText.showTextAligned(stamp.getUnderContent(),
            Element.ALIGN_RIGHT, new Phrase(
                String.format("%d", currentPageNumber),
                new Font(FontFamily.TIMES_ROMAN,3)), 50, 50, 0);
        stamp.alterContents();
        copy.addPage(page);
    }
}
document.close();
Posted on StackOverflow on Feb 4, 2014 by Butani Vijay

You're asking for something that should be trivial, but that isn't. Please take a look at the MergeWithToc example. You'll see that your code to merge PDFs is correct, but in my example, I added one extra feature:

chunk = new Chunk(String.format("Page %d", pageNo));
if (i == 1)
    chunk.setLocalDestination("p" + pageNo);
ColumnText.showTextAligned(stamp.getUnderContent(),
    Element.ALIGN_RIGHT, new Phrase(chunk), 559, 810, 0);

For every first page, I define a named destination as a local destination. We use p followed by the page number as its name.

We'll use these named destinations in an extra page that will serve as a TOC:

PdfReader reader = new PdfReader(SRC3);
page = copy.getImportedPage(reader, 1);
stamp = copy.createPageStamp(page);
Paragraph p;
PdfAction action;
PdfAnnotation link;
float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry entry : toc.entrySet()) {
    p = new Paragraph(entry.getValue());
    p.add(new Chunk(new DottedLineSeparator()));
    p.add(String.valueOf(entry.getKey()));
    ct.addElement(p);
    ct.go();
    action = PdfAction.gotoLocalPage("p" + entry.getKey(), false);
    link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);
    stamp.addAnnotation(link);
    y = ct.getYLine();
}
ct.go();
stamp.alterContents();
copy.addPage(page);

In my example, I assume that the TOC fits on a single page. You'll have to keep track of the y value and create a new page if its value is lower than the bottom margin.

If you want the TOC to be the first page, you need to reorder the pages in a second go. This is shown in the MergeWithToc2 example:

reader = new PdfReader(baos.toByteArray());
n = reader.getNumberOfPages();
reader.selectPages(String.format("%d, 1-%d", n, n-1));
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(filename));
stamper.close();


Ready to use iText?

Try our iText 7 Library and add-ons FREE for 30 days. Test your proof of concept, and see if our solution is right for you.

Get my FREE trial
Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now