> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/ios/get-started/samples/bookmarktest.md).

# Bookmarks

Sample Obj-C code to use Apryse SDK for programmatically reading and editing existing outline items, and for creating new PDF bookmarks using the high-level API.

Sample Obj-C code to use Apryse SDK for programmatically reading and editing existing outline items, and for creating new PDF bookmarks using the high-level API. Learn more about our [iOS SDK](/ios/guides.md) and [PDF Editing & Manipulation Library](/core/page-manipulation/manipulation.md).

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

#import <OBJC/PDFNetOBJC.h>
#import <Foundation/Foundation.h>
#include <stdio.h>

//-----------------------------------------------------------------------------------------
// The sample code illustrates how to read and edit existing outline items and create 
// new bookmarks using the high-level API.
//-----------------------------------------------------------------------------------------

void PrintIndent(PTBookmark *item) 
{
    int ident = [item GetIndent] - 1;
    int i=0;
    for (int i=0; i<ident; ++i) printf("  ");
}

// Prints out the outline tree to the standard output
void PrintOutlineTree(PTBookmark *item)
{
    for (; [item IsValid]; item=[item GetNext])
    {
        PrintIndent(item);
        if ([item IsOpen]) {
            printf("- %s ACTION -> ", [[item GetTitle] UTF8String]);
        }
        else {
            printf("+ %s ACTION -> ", [[item GetTitle] UTF8String]);
        }
        
        // Print Action
        PTAction *action = [item GetAction];
        if ([action IsValid]) {
            if ([action GetType] == e_ptGoTo) {
                PTDestination *dest = [action GetDest];
                if ([dest IsValid]) {
                    PTPage *page = [dest GetPage];
                    printf("GoTo Page #%d\n", [page GetIndex]);
                }
            }
            else {
                puts("Not a 'GoTo' action");
            }
        } else {
            puts("NULL");
        }

        if ([item HasChildren]) // Recursively print children sub-trees
        {
            PrintOutlineTree([item GetFirstChild]);
        }
    }
}

