US and Worldwide: +1 (866) 660-7555
Results 1 to 6 of 6

Thread: How to add a bookmark/outline/toc to a pdf

  1. #1
    Join Date
    Mar 2005
    Posts
    4

    Default How to add a bookmark/outline/toc to a pdf

    Hi all,

    Someone was asking how to add a bookmark/outline/toc to a PDF file, so I thought I'd give our what I did.

    For my app, most of my reports are 2 or more individual reports (up to 8) that are returned to the user as a single PDF file. This way I can show them multiple views of the same data (financial totals with different groupings and sortings) in a single report. The problem I was having is that I needed a way to show the user how many reports were in their PDF file, and a simple way to navigate between the reports in the PDF. This is what I came up with.

    First off, I wrote this a long time ago, and I believe (if I remember correctly) that I got most (if not all) of the code to do this from the iText documentation.

    For me, it's a four step process.
    1 - Use JFR to create the PDF file.
    2 - Call my static method to add a bookmark/title/toc to the pdf report
    3 - Repeate 1 & 2 till done
    4 - Concatenate all the PDF files into a single PDF.

    What's here is step #2 - a static method that is called after each individual report is generated and adds the bookmark. It takes the PDF file that JFR created, and a string name that you want to use as the bookmark. It then creates a new PDF file with the contents of the old PDF file but it has a bookmark/TOC/outline entry (using the passed name) pointing to the first page of the PDF.

    There will be one spot where you need to make a code adjustment - the first line under the "try" statement where it creates a new File object. I've got a couple of my own static method calls that give the new PDF file a random name in a temporary (in the context of my app) directory. Once you fix that that, it should work fine.

    This method also returns a pointer to the new (bookmarked) PDF, but it does nothing to the old (unbookmarked) PDF. I've got a thread process that deletes any old files in my app's temp directory, so I don't need to worry about deleting the original PDF file. Just so you know.

    The final step that I do is I take the 2+ PDF's that are returned (now with bookmarks!) and concatenate them into one big PDF. The concatenation process maintains the bookmark order, and correctly points each bookmark to the right page. I then return the uber-PDF back the user and it's all good.

    If you need/want the contcatenation code, let me know.

    Good luck -
    Jeff

    Code:
    // probably too many imports for just the bookmark part of the code
    import com.lowagie.text.Document;
    import com.lowagie.text.pdf.PdfCopy;
    import com.lowagie.text.pdf.PdfImportedPage;
    import com.lowagie.text.pdf.PdfReader;
    import com.lowagie.text.pdf.SimpleBookmark;
    import com.lowagie.text.pdf.PdfDestination;
    import com.lowagie.text.pdf.PdfOutline;
    import com.lowagie.text.pdf.PdfAction;
    
    import java.io.File;
    import java.io.FileOutputStream;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    	/** add a outline to the pdf, and mark the first page w/a bookmark link */
    	static public File createTitleBookmark(File p_pdf, String p_title) {
    		try {
    			// create output file
    			File m_newfile = new File(Finder.getTempPath(), Crypto.hash(Crypto.getRandomString(20)));
    			String m_resultfile = m_newfile.getCanonicalPath();
    
    			Document document = null;
    			PdfCopy  writer = null;
    			int pageOffset = 0;
    
    			// we create a reader for a certain document
    			PdfReader reader = new PdfReader(p_pdf.getCanonicalPath());
    			reader.consolidateNamedDestinations();
    
    			int n = reader.getNumberOfPages();
    
    			// step 1: creation of a document-object
    			document = new Document(reader.getPageSizeWithRotation(1));
    			// step 2: we create a writer that listens to the document
    			writer = new PdfCopy(document, new FileOutputStream(m_resultfile));
    			writer.setViewerPreferences(writer.PageModeUseOutlines);
    			// step 3: we open the document
    			document.open();
    
    			// step 4: we add content.  loop for each page
    			PdfImportedPage page;
    			for &#40;int i = 0; i < n; &#41; &#123;
    				++i;
    				page = writer.getImportedPage&#40;reader, i&#41;;
    				writer.addPage&#40;page&#41;;
    				System.out.println&#40;"Processed page " + i&#41;;
    			&#125;
    			
    			// add the bookmark action
    			PdfDestination d5 = new PdfDestination&#40;PdfDestination.FIT&#41;;
    			PdfAction m_action = PdfAction.gotoLocalPage&#40;1, d5, writer&#41;;
    			PdfOutline out1 = new PdfOutline&#40;writer.getDirectContent&#40;&#41;.getRootOutline&#40;&#41;, m_action, p_title&#41;;
    
    			// step 5&#58; we close the document
    			document.close&#40;&#41;;
    			return new File&#40;m_resultfile&#41;;
    		&#125; catch &#40;Exception e&#41; &#123;
    			System.out.println&#40;e.getMessage&#40;&#41;&#41;;
    		&#125;
    		return null;
    	&#125;

  2. #2

    Default

    Hi,

    Thanks you, I am sure it will help some people.
    May the Moo force be with you,
    Said Mimil

    A student of JFree & Pentaho Academies,
    Cedric Pronzato

  3. #3
    Join Date
    Mar 2003
    Posts
    4,947

    Default Re: How to add a bookmark/outline/toc to a pdf

    Jeff,
    Could you please post the concatenation code? Thanks!!

    Quote Originally Posted by klyphio
    Hi all,

    Someone was asking how to add a bookmark/outline/toc to a PDF file, so I thought I'd give our what I did.

    For my app, most of my reports are 2 or more individual reports (up to 8) that are returned to the user as a single PDF file. This way I can show them multiple views of the same data (financial totals with different groupings and sortings) in a single report. The problem I was having is that I needed a way to show the user how many reports were in their PDF file, and a simple way to navigate between the reports in the PDF. This is what I came up with.

    First off, I wrote this a long time ago, and I believe (if I remember correctly) that I got most (if not all) of the code to do this from the iText documentation.

    For me, it's a four step process.
    1 - Use JFR to create the PDF file.
    2 - Call my static method to add a bookmark/title/toc to the pdf report
    3 - Repeate 1 & 2 till done
    4 - Concatenate all the PDF files into a single PDF.

    What's here is step #2 - a static method that is called after each individual report is generated and adds the bookmark. It takes the PDF file that JFR created, and a string name that you want to use as the bookmark. It then creates a new PDF file with the contents of the old PDF file but it has a bookmark/TOC/outline entry (using the passed name) pointing to the first page of the PDF.

    There will be one spot where you need to make a code adjustment - the first line under the "try" statement where it creates a new File object. I've got a couple of my own static method calls that give the new PDF file a random name in a temporary (in the context of my app) directory. Once you fix that that, it should work fine.

    This method also returns a pointer to the new (bookmarked) PDF, but it does nothing to the old (unbookmarked) PDF. I've got a thread process that deletes any old files in my app's temp directory, so I don't need to worry about deleting the original PDF file. Just so you know.

    The final step that I do is I take the 2+ PDF's that are returned (now with bookmarks!) and concatenate them into one big PDF. The concatenation process maintains the bookmark order, and correctly points each bookmark to the right page. I then return the uber-PDF back the user and it's all good.

    If you need/want the contcatenation code, let me know.

    Good luck -
    Jeff

    Code:
    // probably too many imports for just the bookmark part of the code
    import com.lowagie.text.Document;
    import com.lowagie.text.pdf.PdfCopy;
    import com.lowagie.text.pdf.PdfImportedPage;
    import com.lowagie.text.pdf.PdfReader;
    import com.lowagie.text.pdf.SimpleBookmark;
    import com.lowagie.text.pdf.PdfDestination;
    import com.lowagie.text.pdf.PdfOutline;
    import com.lowagie.text.pdf.PdfAction;
    
    import java.io.File;
    import java.io.FileOutputStream;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    	/** add a outline to the pdf, and mark the first page w/a bookmark link */
    	static public File createTitleBookmark&#40;File p_pdf, String p_title&#41; &#123;
    		try &#123;
    			// create output file
    			File m_newfile = new File&#40;Finder.getTempPath&#40;&#41;, Crypto.hash&#40;Crypto.getRandomString&#40;20&#41;&#41;&#41;;
    			String m_resultfile = m_newfile.getCanonicalPath&#40;&#41;;
    
    			Document document = null;
    			PdfCopy  writer = null;
    			int pageOffset = 0;
    
    			// we create a reader for a certain document
    			PdfReader reader = new PdfReader&#40;p_pdf.getCanonicalPath&#40;&#41;&#41;;
    			reader.consolidateNamedDestinations&#40;&#41;;
    
    			int n = reader.getNumberOfPages&#40;&#41;;
    
    			// step 1&#58; creation of a document-object
    			document = new Document&#40;reader.getPageSizeWithRotation&#40;1&#41;&#41;;
    			// step 2&#58; we create a writer that listens to the document
    			writer = new PdfCopy&#40;document, new FileOutputStream&#40;m_resultfile&#41;&#41;;
    			writer.setViewerPreferences&#40;writer.PageModeUseOutlines&#41;;
    			// step 3&#58; we open the document
    			document.open&#40;&#41;;
    
    			// step 4&#58; we add content.  loop for each page
    			PdfImportedPage page;
    			for &#40;int i = 0; i < n; &#41; &#123;
    				++i;
    				page = writer.getImportedPage&#40;reader, i&#41;;
    				writer.addPage&#40;page&#41;;
    				System.out.println&#40;"Processed page " + i&#41;;
    			&#125;
    			
    			// add the bookmark action
    			PdfDestination d5 = new PdfDestination&#40;PdfDestination.FIT&#41;;
    			PdfAction m_action = PdfAction.gotoLocalPage&#40;1, d5, writer&#41;;
    			PdfOutline out1 = new PdfOutline&#40;writer.getDirectContent&#40;&#41;.getRootOutline&#40;&#41;, m_action, p_title&#41;;
    
    			// step 5&#58; we close the document
    			document.close&#40;&#41;;
    			return new File&#40;m_resultfile&#41;;
    		&#125; catch &#40;Exception e&#41; &#123;
    			System.out.println&#40;e.getMessage&#40;&#41;&#41;;
    		&#125;
    		return null;
    	&#125;

  4. #4
    Join Date
    Apr 2007
    Posts
    1,990

    Default

    On the very offchance of getting a reply on this thread 4 years later, i'd quite like to see the concatenation code too!

    I have a new requirement to concatenate multiple pdf's, some but not necessarily all of which are generated by jfree.

    Thanks,
    Dan

  5. #5
    Join Date
    Mar 2003
    Posts
    8,009

    Default

    If you are willing to do a bit of Java/JavaScript, then iText already comes with a concat tool.

    Code:
    String[] args = new String[] {
    "/full/path/to/your/first-file.pdf"
    "/full/path/to/your/second-file.pdf"
    "/full/path/to/your/maybe-third.pdf"
    
    "/full/path/to/your/target.pdf"
    };
    
    com.lowagie.tools.concat_pdf.main(args);
    Get the latest news and tips and tricks for Pentaho Reporting at the Pentaho Reporting Blog.

  6. #6
    Join Date
    Apr 2007
    Posts
    1,990

    Default

    yup, thats fine, thanks thats great - i had see a ref to itext concatenation elsewhere - and seems nice and clean to use the same tool the reporting engine uses.

    Thanks!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •