3.5.  Bookmarks and Named Destinations

Overview

Bookmarks are essential for a quick navigation in large PDF documents. The value of a book drops dramatically when the chapters are not available via bookmarks. Use the following tests to ensure that the bookmarks are generated correctly.

// Simple methods:
.hasNumberOfBookmarks(..) 
.hasBookmark()     // Test for one bookmark
.hasBookmarks()    // Test for all bookmarks

// Tests for one bookmark:
.hasBookmark().withLabel(..) 
.hasBookmark().withLabelLinkingToPage(..) 
.hasBookmark().withLinkToName(..) 
.hasBookmark().withLinkToPage(..) 
.hasBookmark().withLinkToURI(..) 

// Tests for all bookmarks:
.hasBookmarks().withDestinations()
.hasBookmarks().withoutDuplicateNames()

We can see bookmarks as starting points and named destinations as the landing points. Named destinations (landing points) can be used by bookmarks and also by HTML links. So, you can jump from a website directly to a specific location within a PDF document.

For named destinations, the following test methods are available:

.hasNamedDestination()
.hasNamedDestination().withName(..)

Named Destinations

The names of named destinations can be tested easily:

@Test
public void hasNamedDestination_WithName() throws Exception {
  String filename = "documentUnderTest.pdf";

  AssertThat.document(filename)
            .hasNamedDestination().withName("Seventies")
            .hasNamedDestination().withName("Eighties")
            .hasNamedDestination().withName("1999")
            .hasNamedDestination().withName("2000")
  ;
}

Because a name also has to work with external links, it may not contain spaces. For example, if a document in LibreOffice has a label "Export to PDF" (which contains spaces) then LibreOffice creates a destination with the label "First2520Bookmark" when exporting it to PDF. A test has to use the escaped value:

@Test
public void hasNamedDestination_ContainingBlanks() throws Exception {
  String filename = "documentUnderTest.pdf";

  AssertThat.document(filename)
            .hasNamedDestination().withName("First2520Bookmark")  1
  ;
}

1

'2520' stands for '%20' and that corresponds to a space.

Existence of Bookmarks

It is easy test to verify the existence of bookmarks:

@Test
public void hasBookmarks() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasBookmarks()
  ;
}

Number of Bookmarks

After testing whether a document contains bookmarks at all, it is worth verifying the number of bookmarks:

@Test
public void hasNumberOfBookmarks() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasNumberOfBookmarks(19)
  ; 
}

Label of a Bookmark

An important property of a bookmark is its label. That is what the reader sees. So, you should test that an expected bookmark has the expected label:

@Test
public void hasBookmark_WithLabel() throws Exception {
  String filename = "documentUnderTest.pdf";

  AssertThat.document(filename)
            .hasBookmark().withLabel("Content on page 3.")
  ;
}

Destinations of Bookmarks

Bookmarks can have different kinds of destinations. A suitable test method is provided for each kind.

Does a particular bookmark point to the expected page number:

@Test
public void hasBookmark_WithLabelLinkingToPage() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasBookmark().withLabelLinkingToPage("Content on first page.", 1)
  ; 
}

Is there any bookmark pointing to an expected page number:

@Test 
public void hasBookmark_WithLinkToPage() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasBookmark().withLinkToPage(1)
  ; 
}

Does a bookmark exist which points to an expected destination:

@Test 
public void hasBookmark_WithLinkToName() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasBookmark().withLinkToName("Destination on Page 1")
  ; 
}

Is there a bookmark pointing to a URI:

@Test 
public void hasBookmark_WithLinkToURI() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasBookmark().withLinkToURI("http://www.wikipedia.org/")
  ; 
}

PDFUnit does not accesses websites, it checks the existence of a link.

And finally, we can check that every bookmark has a destination:

@Test 
public void hasBookmarkWithDestinations() throws Exception {
  String filename = "documentUnderTest.pdf";
  
  AssertThat.document(filename)
            .hasBookmarks().withDestinations()
  ; 
}