int main(int argc, char *argv[])
{
    @autoreleasepool {
        int ret = 0;
        [PTPDFNet Initialize: 0];

        // The following example illustrates how to create and edit the outline tree 
        // using high-level Bookmark methods.
        @try  
        {
            PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilepath: @"../../TestFiles/numbered.pdf"];
            [doc InitSecurityHandler];
            
            // Lets first create the root bookmark items. 
            PTBookmark *red = [PTBookmark Create: doc in_title: @"Red"];
            PTBookmark *green = [PTBookmark Create: doc in_title: @"Green"];
            PTBookmark *blue = [PTBookmark Create: doc in_title: @"Blue"];

            [doc AddRootBookmark: red];
            [doc AddRootBookmark: green];
            [doc AddRootBookmark: blue];

            // You can also add new root bookmarks using Bookmark.AddNext("...")
            [blue AddNextWithTitle: @"foo"];
            [blue AddNextWithTitle: @"bar"];

            // We can now associate new bookmarks with page destinations:

            // The following example creates an 'explicit' destination (see 
            // section '8.2.1 Destinations' in PDF Reference for more details)
            PTDestination *red_dest = [PTDestination CreateFit: [doc GetPage: 1]];
            [red SetAction: [PTAction CreateGoto: red_dest]];

            // Create an explicit destination to the first green page in the document
            [green SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 10]]]];

            // The following example creates a 'named' destination (see 
            // section '8.2.1 Destinations' in PDF Reference for more details)
            // Named destinations have certain advantages over explicit destinations.
            char* buf = "blue1";
            NSData* key = [NSData dataWithBytes: (const void*)buf length: 5];
            PTAction *blue_action = [PTAction CreateGotoWithNamedDestination: key key_sz: 5 dest: [PTDestination CreateFit: [doc GetPage: 19]]];
            
            [blue SetAction: blue_action];

            // We can now add children Bookmarks
            PTBookmark *sub_red1 = [red AddChildWithTitle: @"Red - Page 1"];
            [sub_red1 SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 1]]]];
            PTBookmark *sub_red2 = [red AddChildWithTitle: @"Red - Page 2"];
            [sub_red2 SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 2]]]];
            PTBookmark *sub_red3 = [red AddChildWithTitle: @"Red - Page 3"];
            [sub_red3 SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 3]]]];
            PTBookmark *sub_red4 = [sub_red3 AddChildWithTitle: @"Red - Page 4"];
            [sub_red4 SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 4]]]];
            PTBookmark *sub_red5 = [sub_red3 AddChildWithTitle: @"Red - Page 5"];
            [sub_red5 SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 5]]]];
            PTBookmark *sub_red6 = [sub_red3 AddChildWithTitle: @"Red - Page 6"];
            [sub_red6 SetAction: [PTAction CreateGoto: [PTDestination CreateFit: [doc GetPage: 6]]]];
            
            // Example of how to find and delete a bookmark by title text.
            PTBookmark *foo = [[doc GetFirstBookmark] Find: @"foo"];
            if ([foo IsValid]) 
            {
                [foo Delete];
            }
            else 
            {
                assert(FALSE);
            }

            PTBookmark *bar = [[doc GetFirstBookmark] Find: @"bar"];
            if ([bar IsValid]) 
            {
                [bar Delete];
            }
            else 
            {
                assert(FALSE);
            }

            // Adding color to Bookmarks. Color and other formatting can help readers 
            // get around more easily in large PDF documents.
            [red SetColor: 1 in_g: 0 in_b: 0];
            [green SetColor: 0 in_g: 1 in_b: 0];
            [green SetFlags: 2]; // set bold font
            [blue SetColor: 0 in_g: 0 in_b: 1];
            [blue SetFlags: 3]; // set bold and italic

            [doc SaveToFile: @"../../TestFiles/Output/bookmark.pdf" flags: 0];
            puts("Done. Result saved in bookmark.pdf");
        }
        @catch(NSException *e)
        {
            printf("%s\n", [e.reason UTF8String]);
            ret = 1;
        }

        
        // The following example illustrates how to traverse the outline tree using 
        // Bookmark navigation methods: Bookmark.GetNext(), Bookmark.GetPrev(), 
        // Bookmark.GetFirstChild () and Bookmark.GetLastChild ().
        @try  
        {
            // Open the document that was saved in the previous code sample
            PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilepath: @"../../TestFiles/Output/bookmark.pdf"];
            [doc InitSecurityHandler];
            
            PTBookmark *root = [doc GetFirstBookmark];
            PrintOutlineTree(root);

            puts("Done.");
        }
        @catch(NSException *e)
        {
            printf("%s\n", [e.reason UTF8String]);
            ret = 1;
        }
        
        // The following example illustrates how to create a Bookmark to a page 
        // in a remote document. A remote go-to action is similar to an ordinary 
        // go-to action, but jumps to a destination in another PDF file instead 
        // of the current file. See Section 8.5.3 'Remote Go-To Actions' in PDF 
        // Reference Manual for details.
        @try  
        {
            // Open the document that was saved in the previous code sample
            PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilepath: @"../../TestFiles/Output/bookmark.pdf"];
            [doc InitSecurityHandler];

            // Create file specification (the file referred to by the remote bookmark)
            PTObj * file_spec = [doc CreateIndirectDict]; 
            [file_spec PutName: @"Type" name: @"Filespec"];
            [file_spec PutString: @"F" value: @"bookmark.pdf"];
            PTFileSpec *spec = [[PTFileSpec alloc] initWithF: file_spec];
            PTAction *goto_remote = [PTAction CreateGotoRemoteWithNewWindow: spec page_num: 5 new_window: TRUE];

            PTBookmark *remoteBookmark1 = [PTBookmark Create: doc in_title: @"REMOTE BOOKMARK 1"];
            [remoteBookmark1 SetAction: goto_remote];
            [doc AddRootBookmark: remoteBookmark1];

            // Create another remote bookmark, but this time using the low-level SDF/Cos API.
            // Create a remote action
            PTBookmark *remoteBookmark2 = [PTBookmark Create: doc in_title: @"REMOTE BOOKMARK 2"];
            [doc AddRootBookmark: remoteBookmark2];
            
            PTObj * gotoR = [[remoteBookmark2 GetSDFObj] PutDict: @"A"];
            {
                [gotoR PutName: @"S" name: @"GoToR"]; // Set action type
                [gotoR PutBool: @"NewWindow" value: TRUE];

                // Set the file specification
                [gotoR Put: @"F" obj: file_spec];

                // jump to the first page. Note that pages are indexed from 0.
                PTObj * dest = [gotoR PutArray: @"D"];  // Set the destination
                [dest PushBackNumber: 9]; 
                [dest PushBackName: @"Fit"];
            }

            [doc SaveToFile: @"../../TestFiles/Output/bookmark_remote.pdf" flags: e_ptlinearized];
            
            puts("Done. Result saved in bookmark_remote.pdf");
        }
        @catch(NSException *e)
        {
            printf("%s\n", [e.reason UTF8String]);
            ret = 1;
        }
        [PTPDFNet Terminate: 0];
        return ret;
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2019 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

