Introducing actions Part 1 (iText 5)

If you’re reading this topic from beginning to end, actions shouldn’t be new to you. You created documents containing actions in topic 2, but we didn’t call them actions; instead we talked about remote and local goto links. In this section, you’ll add go to 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.

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 (^).

Timetable with named actions triggered by clicking the arrows


Figure 7.15 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

Listing 7.1 NamedActions.java 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 topic 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 setRemote-Goto() and setLocalGoto() with setAction().

Listing 7.2 LinkActions.java

Listing 7.2 LinkActions.java 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 O. 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:

tmp89-51_thumb

 

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:

tmp89-53_thumb

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 topic 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 Q 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

Listing 7.3 LinkActions.java

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:

tmp89-55_thumbtmp89-56_thumb

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 3 6 8 02 0 and 19 XYZ 36 802 0 need to be explained in more detail. They refer to explicit destinations.

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

tmp89-57 tmp89-58

The current page is displayed with its contents magnified just enough to fit the document window, both horizontally and vertically.

tmp89-59 tmp89-60

The current page is displayed magnified just enough to fit the bounding box of the contents (the smallest rectangle enclosing all of its contents).

tmp89-61 tmp89-62

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.

tmp89-63 tmp89-64

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.

tmp89-65 tmp89-66

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.

tmp89-67 tmp89-68

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.

tmp89-69 tmp89-70

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.

tmp89-71 tmp89-72

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 SimpleNamedDesti-nation 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 topic 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

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() O- 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:

tmp89-74_thumb[1]

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

Listing 7.5 TimetableDestinations.java

Listing 7.5 TimetableDestinations.java Listing 7.5 TimetableDestinations.java

This is a rewrite of the first example in this topic. 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 Simple-NamedDestination class. This class uses reader.getNamedDestination() to get the named destinations. You could use this method too, but you’d get entries like this:

tmp89-77_thumb

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.

Next post:

Previous post: