Sanka
Sanka

Reputation: 1324

Add page as nth page

I have two pdfs:

  1. Master.pdf: contains pages with bookmarks, outline (table of content).
  2. Child.pdf: a file with a single page.

Child.pdf pages need to be appended as page n (in my case as page 10) to the Master.pdf.

The resulting PDF should have a new outline item (a new Bookmark) for the new page. Also the existing bookmarks should keep working fine. Actually: the existing outline tree should be refactored.

Can I achieve this through the iText API? Are there any helpful samples ?

public class ConcatenateBookmarks {
    public static final String SRC1 = "C:\\c\\spring-in-action.pdf";
    public static final String SRC2 = "C:\\c\\SPD-DUAL DS.pdf";
    public static final String DEST = "C:\\c\\final.pdf";

    /**
     * Manipulates a PDF file src with the file dest as result
     * 
     * @param src
     *            the original PDF
     * @param dest
     *            the resulting PDF
     * @throws IOException
     * @throws DocumentException
     */
    public void manipulatePdf(String[] src, String dest) throws IOException, DocumentException {
        int POINT = 3;
        Document document = new Document();
        PdfSmartCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
        document.open();
        PdfReader reader;
        int page_offset = 0;
        int n;
        // Create a list for the bookmarks
        ArrayList<HashMap<String, Object>> bookmarks = new ArrayList<HashMap<String, Object>>();
        List<HashMap<String, Object>> tmp;
        for (int i = 0; i < 1/* src.length */; i++) {
            reader = new PdfReader(src[i]);
            PdfReader reader2 = new PdfReader(src[1]);
            int pagesCount = reader2.getNumberOfPages();
            page_offset = pagesCount;
            if (i == 0) {
                HashMap<String, String> map = SimpleNamedDestination.getNamedDestination(reader, false);
                SimpleNamedDestination.exportToXML(map, new FileOutputStream(dest), "ISO8859-1", false);
                copy.addNamedDestinations(map, 0);
            }
            tmp = SimpleBookmark.getBookmark(reader);
            // this level have to
            // separate up to n don't
            // change and after the
            // should shift
            SimpleBookmark.shiftPageNumbers(tmp, page_offset,
               new int[] { POINT, reader.getNumberOfPages() + pagesCount });
            bookmarks.addAll(tmp);
            // add the pages
            n = reader.getNumberOfPages();
            page_offset += n;
            for (int page = 0; page < n;) {
                copy.addPage(copy.getImportedPage(reader, ++page));
                if (page == POINT) // add child pages to nth point
                {
                    for (int page2 = 0; page2 < pagesCount;) {
                        copy.addPage(copy.getImportedPage(reader2, ++page2));
                    }
                }
            }
            copy.freeReader(reader);
            reader.close();
        }
        // Add the merged bookmarks
        copy.setOutlines(bookmarks);
        // step 5
        document.close();
    }
    /**
     * Main method.
     * 
     * @param args
     *            no arguments needed
     * @throws DocumentException
     * @throws IOException
     * @throws SQLException
     */
    public static void main(String[] args) throws IOException, DocumentException, SQLException {
        new ConcatenateBookmarks().manipulatePdf(new String[] { SRC1, SRC2 }, DEST);
    }
}

Upvotes: 4

Views: 380

Answers (1)

Bruno Lowagie
Bruno Lowagie

Reputation: 77528

Please take a look at the InsertAndAdaptOutlines example. I used PdfStamper instead of PdfCopy, but the code you really need is the code that adapts the PdfOutline.

In my code, I have a file named bookmarks.pdf:

enter image description here

I insert a single page "Hello World" so that it is page 4 (in your case, change 4 to 10) resulting in a file named bookmarks_hello.pdf

enter image description here

Inserting the page is a no-brainer:

PdfReader insert = new PdfReader(INSERT);
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(DEST));
stamper.insertPage(4, insert.getPageSize(1));
PdfContentByte cb = stamper.getOverContent(4);
cb.addTemplate(stamper.getImportedPage(insert, 1), 0, 0);
...
stamper.close();

The most difficult issue is: How to update the outline tree? You can get the outline tree from the PdfReader instance, you can create your own entry which you want to add to the outlines and you can then put the updated outlines back into the PdfStamper instance using the setOutlines() method. (You've already found the similar method for the PdfCopy or PdfSmartCopy class):

List<HashMap<String, Object>> outlines = SimpleBookmark.getBookmark(reader);
HashMap<String, Object> entry = new HashMap<String, Object>();
entry.put("Title", "Hello");
entry.put("Action", "GoTo");
entry.put("Page", "4 Fit");
updateOutline(outlines, entry, 4);
stamper.setOutlines(outlines);

My updateOutline() method makes the assumption that the existing outline tree refers to pages using GoTo actions:

public boolean updateOutline(List<HashMap<String, Object>> outlines, HashMap<String, Object> entry, int p) {
    int index = 0;
    for (HashMap<String, Object> outline : outlines) {
        Object kids = outline.get("Kids");
        if (kids != null) {
            updateOutline((List<HashMap<String, Object>>)kids, entry, p);
        }
        else {
            if (p < getPage(outline)) {
                outlines.add(index, entry);
                return true;
            }
            index++;
        }
    }
    return false;
}

public int getPage(HashMap<String, Object> outline) {
    Object page = outline.get("Page");
    if (page == null) return -1;
    String p = page.toString().substring(0, page.toString().indexOf(" "));
    return Integer.parseInt(p);
}

If your outlines are created differently, you'll have to adapt the updateOutline() method, but this example should give you sufficient inspiration to know how it's done. You have to loop over all the entries in the outline tree and insert the new entry at the appropriate place. Then put that new, altered outline tree back in the resulting PDF.

Upvotes: 1

Related Questions