Chapter 7. Making documents interactive

This chapter covers

  • Creating actions and destinations
  • Working with outlines and bookmarks
  • Adding annotations and JavaScript

In the summary of the previous chapter, table 6.1 outlined the most important iText classes for manipulating documents. You’ve used these classes to manipulate the content of existing documents to copy pages, add extra content, fill out forms, and so forth. In this chapter, you’ll use PdfStamper and PdfCopy to add interactive features to an existing document.

We’ll start by adding actions that will help the end user navigate through the document, similar to what you did with internal and external links in section 2.3.1. Next we’ll look at bookmarks, but instead of using Chapters and Sections, as you did in section 2.3.2, we’ll look at how to create a custom outline tree. Finally, we’ll add annotations. For example, you’ll learn how to put a sticky note on an existing page, and how to show an advertisement when a PDF document is opened.

7.1. Introducing actions

If you’re reading this book from beginning to end, actions shouldn’t be new to you. You created documents containing actions in chapter 2, but we didn’t call them actions; instead we talked about remote and local goto links. In this section, you’ll add goto actions using the PdfAction class, and you’ll also learn how to introduce new actions, such as actions that trigger JavaScript functions embedded in the document.

But let’s start with an example that adds actions to navigate through documents.

7.1.1. Document-navigation actions

Most PDF viewers have buttons that allow the end user of the PDF document to switch to another page. In some cases, these viewer buttons can be removed or the toolbar can be hidden by setting the viewer preferences. Maybe you want to add extra navigational aids to the page itself, to make it easier for the end user to go to the first, previous, next, or last page. For this purpose, ISO-32000-1 defines four named actions that should be supported by every PDF viewer.

Named Actions

In figure 7.1, you can find arrows to jump to the first page (⇐), the previous page (←), the next page (→), and the last page (⇒).

Figure 7.1. Timetable with named actions triggered by clicking the arrows

These arrows correspond to four named actions: /NextPage, /PrevPage, /FirstPage, and /LastPage. You can create these links using the PdfAction class. You can see how to associate an instance of this class with a Chunk using the setAction() method.

Listing 7.1. NamedActions.java

You can add the table from listing 7.1 to the timetable PDF using PdfStamper and the method writeSelectedRows().

 

Note

Most of the functionality discussed in this chapter can also be used when creating documents from scratch.

 

In the next example, you’ll rewrite listing 2.23 using PdfAction instances.

Remote and Local Goto Actions

The MovieLinks examples in section 2.3.1 resulted in two PDF files. The links and destinations in the first PDF were created using the Anchor class. The document contained external links to the IMDB and local destinations that were referred to by a name or a string. These destinations are called named destinations—the country code was the name for the local destination.

The second PDF showed a list of countries with one external link to the IMDB website, an external link to a named destination referring to a country page in the first document, and an internal link to the top of the page. Listing 7.2 duplicates the code from the original MovieLinks2 example but replaces methods such as setRemoteGoto() and setLocalGoto() with setAction().

Listing 7.2. LinkActions.java

Let’s examine all the constructors and methods used to create the PdfActions in this example.

Jump to an External URL

The first action added in the listing will create a link to an external URL . This example uses a URL object, but passing a String with the link address would have worked too. When adding the action to a Chunk, a rectangular clickable area is created. If you add a boolean as an extra parameter, the X and Y coordinate of the position you’ve clicked will be added to the URL as a query string. You could define an action like this:

new PdfAction("http://www.lowagie.com/", true);

Clicking on such a link in the lower-left corner of the clickable area could result in the following hit on the lowagie.com server:

http://www.lowagie.com/?5,3

Inspecting the query string on the server side will reveal that the user’s mouse pointer pointed at the position X = 5, Y = 3 in the clickable rectangle.

Jump to a Location in a Document

There are different ways to create a link to an external document. You can use one of the constructors of PdfAction, as is done in listing 7.2 :

  • new PdfAction(String filename, String name) This will create a link to the document named filename and jump to a named destination with the name name.
  • new PdfAction(String filename, int page) This will create a link to the document named filename and jump to a specific page number: page.

Executing these actions will replace the document that is currently viewed by the new document. If you want to open the new document in a new viewer window, you can create the action like this:

PdfAction action = new PdfAction("other.pdf", 1);
action.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);

If you’ll look inside iText, you’ll see that PdfAction is a PdfDictionary, which means it’s a collection of key-value pairs. The keys are of type PdfName, and the value can be (a reference to) any subclass of PdfObject; in this case, a PdfBoolean. This mechanism will be explained in detail in chapter 13. You can use it to extend iText with functionality that isn’t provided out of the box.

 

Note

A new viewer window is not the same as a new browser window. If you tell iText that the action should open a new window, this will only work if you’re looking at the document in the standalone Adobe Reader application. It will open a new standalone Adobe Reader window. If you’re looking at the document using the Adobe Reader plug-in inside a browser, it will not open a new browser tab or window.

 

In in listing 7.2, the static method gotoRemotePage() is used to create an instance of class PdfAction. This method has four parameters:

  • filename Specifies the filename of the external document.
  • dest Specifies the named destination inside this document.
  • isName Indicates whether the destination is stored as a PdfName (when set to true) or as a PdfString (when set to false). These are two different types of PDF objects that can be used to define a named destination. Note that iText creates named destinations as Strings.
  • newWindow Specifies whether the document will be opened in a new viewer window (if set to true) or not.

Actions that jump to a local destination are created using a static gotoLocalPage() method. In this method is used with a String for the name; the boolean value false indicates that the name was stored as a PdfString, not as a PdfName.

In listing 7.2, you know the names of the named destinations, because you’re creating the documents yourself. But suppose somebody gives you an existing document, and you’re asked to create a new document that links to the named destinations in this existing document. How can you find out which names to use?

Retrieving Named Destinations From an Existing Document

You can create a HashMap and an XML file containing information about the named destinations inside an existing PDF document.

Listing 7.3. LinkActions.java (continued)
PdfReader reader = new PdfReader(src);
HashMap<String,String> map =
SimpleNamedDestination.getNamedDestination(reader, false);
SimpleNamedDestination.exportToXML(map,
new FileOutputStream(RESULT3), "ISO8859-1", true);

With the boolean parameter false, you indicate that you’re interested in the names that are stored as a PdfString. If you change this parameter to true, you ask iText for the names that are stored as a PdfName inside the document.

The keys of such a map are String values. In this case, they are the keys of the countries stored in the database: US, AR, and so on. You can find an example of the values in the Page attribute of the XML that is generated:

<?xml version="1.0" encoding="ISO8859-1"?>
<Destination>
<Name Page="1 XYZ 36 802 0">US</Name>
<Name Page="19 XYZ 36 802 0">AR</Name>
...
</Destination>

In this snippet, an XML file is created using the Latin-1 encoding (ISO8859-1), and you ask iText to escape all non-ASCII characters with the boolean parameter true. The values 1 XYZ 36 802 0 and 19 XYZ 36 802 0 need to be explained in more detail. They refer to explicit destinations.

7.1.2. Explicit destinations

If you look at figure 7.1, you’ll see that I zoomed in on the arrows at the bottom of the page. If you click on one of the arrows, a named action will be executed. You will jump to another page, and that page will be shown using the same zoom factor.

Suppose you don’t want this—suppose you want to instruct the viewer to jump to an exact position on the first, previous, next, or last page using the zoom factor of your choice. In iText, you can achieve this by using the PdfDestination class. The constructor of this class always has at least one parameter: type defines the destination type.

Overview of the Different Types

Table 7.1 gives an overview of the available types of explicit destinations.

Table 7.1. Destination types for creating a PdfDestination object

Type

Extra parameters

Description

FIT - The current page is displayed with its contents magnified just enough to fit the document window, both horizontally and vertically.
FITB - The current page is displayed magnified just enough to fit the bounding box of the contents (the smallest rectangle enclosing all of its contents).
FITH float top The page is displayed so that the page fits within the document window horizontally (the entire width of the page is visible). The extra parameter specifies the vertical coordinate of the top edge of the page.
FITBH float top This option is almost identical to FITH, but the width of the bounding box of the page is visible. This isn’t necessarily the entire width of the page.
FITV float left The page is displayed so that the page fits within the document window vertically (the entire height of the page is visible). The extra parameter specifies the horizontal coordinate of the left edge of the page.
FITBV float left This option is almost identical to FITV, but the height of the bounding box of the page is visible. This isn’t necessarily the entire height of the page.
XYZ float left, float top, float zoom The parameter left defines an X coordinate, top defines a Y coordinate, and zoom defines a zoom factor. If you want to keep the current X coordinate, Y coordinate, or zoom factor, you can pass negative values or 0 for the corresponding parameter.
FITR float left, float bottom, float right, float top The parameters define a rectangle. The page is displayed with its contents magnified just enough to fit this rectangle. If the required zoom factors for the horizontal and the vertical magnification are different, the smaller of the two is used.

Table 7.1 can be used to interpret the output generated with the SimpleNamedDestination class. For instance 1 XYZ 36 802 0 means that you want to jump to the coordinate (36, 802) on page 1, keeping the current zoom factor.

The table can also be used to create a new Map of named destinations “manually.” PdfWriter has an addNamedDestinations() method that can be used to inject such a map in a document that is built from scratch. This method was originally written to work around a problem with named destinations when using PdfCopy.

Named Destinations and Pdfcopy

A recurring question on the iText mailing list involves the concatenation of documents that have named destinations. Suppose you want to concatenate the two files created in listing 7.2. In chapter 6, you learned that most of the interactive features are preserved if you use PdfCopy, but there are exceptions. Using PdfCopy with documents that have named destinations is one of these exceptions. All annotations, such as link annotations, are kept with PdfCopy, but they no longer work for links to local named destinations. There is a workaround for this problem.

Listing 7.4. ConcatenateNamedDestinations.java

If you use listing 6.21 to concatenate the two documents from the previous example (listing 7.2), you’ll find out that the link to go to the named destination “top” has the appearance of a link, but if you click it, it won’t work. You can work around this problem by using the method consolidateNamedDestinations() . This method translates all the local links referring to a named destination into links that use explicit destinations.

That fixes the internal link problem, but it may not be sufficient. You can link to the file LinkActions.RESULT1 from another file using a named destination, but these destinations are lost in the concatenated file. You can restore these links by injecting them into the PdfCopy object . In listing 7.4, you use SimpleNamedDestination to retrieve a map containing the named destinations you want to preserve. Note that page 1 of the original document is no longer page 1 in the concatenated document. When you use the addNamedDestinations() method, you have to use a page offset based on the number of pages in the documents that were added before the document with the named destinations.

And what about the links to named destinations in external files? These will keep on working, but they’ll point to the original external document. Maybe you want to concatenate two documents that are linking to each other, and change the remote goto actions into local goto actions. PdfCopy can’t do this. You have to run the file through PdfStamper and use the makeRemoteNamedDestinationsLocal() method . This method will try to convert remote goto links into local goto links. Only the remote links that refer to a name that isn’t known as a named destination in the local file are preserved as external links.

With these three mechanisms, you can work around the problems that are caused by the limitations of PdfCopy when dealing with named destinations.

Creating Explicit Destinations

Table 7.1 also serves as a reference for creating explicit destinations. The types in the first column are names of public static final int values in the PdfDestination class. You can use these values to construct a PdfDestination object, as follows:

PdfDestination dest = new PdfDestination(PdfDestination.XYZ, 36, 802, 0);

The static method PdfAction.gotoLocalPage() creates an action that jumps to an explicit destination on a specific page.

Listing 7.5. TimetableDestinations.java

This is a rewrite of the first example in this chapter. The output is identical to what is shown in figure 7.1. Instead of using named actions, you create a List containing PdfAction objects. If you look at the parameters of the gotoLocalPage() method, you’ll recognize the page number, the destination of choice, and a third parameter that needs further explanation.

Page Numbers Versus Page References

In listing 7.3, you retrieved information about named destinations using the SimpleNamedDestination class. This class uses reader.getNamedDestination() to get the named destinations. You could use this method too, but you’d get entries like this:

US=[1 0 R, /XYZ, 36, 802, 0]
AR=[210 0 R, /XYZ, 36, 802, 0]

The values 1 0 R and 210 0 R aren’t page numbers but references to page dictionaries. There are no page numbers inside a PDF file. Pages are organized in a page tree, and their position in this tree defines the page number. When you create a link to an explicit destination using gotoLocalPage(), iText needs to translate the page number (for instance, page 19 to jump to the page with films from Argentina) to a reference (such as 210 0 R). iText can only do this if you also pass a PdfWriter instance. In listing 7.5, you pass the writer associated with a PdfStamper object: stamper.getWriter().

You’ll create more destinations in section 7.2, when we talk about bookmarks, but first, let’s introduce JavaScript into your documents.

7.1.3. JavaScript in PDF documents

JavaScript is a scripting language that is primarily used to add client-side functionality to an HTML page and to create dynamic websites. It allows programmatic access to objects within the web browser. JavaScript is also available in PDF viewers such as Adobe Reader.

There’s a JavaScript API for PDF documents that extends the core client-side JavaScript specification and gives you access to Acrobat and Adobe Reader objects. Initially JavaScript 1.2 was used; since Acrobat 5.0, the API has been based on JavaScript 1.5. The most recent versions of Acrobat and Adobe Reader (since 8.0) use JavaScript 1.6. If you want to know more about the complete set of objects and functions, you can download the PDFs Developing Acrobat Applications Using JavaScript and JavaScript for Acrobat API Reference from the adobe.com site (see appendix B for useful links).

We’re going to use some of the objects listed in those references to learn how to introduce JavaScript in a PDF document using iText.

Document-Level JavaScript

Here is an example of a simple script that clears the JavaScript console window, makes it visible, and writes information about the viewer and its version number.

Listing 7.6. viewer_version.js
console.clear();
console.show();
console.println("Hello");
console.println("You are using: " + app.viewerType);
console.println("The version of " + app.viewerType
+ " is: " + app.viewerVersion);

The console is an object that originally wasn’t available in Adobe Reader, only in Acrobat. It was introduced in Adobe Reader 7.0 to report errors and show messages. The script in listing 7.6 prints the value of the viewerType and viewerVersion property of the application (the app object) to the console, as shown in figure 7.2.

Figure 7.2 shows that I opened the document in Adobe Reader version 9.2. You can add the script from listing 7.6 to an existing PDF document.

Figure 7.2. JavaScript Console window

Listing 7.7. AddVersionChecker
PdfReader reader = new PdfReader(HelloWorld.RESULT);
PdfStamper stamper =
new PdfStamper(reader, new FileOutputStream(RESULT));
stamper.addJavaScript(Utilities.readFileToString(RESOURCE));
stamper.close();

What you’re doing in listings 7.6 and 7.7 isn’t very elegant. It works, but the addJavaScript() method should only be used to add JavaScript functions that can be called from a JavaScript action.

Javascript Actions

In chapter 4, you created a day-to-day overview of all the movies that are screened at the festival. The movies are sorted by date and time, but suppose you’d like to offer functionality that allows the end user to search for the occurrence of a specific director in the document. See figure 7.3.

Figure 7.3. Search window in Adobe Reader

This can be achieved with the search object.

Listing 7.8. find_director.js
function findDirector(name) {
if (search.available) {
search.query(name, "ActiveDoc");
}
else {
app.alert("The Search plug-in isn't installed.");
}
}

Note that you first check for the availability of the Search plug-in, because you mustn’t assume that the plug-in is installed in every PDF viewer. If the plug-in is missing, you use the app.alert() method to inform the end user that searching for directors won’t work. This method is similar to the alert() method in plain JavaScript. The viewer application opens an alert box showing the String that is passed to the method.

 

FAQ

Why are some of the methods I’ve found in the API documentation not working? If you look for the query() method in the API documentation provided by Adobe, you’ll see that the method is marked with a red “S”. This means that the usage of the method can be restricted because of security reasons. Some methods only work in the full Acrobat application, not in the free Reader. Or, they are only supposed to work when the document is certified (see chapter 12), or reader-enabled (see chapter 8). Please check the JavaScript for Acrobat API Reference before reporting problems with JavaScript.

 

You can call the method shown from an action.

Listing 7.9. FindDirectors

In this listing, you add the function findDirector(name) as document-level JavaScript . You use that function in the first document in actions triggered when the end user clicks on the name of a director .

You’ll use more JavaScript later on, when you create bookmarks and we talk about annotations and forms.

7.1.4. More actions

The PdfAction object provides more actions—for instance, an action to trigger an action in a Flash application that is embedded in the PDF—but that will be discussed in chapter 16. In this section, we’ll look at launch actions. We’ll also talk about creating a chain of actions and about triggering actions with events.

Launch Actions

According to ISO-32000-1, you can start an application from a PDF file with a launch action. Here is a harmless example of a clickable Paragraph that will open Notepad on a Windows OS showing a simple text file, test.txt, that is supposed to be present in the directory C:itext-coreook esources xt.

Listing 7.10. LaunchAction
Paragraph p = new Paragraph(new Chunk(
"Click to open test.txt in Notepad.")
.setAction(new PdfAction("c:/windows/notepad.exe",
"test.txt", "open", "C:\itext-core\book\resources\txt")));

Recent versions of Adobe Reader show a warning or even disallow this functionality, because executing an external program from a PDF file can be a security hazard. You’ll use the example created with listing 7.10 in section 13.3.2, where you’ll learn how to remove launch actions from existing PDF files. The example in chapter 13 is used on a mail server—PDFs that are attached to mail messages are checked for launch actions, and if such an action is found, it’s replaced with an app.alert() JavaScript action using iText.

Suppose that you don’t want to start an external program, but you want different actions to be executed one after the other. That sounds like programming, but in PDF it’s called chaining actions together.

Chaining Actions

Chaining actions is done with the next() method. Let’s reuse the timetable, and add the text “print this page” to every page. If an end user clicks these words, you’ll cause three actions to be triggered.

Listing 7.11. PrintTimeTable

First, you want to show an alert box saying, “Think before you print!” . Then you want the Print dialog box to open with only the current page selected . That can be done using this JavaScript function.

Listing 7.12. print_page.js
function printCurrentPage() {
var pp = this.getPrintParams();
pp.firstPage = this.pageNum;
pp.lastPage = pp.firstPage;
this.print(pp);
}

In this context, this refers to the Doc object of the current document. You ask the document for its printer parameters, and you ask it to print itself using a slightly altered print range. The Print dialog box will open with that range already selected. The end user can still decide whether or not to print the document. Once the user closes the dialog box, they’ll be invited to visit the “How you can save paper” web page from the World Wide Fund for Nature (WWF).

These three actions happen one after the other because the user clicked a link, but actions can also be started when something happens: they can be triggered by an event.

Events Triggering Actions

In listing 7.7, you used document-level JavaScript that was triggered when the document was opened. But opening a document is an event—it would have been better to trigger the action to open the console from such an event.

If you look at the first day of the Foobar Film Festival on the timetable, you’ll see that there’s only one movie that isn’t reserved for the press: the opening movie. But you know from figure 3.1 that the tickets for this movie are sold out. People consulting the timetable will be disappointed if they’re looking to buy tickets. To avoid this, you can use an open action to tell the viewer application to jump to the second page immediately. That way, the end user can still navigate to the first page, but only after having seen the second page.

Furthermore, you’ll repeat the Think before you print” message upon printing, and tell the user to “Think again next time!” afterwards. Upon closing the document, you’ll wish the user a good festival.

Listing 7.13. EventsAndActions

The setOpenAction() method is specific ; the corresponding action is triggered when a user opens the document. With the setAdditionalAction() method , you can couple an action to the following events:

  • WILL_PRINT The action is triggered just before printing (part of) the document.
  • DID_PRINT The action is triggered just after printing.
  • WILL_SAVE The action is triggered just before saving the document.
  • DID_SAVE The action is triggered just after saving the document.
  • DOCUMENT_CLOSE The action is triggered just before closing the document.

Note that the WILL_SAVE and DID_SAVE actions aren’t triggered if you perform a Save As operation. This implies that these additional actions will only work in the full Acrobat or for Reader-enabled documents.

You can also define page actions . In listing 7.13, you tell the end user that the information on the first page may not be useful; when the user leaves the page, you display the message, “You can buy tickets for all the other days.” This is done with the setPageAction() method and one of these values:

  • PAGE_OPEN The action is triggered when you enter a certain page.
  • PAGE_CLOSE The action is triggered when you leave a certain page.

This method exists for both PdfWriter and PdfStamper. The only difference is that with PdfStamper, you have to pass a page number to tell iText which page you want the action added to.

Before we move on to discuss bookmarks, let me repeat what I wrote in the FAQ entry: just because an action works in one context doesn’t mean it will work in another. In the chained action, for instance, you added a URL action to open a page on the WWF site. This is an example of an action that will be ignored when triggered automatically. That’s good practice. The document should only open a URL when the end user actively clicks a link, or a bookmark, as you’ll find out in the next example.

7.2. Adding bookmarks

In PDF language, we often use the terms outline tree or outlines as synonyms for bookmarks. In chapter 2, you created bookmarks automatically by using Chapter and Section objects. The result was nice, but you can do better if you create the outline using PdfOutline objects. The PdfOutline class offers much more functionality, and you can use it to create bookmarks for existing documents.

Let’s start using them in a document that’s created from scratch.

7.2.1. Creating bookmarks for a new document

Take a look at figure 7.4. The bookmarks consist of movie titles printed in bold; one of the movie titles is shown using Korean characters. If you click one of these titles, you’ll jump to the movie in the document. For every movie, there’s also a bookmark shown in blue to the corresponding site on IMDB. If you click the Instant Info bookmark, an alert window opens showing the year and run length of the movie.

Figure 7.4. Document with bookmarks created using PdfOutline

This is different from what you did before with Chapter and Section objects. These bookmarks aren’t referring to a specific destination in the document; they cause the execution of actions.

Listing 7.14. CreateOutlineTree

The first thing you need is the root of the outline tree. You can get this with the getRootOutline() method . During the creating of the document, you can use this root PdfOutline to create children .

The constructor of the PdfOutline class accepts four parameters:

  • The parent—Another PdfOutline object of which the newly created bookmark is a kid.
  • A destination or an action—A PdfDestination if you want to add a local goto link, or a PdfAction object for any other action.
  • A title for the bookmark—This can be a Paragraph, a String, or even a PdfString.
  • A boolean value open (optional)—Indicates whether the outline has to be open (the default) or closed when the user opens the bookmark panel.

In listing 7.14, you create a PdfDestination to create a traditional bookmark that jumps to the vertical position just before you add a movie title. The zoom factor will be adapted so that the complete horizontal width is visible (look for FITH in table 7.1). Because you are creating the document from scratch, you don’t have to pass the page number; iText uses the reference to the current page.

 

Note

When adding basic building blocks to a document, you normally don’t have to bother about pagination or the current Y position. But if you want to create a PdfDestination object, you need to know the vertical position. You can retrieve this coordinate with the getVerticalPosition() method. This method doesn’t just “get” the Y value. It can also ensure that you get the position of the next line. That’s why you pass the boolean value true in listing 7.14.

 

In listing 7.14, you’re using actions to create outlines that are added as children of the movie title bookmark. serves as a link to IMBD using a URL action. adds a JavaScript action. The script consists of a single line that can be found in the INFO String:

app.alert('Movie produced in %s; run length: %s'),

The String will be formatted for each movie so that %s is replaced with the year and duration found in your database.

 

Note

You can use a Paragraph for the bookmark title, but the style of the Paragraph object will not be taken into account. You can change the style only with the methods setStyle() and setColor() . Observe that Unicode characters are accepted. In listing 7.14, the title of the movie “3-Iron” is replaced with the characters for “Bin-Jip” (which is the original Korean title of this excellent movie).

 

You’ve now created a new PDF document with bookmarks, but this part of the book is mainly about manipulating existing documents. Suppose that you receive a document like the one you’ve just created—how can you retrieve the bookmarks? That’s what we’re going to look at in the next example.

7.2.2. Retrieving bookmarks from an existing document

In section 7.1.1, you used the SimpleNamedDestination class to retrieve the named destinations from a document in the form of a HashMap or an XML file. Here you use a similar object to extract the bookmarks from an existing PDF: SimpleBookmark.

Listing 7.15. CreateOutlineTree

You first obtain a List of HashMap objects. Each HashMap item contains at least one of the keys listed in table 7.2.

Table 7.2. Possible keys for a bookmark entry

Key

Value

Description

Title String The bookmark title that is used in the outline tree.
Color Three float values Color values for red, green, and blue ranging from 0 to 1, defining the color of the title.
Style String Can be empty, "bold", "italic", or "italic bold". Defines the style of the title.
Open boolean If true, the bookmark is open, showing its kids. If false, the end user has to click the + sign in front of the bookmark to see the bookmarks of the sublevel.
Kids List A list with the Maps of the sublevel entries of this bookmark.
Action String Can be "GoTo", "GoToR", "URI", or "Launch". Due to their possibly complex nature, JavaScript actions aren’t shown.
Page String A destination on a page; see table 7.1 for the syntax; this entry occurs in combination with GoTo and GoToR actions.
Named / NamedN String The name of a named destination; this entry occurs in combination with GoTo and GoToR actions. Named is used when the name is stored as a PdfString; NamedN is used for PdfNames.
File String A path to the file to open or execute; this entry occurs in combination with GoToR and Launch actions.
NewWindow boolean Indicates whether the file to be opened must be opened in a new window; this entry occurs in combination with the GoToR action.
URI String The URL that will be opened if the end user clicks the bookmark. This entry occurs in combination with an URI action.

You can export the bookmarks list to an XML file (in listing 7.15) using the Latin-1 encoding (see the "ISO8859-1" parameter), accepting only ASCII characters (see the true parameter). The resulting XML file looks like this:

<?xml version="1.0" encoding="ISO8859-1"?>
<Bookmark>
<Title Action="GoTo" Page="1 FitH 806"
Style="bold" >2001: A Space Odyssey
<Title Action="URI" URI="http://imdb.com/title/tt0062622/"
Color="0 0 1" >link to IMDB</Title>
<Title >instant info</Title>
</Title>
<Title Action="GoTo" Page="1 FitH 734"
Style="bold" >&#48712;&#51665;
<Title Action="URI" URI="http://imdb.com/title/tt0423866/"
Color="0 0 1" >link to IMDB</Title>
<Title >instant info</Title>
</Title>
</Bookmark>

You can use table 7.2 to interpret this XML file. The root tag is always named Bookmark. The Title tags are used for its children. There’s no Kids tag; the entries of the Kids list are nested Title tags. All other key-value pairs are attributes of the Title tag.

You can also use table 7.2 as a reference to create new bookmarks for an existing document.

7.2.3. Adding bookmarks to an existing document

Suppose that you want to add bookmarks to the timetable PDF, as in figure 7.5.

Figure 7.5. Bookmarks added to an existing document

There’s a top-level entry in the Bookmarks list for this timetable with the title “Calendar”. Nothing happens if you click it; it’s just a structural element with eight children: one for each festival day. If you click one of these dates, the corresponding page is opened. You can create this outline tree and add it to an existing document using PdfStamper.

Listing 7.16. BookmarkedTimeTable

You can use table 7.2 to create Maps with titles, kids, and actions. You can consult table 7.1 to create the destination for the Page value. You can use the setOutlines() method to add the bookmarks to the stamper object. This also works for PdfCopy.

You’ve concatenated documents with bookmarks in chapter 6, and found that all your bookmarks were lost. We’ll see how to fix this in the next example.

7.2.4. Concatenating documents with bookmarks

For this example, you’ll take the timetable PDF you created in the previous example, and concatenate it with the MovieHistory document you created in chapter 2. Both documents have bookmarks, and you want these bookmarks to be merged as shown in figure 7.6.

Figure 7.6. Concatenated documents with concatenated bookmarks

In this code sample, we concatenate bookmarks, but in listing 6.21, we concatenated documents.

Listing 7.17. ConcatenateBookmarks

Just like in the PdfStamper example, you create an ArrayList for the bookmarks. You could start adding new entries, but for this example you’ll get the bookmarks from the existing documents. These bookmarks will work correctly for the first document, but a bookmark that points at the first page in the second document won’t. It will point to the first page of the first document in the concatenated PDF. That’s why you need to shift the page numbers using the shiftPageNumbers() method . After that’s done, you add the bookmarks to the new list. You use setOutlines() to inject the new list of bookmarks into the new document.

One of the parameters in was null. That’s because you want to shift the page numbers of the bookmarks for all the pages. You can also pass an array with an even number of int values that define page ranges for which you want to shift the page numbers with the offset defined by the page_offset parameter. There’s also an eliminatePages() method that can be used to remove the bookmarks that point at specific page ranges. That’s an interesting method if you want to split existing documents or remove a couple of pages.

Now that you know everything there is to know about bookmarks in PDF, what about creating bookmarks in HTML? With listing 7.3, you retrieved named destinations; with listing 7.15, you retrieved bookmarks. Wouldn’t it be nice if you could use that information to create a URL that can be used to open a PDF file in a browser at a specific position? The next section will give you an overview of the open parameters that can be used to achieve this.

7.2.5. Open parameters

When starting Adobe Reader from the command line, you can pass an open action (/A) with different parameters. When opening a PDF in a browser using a URL, you can achieve the same result by adding parameters after the # sign.

The following line called from the command line opens the JavaScript API documentation on page 28:

AcroRd32.exe /A "page=28=OpenActions" c:/downloads/js_api_reference.pdf

The following URL opens the documentation about open actions on page 5 using a magnifying factor that ensures that the complete page fits within the viewer window:

Table 7.3 lists the parameters that can be passed with the /A option, or that can be added to a URL, involving named and explicit destinations. For more open parameters, consult the Open Parameters for PDF document that can be found on the Adobe site (see appendix B for the URL).

Table 7.3. Overview of the open parameters

Parameter and value

Description

nameddest=name Jumps to a named destination with name name in the PDF.
page=pagenum Jumps to the page with page number pagenum. This number indicates the actual page, not the label you may have given the page.
zoom=scale zoom=scale,left,top Sets the zoom and scroll factors. A scale value of 100 gives 100 percent zoom; left and top are set in a coordinate system where the origin is the top left of the visible page, regardless of the document rotation.
view=fit view=fit,parameter Sets the zoom factor based on the page size. The value for fit can be Fit, FitH, FitV, FitB, FitBH, or FitVH. The parameter has the same meaning as described in table 7.1. This isn’t supported with the command-line option.
viewrect=left,top, width,height Opens the file so that the rectangle specified with the parameters is visible. This isn’t supported with the command-line option.

You’ve learned about destinations; you’ve learned about actions. You’ve used both with Chunks; you’ve used both with PdfOutlines. But what really makes a document interactive is annotations.

7.3. Creating annotations

According to Merriam-Webster’s Online Dictionary, an annotation is a note added by way of comment or explanation. But an annotation in a PDF can be much more. It can be a movie or a sound that will be played in the document. It can be a field with a value that changes depending on other fields. It can be a shape that changes color if you move over it with the mouse.

But let’s not get ahead of ourselves; let’s start with the simplest type of annotation: a text annotation aka a sticky note.

7.3.1. Text annotations

Figure 7.7 shows two documents with text annotations. The document in the back has small icons in the form of a note; the document in the front has small text balloons. If you move your mouse over such an icon, a tooltip appears. Double-click the icon, and a sticky note window appears. That’s an open text annotation.

Figure 7.7. Text annotations containing more info about a movie

The text annotations in the back are created using the Annotation class.

The Annotation Class

The Annotation class implements the Element interface (see chapter 2), so you can add it to a Document using the add() method. The annotation will be added at the current cursor position in the document.

Listing 7.18. MovieAnnotations1

You create a text annotation using two String values: a title and the contents of the annotation. Note that the functionality of the Annotation class is rather limited. If you want to change the appearance of the annotation, such as to change the color of the open note, you need a more specialized class.

The Pdfannotation Class

PdfAnnotation has a series of static methods that create specific types of annotations. The method createText() is used to create a text annotation.

Listing 7.19. MovieAnnotations2

The createText() method expects six parameters:

  • PdfWriter writer—An instance of PdfWriter; if you’re using PdfStamper, you can use the stamper.getWriter() method to obtain a writer object.
  • Rectangle rect—The rectangle where you want the annotation to appear. In the example, you don’t know in advance where the annotation will be added, so you pass null. The rectangle will be defined as soon as iText renders the Chunk to which the annotation will be added.
  • String title—A title for the annotations.
  • String contents—The content of the text annotation.
  • boolean open—A Boolean value indicating whether the annotation should be open (true) or closed (false).
  • String icon—The icon that should be used. Possible values are "Comment", "Key", "Note", "Help", "NewParagraph", "Paragraph", and "Insert".

Figure 7.8 illustrates the different types of icons you can use for text annotations. The lower half of this figure shows the comments panel, which gives an overview of all the text annotations present in the PDF document. It was opened by clicking the text balloons at the left in the Adobe Reader window.

The annotations in figure 7.8 were added to the document using a generic tag event (see section 5.2.1). In this case, I used the addAnnotation() method to add the PdfAnnotation object to the PdfWriter. That’s the most common way to add annotations.

Figure 7.8. Different types of icons for text annotations

A Generic Way to Create Annotations

In the subsections that follow, you’ll discover several types of annotations that are supported in iText. You’ll use convenience methods or classes to create link, stamp, line, and other annotations. In chapter 16, we’ll discuss different types of rich media annotations.

If this still doesn’t meet your requirements, there’s a more generic way to create an annotation—any type of annotation.

Listing 7.20. GenericAnnotations

In , you create a generic annotation using a PdfWriter instance and a Rectangle. In , you tell iText that you want to create a text annotation.

Suppose some new type of annotation is invented; for instance, a "Foobar" annotation. You could create such an annotation like this:

annotation.put(PdfName.SUBTYPE, new PdfName("Foobar"));

PdfAnnotation is a subclass of PdfDictionary. Just like PdfAction, it’s a collection of key-value pairs. The type of the annotation is defined by the /Subtype key. The other entries of the annotation dictionary depend on this value.

 

Note

You can find references to the annotation dictionaries in the /Annots entry of the page dictionary. Visible annotations will always be rendered on top of the other content. They aren’t part of the content stream of a page.

 

A number of keys, such as the title, will be present in many different types of annotations. That’s why you’ll find methods such as setTitle() in the API of the PdfAnnotation class . For more exotic annotations, such as the (imaginary) Foobar annotation, you’ll have to look at its (future) specification, and add PdfName and PdfObject pairs using the put() method .

 

Note

There’s a complete range of annotations that are used to visualize text edits: Caret annotations, StrikeThrough annotations, Squiggly annotations, and so on. These annotations are typically added by a human reader using Acrobat. You could add these annotations using iText, but that sort of defeats the purpose of the library: iText is better suited to adding annotations in an automated process. The examples that follow are based on my experience and they’re selected to inspire, rather than to give a complete taxonomy.

 

For more info about annotations, please read section 12.5 of ISO-32000-1. There’s a complete overview of all the possible annotations in section 12.5.6 of the ISO specification.

Adding Annotations to an Existing PDF

Suppose you wanted to provide the timetable online so that people can print it, but you want to add extra information that won’t be printed. This is shown in figure 7.9.

Figure 7.9. Text annotations added to the existing timetable

To create the timetable in figure 7.9, you’ll use createText() once more using the Help icon. With the setColor() method, you can make sure that the color of the annotation corresponds with the festival category.

Listing 7.21. TimeTableAnnotations1

In the previous example, you used the PdfWriter.addAnnotation() method to add the annotation to the current page. When you add annotations to an existing page, you need to use the PdfStamper.addAnnotation() method, and you have to specify the page number of the page to which you want to add the annotation.

In this section, text annotations were used to explain different annotations mechanisms: how to create them, how to add them to newly created PDFs, and how to add them to existing PDFs. Now let’s have a look at other types of annotations.

7.3.2. Link annotations

This isn’t the first time you’ve worked with annotations. Whenever you created links—for instance, using the Anchor class (see section 2.3.1) or using the Chunk method setAction() (see section 7.1.1)—you were creating a link annotation. In the next couple of examples, you’ll create a clickable image and add clickable rectangles to the timetable.

Clickable Images

If you compare figure 7.10 with figure 3.17, there’s one major difference: when you move your mouse over a movie poster on the PDF that’s shown in figure 7.10, a tooltip appears, revealing the URL of the corresponding movie on IMDB.

Figure 7.10. Link annotations have been added to all the images; see, for instance, the link to Donnie Darko at IMDB

If you click the movie poster, Adobe Reader will ask you if you want to open that page in a browser window.

Listing 7.22. MoviePosters1

You use the simple Annotation class to create the link annotation. The first four parameters are meant to pass the coordinates for the clickable area, but listing 7.22 shows how you can make the image clickable using the method Image.setAnnotation(), so you don’t have to worry about coordinates. iText will set these parameters so they correspond with the image location.

Adding Clickable Areas to an Existing Document

You can adapt listing 7.21 so that instead of adding text annotations with extra info about each movie, it adds a link annotation that corresponds with each screening.

Listing 7.23. TimetableAnnotations2

Just as with createText(), you need to pass a PdfWriter and a Rectangle object to the createLink() method. The third parameter should be one of the following values:

  • HIGHLIGHT_NONE No highlighting (the default). The links created with Anchor, Chunk.setAnchor(), or Chunk.setAction() aren’t highlighted when you click them.
  • HIGHLIGHT_INVERT Inverts the content of the annotation square when clicked. That’s what is used in listing 7.23. If you click a movie block, the colors will be inverted.
  • HIGHLIGHT_OUTLINE Inverts the annotation border when clicked.
  • HIGHLIGHT_PUSH Displays the annotation as if it were being pushed below the surface of the page.

The destination of the link annotation can be a PdfAction, as in listing 7.23, a String for a named destination, or a PdfDestination for an explicit destination.

Now let’s continue our overview of “iText’s most popular annotations” with file attachments.

7.3.3. File attachments

The document shown in figure 7.11 is almost identical to the documents shown in figure 7.7, but the annotation is visualized as a paperclip. This paperclip indicates that an annotation with subtype /FileAttachment was added.

Figure 7.11. Movie list with file attachments

If you click the paperclip icon in the left sidebar of Adobe Reader, you can get an overview of all the files that are attached to the PDF. As you can see, the attachments are a series of JPEG images with names in the form img_xyz.jpg, where xyz is the primary key of a movie at IMDB. If you click a paperclip next to one of the movie titles, the poster of that movie will be opened using the default image viewer on your computer. Note that, depending on your security preferences, the PDF viewer may ask you if you’re sure before opening another application. How was this PDF document created?

Listing 7.24. MovieAnnotations3

There are different ways to embed files. In section 16.2, you’ll learn how to create document-level attachments and portable collections. For now, you’ll create a file attachment annotation using the createFileAttachment() method and the following parameters:

  • PdfWriter writer—An instance of PdfWriter.
  • Rectangle rect—The rectangle where you want the annotation to appear.
  • String contents—A description for the file attachment.
  • byte[] fileStore—The bytes of the file you want to attach to the PDF, or null if the file parameter is a valid path to a file.
  • String file—The path to the file you want to attach. This path will be ignored if fileStore isn’t null.
  • String fileDisplay—The (new) filename that will be given to the attached file.

In , you set the name of the attachment to Paperclip. If you remove this line, every paperclip shown in figure 7.11 will be replaced by a pushpin, which is the default appearance of file attachments. Possible values for the name are "PushPin", "Paperclip", "Graph", and "Tag".

Let’s finish this section with an example that combines three different types of annotations.

7.3.4. Stamp, line, and rectangle annotations

Rubber stamp annotations are intended to look as if they were stamped on the page with a rubber stamp: “Approved”, “Confidential”, “Draft”. In this section, you’ll use the stamp named “NotForPublicRelease” to stamp an annotation on the press visions.

As soon as you start selling tickets for the Foobar Film Festival, you’ll have to update your timetable to inform customers which screenings are sold out. In this example, you’ll use a line annotation to strike a white line through those screenings and a rectangle annotation with a dashed border to indicate for which screenings there are still tickets available. There are examples of each of these annotation types in figure 7.12.

Figure 7.12. Stamp, rectangle, and line annotations added to an existing document

The screening of the movie Leaving Las Vegas on October 18 is sold out. Just as with text annotations, this information is shown when you move your mouse over the annotation. The annotation for the movie Amores Perros is open, because it’s been double-clicked. The press screenings have an X stamped over them; they are not open to the public.

Listing 7.25. TimetableAnnotations3

  1. If you print the PDFs created in the previous examples, you won’t see any of the annotations you’ve added showing up on paper. By using setFlags() with the FLAGS_PRINT flag in this example, you’ve made the stamp, line, and rectangle visible when printed. Note that this doesn’t include the sticky note. For instance, the opened annotation saying that there are tickets available for Amores Perros won’t be printed; only the dashed lines of the rectangle will be.
  2. The width of the media box of a timetable page is smaller than its height, but you define the annotation rectangle as if the page is in landscape orientation, because the rotation of the page is 90 degrees. iText transforms this rectangle internally to make sure it’s in the right place. To create a line annotation (or other annotations involving coordinates, such as /Polygon and /PolyLine annotations), however, you need to provide extra coordinates. In this case, you have to perform the transformation yourself if the media box is rotated.
  3. You can change the border of the annotation using the setBorderStyle() method. Using the BorderStyleDictionary object, you can create different border styles.
  4. You can also change the border using the setBorder() method. This method expects an object of type BorderArray.
    Note

    The effect of and depends on the type of annotation, and even on the viewer that is used. You’re not creating an appearance using graphics state operators and operands in this example; it’s up to the viewer application to decide how to render the annotation.


    We could go on with more types of annotations and more properties, but this isn’t the place or time to do so. This book doesn’t replace the PDF reference or ISO-32000-1, and if we want to create interactive forms in the next chapter, we need to do a little bit of JavaScript programming first.

7.4. JavaScript programming in PDF

You’ve already introduced JavaScript into your PDF files in section 7.1.3. You’ve added document-level JavaScript, and you’ve created JavaScript actions triggered from a link annotation or an event. But that isn’t the whole story. You didn’t know you were using annotations at the time. Now that you do, you can do more cool things. At the end of this section, you’ll even create a calculator application in a PDF. Along the way, you’ll meet new types of annotations.

7.4.1. Triggering JavaScript from a button

The next chapter will be dedicated entirely to interactive forms. Forms consist of fields, and fields are visualized using a special type of annotations: widget annotations. In this section, we won’t create a fillable form yet, but we’ll experiment with the interactive features of widget annotations. In a first example, we’ll stamp the timetable PDF once again. We’ll add two buttons at the bottom of the page to trigger menu actions.

Listing 7.26. ButtonsActions

You’re no longer using a static method of PdfAnnotation to create an instance. Instead, you use a convenience class named PushbuttonField. This class allows you to define the layout of the annotation in a programmer-friendly way. Once you’re done, you use getField() to obtain the corresponding PdfAnnotation object, and you can add a JavaScript action.

The JavaScript method execMenuItem() executes a menu item in Adobe Reader. In this case, clicking the buttons will have the same effect as if the user selected File > Save a Copy, and File > Attach to Email.

You’ve created buttons with a layout that only contained text (LAYOUT_LABEL_ ONLY). In the next example, we’ll look at how to introduce icons.

7.4.2. Showing and hiding an annotation

Do you also hate the aggressive advertisements that prevent you from viewing a web page unless you click a button to make them disappear? If that’s the case, I have bad news for you.

I once talked to a manager responsible for a large newspaper group in a very small country (Belgium). I bragged that The New York Times had used iText to publish newspaper archives on the internet in the form of PDF documents. The manager said: “Well, that’s nice, but we can’t do that. We have to make money with our content, and it’s almost impossible to add ads to a PDF file. We can only do that in HTML.”

I immediately created the PDF shown in figure 7.13 to prove him wrong.

Figure 7.13. An advertisement that can be clicked away, added to an existing document

You’ll recognize the original PDF from chapter 4, but I’ve added a shameless ad to promote this book on top of the existing content. The user can make it disappear by clicking the top bar. The ad consists of two button fields.

Listing 7.27. Advertisement

In this example, you’re creating button fields with a label and an icon. You can use an Image object for the icon , or a PdfTemplate (in this case, you’re using an imported page). I’ve used this functionality in a real-world project to create online examinations. Every question had a button that allowed the student to get a hint. If that button was clicked, an annotation was made visible and a hidden field was set. The value of this hidden field was posted together with the answers, so that the tutor could see for which questions a hint was used.

Some very simple JavaScript is used to hide (or reveal) the fields (or annotations) . You get a field instance with the getField() JavaScript method for interactive fields, or with getAnnot() for ordinary annotations. Then you change the properties of these objects as explained in the JavaScript reference. In this example, clicking the upper button (named click) hides both buttons. Clicking the lower button (named advertisement) opens the web page dedicated to this book at Manning.com.

Pushbuttons aren’t always meant to be pushed (or clicked). In the next example, we’ll use pushbuttons as “hot areas” that trigger an action when the mouse moves over them.

7.4.3. A popup triggered by a button that doesn’t need to be pushed

A popup annotation has no appearance stream or associated actions of its own. It’s always associated with a parent annotation. Figure 7.14 shows a text annotation as a popup. If you take a close look at the image, you’ll also see a widget annotation on top of the Donnie Darko poster. If you move the mouse inside the borders of this widget annotation, the popup with the text annotation will appear; if you move the mouse pointer outside the widget annotation, the popup will disappear.

Figure 7.14. Text annotation in a popup using a button and its events

Here is the mechanism behind this custom-made tooltip.

Listing 7.28. MoviePosters2

  1. You create a text annotation for every movie. Each annotation has a unique name, IMDBxyz, where xyz is a key in IMDB. You also set the annotation flags so that the text area will be read-only and hidden.
  2. You create a popup annotation using a rectangle that’s slightly different from the original one for two reasons: it must be big enough so that it can show the text without scrollbars, and it shouldn’t cover the entire movie poster.
  3. You define the text annotation as the parent of the popup annotation. You tell the text annotation that the popup annotation is ... its popup.
  4. You add both annotations to the stamper—or to the writer if you’re creating a document from scratch. So far, the text annotation isn’t visible.
  5. You create a PushbuttonField and obtain the widget annotation using getField().
  6. You add JavaScript actions to the widget annotation as additional actions.
  7. You add the widget annotation to the stamper. This listing doesn’t define a “push” action for the button, because you’re not looking to trigger an action when the user clicks the button. Instead you’ve defined two additional actions. One action is triggered when the mouse enters the area defined by the button:
    public static final String JS1 =
    "var t = this.getAnnot(this.pageNum, 'IMDB%1$s'), t.popupOpen = true; var w = this.getField('b%1$s'), w.setFocus();";

Another action is triggered when the mouse exits the area defined by the button:

public static final String JS2 =
"var t = this.getAnnot(this.pageNum, 'IMDB%s'), t.popupOpen = false;";

In these snippets, you set the popupOpen property of the text annotation to true or false depending on the additional action type you want. When the popup is opened, you also set the focus to the corresponding button field.

Enter (“E”) and exit (“X”) aren’t the only additional actions that can be defined for widget annotations.

7.4.4. Additional actions

Table 7.4 lists the annotation events that are available. Most of these additional actions are used to enhance the user experience while filling out a form. In the next chapter, you’ll use JavaScript to change the content of a text field to uppercase, to validate a date field, and so on. But we’ll finish this section as promised: with a PDF calculator application written in JavaScript.

Table 7.4. Additional actions of an interactive form field

Entry

PdfName

Meaning

The action will be performed when...

PdfName.E /E Enter ... the cursor enters the annotation’s active area.
PdfName.X /X Exit ... the cursor exits the annotation’s active area.
PdfName.D /D Down ... the mouse button is pressed inside the annotation’s active area.
PdfName.U /U Up ... the mouse button is released inside the annotation’s active area.
PdfName.FO /Fo Focus ... the annotation receives the input focus.
PdfName.BL /Bl Blurred ... the annotation loses the input focus.
PdfName.K /K Keystroke ... the user modifies the content of the field using a keystroke, or by selecting an entry in a list box.
PdfName.F /F Formatted ... the content of the field is about to be formatted.
PdfName.V /V Validate ... the field value has changed. This action can check the new value for its validity.
PdfName.C /C Calculate ... the field needs to be recalculated.
PdfName.PO /PO Page open ... the page containing the annotation is opened. The action will be performed after the open action of the document and after the PAGE_OPEN action if such an action is defined (see section 7.1.4).
PdfName.PC /PC Page close ... the page containing the annotation is closed. The action will be performed before the PAGE_CLOSE action.
Pdfname.PV /PV Page visible ... the page containing the annotation becomes visible in the viewer application’s user interface.
PdfName.PI /PI Page invisible ... the page containing the annotation is no longer visible in the viewer application’s user interface.

7.4.5. A PDF calculator

Figure 7.15 looks like the number pad of a simple calculator. In reality, it’s a PDF file with buttons and text fields.

Figure 7.15. A calculator application in a PDF demonstrating the use of annotations and JavaScript

Listing 7.29 contains the method addTextField(). It creates a text field that shows the “display” of the calculator (named result), and an extra text field (named move). The content of this second text field will change when you move over the button fields.

Listing 7.29. Calculator

There are 17 buttons on the calculator: 10 digits, 4 operators, 1 equals sign, 1 button to clear the display, and 1 button to clear the display and the memory. Observe that listing 7.29 creates different appearances:

  • APPEARANCE_NORMAL This appearance is used when the annotation is not interacting with the user. It’s also used for printing the annotation.
  • APPEARANCE_ROLLOVER This appearance is used when the user moves the cursor into the annotation’s active area without pressing the mouse button.
  • APPEARANCE_DOWN This appearance is used when the mouse button is pressed or held down within the annotation’s active area.

In the calculator application, the buttons are gray when the document is opened. As soon as you move over a button with the mouse, its color is changed to red. When you click the button, it’s blue as long as you keep the mouse button down. This mechanism is often used for link annotations, to show the end user that a specific area can be clicked to go to another page or URL. The different appearances are created the same way you created XObjects in chapter 3.

You’ve used additional actions to change the value of the text field named move. You can find the implementation of the showMove() function in listing 7.30. Clicking a digit triggers the augment() method. The register() method is called when the end user clicks an operator. The calculateResult() method corresponds to the equals sign. The C and CE buttons trigger reset().

Listing 7.30. calculator.js
var previous = 0;
var current = 0;
var operation = '';
function showCurrent() {
this.getField('result').value = current;
}
function showMove(s) {
this.getField('move').value = s;
}
function augment(digit) {
current = current * 10 + digit;
showCurrent();
}
function register(op) {
previous = current;
current = 0;
operation = op;
showCurrent();
}
function calculateResult() {
if (operation == '+')
current = previous + current;
else if (operation == '-')
current = previous - current;
else if (operation == '*')
current = previous * current;
else if (operation == '/')
current = previous / current;
showCurrent();
}
function reset(all) {
current = 0;
if(all) previous = 0;
showCurrent();
}
showCurrent();

You compose a number by clicking the digits. This number is stored in the current variable until you click an operator. Then it’s moved to the previous variable; the operator that was clicked is stored in the operator variable. A new current number is composed. Clicking the equals sign causes the current number to be updated with the previous number using the operator.

This was fun, wasn’t it? Let’s take a look at what we’ve done in this chapter.

7.5. Summary

In this chapter, we’ve explored different interactive features that can be added to a document. You already knew that links are actions that jump to a destination, but now you’ve learned how to create explicit destinations, how to retrieve named destinations from an existing document, and how to preserve links and destinations when concatenating different PDF documents. You found out that there are other types of actions besides link actions; for instance, JavaScript actions. You’ve used this knowledge to create bookmarks. You also learned how to retrieve and manipulate bookmarks in existing documents. For instance, how to combine the bookmarks of different PDF documents that are concatenated.

Then you discovered that the links you’ve been creating are a special type of annotation. You used text annotations to get acquainted with the concept, and you’ve experimented with file attachment, stamp, line, and rectangle annotations. Eventually, you were even able to create an application in a PDF using widget annotations and JavaScript.

In the next chapter, we’ll continue working with these widget annotations, and we’ll discover how they’re related to interactive form fields. Instead of creating a form using Open Office, as you did in the previous chapter, you’ll create a form using iText.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset