Products
PDFKit.NET 4.0
Created
3/4/2014
Tags
Content extraction

This article explains how one can determine which page a particular bookmark refers to.

If one considers the PDFKit.NET Bookmark class, there appears to be no obvious place where it holds page destinations. The reason for this is simply that a PDF Bookmark is not just a true bookmark. Instead, it is an entity that can have various actions associated with it when clicked on. This can be an action that jumps to a particular page, but also an action that executes some javascript code for example. A Bookmark can have a sequence of such actions associated with it, and these will all be executed in order when someone clicks on it.

This means that one will need to inspect the actions in order to determine whether a Bookmark actually jumps to a page, and if so, which one.

Another complicating factor is that there can be multiple ways to perform such a jump. The code below takes into account internal destinations and named destinations. These are the most common cases. It is also possible to jump to a particular page via a javascript action. This is highly unusual though, - and rather complex to detect - so we ignore that possibility below.

private void listBookmarks(BookmarkCollection collection)
{
    foreach (Bookmark bookmark in collection)
    {
       System.Diagnostics.Trace.WriteLine("bookmark: " + bookmark.Title);

       foreach (Action action in bookmark.Actions)
       {
           GoToAction gotoAction = action as GoToAction;
           if (gotoAction != null)
           {
              // Arbitrary actions can be attached to a bookmark, but for references inside
              // a document this is normally a GoToAction with either an InternalDestination
              // or a NamedDestination. The positions of an internal destination may only
              // be partially defined. In that case, the Top, Left, Bottom, and
              // Right values may contain NaN values (Not-A-Number).
              //
              // Please note that it is also possible to attach javascript to a bookmark and
              // that this may execute code that goes to a particular page. This is quite
              // unusual though.

              InternalDestination internalDestination = gotoAction.Destination as InternalDestination;
              if (internalDestination == null)
              {
                 NamedDestination namedDestination = gotoAction.Destination as NamedDestination;
                 if (namedDestination != null)
                 {
                    internalDestination = document.NamedDestinations[namedDestination.Name];
                 }
              }

              if (internalDestination != null)
              {
                Console.WriteLine(
                   string.Format("internal -> page {0}, position ({1},{2})",
                   internalDestination.Page.Index, internalDestination.Left, internalDestination.Top));
              }
           }
       }

       // list sub-bookmarks.
       listBookmarks(bookmark.Bookmarks);
    }
}

Please also note that an internal destination may have undefined values (NaN) for its destination rectangle. Some destinations will have no valid rectangle at all, and will just refer to some page index.