Document Outline Navigate interactively from one part of the document to another.

Introduction

A PDF document may contain a document outline, sometimes called bookmarks, that may allow the user to navigate from one part of the document to another. The outline consists of a tree-structured hierarchy of items to which individual actions could be attached. Commonly these actions will enable the user to navigate in the document.

The whole tree is configrable and manageable through the SetaPDF-Core component. 

Get an Instance of the Outlines Class

To get access the outline of a document, the SetaPDF-Core component offers a helper method on the catalog instance of a document. The method will return an instance of SetaPDF_Core_Document_Catalog_Outlines, which represents the root outline dictionary of a document: 

PHP
$outlines = $document->getCatalog()->getOutlines();

Navigate through the Outline

As already written the Outline consists of a tree-structure hierarchy of items. These items are represented by SetaPDF_Core_Document_OutlinesItem instances.

Internally the items are structured in a linked list, while the root of the list is represented by a SetaPDF_Core_Document_Catalog_Outlines instance. 

To get access to existing items of a bookmark outline, the root dictionary, a SetaPDF_Core_Document_Catalog_Outlines instance, offers common methods to start with:

__construct()

The constructor.

cleanUp()

Release memory / Cycled references.

getDocument()

Get the document instance.

getFirstItem()

Get an item instance of the item referenced in the 'First' key.

getLastItem()

Get an item instance of the item referenced in the 'Last' key.

Both methods are also available in the item instances. Unfortunately the item instances offer much more methods to navigate though the linked list:

getNext()

Get an item instance of the item referenced in the Next key.

getParent()

Get an item instance of the item referenced in the Parent key.

getPrevious()

Get an item instance of the item referenced in the Prev key.

hasFirstItem()

Checks if this item has a 'First' value set.

It is possible to walk through the linked list manually or by using implemented interfaces. The root node class implements the IteratorAggregate and ArrayAccess  interfaces. The outline items implement the RecursiveIterator and ArrayAccess interfaces: 

PHP
// get the outlines
$outlines = $document->getCatalog()->getOutlines();

// iterate manually over the root items
$item = $outlines->getFirstItem();
while ($item !== false) {
    echo $item->getTitle() . "\n";
    $item = $item->getNext();
}

// iterate over an outline:
$iterator = $outlines->getIterator();
foreach ($iterator AS $outline) {
    echo str_repeat(' ', $iterator->getDepth() * 4);
    echo $outline->getTitle() . "\n";
}

// ArrayAccess
echo $outline[0]->getTitle() . "\n";
echo $outline[0][1]->getTitle() . "\n";

The Outlines Item Class

An outlines item class represents an item in the outline tree-structure. It allows you to define the visual appearance and the action that is triggered if the item is activated.

Create an Item

An individual instance can be created with the static helper method create():

Description
public static SetaPDF_Core_Document_OutlinesItem::create (
SetaPDF_Core_Document $document, string|array $titleOrConfig [, array $config = array ( ) ]
): SetaPDF_Core_Document_OutlinesItem

Creates an outline item.

The configuration array could hold keyed values:

$config = array(
    SetaPDF_Core_Document_OutlinesItem::TITLE => string,
    SetaPDF_Core_Document_OutlinesItem::DEST => SetaPDF_Core_Document_Destination|SetaPDF_Core_Type_Array|string,
    SetaPDF_Core_Document_OutlinesItem::ACTION => SetaPDF_Core_Document_Action|SetaPDF_Core_Type_Dictionary,
    SetaPDF_Core_Document_OutlinesItem::COLOR => SetaPDF_Core_DataStructure_Color_Rgb|array
    SetaPDF_Core_Document_OutlinesItem::BOLD => boolean,
    SetaPDF_Core_Document_OutlinesItem::ITALIC => boolean,
);
Parameters
$document : SetaPDF_Core_Document

The document instance in which context the item will be used

$titleOrConfig : string|array

The title or a configuration array

$config : array

A configuration array

Exceptions

Throws SetaPDF_Core_Type_Exception

 So a simple item with a named destination could be create this way: 

PHP
$item = \SetaPDF_Core_Document_OutlinesItem::create($document, 'Glossar', 'glossar');
// or
$item = \SetaPDF_Core_Document_OutlinesItem::create($document, array(
    \SetaPDF_Core_Document_OutlinesItem::TITLE => 'Glossar',
    \SetaPDF_Core_Document_OutlinesItem::DEST => 'glossar'
));

Configure Appearance and Behavior

To specify or access destinations, actions or other configuration data the class offers setter and getter methods:

getAction()

Get the action of the item.

getColor()

Get the color of the item.

getDestination()

Get the destination of the item.

getTitle()

Get the item title.

isBold()

Checks if the item should be displayed bold.

isItalic()

Checks if the item should be displayed italic.

setAction()

Set the action of the item.

setBold()

Sets whether the item should be displayed bold or not.

setColor()

Set the color of the item.

setDestination()

Set the destination of the item.

setItalic()

Sets whether the item should be displayed italic or not.

setTitle()

Set the item title.

 To create an item with an Uri action attached to it, following code could be used: 

PHP
$item = \SetaPDF_Core_Document_OutlinesItem::create($document, '© Setasign');
// create an Uri action
$action = new \SetaPDF_Core_Document_Action_Uri('http://www.setasign.com');
// add the action to the item
$item->setAction($action);

Add an Item to the Outline

Until now all items were simply created but were not used in an outline structure. To add items or complete item structures to existing items the items classes offer nearly same speaking methods:

append()

Append another item to this item.

appendChild()

Append another item as a child of this item.

appendChildCopy()

Appends a copy of another item or outline as a child to this item.

appendCopy()

Appends a copy of another item to this item.

move()

Move this item to another item or root outline.

prepend()

Prepend another item to this item.

prependCopy()

Prepends a copy of another item to this item.

remove()

Remove the item from the outline.

The SetaPDF_Core_Document_Catalog_Outlines class offers following methods to append items to it. The further management could be done through the item instances: 

appendChild()

Append an item to the outline.

appendChildCopy()

Append a copy of an item or outline to this outline.

Open and Close Items

As outline items are arranged in a tree structured the nodes with descendants can be opened or closed.

To check whether a node is open or close, or if the node doesn't holds any descendant nodes you can use the isOpen() method of a SetaPDF_Core_Document_OutlinesItem instance: 

Description
public SetaPDF_Core_Document_OutlinesItem::isOpen (
void
): null|boolean

Checks whether the item is open or not or if the item does not holds any descendants.

Return Values

Returns true if the item is open, false if it is closed or null if the item does not holds any descendants.

The status can be changed respectively with the open() and close() methods: 

close()

Close the item.

open()

Open the item.

Examples

Following example will create a 10 page document with blank pages. The bookmark outline will be visible at opening time and will show links to the ten pages and a link to www.setasign.com: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

$writer = new \SetaPDF_Core_Writer_Http('outlines.pdf', true);
$document = new \SetaPDF_Core_Document($writer);

// let's create some pages and destinations to them
$pages = $document->getCatalog()->getPages();
$destinations = array();

for ($i = 1; $i <= 10; $i++) {
    $page = $pages->create(\SetaPDF_Core_PageFormats::A4);
    $destinations['Page ' . $i] = \SetaPDF_Core_Document_Destination::createByPage($page);
}

// get the outlines dictionary
$outlines = $document->getCatalog()->getOutlines();

// create a root node named TOC without any action attached to it
$toc = \SetaPDF_Core_Document_OutlinesItem::create($document, 'TOC');
$outlines->appendChild($toc);
// now add the pages below this item
foreach ($destinations AS $title => $destination) {
    $item = \SetaPDF_Core_Document_OutlinesItem::create($document, $title);
    $item->setDestination($destination);
    $toc->appendChild($item);
}

// create an additional item at the root
$item = \SetaPDF_Core_Document_OutlinesItem::create($document, '© Setasign');
// create an Uri action
$action = new \SetaPDF_Core_Document_Action_Uri('http://www.setasign.com');
// add the action to the item
$item->setAction($action);

$outlines->appendChild($item);

// show the outline
$document->getCatalog()->setPageMode(\SetaPDF_Core_Document_PageMode::USE_OUTLINES);

$document->save()->finish();

The next PHP demo will extract an outline and its information: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

$filename = "files/pdfs/Brand-Guide.pdf";

// load the document instance
$document = \SetaPDF_Core_Document::loadByFilename($filename);

// get the outlines helper
$outlines = $document->getCatalog()->getOutlines();

// get the recursive iterator
$iterator = $outlines->getIterator();

if ($iterator instanceof EmptyIterator) {
    echo 'No outline items found in "' . $filename . '".';
} else {
    echo 'Bookmark outline in "' . $filename . "\":\n\n";
}

echo '<pre>';

// now iterate over the outline tree
foreach ($iterator AS $outlineItem) {
    // Get the title
    $title = $outlineItem->getTitle();
    // Get the action of the outline item (if available)
    $action = $outlineItem->getAction();
    // Get the destination of the outline item (if available)
    // If action is available, this will ignored
    $destination = $outlineItem->getDestination($document);
    if ($action !== false) {
        $destionationOrAction = $action->getType() . ' Action';
        switch (1) {
            // Handle GoTo Actions
            case $action instanceof \SetaPDF_Core_Document_Action_GoTo:
                $destination = $action->getDestination($document);
                $destionationOrAction .= ': Destination on Page ' . $destination->getPageNo($document);
                break;

            // Handle Named Actions
            case $action instanceof \SetaPDF_Core_Document_Action_Named:
                $destionationOrAction .= ': ' . $action->getName();
                break;

            // Handle JavaScript actions
            case $action instanceof \SetaPDF_Core_Document_Action_JavaScript:
                $destionationOrAction .= ': ' . substr($action->getJavaScript(), 0, 100);
                break;

            // Handle URI actions
            case $action instanceof \SetaPDF_Core_Document_Action_Uri:
                $destionationOrAction .= ': ' . $action->getUri();
                break;
        }
    } else if ($destination !== false) {
        $destionationOrAction = 'Destination on Page ' . $destination->getPageNo($document);
    } else {
        $destionationOrAction = 'No Action';
    }

    $indent = str_repeat(' ', $iterator->getDepth() * 4);

    echo  $indent . $title . " --> $destionationOrAction\n";
}

echo '</pre>';