Reputation: 1324
I have two pdfs:
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
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:
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
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