Wilco
Wilco

Reputation: 1093

Create Multi-page Index File(TOC) for merged pdf using itext library in java

How can I write a multi-page ToC to the end of a PDF consisting of merged documents, using iTextSharp?

The answer to Create Index File(TOC) for merged pdf using itext library in java explains how to create a ToC page when merging PDFs (catalogued in the iTextSharp book http://developers.itextpdf.com/examples/merging-pdf-documents/merging-documents-and-create-table-contents#795-mergewithtoc.java). Code in this answer is based on those examples.

However it only works if the ToC is 1 page long. If the content becomes longer, then it repeats itself on the same page rather than spanning into the next page.

Trying to add the link directly to the text via:

ct.Add(new Chunk("link").SetLocalGoto("p1"))

causes an exception ("Cannot add Annotations, not enough pages in document").

Can anyone explain a method that will allow me to append multiple pages of content to a PDF when merging them (the more general the approach, the better). Is there a way to write into the document using Document.Add() instead of having to copy in template pages and write on the top of them?

(Note, code is in c#)

Upvotes: 0

Views: 1191

Answers (1)

Wilco
Wilco

Reputation: 1093

This answer is based on the example from the iTextSharp documentation, but converted to C#.

To make the added text span multiple pages, I found I could use ColumnText.HasMoreText(ct.Go()) to tell me if there was more text than could fit on the current page. You can then save the current page, re-create a new page template, and move the columntext to the new page. Below this is in a function called CheckForNewPage:

    private bool CheckForNewPage(PdfCopy copy, ref PdfImportedPage page, ref PdfCopy.PageStamp stamp, ref PdfReader templateReader, ColumnText ct)
    {
        if (ColumnText.HasMoreText(ct.Go()))
        {
            //Write current page
            stamp.AlterContents();
            copy.AddPage(page);

            //Start a new page
            ct.SetSimpleColumn(36, 36, 559, 778);
            templateReader = new PdfReader("template.pdf");
            page = copy.GetImportedPage(templateReader, 1);
            stamp = copy.CreatePageStamp(page);
            ct.Canvas = stamp.GetOverContent();
            ct.Go();
            return true;
        }
        return false;
    }

This should be called each time text is added to the ct variable.

If CheckForNewPage returns true you can then increment the page count, and reset the y variable to the top of the new page so that link annotation is in the correct place on the new page.

e.g.

var tocPageCount = 0;


var para = new iTextSharp.text.Paragraph(documentName);
ct.AddElement(para);
ct.Go();
if (CheckForNewPage(context, copy, ref page, ref stamp, ref tocReader, ct))
{
    tocPageCount++;
    y = 778;
}

//Add link annotation
action = PdfAction.GotoLocalPage(d.DocumentID.ToString(), false);
link = new PdfAnnotation(copy, TOC_Page.Left, ct.YLine, TOC_Page.Right, y, action);
stamp.AddAnnotation(link);
y = ct.YLine;

This creates the pages correctly. The below code adapts the end of ToC2 example for re-ordering the pages, in order to handle more than 1 page.

var rdr = new PdfReader(baos.toByteArray()); 
var totalPageCount = rdr.NumberOfPages;
rdr.SelectPages(String.Format("{0}-{1}, 1-{2}", totalPageCount - tocPageCount +1, totalPageCount, totalPageCount - tocPageCount));
PdfStamper stamper = new PdfStamper(rdr, new FileStream(outputFilePath, FileMode.Create));
stamper.Close();

By re-using the CheckForNewPage function, you should be able to add any content to new pages you create, and have it span multiple pages. If you don't need the annnotations you call CheckForNewPage in a loop at the end of adding all your content (just don't call ct.Go() beforehand).

Upvotes: 0

Related Questions