Automating bookmark creation with Acrobat JavaScript

Learn how to automate the creation of PDF bookmarks using Acrobat JavaScript.

By Thom Parker – August 7, 2008

 

Scope: Acrobat 7.0 and later
Skill Level: Intermediate and Advanced
Prerequisites: Basic Acrobat JavaScript Programming

PDF bookmarks are normally created during the conversion from the original format or manually in Acrobat. But there are some situations where neither of these methods is satisfactory. For example, documents that are converted without bookmarks, or documents that are built piece-meal over a period of time before being distributed. For these situations, Acrobat provides two methods for automatically creating bookmarks with JavaScript.

Why automate bookmark creation?

Automation saves time and reduces human error. Since bookmarks require a label, creating them is not as easy to automate as some other tasks in Acrobat, but it can still be a worthwhile task to automate. The problematic issue is, where do the bookmark labels come from? This simple requirement limits the kinds of documents where bookmark automation makes sense. The best situation is where the bookmark labels can be acquired from some ready source, possibly from text already on the page. For example, if the pages each have a title in the same location, regular Acrobat JavaScript methods can be used to scrape the titles off the page. Another situation where automating bookmark creation is useful is in the workflow for creating the previously mentioned piece-meal-built documents, which will be the example for this article.

A built document is a PDF that has a standard format and incorporates several documents from different sources that are organized and combined together, not necessarily at one time. Documents that fit into this category include various kinds of report documents, such as an investment summary, a tax package, a corporate benefits report, an evidence package or an FDA submission. All of these documents have a standard format, but contain varying contents. For these, a standard skeleton set of bookmarks can be created and automatically updated throughout the document-building process.

Bookmark-creation methods

As stated earlier, there are two ways to create bookmarks. Each has different advantages and disadvantages. The most obvious method is the bookmark.createChild() function. This function creates a new bookmark underneath an existing bookmark. All PDF documents, even ones with no bookmarks at all, have an invisible top-level bookmark called doc.bookmarkRoot. So we don’t need to worry about creating the first bookmark in the PDF, bookmark.createChild() works for creating any bookmark we might need. This function is easy to use and creates the bookmark in exactly the position it needs to be in. The downside to using this function is that it can only create bookmarks that execute JavaScript. None of the other bookmark actions can be set from an automation script. This works fine if we want our new bookmark to run a script, but it doesn’t help for making position-independent, page-navigation bookmarks.

When a bookmark is created manually with the “New Bookmark” button on the Bookmarks Panel in Acrobat, its action is automatically set to “Go To Page” with the destination being the current page view (page number, zoom, and scroll). Moving either the page or the bookmark to another location makes no difference in how it works. The bookmark will always navigate to the same page view. This is position-independent page navigation and it’s important for built documents because pages may be added or moved after a bookmark is created, potentially changing the locations (and page numbers) of other pages in the document.

This brings us to the second method for creating bookmarks, using the “NewBookmark” menu item. This method creates a bookmark in exactly the same way it is done manually, i.e., it creates a bookmark with a “Go To Page” action pointing to the current page view. This is the only way to use Acrobat JavaScript to automate creation of bookmarks for position-independent navigation. However, it is a bit more difficult to work with than the other method.

The “NewBookmark” menu item creates a bookmark immediately below the currently selected bookmark and names it “Untitled.” The difficult part of working with this menu item in JavaScript is that the current bookmark selection cannot the identified or controlled with JavaScript, so the script does not immediately know where in the bookmark hierarchy it just created the bookmark. To use this method, we need code to find the newly created bookmark and name it. We also have to assume that the bookmark was created in the correct location, that the user will move it at a later time, or write code to move it to the proper location. In the interest of simplicity, the example presented here will assume the user set the bookmark selection so that the new bookmark is created in the correct location.

Building a standard bookmark framework, example #1

For a built document, the initial bookmark framework contains the top level bookmarks and possibly some example children. In this example, the bookmark framework is added at the beginning of the project before any real content pages are added. Since navigation is not required at this point, the script uses the bookmark.createChild() function. Destinations are either set manually by the designer or with another script, as the real content pages are added. Of course, this could be done differently by modifying the code to use the “NewBookmark” menu item, but the intent here is to show the use of the bookmark.createChild() function.

The following scripts can be copied and run from the Acrobat JavaScript Console or placed in a Folder Level script and run from a toolbar button. You can find information on creating a toolbar button in the article on Applying Security to a PDF.

var aBkMkNames = [ "TOC", ["Tax Forms","1040"], ["Schedules","Schedule A"], ["Wages & Tips","W2-Work"], ["Interest & Div","Bank W9","Stocks W9"], "Receipts" ];
function MakeBkMks(oBkMkParent, aBkMks) {
	for(var index=0;i<aBkMks.length;i++) {
		if(typeof(aBkMks[index]) == "string") oBkMkParent.createChild({cName:aBkMks[i], nIndex:i });
		else {
			// Assume this is a sub Array
			oBkMkParent.createChild({cName:aBkMks[i][0], nIndex:i});
			MakeBkMks(oBkMkParent.children[index], aBkMks[i].slice(1) );
		}
	}
}
MakeBkMks(this.bookmarkRoot, aBkMkNames);

The first part of the script is a hierarchical array of bookmark labels, aBkMkNames. It defines the organization of the built document. Array entries that are plain text are a bookmark at that level. Array entries that are another array represent a sub-level of hierarchy, where the first entry is the bookmark at the current level and all the following entries are the child bookmarks on the sub-level. This type of “arrays in arrays” structure allows for any depth of hierarchy. The example code contains entries that define a tax package, but it could be anything.

The second part of the script is a recursive function MakeBkMks(), that does the actual bookmark creation. A recursive function is a function that can call itself. This is important for handling hierarchical data structures like the bookmarks array.

The function takes two inputs, the bookmark parent and the array of bookmark names for that level. The function itself only handles one level of hierarchy. It loops through all the bookmark labels, creating a new bookmark for each one. If it encounters a sub-level, it then calls itself, passing in the parameters for the next level.

The last part of the script starts the whole process. It calls MakeBkMks(), passing in the bookmarkRoot and the entire bookmark labels array. Try it out by first creating, or opening, a document with no bookmarks. Then copy the code above to the Acrobat JavaScript Console and run it.

Create new named bookmark, example #2

In this script, the user picks from a list of pre-defined bookmark labels. A bookmark is then created for the current page view and placed in the bookmark hierarchy just below the currently selected bookmark. This script requires the user to either select a bookmark before running the script, or move the newly created bookmark at a later time.

This script is a good companion for the previous script that creates a bookmark framework. Once the bookmark framework is created, pages are added to the PDF and new bookmarks need to be added for these pages. Because the document is structured, many of these added pages will have a common origin, so it makes sense to have a common list of names to choose from. For example, the tax package in the previous example has a parent bookmark for “Tax Schedules.” All of these forms are already known, so it makes sense to have a quick pick-list of schedule form names. That’s the example we’ll use here. However, this script could be used for any situation where a standard list of bookmark names is used.

In order to create bookmarks with true destinations, the “NewBookmark” menu item is used. As discussed earlier, this menu item has some drawbacks. The most obvious is that it places the new bookmark below the currently selected bookmark, and bookmark selection cannot be detected or controlled from JavaScript. So, a helper function is needed to find the new bookmark. Here’s the code for the find function:

function FindBookmarkByName(oBkMkParent,cFindName) {
	var aBkMks = oBkMkParent.children; var oRtn = null; if(aBkMks != null) {
		for(var i=0;i<aBkMks.length;i++) {
			if(aBkMks[i].name == cFindName) oRtn = aBkMks[i];
			else if(aBkMks[i].children != null) oRtn = FindBookmarkByName(aBkMks[i],cFindName,nInc);
			if(oRtn != null) break;
		}
	}
	return oRtn;
}

This function looks through all the bookmarks at one level of the bookmark hierarchy, oBkMkParent, and returns the bookmark that matches cFindName. Since this function only checks the names on a single level of bookmark hierarchy, it calls itself recursively each time it encounters a bookmark with children.

Before getting into the main code, there is another issue. When a bookmark is created from the “NewBookmark” menu item, it is in edit mode and has the keyboard focus. This is not good for automation. An attempt to remove the focus is made by creating a second bookmark and then deleting it. Sometimes this does not work and the focus has to be moved manually by clicking on the bookmarks. However, even with these limitations and issues, automating bookmark creation can significantly improve workflow performance and completely eliminate typos, making it a worthwhile effort.

// Define Bookmark Names
//Change these names for your own bookmarks
var aSchedNames = [ "A - Form 5500", "B - Form 1120", "C - Form 1040", "D - Form 1040", "E - Form 990" ];
var cRtn = app.popUpMenu.apply(app, aSchedNames);
if(cRtn != null) {
	// Create new Bookmark with destination
	app.execMenuItem("NewBookmark");
	// Create an extra bookmark to move the focus
	app.execMenuItem("NewBookmark");
	// Find First Bookmark and rename it
	var oBkMk = FindBookmarkByName(this.bookmarkRoot,"Untitled");
	if(oBkMk != null) oBkMk.name = cRtn;
	// Now find extra bookmark and delete it
	var oBkMkEx = FindBookmarkByName(this.bookmarkRoot,"Untitled");
	if(oBkMkEx != null) oBkMkEx.remove();
}

The first part of the script displays a popup menu of bookmark labels. This can be changed to your own list. If the user selects a name from the menu, two new bookmarks are created. As stated above, the second bookmark is to take the keyboard focus off the first bookmark. Now, all that’s left to do is to set the name on the new bookmark. In order to do that, the bookmark has to be located, which is where the FindBookmarkByName() function comes in. And finally, this same function is used to find the second new bookmark and delete it. If everything has gone correctly, there will be a new bookmark in the correct location with the correct name. If things didn’t go correctly, there will be a new bookmark and it will be named correctly, but you won’t know it because it will still have the keyboard focus. In this case, you’ll need to click on the new bookmark and then click off of it.

This code can be tested from the Acrobat JavaScript Console. For a production situation, the code should be placed in a Folder Level script and modified to run from an Acrobat toolbar button.

References

The code for both examples can be found in this file:
MakeBookmark_Example.pdf.

For more information on the bookmark functions, see both The Acrobat JavaScript Reference and The Acrobat JavaScript Guide.
https://www.adobe.com/devnet/acrobat.html

Click on the Documentation tab and scroll down to the JavaScript section.



Related topics:

JavaScript

Top Searches:


5 comments

Comments for this tutorial are now closed.

Lori Kassuba

3, 2015-10-09 09, 2015

Hi Libby,

Bookmarks can be automatically generated a few different ways:
1. When you create the PDF from Word and turn this on.
2. From a PDF file that has been tagged and then use the create bookmark from structure command.
3. Use the Combine Files command. The Options menu allows you to turn on bookmarks.

Thanks,
Lori

Libby

3, 2015-10-07 07, 2015

I am creating a large document in IDCC and my PDF needs bookmarks. Since the document is not yet complete, pages are being added and removed as I move along. I need bookmarks in place to navigate through the document. I have been manually inserting bookmarks, but I am wondering if there is some way I can get bookmarks to “automatically” show up? I know nothing about scripting, so simple direction is preferred.

Lori Kassuba

3, 2015-04-02 02, 2015

Hi YOGESH,

Can you post your question here and be sure to select the JavaScript category so our experts can help you:
https://answers.acrobatusers.com/AskQuestion.aspx

Thanks,
Lori

YOGESH

2, 2015-03-26 26, 2015

i wanted to add automatic bookmark script for my site. please help

Lori Kassuba

2, 2014-05-19 19, 2014

Hi Chris G,

You might see if the technique mentioned in this tutorial works for you:
https://acrobatusers.com/tutorials/how-to-create-structured-bookmarks

Thanks,
Lori

Chris G

5, 2014-05-12 12, 2014

glad to see a recent comment.

This looks like something I would want to use, but I am not a JavaScript user.  I was able to copy some of the stuff and make it work, but was wondering if there is a more detailed simplified process?

I am creating a pdf from an Access db and want to automate the bookmark process afterwards.  Is there a way?  (We cannot use add ons)

Lori Kassuba

2, 2014-05-12 12, 2014

Hi sahibibilircom,

Bookmarks are used within a PDF file for navigation purposes.

Thanks,
Lori

sahibibilircom

10, 2014-05-09 09, 2014

I Don’t understand anything :/ İs it a for bookmark submitter for sites?

Kl Heban

9, 2012-10-16 16, 2012

Watch out in the example #1, sometimes you use ‘i’ and sometimes ‘index’.

Comments for this tutorial are now closed.