-1

I need to create TOC in a PDF. It can be of 1 page or multi pages depending on the number of pages in PDF. I have learnt that PdfStamper, PdfAction, PdfAnnotaion can be used to achieve this.

I am currently merging more than one document and creating both bookmarks and TOC for all the documents in JAVA. I have got rid of bookmarks but got stuck on multi-pages TOC.

Also, please explain this line of your code - link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);... What I have understood is you are passing the dimensions of a rectangle on the page on which it will be going after clicking the link (, 36, ct.getYLine(), 559, y, ).. And thus I am facing an issue of not going to correct position of the page on clicking the link, in the case if page size is different from US Letter Portrait.

Here is the snippet -

int tocPages = 1;
Document tocDocument = new Document();
String tocFilename ="toc-filename";
Phrase tocPhrase =
            new Phrase("Table of Contents", new Font(Font.FontFamily.HELVETICA, 20, Font.BOLD, BaseColor.BLACK));
PdfWriter writer = PdfWriter.getInstance(tocDocument, new FileOutputStream(tocFilename));
tocDocument.open();
tocDocument.add(new Paragraph(tocPhrase));
PdfReader reader = new PdfReader(tocFilename);
page = copy.getImportedPage(reader, tocPages);
stamp = copy.createPageStamp(page);

float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry<Integer, String> entry : toc.entrySet()) {
if (y <= 20) {
copy.addPage(page);
                copy.newPage(); //(tried with writer.newPage() and tocDocument.newPage(), not working )
               page = copy.getImportedPage(reader, ++tocPages);
            }
            p = new Paragraph(entry.getValue());
            p.add(new Chunk(new DottedLineSeparator()));
            p.add(String.valueOf(entry.getKey() + 1));
            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);
        tocDocument.close();
        reader.close();          

com.itextpdf.text.exceptions.InvalidPdfException: PDF header signature not found. at com.itextpdf.text.pdf.PRTokeniser.getHeaderOffset(PRTokeniser.java:227) at com.itextpdf.text.pdf.PdfReader.getOffsetTokeniser(PdfReader.java:442) at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:176) at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:219) at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:207) at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:197)

Excetion at line - PdfReader reader = new PdfReader(tocFilename);

Kapil
  • 13
  • 7
  • There's an example that answers the duplicate question [Create Index File(TOC) for merged pdf using itext library in java](http://stackoverflow.com/questions/21548552/create-index-filetoc-for-merged-pdf-using-itext-library-in-java). You should also explain more abou the process. Do you need to create an *outline tree* (aka bookmarks) too. You refer to `PdfStamper`, but it isn't clear why you'd need that if you're creating a document from scratch. Please clarify if you want an accurate answer. – Bruno Lowagie Apr 22 '15 at 22:06
  • Let me just repeat: if you expect an answer on this question, please explain what you're doing already: are you creating a document from scratch? Are you trying to create a TOC for an existing document (if so, does it have bookmarks)? Are you merging documents? Are you writing code in Java or C#? – Bruno Lowagie Apr 22 '15 at 23:36
  • Please phrase your comment as a real question providing sufficient context so that people know what your question is about. – Bruno Lowagie Apr 25 '15 at 15:48
  • Your question is not self-contained. I have provided the needed context for other people to understand the question in my answer. – Bruno Lowagie Apr 25 '15 at 17:09

1 Answers1

0

You have adapted your question so that it became a question about this snippet from my answer to Create Index File(TOC) for merged pdf using itext library in java

Paragraph p;
PdfAction action;
PdfAnnotation link;
float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry<Integer, String> 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();

Your question was reduced to: please explain this line of your code:

link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);

I have pasted the complete snippet, because this line can not be explained out of context,

In the snippet, we create a link annotation using the PdfAnnotation class. A link annotation is a area somewhere on a page, that triggers an action when clicked.

Which action is triggered in this case? That's what the action object is about, in this case, it jumps to a local page that is defined by a named destination. This is very similar to HTML where you have <a name="dest" /> and <a href="#dest">Jump to a specific destination on the current page</a>.

When creating a PdfAnotation, you always need a PdfWriter instance. In this case, we are merging documents using a PdfCopy instance named copy. As PdfCopy extends PdfWriter, we can pass the copy instance as a parameter.

Finally, we define the clickable area. This is always a rectangle that is defined using two coordinates: the coordinate of the lower-left corner and the coordinate of the upper-right corner.

In the above snippet, we add paragraphs using ColumnText. ColumnText allows you to get information about the current y position after adding content. For instance, when we do this:

ct.addElement(p);
ct.go();

We can do this to get the current Y-coordinate:

float y = ct.getYLine();

In our code snippet, we keep track of the previous y value (the Y position before we add p) and we use the current value of ct.getYLine() to get the current y position.

This way, I can define the coordinate of the lower-left corner like this:

float llx = 36;
float lly = ct.getYLine();

And the coordinate of the upper-right corner like this:

float urx = 559;
float ury = y;

These are the values that you can see when we construct the link annotation.

I have hardcode the x values. They are based on the fact that I am creating a document with pages of size A4 and margins of half an inch. An A-4 page is 595 user units wide. To the left, I have a margin of 36 user units; to the right I also have a margin of 36 user units, which I have to subtract from the width of the page: 595 - 36 = 559.

If you have a page of which the format is LETTER, you need to adapt these values. However: it's better to calculate them based on the actual value of the MediaBox / CropBox of the existing page. This way, your code keeps working when you're accidentally confronted with documents that have a different page size.

You can read more about the MediaBox and the CropBox in my answer to this question: How to get dimensions of each page of a pdf file

Community
  • 1
  • 1
Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
  • @Kapil Maybe if you started by posting a question that explains what you're trying to do. You want to create a TOC, but based on what? That is **absolutely unclear** in your question. Bad question = Bad answer. – Bruno Lowagie Apr 26 '15 at 18:47
  • What is the difference? What isn't working when there's more than one page? – Bruno Lowagie Apr 27 '15 at 11:13