import PDFNet
import Foundation

//-----------------------------------------------------------------------------------------
// The sample code illustrates how to read and edit existing outline items and create
// new bookmarks using the high-level API.
//-----------------------------------------------------------------------------------------

func PrintIndent(item: PTBookmark) -> String {
    let indent = item.getIndent() - 1
    var i = 0
    var str = ""
    
    while i < indent {
        str = str + ("  ")
        i += 1
    }
    return str
}

// Prints out the outline tree to the standard output
func PrintOutlineTree(item: PTBookmark) {
    var currentItem = item
    while currentItem.isValid() {
        let indent: String = PrintIndent(item: currentItem)
        if currentItem.isOpen() {
            print("\(indent)- \(currentItem.getTitle()!) ACTION -> ")
        }
        else {
            print("\(indent)+ \(currentItem.getTitle()!) ACTION -> ")
        }
        
        // Print Action
        let action: PTAction = currentItem.getAction()
        if action.isValid() {
            if action.getType() == e_ptGoTo {
                let dest: PTDestination = action.getDest()
                if dest.isValid() {
                    let page: PTPage = dest.getPage()
                    print("GoTo Page #\(page.getIndex())")
                }
            }
            else {
                print("Not a 'GoTo' action")
            }
        }
        else {
            print("NULL")
        }
        
        if currentItem.hasChildren() {
            PrintOutlineTree(item: currentItem.getFirstChild())
        }
        currentItem = currentItem.getNext()
    }
}

func runBookmarkTest() -> Int {
    return autoreleasepool {
        var ret: Int = 0
        
        
        // The following example illustrates how to create and edit the outline tree
        // using high-level Bookmark methods.
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc(filepath: Bundle.main.path(forResource: "numbered", ofType: "pdf"))
                doc.initSecurityHandler()
                
                // Lets first create the root bookmark items.
                let red: PTBookmark = PTBookmark.create(doc, in_title: "Red")
                let green: PTBookmark = PTBookmark.create(doc, in_title: "Green")
                let blue: PTBookmark = PTBookmark.create(doc, in_title: "Blue")
                
                doc.addRootBookmark(red)
                doc.addRootBookmark(green)
                doc.addRootBookmark(blue)
                
                // You can also add new root bookmarks using Bookmark.AddNext("...")
                blue.addNext(withTitle: "foo")
                blue.addNext(withTitle: "bar")
                
                // We can now associate new bookmarks with page destinations:
                
                // The following example creates an 'explicit' destination (see
                // section '8.2.1 Destinations' in PDF Reference for more details)
                let red_dest = PTDestination.createFit(doc.getPageIterator(1).current())
                red.setAction(PTAction.createGoto(red_dest))
                
                // Create an explicit destination to the first green page in the document
                green.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(10))))
                
                // The following example creates a 'named' destination (see
                // section '8.2.1 Destinations' in PDF Reference for more details)
                // Named destinations have certain advantages over explicit destinations.
                let buf = "blue1"
                let key = buf.data(using: .utf8)
                let blue_action = PTAction.createGoto(withNamedDestination: key, key_sz: 5, dest: PTDestination.createFit(doc.getPage(19)))
                
                blue.setAction(blue_action)
                
                // We can now add children Bookmarks
                let sub_red1: PTBookmark = red.addChild(withTitle: "Red - Page 1")
                sub_red1.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(1))))
                let sub_red2: PTBookmark = red.addChild(withTitle: "Red - Page 2")
                sub_red2.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(2))))
                let sub_red3: PTBookmark = red.addChild(withTitle: "Red - Page 3")
                sub_red3.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(3))))
                let sub_red4: PTBookmark = sub_red3.addChild(withTitle: "Red - Page 4")
                sub_red4.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(4))))
                let sub_red5: PTBookmark = sub_red3.addChild(withTitle: "Red - Page 5")
                sub_red5.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(5))))
                let sub_red6: PTBookmark = sub_red3.addChild(withTitle: "Red - Page 6")
                sub_red6.setAction(PTAction.createGoto(PTDestination.createFit(doc.getPage(6))))
                
                // Example of how to find and delete a bookmark by title text.
                let foo: PTBookmark = doc.getFirstBookmark().find("foo")
                if foo.isValid() {
                    foo.delete()
                }
                else {
                    assert(false)
                }
                
                let bar: PTBookmark = doc.getFirstBookmark().find("bar")
                if bar.isValid() {
                    bar.delete()
                }
                else {
                    assert(false)
                }
                
                // Adding color to Bookmarks. Color and other formatting can help readers
                // get around more easily in large PDF documents.
                red.setColor(1, in_g: 0, in_b: 0)
                green.setColor(0, in_g: 1, in_b: 0)
                green.setFlags(2)   // set bold font
                blue.setColor(0, in_g: 0, in_b: 1)
                blue.setFlags(3)    // set bold and italic
                
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("bookmark.pdf").path, flags: 0)
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }

        // The following example illustrates how to traverse the outline tree using
        // Bookmark navigation methods: Bookmark.GetNext(), Bookmark.GetPrev(),
        // Bookmark.GetFirstChild () and Bookmark.GetLastChild ().
        do {
            try PTPDFNet.catchException {
                // Open the document that was saved in the previous code sample
                let doc: PTPDFDoc = PTPDFDoc(filepath: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("bookmark.pdf").path)
                doc.initSecurityHandler()
                
                let root: PTBookmark = doc.getFirstBookmark()
                PrintOutlineTree(item: root)
                
                print("Done.")
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        // The following example illustrates how to create a Bookmark to a page
        // in a remote document. A remote go-to action is similar to an ordinary
        // go-to action, but jumps to a destination in another PDF file instead
        // of the current file. See Section 8.5.3 'Remote Go-To Actions' in PDF
        // Reference Manual for details.
        do {
            try PTPDFNet.catchException {
                // Open the document that was saved in the previous code sample
                let doc: PTPDFDoc = PTPDFDoc(filepath: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("bookmark.pdf").path)
                doc.initSecurityHandler()
                
                // Create file specification (the file referred to by the remote bookmark)
                let file_spec: PTObj = doc.createIndirectDict()
                file_spec.putName("Type", name: "Filespec")
                file_spec.put("F", value: "bookmark.pdf")
                let spec = PTFileSpec(f: file_spec)
                let goto_remote = PTAction.createGotoRemote(withNewWindow: spec, page_num: 5, new_window: true)
                
                let remoteBookmark1: PTBookmark = PTBookmark.create(doc, in_title: "REMOTE BOOKMARK 1")
                remoteBookmark1.setAction(goto_remote)
                doc.addRootBookmark(remoteBookmark1)
                
                // Create another remote bookmark, but this time using the low-level SDF/Cos API.
                // Create a remote action
                let remoteBookmark2: PTBookmark = PTBookmark.create(doc, in_title: "REMOTE BOOKMARK 2")
                doc.addRootBookmark(remoteBookmark2)
                
                let gotoR: PTObj = remoteBookmark2.getSDFObj().putDict("A")
                do {
                    gotoR.putName("S", name: "GoToR")   // Set action type
                    gotoR.putBool("NewWindow", value: true)
                    
                    // Set the file specification
                    gotoR.put("F", obj: file_spec)
                    
                    // jump to the first page. Note that pages are indexed from 0.
                    let dest: PTObj = gotoR.putArray("D")  // Set the destination
                    dest.pushBackNumber(9)
                    dest.pushBackName("Fit")
                }
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("bookmark_remote.pdf").path, flags: e_ptlinearized.rawValue)
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        return ret
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/ios/get-started/samples/bookmarktest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
