> 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/core/low-level-pdf-api/sdf.md).

# SDF / COS object model

Discover the complexities of PDF documents and how to edit them with ease using Apryse CosEdit. Explore the low-level object model of PDFs and unlock unprecedented control over your files. The Apryse

Real-life PDF documents are much more complex than the "Hello World" PDF sample from the previous section. Streams in a PDF document can be compressed and encrypted, objects can form complex networks, and, in PDF 1.5, parts of the object graph can be compressed and embedded in so-called "object streams". All this makes manual editing of PDF documents extremely difficult — even impossible. The good news is that Apryse released CosEdit — a graphical utility for browsing and editing PDFdocuments at the object level, offering unprecedented ease and control. Apryse SDK also provides a full SDF/COS level API making it very easy to read, write, and edit PDF and FDF at the atomic level. Furthermore, Apryse SDK also provides a high-level API for reading, writing, and editing PDF documents at the level of pages, bookmarks, graphical primitives, and so on.

**SDF** (Structured Document Format) and **COS** (Carousel Object System; Carousel was a codename for Acrobat 1.0) are synonyms for PDF low-level object model. SDF is the acronym used in Apryse SDK, whereas COS is a legacy word used in the Acrobat SDK.

In many ways, SDF is to PDF what [XML](http://www.w3.org/XML/) and [DOM](http://www.w3.org/XML/) are to [SVG](http://www.w3.org/Graphics/SVG/Overview.htm8) (Scalable Vector Graphics). The SDF/COS object system provides the low-level object type and file structure used in PDF documents. PDF documents are graphs of SDF objects. SDF objects can represent document components such as bookmarks, pages, fonts, and annotations, and so on.

PDF is not the only document format built on top of SDF/COS. FDF (Form Data Format) and PJTF (Portable Job Ticket Format) are also built on top of SDF/COS.

## SDF.Obj

The SDF layer deals directly with the data that is in a PDF document. The data types are referred to as SDF objects. There are eight data types found in PDF documents. They are arrays, dictionaries, numbers, boolean values, names, strings, streams, and the null object. Apryse SDK implements these objects as shown in the following graph:

![](https://3779731113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fziw3GiL98Xfj63F3He8h%2Fuploads%2Fgit-blob-f7fe493cec4b5aa5635559dd468547ed320bf665%2Feb4f28e5b505d78148b3f039809a7dafca437ca5-537x260.gif?alt=media)

All objects ultimately derive from the Object class. Similarly, all SDF objects ultimately derive from the Obj class. Following the Composite design pattern, Obj implements each method found in its derived classes. Thus you can invoke a member function of any derived object through the base Obj interface. This object hierarchy is shown in the graph above.

If a member function is not supported on a given object (e.g. if you are invoking obj.GetName() on a Bool object), an Exception will be thrown.

In order to find out type-information at run-time, use `obj.GetType()` or `obj.Is_**type**_()` methods (where ***type*** could be Array, Number, Bool, Str, Dict, or Stream). Usually, an object's type can be inferred from the PDF/FDF specification. For example, when you call `doc.GetTrailer()`, you can assume that the returned object is a dictionary object because this is mandated by PDF specification. If an object is not a dictionary, calling a dictionary method on it throws an exception. These semantics are important for stylistic reasons — since type casts and type checks are not required, you can keep your code efficient and elegant. In case there is an ambiguity in PDF/FDF specification, you can use `GetType()` or `Is_**type**_()` methods.

As mentioned in the previous section, SDF objects can be either direct or indirect. *Direct* objects can be created using `Obj.Create_**type**_()` methods. The following example illustrates how to create direct number and direct name objects inside Dict objects. Note that the same approach will work for Array objects.

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

```csharp
PDFDoc doc = new PDFDoc(filename);
Obj root = doc.GetRoot(); // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
root.PutNumber("My number key", 100);
root.PutDict("My dict key");
root.PutName("My name key", "My name value");
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
PDFDoc doc(filename);
Obj root = doc.GetRoot(); // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
root.PutNumber("My number key", 100);
root.PutDict("My dict key");
root.PutName("My name key", "My name value");
```

{% endcode %}
{% endtab %}

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

```go
doc := NewPDFDoc(filename)
root := doc.GetRoot() // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
root.PutNumber("My number key", 100)
root.PutDict("My dict key")
root.PutName("My name key", "My name value")
```

{% endcode %}
{% endtab %}

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

```java
PDFDoc doc = new SDFDoc(filename);
Obj root = doc.getRoot(); // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
root.putNumber("My number key", 100);
root.putDict("My dict key");
root.putName("My name key", "My name value");
```

{% endcode %}
{% endtab %}

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

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const root = await doc.getRoot(); // (/Root entry within the trailer dictionary)

  // you can create direct objects inside container objects.
  root.putNumber("My number key", 100);
  root.putDict("My dict key");
  root.putName("My name key", "My name value");
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}

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

```kotlin
val doc = PDFDoc(filename)
val root = doc.getRoot(); // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
root.putNumber("My number key", 100.0)
root.putDict("My dict key")
root.putName("My name key", "My name value")
```

{% endcode %}
{% endtab %}

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

```objc
PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilepath: filename];
PTObj *root = [doc GetRoot]; // (/Root entry within the trailer dictionary

// you can create direct objects inside container objects.
[root PutNumber: @"My number key" value: 100];
[root PutDict: @"My dict key"];
[root PutName: @"My name key" value: @"My name value"];
```

{% endcode %}
{% endtab %}

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

```swift
let doc: PTSDFDoc = PTSDFDoc(filepath: filename, ofType: "pdf")
let root: PTObj = doc.getRoot() // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
root.putNumber("My number key", value: 100)
root.putDict("My dict key")
root.putName("My name key", value: "My name value")
```

{% endcode %}
{% endtab %}

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

```php
$doc = new PDFDoc($filename);
$root = $doc->GetRoot(); // (/Root entry within the trailer dictionary)

// you can create direct objects inside container objects.
$root->PutNumber("My number key", 100);
$root->PutDict("My dict key");
$root->PutName("My name key", "My name value");
```

{% endcode %}
{% endtab %}

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

```python
doc = SDFDoc(filename)
root = doc.GetRoot() # (/Root entry within the trailer dictionary)

# you can create direct objects inside container objects.
root.PutNumber("My number key", 100)
root.PutDict("My dict key")
root.PutName("My name key", "My name value")
```

{% endcode %}
{% endtab %}

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

```ruby
doc = SDFDoc.new(filename)
root = doc.GetRoot() # (/Root entry within the trailer dictionary)

# you can create direct objects inside container objects.
root.PutNumber("My number key", 100)
root.PutDict("My dict key")
root.PutName("My name key", "My name value")
```

{% endcode %}
{% endtab %}

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

```vb
Dim doc As SDFDoc = New SDFDoc(filename)
Dim root As Obj = doc.GetRoot() ' (/Root entry within the trailer dictionary)

' you can create direct objects inside container objects.
root.PutNumber("My number key", 100)
root.PutDict("My dict key")
root.PutName("My name key", "My name value")
```

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

New *indirect* objects can be *created* using `doc.CreateIndirect_**type**_()` methods on an SDF document. The following code shows how to create new Number and Dictionary indirect objects:

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

```csharp
SDFDoc doc = new SDFDoc(filename);
Obj mynumber = doc.CreateIndirectNumber(100);
Obj mydict = doc.CreateIndirectDict();
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
SDFDoc doc(filename);
Obj mynumber = doc.CreateIndirectNumber(100);
Obj mydict = doc.CreateIndirectDict();
```

{% endcode %}
{% endtab %}

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

```go
doc := NewSDFDoc(filename)
myNumber := doc.CreateIndirectNumber(100)
myDict := doc.CreateIndirectDict()
```

{% endcode %}
{% endtab %}

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

```java
SDFDoc doc = new SDFDoc(filename);
Obj mynumber = doc.createIndirectNumber(100);
Obj mydict = doc.createIndirectDict();
```

{% endcode %}
{% endtab %}

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

```js
async function main() {
  const pdf_doc = await PDFNet.PDFDoc.createFromURL(filename);
  const doc =  await pdf_doc.getSDFDoc();
  const mynumber = await doc.createIndirectNumber(100);
  const mydict = await doc.createIndirectDict();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}

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

```kotlin
val doc = SDFDoc(filename)
val mynumber = doc.createIndirectNumber(100)
val mydict = doc.createIndirectDict()
```

{% endcode %}
{% endtab %}

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

```objc
PTSDFDoc *doc = [[PTSDFDoc alloc] initWithFilepath: filename];
PTObj *mynumber = [doc CreateIndirectNumber:100];
PTObj *mydict = [doc CreateIndirectDict];
```

{% endcode %}
{% endtab %}

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

```swift
let doc: PTSDFDoc = PTSDFDoc(filepath: filename, ofType: "pdf")
let mynumber: PTObj = doc.createIndirectNumber(100)
let mydict: PTObj = doc.CreateIndirectDict();
```

{% endcode %}
{% endtab %}

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

```php
$doc = new SDFDoc($filename);
$mynumber = $doc->CreateIndirectNumber(100);
$mydict = $doc->CreateIndirectDict();
```

{% endcode %}
{% endtab %}

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

```python
doc = SDFDoc(filename)
mynumber = doc.CreateIndirectNumber(100)
mydict = doc.CreateIndirectDict()
```

{% endcode %}
{% endtab %}

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

```ruby
doc = SDFDoc.new(filename)
mynumber = doc.CreateIndirectNumber(100)
mydict = doc.CreateIndirectDict()
```

{% endcode %}
{% endtab %}

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

```vb
Dim doc As SDFDoc = New SDFDoc(filename)
Dim mynumber As Obj = doc.CreateIndirectNumber(100)
Dim mydict As Obj = doc.CreateIndirectDict()
```

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

Apryse SDK SDF provides many utility methods that can be used to efficiently traverse an SDF object graph. Here is an example on how to get to a document's page root:

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

```csharp
Obj trailer = doc.GetTrailer();
DictIterator root_itr = trailer.Get("Root");
Obj root = root_itr.Value();
DictIterator pages_itr = root.Get("Pages");
Obj pages = pages_itr.Value();
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
Obj trailer = doc.GetTrailer();
DictIterator root_itr = trailer.Get("Root");
Obj root = root_itr.Value();
DictIterator pages_itr = root.Get("Pages");
Obj pages = pages_itr.Value();
```

{% endcode %}
{% endtab %}

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

```go
trailer := doc.GetTrailer()
rootItr := trailer.Get("Root")
root := rootItr.Value()
pagesItr := root.Get("Pages")
pages := pagesItr.Value()
```

{% endcode %}
{% endtab %}

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

```java
Obj trailer = doc.getTrailer();
DictIterator root_itr = trailer.get("Root");
Obj root = root_itr.value();
DictIterator pages_itr = root.get("Pages");
Obj pages = pages_itr.value();
```

{% endcode %}
{% endtab %}

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

```js
async function main() {
  const trailer = await doc.getTrailer();
  const root_itr = await trailer.get("root");
  const root = await root_itr.value();
  const pages_itr =  await root.get("Pages");
  const pages = await pages_itr.value();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}

[PDFNet.PDFDoc.getTrailer](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#getTrailer__anchor) [PDFNet.Obj.get](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#get__anchor) [PDFNet.DictIterator.value](https://sdk.apryse.com/api/web/Core.PDFNet.DictIterator.html#value__anchor)
{% endtab %}

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

```kotlin
val trailer = doc.getTrailer()
val root_itr = trailer.get("Root")
val root = root_itr.value()
val pages_itr = root.get("Pages")
val pages = pages_itr.value()
```

{% endcode %}
{% endtab %}

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

```objc
PTObj * trailer = [doc GetTrailer];
PTDictIterator * root_itr = [trailer Get: @"root"];
PTObj * root [root_itr Value];
PTDictIterator * pages_itr = [root Get: @"Pages"];
PTObj * pages = [pages_itr Value];
```

{% endcode %}
{% endtab %}

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

```swift
let trailer: PTObj = doc.getTrailer()
let root_itr: PTDictIterator = trailer.get("Root")
let root: PTObj = root_itr.value()
let pages_itr: PTDictIterator = root.get("Pages")
let pages: PTObj = pages_itr.value()
```

{% endcode %}
{% endtab %}

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

```php
$trailer = $doc->GetTrailer();
$root_itr = $trailer->Get("Root");
$root = $root_itr->Value();
$pages_itr = $root->Get("Pages");
$pages = $pages_itr->Value();
```

{% endcode %}
{% endtab %}

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

```python
trailer = doc.GetTrailer()
root_itr = trailer.Get("Root")
root = root_itr.Value()
pages_itr = root.Get("Pages")
pages = pages_itr.Value()
```

{% endcode %}
{% endtab %}

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

```ruby
trailer = doc.GetTrailer()
root_itr = trailer.Get("Root")
root = root_itr.Value()
pages_itr = root.Get("Pages")
pages = pages_itr.Value()
```

{% endcode %}
{% endtab %}

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

```vb
Dim trailer As Obj = doc.GetTrailer()
Dim root_itr As DictIterator = trailer.Get("Root")
Dim root As Obj = root_itr.Value()
Dim pages_itr As DictIterator = root.Get("Pages")
Dim pages As Obj = pages_itr.Value()
```

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

Because the PDF specification mandates that "Root" is always a dictionary, we can directly reference the "Pages" object by calling `Get("key")`. Also some so-called "PDF" documents can be *corrupt* from incompliance, meaning the document does not follow the PDF specification. In some corrupt PDF documents, the "Root" may be missing or may not be a dictionary object. In these and similar cases, the Apryse SDK throws an exception.

In order to retrieve an object that may or may not be present in a dictionary, use the `dict.FindObj("key")` method. For example:

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

```csharp
Obj my_value = dict.FindObj("my_key");
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
Obj my_value = dict.FindObj("my_key");
```

{% endcode %}
{% endtab %}

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

```go
myValue := dict.FindObj("my_key")
```

{% endcode %}
{% endtab %}

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

```java
Obj my_value = dict.findObj("my_key");
```

{% endcode %}
{% endtab %}

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

```js
const my_value = await dict.findObj("my_key");
```

{% endcode %}

[PDfNet.Obj.findObj](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#findObj__anchor)
{% endtab %}

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

```kotlin
val my_value = dict.findObj("my_key");
```

{% endcode %}
{% endtab %}

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

```objc
PTObj * my_value = [dict FindObj: @"my_key"];
```

{% endcode %}
{% endtab %}

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

```swift
let my_value: PTObj = dict.find("my_key")
```

{% endcode %}
{% endtab %}

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

```php
$my_value = $dict->Find("my_key");
```

{% endcode %}
{% endtab %}

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

```python
my_value = dict.Find("my_key")
```

{% endcode %}
{% endtab %}

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

```ruby
my_value = dict.Find("my_key")
```

{% endcode %}
{% endtab %}

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

```vb
Dim my_value As Obj = dict.Find("my_key")
```

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

You can use DictIterator in order to traverse key-value pairs within a dictionary:

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

```csharp
DictIterator itr = dict.GetDictIterator();
for (; itr.HasNext(); itr.Next()) {
  // itr.Key();
  // itr.Value();
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
DictIterator itr = dict.GetDictIterator();
for (; itr.HasNext(); itr.Next()) {
  // itr.Key();
  // itr.Value();
}
```

{% endcode %}
{% endtab %}

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

```go
itr := dict.GetDictIterator()
for itr.HasNext() {
  // itr.Key();
  // itr.Value();
  itr.Next()
}
```

{% endcode %}
{% endtab %}

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

```java
DictIterator itr = dict.getDictIterator();
for (; itr.hasNext(); itr.next()) {
  // itr.key();
  // itr.value();
}
```

{% endcode %}
{% endtab %}

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

```js
const itr = dict.getDictIterator();
for (; itr.hasNext(); itr.next()) {
  // itr.key();
  // itr.value();
}
```

{% endcode %}

[PDFNet.Obj.getDictIterator](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#getDictIterator__anchor) [PDFNet.DictIterator.hasNext](https://sdk.apryse.com/api/web/Core.PDFNet.DictIterator.html#hasNext__anchor) [PDFNet.DictIterator.next](https://sdk.apryse.com/api/web/Core.PDFNet.DictIterator.html#next__anchor)
{% endtab %}

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

```kotlin
val itr = dict.getDictIterator()
while (itr.hasNext()) {
  // itr.key()
  // itr.value()
  itr.next()
}
```

{% endcode %}
{% endtab %}

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

```objc
PTDictIterator * itr = [dict GetDictIterator];
for (; [itr HasNext]; [itr Next]) {
  // [itr Key];
  // [itr Value];
}
```

{% endcode %}
{% endtab %}

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

```swift
let itr: PTDictIterator = dict.getDictIterator()
for (; itr.hasNext(); itr.next()) {
  // itr.key()
  // itr.value()
}
```

{% endcode %}
{% endtab %}

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

```php
$itr = dict->GetDictIterator();
for (; $itr->HasNext(); $itr->Next()) {
  // $itr->Key();
  // $itr->Value();
}
```

{% endcode %}
{% endtab %}

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

```python
itr = dict.GetDictIterator()
while itr.HasNext():
    # itr.Key()
    # itr.Value()
    itr.Next()
```

{% endcode %}
{% endtab %}

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

```ruby
itr = dict.GetDictIterator()
while itr.HasNext() do
    # itr.Key()
    # itr.Value()
    itr.Next()
end
```

{% endcode %}
{% endtab %}

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

```vb
Dim itr As DictIterator = dict.GetDictIterator()
While itr.HasNext()
    ' itr.Key()
    ' itr.Value()
    itr.Next()
End While
```

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

To retrieve objects from an Array object, use `array.GetAt(idx)` method:

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

```csharp
for (int i = 0; i < array.Size(); ++i) {
  Obj obj = array.GetAt(i);
  // ...
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
for (int i = 0; i < array.Size(); ++i) {
  Obj obj = array.GetAt(i);
  // ...
}
```

{% endcode %}
{% endtab %}

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

```go
i := 1
for i < array.Size() {
  obj := array.GetAt(i)
  // ...
  i = i + 1
}
```

{% endcode %}
{% endtab %}

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

```java
for (int i = 0; i < array.size(); ++i) {
  Obj obj = array.getAt(i);
  // ...
}
```

{% endcode %}
{% endtab %}

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

```js
for (int i = 0; i < await array.size(); ++i) {
  const obj = await array.getAt(i);
  // ...
}
```

{% endcode %}
{% endtab %}

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

```kotlin
for (i in 1 until array.size()) {
  Obj obj = array.getAt(i)
  // ...
}
```

{% endcode %}
{% endtab %}

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

```objc
for (int i = 0; i < [array Size]; ++i) {
  PTObj * obj = [array GetAt: i];
  // ...
}
```

{% endcode %}
{% endtab %}

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

```swift
for i in 1..<array.size() {
  let obj: PTObj = array.getAt(i)
  // ...
}
```

{% endcode %}
{% endtab %}

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

```php
for ($i = 0; $i < array->Size(); ++$i) {
  $obj = array->GetAt(i)
  // ...
}
```

{% endcode %}
{% endtab %}

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

```python
for i in range(1, array.Size()):
  obj = array.GetAt(i)
  # ...
```

{% endcode %}
{% endtab %}

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

```ruby
for i in 1..array.Size() do
  obj = array.GetAt(i)
  # ...
end
```

{% endcode %}
{% endtab %}

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

```vb
For i As Integer = 1 To array.Size()
  Dim obj As Obj = array.GetAt(i)
  ' ...
Next
```

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

In the previous section, we learned how to create indirect objects by calling the `SDFDoc.CreateIndirect_**type**_()` methods. Now, let's look at how to create references to those indirect objects. The following code shows how:

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

```csharp
Obj shared_dict = doc.CreateIndirectDict();
shared_dict.PutName("My key", "My value");

// Add indirect reference to 'shared_dict'.
Obj trailer_dict = doc.GetTrailer();
DictIterator info_itr = trailer_dict.Get("Info");
Obj info_dict = info_itr.Value();
info_dict.Put("MyDict", shared_dict);

// Add a second indirect reference to 'shared_dict'.
DictIterator root_itr = trailer_dict.Get("Root");
Obj root_dict = root_itr.Value();
root_dict.Put("MyDict", shared_dict);
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
Obj shared_dict = doc.CreateIndirectDict();
shared_dict.PutName("My key", "My value");

// Add indirect reference to 'shared_dict'.
Obj trailer_dict = doc.GetTrailer();
DictIterator info_itr = trailer_dict.Get("Info");
Obj info_dict = info_itr.Value();
info_dict.Put("MyDict", shared_dict);

// Add a second indirect reference to 'shared_dict'.
DictIterator root_itr = trailer_dict.Get("Root");
Obj root_dict = root_itr.Value();
root_dict.Put("MyDict", shared_dict);
```

{% endcode %}
{% endtab %}

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

```go
sharedDict := doc.CreateIndirectDict()
sharedDict.PutName("My key", "My value")

// Add indirect reference to 'shared_dict'.
trailer_dict := doc.GetTrailer()
infoItr := trailerDict.Get("Info")
infoDict := infoItr.Value()
infoDict.Put("MyDict", shared_dict)

// Add a second indirect reference to 'shared_dict'.
rootItr := trailerDict.Get("Root")
rootDict := rootItr.Value()
rootDict.Put("MyDict", shared_dict)
```

{% endcode %}
{% endtab %}

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

```java
Obj shared_dict = doc.createIndirectDict();
shared_dict.putName("My key", "My value");

// Add indirect reference to 'shared_dict'.
Obj trailer_dict = doc.getTrailer();
DictIterator info_itr = trailer_dict.get("Info");
Obj info_dict = info_itr.value();
info_dict.put("MyDict", shared_dict);

// Add a second indirect reference to 'shared_dict'.
DictIterator root_itr = trailer_dict.get("Root");
Obj root_dict = root_itr.value();
root_dict.put("MyDict", shared_dict);
```

{% endcode %}
{% endtab %}

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

```js
async function main() {
  const shared_dict = await doc.createIndirectDict();
  shared_dict.putName("My key", "My value");

  // Add indirect reference to 'shared_dict'.
  const trailer_dict = await doc.getTrailer();
  const info_itr = await trailer_dict.get("Info");
  const info_dict = await info_itr.value();
  info_dict.Put("MyDict", shared_dict);

  // Add a second indirect reference to 'shared_dict'.
  const root_itr = trailer_dict.get("Root");
  const root_dict = root_itr.value();
  root_dict.put("MyDict", shared_dict);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}

[PDFNet.PDFDoc.createIndirectDict](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#createIndirectDict__anchor) [PDFNet.PDFDoc.getTrailer](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#getTrailer__anchor) [PDFNet.Obj.putName](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#putName__anchor) [PDFNet.Obj.get](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#get__anchor) [PDFNet.Obj.put](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#put__anchor) [PDFNet.DictIterator.value](https://sdk.apryse.com/api/web/Core.PDFNet.DictIterator.html#value__anchor)
{% endtab %}

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

```kotlin
val shared_dict = doc.createIndirectDict()
shared_dict.putName("My key", "My value")

// Add indirect reference to 'shared_dict'.
val trailer_dict = doc.getTrailer()
val info_itr = trailer_dict.get("Info")
val info_dict = info_itr.value()
info_dict.put("MyDict", shared_dict)

// Add a second indirect reference to 'shared_dict'.
val root_itr = trailer_dict.get("Root")
val root_dict = root_itr.value()
root_dict.put("MyDict", shared_dict)
```

{% endcode %}
{% endtab %}

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

```objc
PTObj * shared_dict = [doc CreateIndirectDict];
[shared_dict PutName: @"My key" Value: @"My value"];

// Add indirect reference to 'shared_dict'.
PTObj * trailer_dict = [doc GetTrailer];
PTDictIterator * info_itr = [trailer Get: @"Info"];
PTObj * info_dict = [info_itr Value];
[info_dict Put: @"MyDict" Value: shared_dict];

// Add a second indirect reference to 'shared_dict'.
PTDictIterator * root_itr = [trailer_dict Get("Root")];
PTObj * root_dict = [root_itr Value];
[root_dict Put: @"MyDict" Value: shared_dict];
```

{% endcode %}
{% endtab %}

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

```swift
let shared_dict: PTObj = doc.createIndirectDict()
shared_dict.putName("My key", "My value")

// Add indirect reference to 'shared_dict'.
let trailer_dict: PTObj = doc.getTrailer()
let info_itr: PTDictIterator = trailer_dict.get("Info")
let info_dict: PTObj = info_itr.value()
info_dict.put("MyDict", shared_dict)

// Add a second indirect reference to 'shared_dict'.
let root_itr: PTDictIterator = trailer_dict.get("Root")
let root_dict: PTObj = root_itr.value()
root_dict.put("MyDict", shared_dict)
```

{% endcode %}
{% endtab %}

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

```php
$shared_dict = $doc->CreateIndirectDict();
$shared_dict->PutName("My key", "My value");

// Add indirect reference to 'shared_dict'.
$trailer_dict = $doc->GetTrailer();
$info_itr = $trailer_dict->Get("Info");
$info_dict = $info_itr->Value();
$info_dict->Put("MyDict", shared_dict);

// Add a second indirect reference to 'shared_dict'.
$root_itr = $trailer_dict->Get("Root");
$root_dict = $root_itr->Value();
$root_dict->Put("MyDict", shared_dict);
```

{% endcode %}
{% endtab %}

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

```python
shared_dict = doc.CreateIndirectDict()
shared_dict.PutName("My key", "My value")

# Add indirect reference to 'shared_dict'.
trailer_dict = doc.GetTrailer()
info_itr = trailer_dict.Get("Info")
info_dict = info_itr.Value()
info_dict.Put("MyDict", shared_dict)

# Add a second indirect reference to 'shared_dict'.
root_itr = trailer_dict.Get("Root")
root_dict = root_itr.Value()
root_dict.Put("MyDict", shared_dict)
```

{% endcode %}
{% endtab %}

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

```ruby
shared_dict = doc.CreateIndirectDict()
shared_dict.PutName("My key", "My value")

# Add indirect reference to 'shared_dict'.
trailer_dict = doc.GetTrailer()
info_itr = trailer_dict.Get("Info")
info_dict = info_itr.Value()
info_dict.Put("MyDict", shared_dict)

# Add a second indirect reference to 'shared_dict'.
root_itr = trailer_dict.Get("Root")
root_dict = root_itr.Value()
root_dict.Put("MyDict", shared_dict)
```

{% endcode %}
{% endtab %}

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

```vb
Dim shared_dict As Obj = doc.CreateIndirectDict()
shared_dict.PutName("My key", "My value")

' Add indirect reference to 'shared_dict'.
Dim trailer_dict As Obj = doc.GetTrailer()
Dim info_itr As DictIterator = trailer_dict.Get("Info")
Dim info_dict As Obj = info_itr.Value()
info_dict.Put("MyDict", shared_dict)

' Add a second indirect reference to 'shared_dict'.
Dim root_itr As DictIterator = trailer_dict.Get("Root")
Dim root_dict As Obj = root_itr.Value()
root_dict.Put("MyDict", shared_dict)
```

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

So it's possible for multiple objects to refer to the same object. We call such objects *shared objects*. But shared objects must always be [indirect objects](https://docs.apryse.com). So if you want to share an object, it must have be created using `SDFDoc.CreateIndirect_**type**_`, or you should test `Obj.IsIndirect()` to make sure it's an indirect object.

Because the PDF document format disallows creating multiple links to direct objects, Apryse SDK will throw an exception should you try to create multiple links/references to a direct object. Here is an example below:

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

```csharp
Obj trailer_dict = doc.GetTrailer();
DictIterator info_itr = trailer_dict.Get("Info");
Obj info_dict = info_itr.Value();

// Create an inline dictionary
Obj direct_obj = info_dict.PutDict("Link1");

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
DictIterator root_itr = trailer_dict.Get("Root");
Obj root_dict = root_itr.Value();
root_dict.Put("Link2", direct_obj);
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
Obj trailer_dict = doc.GetTrailer();
DictIterator info_itr = trailer_dict.Get("Info");
Obj info_dict = info_itr.Value();

// Create an inline dictionary
Obj direct_obj = info_dict.PutDict("Link1");

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
DictIterator root_itr = trailer_dict.Get("Root");
Obj root_dict = root_itr.Value();
root_dict.Put("Link2", direct_obj);
```

{% endcode %}
{% endtab %}

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

```go
trailerDict := doc.GetTrailer()
infoItr := trailerDict.Get("Info")
infoDict := infoItr.Value()

// Create an inline dictionary
directObj := infoDict.PutDict("Link1")

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
rootItr := trailerDict.Get("Root")
rootDict := root_itr.Value()
rootDict.Put("Link2", directObj)
```

{% endcode %}
{% endtab %}

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

```java
Obj trailer_dict = doc.getTrailer();
DictIterator info_itr = trailer_dict.get("Info");
Obj info_dict = info_itr.value();

// Create an inline dictionary
Obj direct_obj = info_dict.putDict("Link1");

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
DictIterator root_itr = trailer_dict.get("Root");
Obj root_dict = root_itr.value();
root_dict.put("Link2", direct_obj);
```

{% endcode %}
{% endtab %}

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

```js
async function main() {
  const trailer_dict = await doc.getTrailer();
  DictIterator info_itr = await trailer_dict.get("Info");
  Obj info_dict = await info_itr.value();

  // Create an inline dictionary
  Obj direct_obj = await info_dict.putDict("Link1");

  // Attempt to create a second link to direct_obj.
  // This will copy the object. If you want to
  // share objects, create them using the
  // PDFDoc.CreateIndirect() methods.
  DictIterator root_itr = await trailer_dict.get("Root");
  Obj root_dict = await root_itr.value();
  root_dict.put("Link2", direct_obj);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}

[PDFNet.PDFDoc.getTrailer](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#getTrailer__anchor) [PDFNet.Obj.get](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#get__anchor) [PDFNet.Obj.put](https://sdk.apryse.com/api/web/Core.PDFNet.Obj.html#put__anchor) [PDFNet.DictIterator.value](https://sdk.apryse.com/api/web/Core.PDFNet.DictIterator.html#value__anchor)
{% endtab %}

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

```kotlin
val trailer_dict = doc.getTrailer()
val info_itr = trailer_dict.get("Info")
val info_dict = info_itr.value()

// Create an inline dictionary
val direct_obj = info_dict.putDict("Link1")

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
val root_itr = trailer_dict.get("Root")
val root_dict = root_itr.value()
root_dict.put("Link2", direct_obj)
```

{% endcode %}
{% endtab %}

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

```objc
PTObj * trailer_dict = [doc GetTrailer];
PTDictIterator * info_itr = [trailer_dict Get: @"Info"];
PTObj * info_dict = [info_itr Value];

// Create an inline dictionary
PTObj * direct_obj = [info_dict PutDict: @"Link1"];

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
PTDictIterator * root_itr = [trailer_dict Get: @"Root"];
PTObj * root_dict = [root_itr Value];
[root_dict Put: @"Link2" Value: direct_obj];
```

{% endcode %}
{% endtab %}

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

```swift
let trailer_dict: PTObj = doc.getTrailer()
let info_itr: PTDictIterator = trailer_dict.get("Info")
let info_dict: PTObj = info_itr.value()

// Create an inline dictionary
let direct_obj: PTObj = info_dict.putDict("Link1")

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
let root_itr: PTDictIterator = trailer_dict.get("Root")
let root_dict: PTObj = root_itr.value()
root_dict.put("Link2", direct_obj)
```

{% endcode %}
{% endtab %}

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

```php
$trailer_dict = $doc->GetTrailer();
$info_itr = $trailer_dict->Get("Info");
$info_dict = $info_itr->Value();

// Create an inline dictionary
$direct_obj = $info_dict->PutDict("Link1");

// Attempt to create a second link to direct_obj.
// This will copy the object. If you want to
// share objects, create them using the
// PDFDoc.CreateIndirect() methods.
$root_itr = $trailer_dict->Get("Root");
$root_dict = $root_itr->Value();
$root_dict->Put("Link2", $direct_obj);
```

{% endcode %}
{% endtab %}

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

```python
trailer_dict = doc.GetTrailer()
info_itr = trailer_dict.Get("Info")
info_dict = info_itr.Value()

# Create an inline dictionary
direct_obj = info_dict.PutDict("Link1")

# Attempt to create a second link to direct_obj.
# This will copy the object. If you want to
# share objects, create them using the
# PDFDoc.CreateIndirect() methods.
root_itr = trailer_dict.Get("Root")
root_dict = root_itr.Value()
root_dict.Put("Link2", direct_obj)
```

{% endcode %}
{% endtab %}

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

```ruby
trailer_dict = doc.GetTrailer()
info_itr = trailer_dict.Get("Info")
info_dict = info_itr.Value()

# Create an inline dictionary
direct_obj = info_dict.PutDict("Link1")

# Attempt to create a second link to direct_obj.
# This will copy the object. If you want to
# share objects, create them using the
# PDFDoc.CreateIndirect() methods.
root_itr = trailer_dict.Get("Root")
root_dict = root_itr.Value()
root_dict.Put("Link2", direct_obj)
```

{% endcode %}
{% endtab %}

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

```vb
Dim trailer_dict As Obj = doc.GetTrailer()
Dim info_itr As DictIterator = trailer_dict.Get("Info")
Dim info_dict As Obj = info_itr.Value()

' Create an inline dictionary
Dim direct_obj As Obj = info_dict.PutDict("Link1")

' Attempt to create a second link to direct_obj.
' This will copy the object. If you want to
' share objects, create them using the
' PDFDoc.CreateIndirect() methods.
Dim root_itr As DictIterator = trailer_dict.Get("Root")
Dim root_dict As Obj = root_itr.Value()
root_dict.Put("Link2", direct_obj)
```

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

In addition to the basic types of objects mentioned so far, PDF also supports stream objects. A stream object is essentially a dictionary with an attached binary stream. In Apryse SDK, all methods that apply to dictionaries apply to streams as well.

For a more complete discussion on Apryse SDK Filters see Apryse SDK [Filters and Streams ](/core/low-level-pdf-api/filters.md).

## SDFDoc

Our overview of the SDF object model could not be complete without mentioning SDFDoc. SDFDoc brings together document security, document utility methods, and all SDF objects.

An SDF document can be created from scratch using a default constructor `new SDFDoc()` or from an existing file `new SDFDoc(filename)`. It can be created from a memory buffer `new SDFDoc(memoryFilter)` or some other Filter/Stream as well. Finally, an SDF document can be accessed from a high-level PDF document using `doc.GetSDFDoc()`.

Note that the examples above use `sdfdoc.GetTrailer()` in order to access the document trailer, which is the starting SDF object (root node) in every document. Following the trailer links, we can visit all low-level objects in a document (e.g. all pages, outlines, fonts, and so on).

SDFDoc also provides utility methods used to import objects and object collections from one document to another. These methods can be useful for copy operations between documents such as a high-level page merge and document assembly.


---

# 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/core/low-level-pdf-api/sdf.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.
