> 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/get-started/samples/logicalstructuretest.md).

# PDF Logical Structure Reader - LogicalStructure

Sample code for using Apryse Server SDK to explore the logical structure and content of a tagged PDF file, then dumps the information to the console window. Sample code provided in Python, C++, C#, Ja

Sample code for using Apryse Server SDK to explore the logical structure and content of a tagged PDF file, then dumps the information to the console window. In tagged PDF files, StructTree acts as a central repository for information related to a PDF document's logical structure. The tree consists of StructElement-s and ContentItem-s which are leaf nodes of the structure tree. Sample code provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Learn more about our full [PDF Data Extraction SDK Capabilities](https://apryse.com/capabilities/extraction).

To start your free trial, [get stated with Server SDK](/core/get-started/get-started.md).

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

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

using System;
using System.Collections;

using pdftron;
using pdftron.Common;
using pdftron.Filters;
using pdftron.SDF;
using pdftron.PDF;
using pdftron.PDF.Struct;


namespace LogicalStructureTestCS
{
	//---------------------------------------------------------------------------------------
	// This sample explores the structure and content of a tagged PDF document and dumps 
	// the structure information to the console window.
	//
	// In tagged PDF documents StructTree acts as a central repository for information 
	// related to a PDF document's logical structure. The tree consists of StructElement-s
	// and ContentItem-s which are leaf nodes of the structure tree.
	//
	// The sample can be extended to access and extract the marked-content elements such 
	// as text and images.
	//---------------------------------------------------------------------------------------
	class Class1
	{
		static void PrintIndent(int indent) { Console.WriteLine(); for (int i=0; i<indent; ++i) Console.Write("  "); }

		// Used in code snippet 1.
		static void ProcessStructElement(SElement element, int indent)
		{
			if (!element.IsValid()) {
				return;
			}

			// Print out the type and title info, if any.
			PrintIndent(indent++);
			Console.Write("Type: " + element.GetType());
			if (element.HasTitle()) {
				Console.Write(". Title: "+ element.GetTitle());
			}

			int num = element.GetNumKids();
			for (int i=0; i<num; ++i) 
			{
				// Check is the kid is a leaf node (i.e. it is a ContentItem).
				if (element.IsContentItem(i)) { 
					ContentItem cont = element.GetAsContentItem(i); 
					ContentItem.Type type = cont.GetType();

					Page page = cont.GetPage();

					PrintIndent(indent);
					Console.Write("Content Item. Part of page #" + page.GetIndex());

					PrintIndent(indent);
					switch (type) {
						case ContentItem.Type.e_MCID:
						case ContentItem.Type.e_MCR:
							Console.Write("MCID: " + cont.GetMCID());
							break;
						case ContentItem.Type.e_OBJR:
							{
								Console.Write("OBJR ");
								Obj ref_obj = cont.GetRefObj();
								if (ref_obj!=null)
									Console.Write("- Referenced Object#: " + ref_obj.GetObjNum());
							}
							break;
						default: 
							break;
					}
				}
				else {  // the kid is another StructElement node.
					ProcessStructElement(element.GetAsStructElem(i), indent);
				}
			}
		}

		// Used in code snippet 2.
		static void ProcessElements(ElementReader reader)
		{
			Element element;
			while ((element = reader.Next())!=null) 	// Read page contents
			{
				// In this sample we process only paths & text, but the code can be 
				// extended to handle any element type.
				Element.Type type = element.GetType();
				if (type == Element.Type.e_path || type == Element.Type.e_text || type == Element.Type.e_path) 
				{   
					switch (type)	{
					case Element.Type.e_path:               // Process path ...
						Console.WriteLine();
						Console.Write("PATH: ");
						break; 
					case Element.Type.e_text: 				// Process text ...
						Console.WriteLine();
						Console.WriteLine("TEXT: " + element.GetTextString());
						break;
					case Element.Type.e_form:				// Process form XObjects
						Console.WriteLine();
						Console.Write("FORM XObject: ");
						//reader.FormBegin(); 
						//ProcessElements(reader);
						//reader.End(); 
						break; 
					}

					// Check if the element is associated with any structural element.
					// Content items are leaf nodes of the structure tree.
					SElement struct_parent = element.GetParentStructElement();
					if (struct_parent.IsValid()) {
						// Print out the parent structural element's type, title, and object number.
						Console.Write(" Type: " + struct_parent.GetType() 
							+ ", MCID: " + element.GetStructMCID());
						if (struct_parent.HasTitle()) {
							Console.Write(". Title: "+ struct_parent.GetTitle());
						}
						Console.Write(", Obj#: " + struct_parent.GetSDFObj().GetObjNum());
					}
				}
			}
		}

		// Used in code snippet 3.
		//typedef map<int, string> MCIDPageMap;
		//typedef map<int, MCIDPageMap> MCIDDocMap;

		// Used in code snippet 3.
		static void ProcessElements2(ElementReader reader, Hashtable mcid_page_map)
		{
			Element element;
			while ((element = reader.Next())!=null) // Read page contents
			{
				// In this sample we process only text, but the code can be extended 
				// to handle paths, images, or any other Element type.
				int mcid = element.GetStructMCID();
				if (mcid>= 0 && element.GetType() == Element.Type.e_text) {
					String val = element.GetTextString();
					if (mcid_page_map.ContainsKey(mcid)) mcid_page_map[mcid] = ((String)(mcid_page_map[mcid])+ val); 
					else mcid_page_map.Add(mcid, val);
				}
			}
		}

		// Used in code snippet 3.
		static void ProcessStructElement2(SElement element, Hashtable mcid_doc_map, int indent)
		{
			if (!element.IsValid()) {
				return;
			}

			// Print out the type and title info, if any.
			PrintIndent(indent);
			Console.Write("<" + element.GetType());
			if (element.HasTitle()) {
				Console.Write(" title=\""+ element.GetTitle() + "\"");
			}
			Console.Write(">");

			int num = element.GetNumKids();
			for (int i=0; i<num; ++i) 
			{		
				if (element.IsContentItem(i)) { 
					ContentItem cont = element.GetAsContentItem(i); 
					if (cont.GetType() == ContentItem.Type.e_MCID) {
						int page_num = cont.GetPage().GetIndex();
						if (mcid_doc_map.ContainsKey(page_num)) {
							Hashtable mcid_page_map = (Hashtable)(mcid_doc_map[page_num]);
							int mcid = cont.GetMCID();
							if (mcid_page_map.ContainsKey(mcid)) {
								Console.Write(mcid_page_map[mcid]); 
							}                    
						}
					}
				}
				else {  // the kid is another StructElement node.
					ProcessStructElement2(element.GetAsStructElem(i), mcid_doc_map, indent+1);
				}
			}

			PrintIndent(indent);
			Console.Write("</" + element.GetType() + ">");
		}

		private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
		static Class1() {}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		static void Main(string[] args)
		{
			PDFNet.Initialize(PDFTronLicense.Key);
			// Relative path to the folder containing test files.
			string input_path =  "../../../../TestFiles/";
			string output_path = "../../../../TestFiles/Output/";

			try  // Extract logical structure from a PDF document
			{
				using (PDFDoc doc = new PDFDoc(input_path + "tagged.pdf"))
				{
					doc.InitSecurityHandler();

					bool example1 = true;
					bool example2 = true;
					bool example3 = true;

					if (example1)
					{
						Console.WriteLine("____________________________________________________________");
						Console.WriteLine("Sample 1 - Traverse logical structure tree...");

						STree tree = doc.GetStructTree();
						if (tree.IsValid()) 
						{
							Console.WriteLine("Document has a StructTree root.");
							for (int i=0; i<tree.GetNumKids(); ++i) 
							{
								// Recursively get structure  info for all all child elements.
								ProcessStructElement(tree.GetKid(i), 0);
							}
						}
						else 
						{
							Console.WriteLine("This document does not contain any logical structure.");
						}

						Console.WriteLine();
						Console.WriteLine("Done 1.");
					}

					if (example2)
					{
						Console.WriteLine("____________________________________________________________");
						Console.WriteLine("Sample 2 - Get parent logical structure elements from");
						Console.WriteLine("layout elements.");
						
						ElementReader reader=new ElementReader();
						for (PageIterator itr = doc.GetPageIterator(); itr.HasNext(); itr.Next()) 
						{				
							reader.Begin(itr.Current());
							ProcessElements(reader);
							reader.End();
						}
						Console.WriteLine();
						Console.WriteLine("Done 2.");
					}

					if (example3)
					{
						Console.WriteLine("____________________________________________________________");
						Console.WriteLine("Sample 3 - 'XML style' extraction of PDF logical structure and page content.");
						
						//A map which maps page numbers(as Integers)
						//to page Maps(which map from struct mcid(as Integers) to
						//text Strings)
						Hashtable mcid_doc_map=new Hashtable();
						ElementReader reader=new ElementReader();
						for (PageIterator itr = doc.GetPageIterator(); itr.HasNext(); itr.Next()) 
						{				
							Page pg = itr.Current();
							reader.Begin(pg);
							Hashtable page_mcid_map=new Hashtable();
							mcid_doc_map.Add(pg.GetIndex(), page_mcid_map);
							ProcessElements2(reader, page_mcid_map);
							reader.End();
						}
						
						STree tree = doc.GetStructTree();
						if (tree.IsValid()) 
						{
							for (int i=0; i<tree.GetNumKids(); ++i) 
							{
								ProcessStructElement2(tree.GetKid(i), mcid_doc_map, 0);
							}
						}
						Console.WriteLine();
						Console.WriteLine("Done 3.");
					}

					doc.Save(output_path + "LogicalStructure.pdf", 0);
				}
			}
			catch (PDFNetException e)
			{
				Console.WriteLine(e.Message);
			}
			PDFNet.Terminate();
		}
	}
}
```

{% endcode %}
{% endtab %}

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

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

#include <PDF/PDFNet.h>
#include <PDF/PDFDoc.h>
#include <PDF/ElementReader.h>
#include <iostream>
#include <map>
#include "../../LicenseKey/CPP/LicenseKey.h"

using namespace pdftron;
using namespace PDF;
using namespace std;

//---------------------------------------------------------------------------------------
// This sample explores the structure and content of a tagged PDF document and dumps 
// the structure information to the console window.
//
// In tagged PDF documents StructTree acts as a central repository for information 
// related to a PDF document's logical structure. The tree consists of StructElement-s
// and ContentItem-s which are leaf nodes of the structure tree.
//
// The sample can be extended to access and extract the marked-content elements such 
// as text and images.
//---------------------------------------------------------------------------------------


void PrintIndent(int indent) { cout << '\n'; for (int i=0; i<indent; ++i) cout << "  "; }

// Used in code snippet 1.
void ProcessStructElement(Struct::SElement element, int ident) 
{
	if (!element.IsValid()) {
		return;
	}

	// Print out the type and title info, if any.
	PrintIndent(ident++);
	cout << "Type: "<< element.GetType();
	if (element.HasTitle()) {
		cout << ". Title: "<< element.GetTitle();
	}

	int num = element.GetNumKids();
	for (int i=0; i<num; ++i) 
	{
		// Check is the kid is a leaf node (i.e. it is a ContentItem).
		if (element.IsContentItem(i)) { 
			Struct::ContentItem cont = element.GetAsContentItem(i); 
			Struct::ContentItem::Type type = cont.GetType();

			Page page = cont.GetPage();

			PrintIndent(ident);
			cout << "Content Item. Part of page #" << page.GetIndex();

			PrintIndent(ident);
			switch (type) {
				case Struct::ContentItem::e_MCID:
				case Struct::ContentItem::e_MCR:
					cout << "MCID: " << cont.GetMCID();
					break;
				case Struct::ContentItem::e_OBJR:
					{
						cout << "OBJR ";
						if (SDF::Obj ref_obj = cont.GetRefObj())
							cout << "- Referenced Object#: " << ref_obj.GetObjNum();
					}
					break;
				default: 
					break;
			}
		}
		else {  // the kid is another StructElement node.
			ProcessStructElement(element.GetAsStructElem(i), ident);
		}
	}
}

// Used in code snippet 2.
void ProcessElements(ElementReader& reader) 
{
	Element element;
	while (element = reader.Next()) 	// Read page contents
	{
		// In this sample we process only paths & text, but the code can be 
		// extended to handle any element type.
		Element::Type type = element.GetType();
		if (type == Element::e_path || type == Element::e_text || type == Element::e_path) 
		{   
			switch (type)	{
			case Element::e_path:				// Process path ...
				cout << "\nPATH: ";
				break; 
			case Element::e_text: 				// Process text ...
				cout << "\nTEXT: " << element.GetTextString() << endl;
				break;
			case Element::e_form:				// Process form XObjects
				cout << "\nFORM XObject: ";
				//reader.FormBegin(); 
				//ProcessElements(reader);
				//reader.End(); 
				break; 
			}

			// Check if the element is associated with any structural element.
			// Content items are leaf nodes of the structure tree.
			Struct::SElement struct_parent = element.GetParentStructElement();
			if (struct_parent.IsValid()) {
				// Print out the parent structural element's type, title, and object number.
				cout << " Type: " << struct_parent.GetType() 
					<< ", MCID: " << element.GetStructMCID();
				if (struct_parent.HasTitle()) {
					cout << ". Title: "<< struct_parent.GetTitle();
				}
				cout << ", Obj#: " << struct_parent.GetSDFObj().GetObjNum();
			}
		}
	}
}

// Used in code snippet 3.
typedef map<int, string> MCIDPageMap;
typedef map<int, MCIDPageMap> MCIDDocMap;

// Used in code snippet 3.
void ProcessElements2(ElementReader& reader, MCIDPageMap& mcid_page_map) 
{
	Element element;
	while (element = reader.Next()) // Read page contents
	{
		// In this sample we process only text, but the code can be extended 
		// to handle paths, images, or any other Element type.
		int mcid = element.GetStructMCID();
		if (mcid>= 0 && element.GetType() == Element::e_text) {
			string val = element.GetTextString().ConvertToAscii();
			MCIDPageMap::iterator itr = mcid_page_map.find(mcid);
			if (itr != mcid_page_map.end()) itr->second += val; 
			else mcid_page_map.insert(MCIDPageMap::value_type(mcid, val));
		}
	}
}

// Used in code snippet 3.
void ProcessStructElement2(Struct::SElement element, MCIDDocMap& mcid_doc_map, int ident) 
{
	if (!element.IsValid()) {
		return;
	}

	// Print out the type and title info, if any.
	PrintIndent(ident);
	cout << "<" << element.GetType();
	if (element.HasTitle()) {
		cout << " title=\""<< element.GetTitle() << "\"";
	}
	cout << ">";

	int num = element.GetNumKids();
	for (int i=0; i<num; ++i) 
	{		
		if (element.IsContentItem(i)) { 
			Struct::ContentItem cont = element.GetAsContentItem(i); 
			if (cont.GetType() == Struct::ContentItem::e_MCID) {
				int page_num = cont.GetPage().GetIndex();
				MCIDDocMap::iterator itr = mcid_doc_map.find(page_num);
				if (itr!=mcid_doc_map.end()) {
					MCIDPageMap& mcid_page_map = itr->second;
					MCIDPageMap::iterator itr2 = mcid_page_map.find(cont.GetMCID());
					if (itr2 != mcid_page_map.end()) {
						cout << itr2->second; 
					}                    
				}
			}
		}
		else {  // the kid is another StructElement node.
			ProcessStructElement2(element.GetAsStructElem(i), mcid_doc_map, ident+1);
		}
	}

	PrintIndent(ident);
	cout << "</" << element.GetType() << ">";
}


int main(int argc, char *argv[])
{
	int ret = 0;
	PDFNet::Initialize(LicenseKey);

	// Relative path to the folder containing test files.
	string input_path =  "../../TestFiles/";
	string output_path = "../../TestFiles/Output/";

	try	// Extract logical structure from a PDF document
	{
		PDFDoc doc((input_path + "tagged.pdf").c_str());
		doc.InitSecurityHandler();

		cout << "____________________________________________________________" << endl;
		cout << "Sample 1 - Traverse logical structure tree..." << endl;
		{
			Struct::STree tree = doc.GetStructTree();
			if (tree.IsValid()) {
				cout << "Document has a StructTree root." << endl;				

				for (int i=0; i<tree.GetNumKids(); ++i) {
					// Recursively get structure info for all child elements.
					ProcessStructElement(tree.GetKid(i), 0);
				}
			}
			else {
				cout << "This document does not contain any logical structure." << endl;
			}
		}
		cout << "\nDone 1." << endl;

		cout << "____________________________________________________________" << endl;
		cout << "Sample 2 - Get parent logical structure elements from" << endl;
		cout << "layout elements." << endl;
		{
			ElementReader reader;
			for (PageIterator itr = doc.GetPageIterator(); itr.HasNext(); itr.Next()) {				
				reader.Begin(itr.Current());
				ProcessElements(reader);
				reader.End();
			}
		}
		cout << "\nDone 2." << endl;

		cout << "____________________________________________________________" << endl;
		cout << "Sample 3 - 'XML style' extraction of PDF logical structure and page content." << endl;
		{
			MCIDDocMap mcid_doc_map;
			ElementReader reader;
			for (PageIterator itr = doc.GetPageIterator(); itr.HasNext(); itr.Next()) {				
				reader.Begin(itr.Current());
				pair<MCIDDocMap::iterator, bool> r = mcid_doc_map.insert(MCIDDocMap::value_type(itr.Current().GetIndex(), MCIDPageMap()));
				MCIDPageMap& page_mcid_map = (r.first)->second;
				ProcessElements2(reader, page_mcid_map);
				reader.End();
			}

			Struct::STree tree = doc.GetStructTree();
			if (tree.IsValid()) {
				for (int i=0; i<tree.GetNumKids(); ++i) {
					ProcessStructElement2(tree.GetKid(i), mcid_doc_map, 0);
				}
			}
		}
		cout << "\nDone 3." << endl;

		doc.Save(output_path + "LogicalStructure.pdf", 0);
	}
	catch(Common::Exception& e) 
	{
		cout << e << endl;
		ret = 1;
	}
	catch(...) 
	{
		cout << "Unknown Exception" << endl;
		ret = 1;
	}

	PDFNet::Terminate();
	return ret;
}
```

{% endcode %}
{% endtab %}

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

```go
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2021 by PDFTron Systems Inc. All Rights Reserved.
// Consult LICENSE.txt regarding license information.
//---------------------------------------------------------------------------------------

package main
import (
	"fmt"
    "strconv"
    "os"
	. "pdftron"
)

import  "pdftron/Samples/LicenseKey/GO"

//---------------------------------------------------------------------------------------
// This sample explores the structure and content of a tagged PDF document and dumps 
// the structure information to the console window.
//
// In tagged PDF documents StructTree acts as a central repository for information 
// related to a PDF document's logical structure. The tree consists of StructElement-s
// and ContentItem-s which are leaf nodes of the structure tree.
//
// The sample can be extended to access and extract the marked-content elements such 
// as text and images.
//---------------------------------------------------------------------------------------

func PrintIndent(indent int){
   os.Stdout.Write([]byte("\n"))
   i := 0
    for i < indent{
        os.Stdout.Write([]byte("  "))
        i = i + 1
    }
}

func ProcessStructElement(element SElement, indent int){
    if !element.IsValid(){
        return
    }

    // Print out the type and title info, if any.
    PrintIndent(indent)
    indent = indent + 1
    os.Stdout.Write([]byte("Type: " + element.GetType()))
    if element.HasTitle(){
        os.Stdout.Write([]byte(". Title:" + element.GetTitle()))
    }
    num := element.GetNumKids()
    i := 0
    for i < num{
        // Check if the kid is a leaf node (i.e. it is a ContentItem)
        if element.IsContentItem(i){
            cont := element.GetAsContentItem(i)
            etype := cont.GetType()
            
            page := cont.GetPage()
            
            PrintIndent(indent)
            os.Stdout.Write([]byte("Content Item. Part of page //" + strconv.Itoa(page.GetIndex())))
            PrintIndent(indent)
            if etype == ContentItemE_MCID{
                os.Stdout.Write([]byte("MCID: " + strconv.Itoa(cont.GetMCID())))
            }else if etype == ContentItemE_MCR{
                os.Stdout.Write([]byte("MCID: " + strconv.Itoa(cont.GetMCID())))
            }else if etype == ContentItemE_OBJR{
                os.Stdout.Write([]byte("OBJR "))
                refObj := cont.GetRefObj()
                if refObj != nil{
                    os.Stdout.Write([]byte("- Referenced Object//: " + strconv.Itoa(int(refObj.GetObjNum()))))
                }
            }
        }else{
            ProcessStructElement(element.GetAsStructElem(i), indent)
        }
        i = i + 1
    }
}    

// Used in code snippet 3.
func ProcessElements2(reader ElementReader, mcidPageMap map[int]string){
    element := reader.Next()
    for element.GetMp_elem().Swigcptr() != 0{ // Read page contents
        // In this sample we process only text, but the code can be extended
        // to handle paths, images, or other Element type.
        mcid := element.GetStructMCID()
        
        if mcid >= 0 && element.GetType() == ElementE_text{
            val := element.GetTextString()
            if _, ok := mcidPageMap[mcid]; ok {
                mcidPageMap[mcid] = mcidPageMap[mcid] + val
            }else{
                mcidPageMap[mcid] = val
            }
        }
        element = reader.Next()
    }
}

// Used in code snippet 2.
func ProcessElements(reader ElementReader){
    element := reader.Next()
    for element.GetMp_elem().Swigcptr() != 0{  // Read page contents
        // In this sample we process only paths & text, but the code can be 
        // extended to handle any element type.
        etype := element.GetType()
        if (etype == ElementE_path ||
            etype == ElementE_text ||
            etype == ElementE_path){
            if etype == ElementE_path{      // Process path ...
                os.Stdout.Write([]byte("\nPATH: "))
            }else if etype == ElementE_text{    // Process text ...
                os.Stdout.Write([]byte("\nTEXT: " + element.GetTextString() + "\n"))
            }else if etype == ElementE_path{    // Process from XObjects
                os.Stdout.Write([]byte("\nFORM XObject: "))
            }

            // Check if the element is associated with any structural element.
            // Content items are leaf nodes of the structure tree.
            structParent := element.GetParentStructElement()
            if structParent.IsValid(){
                // Print out the parent structural element's type, title, and object number.
                os.Stdout.Write([]byte(" Type: " + structParent.GetType() + ", MCID: " + strconv.Itoa(element.GetStructMCID())))
                if structParent.HasTitle(){
                    os.Stdout.Write([]byte(". Title: " + structParent.GetTitle()))
                }
                os.Stdout.Write([]byte(", Obj//: " + strconv.Itoa(int(structParent.GetSDFObj().GetObjNum()))))
            }
        }
        element = reader.Next()
    }
}        
        
func ProcessStructElement2(element SElement, mcidDocMap map[int](map[int]string), indent int){
    if !element.IsValid(){
        return
    }
    // Print out the type and title info, if any
    PrintIndent(indent)
    os.Stdout.Write([]byte("<" + element.GetType()))
    if element.HasTitle(){
        os.Stdout.Write([]byte(" title=\"" + element.GetTitle() + "\""))
    }
    os.Stdout.Write([]byte(">"))
    
    num := element.GetNumKids()
    i := 0
    for i < num{
        if element.IsContentItem(i){
            cont := element.GetAsContentItem(i)
            if cont.GetType() == ContentItemE_MCID{
                pageNum := cont.GetPage().GetIndex()
                if _, ok := mcidDocMap[pageNum]; ok{
                    mcidPageMap := mcidDocMap[pageNum]
                    mcidKey := cont.GetMCID()
                    if _, ok := mcidPageMap[mcidKey]; ok{
                        os.Stdout.Write([]byte(mcidPageMap[mcidKey]))
                    }
                }
            }
        }else{ // the kid is another StructElement node.
            ProcessStructElement2(element.GetAsStructElem(i), mcidDocMap, indent+1)     
        } 
        i = i + 1
    }
    PrintIndent(indent)
    os.Stdout.Write([]byte("</" + element.GetType() + ">"))
}        

func main(){
    PDFNetInitialize(PDFTronLicense.Key)
    
    // Relative path to the folder containing the test files.
    inputPath := "../../TestFiles/"
    outputPath := "../../TestFiles/Output/"
    
    // Extract logical structure from a PDF document
    doc := NewPDFDoc(inputPath + "tagged.pdf")
    doc.InitSecurityHandler()
    
    fmt.Println("____________________________________________________________")
    fmt.Println("Sample 1 - Traverse logical structure tree...")
    
    tree := doc.GetStructTree()
    if tree.IsValid(){
        fmt.Println("Document has a StructTree root.")
        
        i := 0
        for i < tree.GetNumKids(){
            // Recursively get structure info for all child elements.
            ProcessStructElement(tree.GetKid(i), 0)
            i = i + 1
        }
    }else{
        fmt.Println("This document does not contain any logical structure.")
    }

    fmt.Println("\nDone 1.")

    fmt.Println("____________________________________________________________")
    fmt.Println("Sample 2 - Get parent logical structure elements from")
    fmt.Println("layout elements.")
    
    reader := NewElementReader()
    itr := doc.GetPageIterator()
    for itr.HasNext(){
        reader.Begin(itr.Current())
        ProcessElements(reader)
        reader.End()
        itr.Next()
    }

    fmt.Println("\nDone 2.")
    
    fmt.Println("____________________________________________________________")
    fmt.Println("Sample 3 - 'XML style' extraction of PDF logical structure and page content.")
    // A map which maps page numbers(as Integers)
    // to page Maps(which map from struct mcid(as Integers) to
    // text Strings)
    var mcidDocMap = make(map[int](map[int]string))
    reader = NewElementReader()
    itr = doc.GetPageIterator()
    for itr.HasNext(){
        reader.Begin(itr.Current())
        var pageMcidMap = make(map[int]string)
        mcidDocMap[itr.Current().GetIndex()] = pageMcidMap
        ProcessElements2(reader, pageMcidMap)
        reader.End()
        itr.Next() 
    } 
    tree = doc.GetStructTree()
    if tree.IsValid(){
        i := 0
        for i < tree.GetNumKids(){
            ProcessStructElement2(tree.GetKid(i), mcidDocMap, 0)
            i = i + 1  
        }
    }
    fmt.Println("\nDone 3.")
    doc.Save(outputPath + "LogicalStructure.pdf", uint(SDFDocE_linearized))
    doc.Close()        
    PDFNetTerminate()
}
```

{% endcode %}
{% endtab %}

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

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

import java.util.Map;
import java.util.TreeMap;

import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.struct.*;
import com.pdftron.pdf.*;
import com.pdftron.sdf.*;

//---------------------------------------------------------------------------------------
// This sample explores the structure and content of a tagged PDF document and dumps 
// the structure information to the console window.
//
// In tagged PDF documents StructTree acts as a central repository for information 
// related to a PDF document's logical structure. The tree consists of StructElement-s
// and ContentItem-s which are leaf nodes of the structure tree.
//
// The sample can be extended to access and extract the marked-content elements such 
// as text and images.
//---------------------------------------------------------------------------------------
public class LogicalStructureTest {
    static void PrintIndent(int indent) {
        System.out.println();
        for (int i = 0; i < indent; ++i) System.out.print("  ");
    }

    // Used in code snippet 1.
    static void ProcessStructElement(SElement element, int indent) throws PDFNetException {
        if (!element.isValid()) {
            return;
        }

        // Print out the type and title info, if any.
        PrintIndent(indent++);
        System.out.print("Type: " + element.getType());
        if (element.hasTitle()) {
            System.out.print(". Title: " + element.getTitle());
        }

        int num = element.getNumKids();
        for (int i = 0; i < num; ++i) {
            // Check is the kid is a leaf node (i.e. it is a ContentItem).
            if (element.isContentItem(i)) {
                ContentItem cont = element.getAsContentItem(i);
                int type = cont.getType();

                Page page = cont.getPage();

                PrintIndent(indent);
                System.out.print("Content Item. Part of page #" + page.getIndex());

                PrintIndent(indent);
                switch (type) {
                    case ContentItem.e_MCID:
                    case ContentItem.e_MCR:
                        System.out.print("MCID: " + cont.getMCID());
                        break;
                    case ContentItem.e_OBJR: {
                        System.out.print("OBJR ");
                        Obj ref_obj = cont.getRefObj();
                        if (ref_obj != null)
                            System.out.print("- Referenced Object#: " + ref_obj.getObjNum());
                    }
                    break;
                    default:
                        break;
                }
            } else {  // the kid is another StructElement node.
                ProcessStructElement(element.getAsStructElem(i), indent);
            }
        }
    }

    // Used in code snippet 2.
    static void ProcessElements(ElementReader reader) throws PDFNetException {
        Element element;
        while ((element = reader.next()) != null)    // Read page contents
        {
            // In this sample we process only paths & text, but the code can be
            // extended to handle any element type.
            int type = element.getType();
            if (type == Element.e_path || type == Element.e_text || type == Element.e_path) {
                switch (type) {
                    case Element.e_path:                // Process path ...
                        System.out.print("\nPATH: ");
                        break;
                    case Element.e_text:                // Process text ...
                        System.out.print("\nTEXT: " + element.getTextString() + "\n");
                        break;
                    case Element.e_form:                // Process form XObjects
                        System.out.print("\nFORM XObject: ");
                        //reader.FormBegin();
                        //ProcessElements(reader);
                        //reader.End();
                        break;
                }

                // Check if the element is associated with any structural element.
                // Content items are leaf nodes of the structure tree.
                SElement struct_parent = element.getParentStructElement();
                if (struct_parent.isValid()) {
                    // Print out the parent structural element's type, title, and object number.
                    System.out.print(" Type: " + struct_parent.getType()
                            + ", MCID: " + element.getStructMCID());
                    if (struct_parent.hasTitle()) {
                        System.out.print(". Title: " + struct_parent.getTitle());
                    }
                    System.out.print(", Obj#: " + struct_parent.getSDFObj().getObjNum());
                }
            }
        }
    }

    // Used in code snippet 3.
    //typedef map<int, string> MCIDPageMap;
    //typedef map<int, MCIDPageMap> MCIDDocMap;

    // Used in code snippet 3.
    static void ProcessElements2(ElementReader reader, Map<Integer, String> mcid_page_map) throws PDFNetException {
        Element element;
        while ((element = reader.next()) != null) // Read page contents
        {
            // In this sample we process only text, but the code can be extended
            // to handle paths, images, or any other Element type.
            int mcid = element.getStructMCID();
            Integer key_mcid = new Integer(mcid);
            if (mcid >= 0 && element.getType() == Element.e_text) {
                String val = element.getTextString();
                if (mcid_page_map.containsKey(key_mcid))
                    mcid_page_map.put(key_mcid, ((String) (mcid_page_map.get(key_mcid)) + val));
                else mcid_page_map.put(key_mcid, val);
            }
        }
    }

    // Used in code snippet 3.
    static void ProcessStructElement2(SElement element, Map<Integer, Map<Integer, String>> mcid_doc_map, int indent) throws PDFNetException {
        if (!element.isValid()) {
            return;
        }

        // Print out the type and title info, if any.
        PrintIndent(indent);
        System.out.print("<" + element.getType());
        if (element.hasTitle()) {
            System.out.print(" title=\"" + element.getTitle() + "\"");
        }
        System.out.print(">");

        int num = element.getNumKids();
        for (int i = 0; i < num; ++i) {
            if (element.isContentItem(i)) {
                ContentItem cont = element.getAsContentItem(i);
                if (cont.getType() == ContentItem.e_MCID) {
                    int page_num = cont.getPage().getIndex();
                    Integer page_num_key = new Integer(page_num);
                    if (mcid_doc_map.containsKey(page_num_key)) {
                        Map<Integer, String> mcid_page_map = mcid_doc_map.get(page_num_key);
                        Integer mcid_key = new Integer(cont.getMCID());
                        if (mcid_page_map.containsKey(mcid_key)) {
                            System.out.print(mcid_page_map.get(mcid_key));
                        }
                    }
                }
            } else {  // the kid is another StructElement node.
                ProcessStructElement2(element.getAsStructElem(i), mcid_doc_map, indent + 1);
            }
        }

        PrintIndent(indent);
        System.out.print("</" + element.getType() + ">");
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        PDFNet.initialize(PDFTronLicense.Key());

        // Relative path to the folder containing test files.
        String input_path = "../../TestFiles/";
        String output_path = "../../TestFiles/Output/";

        try (PDFDoc doc = new PDFDoc((input_path + "tagged.pdf")))    // Extract logical structure from a PDF document
        {
            doc.initSecurityHandler();

            System.out.println("____________________________________________________________");
            System.out.println("Sample 1 - Traverse logical structure tree...");
            {
                STree tree = doc.getStructTree();
                if (tree.isValid()) {
                    System.out.println("Document has a StructTree root.");

                    for (int i = 0; i < tree.getNumKids(); ++i) {
                        // Recursively get structure  info for all all child elements.
                        ProcessStructElement(tree.getKid(i), 0);
                    }
                } else {
                    System.out.println("This document does not contain any logical structure.");
                }
            }
            System.out.println("\nDone 1.");

            System.out.println("____________________________________________________________");
            System.out.println("Sample 2 - Get parent logical structure elements from");
            System.out.println("layout elements.");
            {
                ElementReader reader = new ElementReader();
                for (PageIterator itr = doc.getPageIterator(); itr.hasNext(); ) {
                    reader.begin(itr.next());
                    ProcessElements(reader);
                    reader.end();
                }
            }
            System.out.println("\nDone 2.");

            System.out.println("____________________________________________________________");
            System.out.println("Sample 3 - 'XML style' extraction of PDF logical structure and page content.");
            {
                //A map which maps page numbers(as Integers)
                //to page Maps(which map from struct mcid(as Integers) to
                //text Strings)
                Map<Integer, Map<Integer, String>> mcid_doc_map = new TreeMap<Integer, Map<Integer, String>>();
                ElementReader reader = new ElementReader();
                for (PageIterator itr = doc.getPageIterator(); itr.hasNext(); ) {
                    Page current = itr.next();
                    reader.begin(current);
                    Map<Integer, String> page_mcid_map = new TreeMap<Integer, String>();
                    mcid_doc_map.put(new Integer(current.getIndex()), page_mcid_map);
                    ProcessElements2(reader, page_mcid_map);
                    reader.end();
                }

                STree tree = doc.getStructTree();
                if (tree.isValid()) {
                    for (int i = 0; i < tree.getNumKids(); ++i) {
                        ProcessStructElement2(tree.getKid(i), mcid_doc_map, 0);
                    }
                }
            }
            System.out.println("\nDone 3.");
            doc.save((output_path + "LogicalStructure.pdf"), SDFDoc.SaveMode.LINEARIZED, null);
        } catch (Exception e) {
            e.printStackTrace();
        }

        PDFNet.terminate();
    }

}
```

{% endcode %}
{% endtab %}

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

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

//---------------------------------------------------------------------------------------
// This sample explores the structure and content of a tagged PDF document and dumps
// the structure information to the console window.
//
// In tagged PDF documents StructTree acts as a central repository for information
// related to a PDF document's logical structure. The tree consists of StructElement-s
// and ContentItem-s which are leaf nodes of the structure tree.
//
// The sample can be extended to access and extract the marked-content elements such
// as text and images.
//---------------------------------------------------------------------------------------


const { PDFNet } = require('@pdftron/pdfnet-node');
const PDFTronLicense = require('../LicenseKey/LicenseKey');

((exports) => {

  exports.runLogicalStructureTest = () => {

    const printAndIndent = (printState, indent) => {
      console.log(printState.str);

      let indentStr = '';
      for (let i = 0; i < indent; ++i) {
        indentStr += '  ';
      }
    printState.str = indentStr;
    };

    // Used in code snippet 1.
    const processStructElement = async(element, indent, printState) => {
      if (!(await element.isValid())) {
        return;
      }


      // Print out the type and title info, if any.
      printAndIndent(printState, indent++);
      printState.str += 'Type: ' + (await element.getType());
      if (await element.hasTitle()) {
        printState.str += '. Title: ' + (await element.getTitle());
      }

      const num = await element.getNumKids();
      for (let i = 0; i < num; ++i) {
        // Check is the kid is a leaf node (i.e. it is a ContentItem).
        if (await element.isContentItem(i)) {
          const cont = await element.getAsContentItem(i);
          const type = await cont.getType();

          const page = await cont.getPage();

          printAndIndent(printState, indent);
          printState.str += 'Content Item. Part of page #' + (await page.getIndex());

          printAndIndent(printState, indent);
          switch (type) {
            case PDFNet.ContentItem.Type.e_MCID:
            case PDFNet.ContentItem.Type.e_MCR:
              printState.str += 'MCID: ' + (await cont.getMCID());
              break;
            case PDFNet.ContentItem.Type.e_OBJR:
              {
                printState.str += 'OBJR ';
                const refObj = await cont.getRefObj();
                if (refObj) {
                  printState.str += '- Referenced Object#: ' + refObj.getObjNum();
                }
              }
              break;
            default:
              break;
          }
        } else { // the kid is another StructElement node.
          await processStructElement(await element.getAsStructElem(i), indent, printState);
        }
      }
    };

    // Used in code snippet 2.
    const processElements = async(reader, printState) => {
      let element;
      while (element = await reader.next()) { // Read page contents
        // In this sample we process only paths & text, but the code can be
        // extended to handle any element type.
        const type = await element.getType();
        if (type === PDFNet.Element.Type.e_path || type === PDFNet.Element.Type.e_text || type === PDFNet.Element.Type.e_path) {
          switch (type) {
            case PDFNet.Element.Type.e_path: // Process path ...
              printState.str += '\nPATH: ';
              break;
            case PDFNet.Element.Type.e_text: // Process text ...
              printState.str += '\nTEXT: ' + (await element.getTextString()) + '\n';
              break;
            case PDFNet.Element.Type.e_form: // Process form XObjects
              printState.str += '\nFORM XObject: ';
              // reader.formBegin();
              // await ProcessElements(reader);
              // reader.end();
              break;
          }

          // Check if the element is associated with any structural element.
          // Content items are leaf nodes of the structure tree.
          const structParent = await element.getParentStructElement();
          if (await structParent.isValid()) {
            // Print out the parent structural element's type, title, and object number.
            printState.str += ' Type: ' + (await structParent.getType()) + ', MCID: ' + (await element.getStructMCID());
            if (await structParent.hasTitle()) {
              printState.str += '. Title: ' + (await structParent.getTitle());
            }
            printState.str += ', Obj#: ' + (await (await structParent.getSDFObj()).getObjNum());
          }
        }
      }
    };

    // Used in code snippet 3.
    const processElements2 = async(reader, mcidPageMap) => {
      let element;
      while (element = await reader.next()) { // Read page contents
        // In this sample we process only text, but the code can be extended
        // to handle paths, images, or any other Element type.
        const mcid = await element.getStructMCID();
        if (mcid >= 0 && (await element.getType()) === PDFNet.Element.Type.e_text) {
          const val = await element.getTextString();
          if (mcid in mcidPageMap) {
            mcidPageMap[mcid] += val;
          } else {
            mcidPageMap[mcid] = val;
          }
        }
      }
    };

    // Used in code snippet 3.
    const processStructElement2 = async(element, mcidDocMap, indent, printState) => {
      if (!(await element.isValid())) {
        return;
      }

      // Print out the type and title info, if any.
      printAndIndent(printState, indent);
      printState.str += '<' + (await element.getType());
      if (await element.hasTitle()) {
        printState.str += ' title="' + (await element.getTitle()) + '"';
      }
      printState.str += '>';

      const num = await element.getNumKids();
      for (let i = 0; i < num; ++i) {
        if (await element.isContentItem(i)) {
          const cont = await element.getAsContentItem(i);
          if ((await cont.getType()) === PDFNet.ContentItem.Type.e_MCID) {
            const pageNum = await (await cont.getPage()).getIndex();
            const mcidPageMap = mcidDocMap[pageNum];
            if (mcidPageMap) {
              const mcid = await cont.getMCID();
              if (mcid in mcidPageMap) {
                printState.str += mcidPageMap[mcid];
              }
            }
          }
        } else { // the kid is another StructElement node.
          await processStructElement2(await element.getAsStructElem(i), mcidDocMap, indent + 1, printState);
        }
      }

      printAndIndent(printState, indent);
      printState.str += '</' + (await element.getType()) + '>';
    };

    const main = async() => {
      // Relative path to the folder containing test files.
      const inputPath = '../TestFiles/';
      const printState = { str: '' };
      try { // Extract logical structure from a PDF document
        const doc = await PDFNet.PDFDoc.createFromFilePath(inputPath + 'tagged.pdf');
        doc.initSecurityHandler();

        let reader = null;
        let tree = null;

        console.log('____________________________________________________________');
        console.log('Sample 1 - Traverse logical structure tree...');
        tree = await doc.getStructTree();
        if (await tree.isValid()) {
          console.log('Document has a StructTree root.');
          for (let i = 0, numKids = await tree.getNumKids(); i < numKids; ++i) {
            // Recursively get structure info for all child elements.
            await processStructElement(await tree.getKid(i), 0, printState);
          }
        } else {
          console.log('This document does not contain any logical structure.');
        }
        printAndIndent(printState, 0);
        console.log('Done 1.');

        console.log('____________________________________________________________');
        console.log('Sample 2 - Get parent logical structure elements from');
        console.log('layout elements.');
        reader = await PDFNet.ElementReader.create();
        for (let itr = await doc.getPageIterator(); await itr.hasNext(); itr.next()) {
          reader.beginOnPage(await itr.current());
          await processElements(reader, printState);
          reader.end();
        }
        printAndIndent(printState, 0);
        console.log('Done 2.');

        console.log('____________________________________________________________');
        console.log("Sample 3 - 'XML style' extraction of PDF logical structure and page content.");
        {
          const mcidDocMap = {};
          for (let itr = await doc.getPageIterator(); await itr.hasNext(); itr.next()) {
            const page = await itr.current();
            reader.beginOnPage(page);
            const pageNum = await page.getIndex();
            const pageMcidMap = {};
            mcidDocMap[pageNum] = pageMcidMap;
            await processElements2(reader, pageMcidMap);
            reader.end();
          }

          tree = await doc.getStructTree();
          if (await tree.isValid()) {
            for (let i = 0, numKids = await tree.getNumKids(); i < numKids; ++i) {
              await processStructElement2(await tree.getKid(i), mcidDocMap, 0, printState);
            }
          }
        }
        printAndIndent(printState, 0);
        console.log('Done 3.');
        await doc.save(inputPath + 'Output/LogicalStructure.pdf', 0);
      } catch (err) {
        console.log(err);
      }
    };

    PDFNet.runWithCleanup(main, PDFTronLicense.Key).catch(function(error){console.log('Error: ' + JSON.stringify(error));}).then(function(){return PDFNet.shutdown();});
  };
  exports.runLogicalStructureTest();
})(exports);
// eslint-disable-next-line spaced-comment
//# sourceURL=LogicalStructureTest.js
```

{% endcode %}
{% endtab %}

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

```php
<?php
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
// Consult LICENSE.txt regarding license information.
//---------------------------------------------------------------------------------------
if(file_exists("../../../PDFNetC/Lib/PDFNetPHP.php"))
include("../../../PDFNetC/Lib/PDFNetPHP.php");
include("../../LicenseKey/PHP/LicenseKey.php");

// Relative path to the folder containing the test files.
$input_path = getcwd()."/../../TestFiles/";
$output_path = $input_path."Output/";

//---------------------------------------------------------------------------------------
// This sample explores the structure and content of a tagged PDF document and dumps 
// the structure information to the console window.
//
// In tagged PDF documents StructTree acts as a central repository for information 
// related to a PDF document's logical structure. The tree consists of StructElement-s
// and ContentItem-s which are leaf nodes of the structure tree.
//
// The sample can be extended to access and extract the marked-content elements such 
// as text and images.
//---------------------------------------------------------------------------------------

function PrintIdent($ident) { echo nl2br("\n"); for ($i=0; $i<$ident; ++$i) echo "  "; }

// Used in code snippet 1.
function ProcessStructElement($element, $ident) 
{
	if (!$element->IsValid()) {
		return;
	}

	// Print out the type and title info, if any.
	PrintIdent($ident++);
	echo "Type: ".$element->GetType();
	if ($element->HasTitle()) {
		echo ". Title: ".$element->GetTitle();
	}

	$num = $element->GetNumKids();
	for ($i=0; $i<$num; ++$i) 
	{
		// Check is the kid is a leaf node (i.e. it is a ContentItem).
		if ($element->IsContentItem($i)) { 
			$cont = $element->GetAsContentItem($i); 
			$type = $cont->GetType();

			$page = $cont->GetPage();

			PrintIdent($ident);
			echo "Content Item. Part of page #".$page->GetIndex();

			PrintIdent($ident);
			switch ($type) {
				case ContentItem::e_MCID:
				case ContentItem::e_MCR:
					echo "MCID: ".$cont->GetMCID();
					break;
				case ContentItem::e_OBJR:
					{
						echo "OBJR ";
						if ($ref_obj = $cont->GetRefObj())
							echo "- Referenced Object#: ".$ref_obj->GetObjNum();
					}
					break;
				default: 
					break;
			}
		}
		else {  // the kid is another StructElement node.
			ProcessStructElement($element->GetAsStructElem($i), $ident);
		}
	}
}

// Used in code snippet 2.
function ProcessElements($reader) 
{
	while ($element = $reader->Next()) 	// Read page contents
	{
		// In this sample we process only paths & text, but the code can be 
		// extended to handle any element type.
		$type = $element->GetType();
		if ($type == Element::e_path || $type == Element::e_text || $type == Element::e_path) 
		{   
			switch ($type)	{
			case Element::e_path:				// Process path ...
				echo nl2br("\nPATH: ");
				break; 
			case Element::e_text: 				// Process text ...
				echo nl2br("\nTEXT: ".$element->GetTextString()."\n");
				break;
			case Element::e_form:				// Process form XObjects
				echo nl2br("\nFORM XObject: ");
				//$reader->FormBegin(); 
				//ProcessElements($reader);
				//$reader->End(); 
				break; 
			}

			// Check if the element is associated with any structural element.
			// Content items are leaf nodes of the structure tree.
			$struct_parent = $element->GetParentStructElement();
			if ($struct_parent->IsValid()) {
				// Print out the parent structural element's type, title, and object number.
				echo " Type: ".$struct_parent->GetType() 
					.", MCID: ".$element->GetStructMCID();
				if ($struct_parent->HasTitle()) {
					echo ". Title: ".$struct_parent->GetTitle();
				}
				echo ", Obj#: ".$struct_parent->GetSDFObj()->GetObjNum();
			}
		}
	}
}

// Used in code snippet 3.
function ProcessElements2($reader, &$mcid_page_map) 
{
	while (($element = $reader->Next()) != null) // Read page contents
	{
		// In this sample we process only text, but the code can be extended 
		// to handle paths, images, or any other Element type.
		$mcid = $element->GetStructMCID();
		if ($mcid>= 0 && $element->GetType() == Element::e_text) {
			$val = $element->GetTextString();
			$exist = array_key_exists($mcid, $mcid_page_map);
			if ($exist == true) {
				$mcid_page_map[$mcid] = $mcid_page_map[$mcid].$val;
			}
			else {
				$mcid_page_map[$mcid] = $val;
			}
		}
	}
}

// Used in code snippet 3.
function ProcessStructElement2($element, &$mcid_doc_map, $ident) 
{
	if (!$element->IsValid()) {
		return;
	}

	// Print out the type and title info, if any.
	PrintIdent($ident);
	echo "<".$element->GetType();
	if ($element->HasTitle()) {
		echo " title=\"".$element->GetTitle()."\"";
	}
	echo ">";

	$num = $element->GetNumKids();
	for ($i=0; $i<$num; ++$i) 
	{		
		if ($element->IsContentItem($i)) { 
			$cont = $element->GetAsContentItem($i); 
			if ($cont->GetType() == ContentItem::e_MCID) {
				$page_num = $cont->GetPage()->GetIndex();
				if (array_key_exists($page_num, $mcid_doc_map)) {
					$mcid_page_map = $mcid_doc_map[$page_num];
					if (array_key_exists($cont->GetMCID(), $mcid_page_map)) {
						echo $mcid_page_map[$cont->GetMCID()]; 
					}                    
				}
			}
		}
		else {  // the kid is another StructElement node.
			ProcessStructElement2($element->GetAsStructElem($i), $mcid_doc_map, $ident+1);
		}
	}

	PrintIdent($ident);
	echo "</".$element->GetType().">";
}

	PDFNet::Initialize($LicenseKey);
	PDFNet::GetSystemFontList();    // Wait for fonts to be loaded if they haven't already. This is done because PHP can run into errors when shutting down if font loading is still in progress.

	// Extract logical structure from a PDF document

	$doc = new PDFDoc($input_path."tagged.pdf");
	$doc->InitSecurityHandler();

	echo nl2br("____________________________________________________________\n");
	echo nl2br("Sample 1 - Traverse logical structure tree...\n");

	$tree = $doc->GetStructTree();
	if ($tree->IsValid()) {
		echo nl2br("Document has a StructTree root.\n");

		for ($i=0; $i<$tree->GetNumKids(); ++$i) {
			// Recursively get structure info for all child elements.
			ProcessStructElement($tree->GetKid($i), 0);
		}
	}
	else {
		echo nl2br("This document does not contain any logical structure.\n");
	}

	echo nl2br("\nDone 1.\n");

	echo nl2br("____________________________________________________________\n");
	echo nl2br("Sample 2 - Get parent logical structure elements from\n");
	echo nl2br("layout elements.\n");
	
	$reader = new ElementReader();
	for ($itr = $doc->GetPageIterator(); $itr->HasNext(); $itr->Next()) {				
		$reader->Begin($itr->Current());
		ProcessElements($reader);
		$reader->End();
	}
	
	echo nl2br("\nDone 2.\n");

	echo nl2br("____________________________________________________________\n");
	echo nl2br("Sample 3 - 'XML style' extraction of PDF logical structure and page content.\n");
	
	$mcid_doc_map = array();
	$reader = new ElementReader();
	for ($itr = $doc->GetPageIterator(); $itr->HasNext(); $itr->Next()) {				
		$reader->Begin($itr->Current());
		$mcid_doc_map[$itr->Current()->GetIndex()] = array();
		ProcessElements2($reader, $mcid_doc_map[$itr->Current()->GetIndex()]);
		$reader->End();
	}
	$tree = $doc->GetStructTree();
	if ($tree->IsValid()) {
		for ($i=0; $i<$tree->GetNumKids(); ++$i) {
			ProcessStructElement2($tree->GetKid($i), $mcid_doc_map, 0);
		}
	}
	
	echo nl2br("\nDone 3.\n");	
	$doc->Save(($output_path ."LogicalStructure.pdf"), SDFDoc::e_linearized);
	$doc->Close();     
	PDFNet::Terminate();   
?>
```

{% endcode %}
{% endtab %}

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

```python
#---------------------------------------------------------------------------------------
# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
# Consult LICENSE.txt regarding license information.
#---------------------------------------------------------------------------------------

import site
site.addsitedir("../../../PDFNetC/Lib")
import sys
from PDFNetPython import *

sys.path.append("../../LicenseKey/PYTHON")
from LicenseKey import *

#---------------------------------------------------------------------------------------
# This sample explores the structure and content of a tagged PDF document and dumps 
# the structure information to the console window.
#
# In tagged PDF documents StructTree acts as a central repository for information 
# related to a PDF document's logical structure. The tree consists of StructElement-s
# and ContentItem-s which are leaf nodes of the structure tree.
#
# The sample can be extended to access and extract the marked-content elements such 
# as text and images.
#---------------------------------------------------------------------------------------

def PrintIndent(indent):
    sys.stdout.write("\n")
    i = 0
    while i < indent:
        sys.stdout.write("  ")
        i = i + 1
        
def ProcessStructElement(element, indent):
    if not element.IsValid():
        return
    
    # Print out the type and title info, if any.
    PrintIndent(indent)
    indent = indent + 1
    sys.stdout.write("Type: " + element.GetType())
    if element.HasTitle():
        sys.stdout.write(". Title:" + element.GetTitle())
    
    num = element.GetNumKids()
    i = 0
    while i < num:
        # Check if the kid is a leaf node (i.e. it is a ContentItem)
        if element.IsContentItem(i):
            cont = element.GetAsContentItem(i)
            type = cont.GetType()
            
            page = cont.GetPage()
            
            PrintIndent(indent)
            sys.stdout.write("Content Item. Part of page #" + str(page.GetIndex()))
            PrintIndent(indent)
            if type == ContentItem.e_MCID:
                sys.stdout.write("MCID: " + str(cont.GetMCID()))
            elif type == ContentItem.e_MCR:
                sys.stdout.write("MCID: " + str(cont.GetMCID()))
            elif type == ContentItem.e_OBJR:
                sys.stdout.write("OBJR ")
                ref_obj = cont.GetRefObj()
                if ref_obj != None:
                    sys.stdout.write("- Referenced Object#: " + str(ref_obj.GetObjNum()))
        else:
            ProcessStructElement(element.GetAsStructElem(i), indent)
        i = i + 1
    

# Used in code snippet 3.
def ProcessElements2(reader, mcid_page_map):
    element = reader.Next()
    while element != None: # Read page contents
        # In this sample we process only text, but the code can be extended
        # to handle paths, images, or other Element type.
        mcid = element.GetStructMCID()
        
        if mcid>=0 and element.GetType() == Element.e_text:
            val = element.GetTextString()
            
            if mcid in mcid_page_map:
                mcid_page_map[mcid] = str(mcid_page_map[mcid]) + val
            else:
                mcid_page_map[mcid] = val
        element = reader.Next()

# Used in code snippet 2.
def ProcessElements(reader):
    element = reader.Next()
    while element != None:  # Read page contents
        # In this sample we process only paths & text, but the code can be 
        # extended to handle any element type.
        type = element.GetType()
        if (type == Element.e_path or
            type == Element.e_text or
            type == Element.e_path):
            if type == Element.e_path:      # Process path ...
                sys.stdout.write("\nPATH: ")
            elif type == Element.e_text:    # Process text ...
                sys.stdout.write("\nTEXT: " + element.GetTextString() + "\n")
            elif type == Element.e_path:    # Process from XObjects
                sys.stdout.write("\nFORM XObject: ")
            
            # Check if the element is associated with any structural element.
            # Content items are leaf nodes of the structure tree.
            struct_parent = element.GetParentStructElement()
            if struct_parent.IsValid():
                # Print out the parent structural element's type, title, and object number.
                sys.stdout.write(" Type: " + str(struct_parent.GetType()) 
                                 + ", MCID: " + str(element.GetStructMCID()))
                if struct_parent.HasTitle():
                    sys.stdout.write(". Title: " + struct_parent.GetTitle())
                sys.stdout.write(", Obj#: " + str(struct_parent.GetSDFObj().GetObjNum()))
        element = reader.Next()
        
        
def ProcessStructElement2(element, mcid_doc_map, indent):
    if not element.IsValid():
        return
    
    # Print out the type and title info, if any
    PrintIndent(indent)
    sys.stdout.write("<" + element.GetType())
    if element.HasTitle():
        sys.stdout.write(" title=\"" + element.GetTitle() + "\"")
    sys.stdout.write(">")
    
    num = element.GetNumKids()
    i = 0
    while i < num:
        if element.IsContentItem(i):
            cont = element.GetAsContentItem(i)
            if cont.GetType() == ContentItem.e_MCID:
                page_num = cont.GetPage().GetIndex()
                if page_num in mcid_doc_map:
                    mcid_page_map = mcid_doc_map[page_num]
                    mcid_key = cont.GetMCID()
                    if mcid_key in mcid_page_map:
                        sys.stdout.write(mcid_page_map[mcid_key])
        else: # the kid is another StructElement node.
            ProcessStructElement2(element.GetAsStructElem(i), mcid_doc_map, indent+1)      
        i = i + 1
    PrintIndent(indent)
    sys.stdout.write("</" + element.GetType() + ">")
        

def main():
    PDFNet.Initialize(LicenseKey)
    
    # Relative path to the folder containing the test files.
    input_path = "../../TestFiles/"
    output_path = "../../TestFiles/Output/"
    
    # Extract logical structure from a PDF document
    doc = PDFDoc(input_path + "tagged.pdf")
    doc.InitSecurityHandler()
    
    print("____________________________________________________________")
    print("Sample 1 - Traverse logical structure tree...")
    
    tree = doc.GetStructTree()
    if tree.IsValid():
        print("Document has a StructTree root.")
        
        i = 0
        while i<tree.GetNumKids():
            # Recursively get structure info for all child elements.
            ProcessStructElement(tree.GetKid(i), 0)
            i = i + 1
    else:
        print("This document does not contain any logical structure.")
    
    print("\nDone 1.")

    print("____________________________________________________________")
    print("Sample 2 - Get parent logical structure elements from")
    print("layout elements.")
    
    reader = ElementReader()
    itr = doc.GetPageIterator()
    while itr.HasNext():
        reader.Begin(itr.Current())
        ProcessElements(reader)
        reader.End()
        itr.Next()
    
    print("\nDone 2.")
    
    print("____________________________________________________________")
    print("Sample 3 - 'XML style' extraction of PDF logical structure and page content.")
    # A map which maps page numbers(as Integers)
    # to page Maps(which map from struct mcid(as Integers) to
    # text Strings)
    mcid_doc_map = dict()
    reader = ElementReader()
    itr = doc.GetPageIterator()
    while itr.HasNext():
        reader.Begin(itr.Current())
        page_mcid_map = dict()
        mcid_doc_map[itr.Current().GetIndex()] = page_mcid_map
        ProcessElements2(reader, page_mcid_map)
        reader.End()
        itr.Next()  
    tree = doc.GetStructTree()
    if tree.IsValid():
        i = 0
        while i < tree.GetNumKids():
            ProcessStructElement2(tree.GetKid(i), mcid_doc_map, 0)
            i = i + 1  
    print("\nDone 3.")
    doc.Save((output_path + "LogicalStructure.pdf"), SDFDoc.e_linearized)
    doc.Close()        
    PDFNet.Terminate()

if __name__ == '__main__':
    main()
```

{% endcode %}
{% endtab %}

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

```ruby
#---------------------------------------------------------------------------------------
# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
# Consult LICENSE.txt regarding license information.
#---------------------------------------------------------------------------------------

require '../../../PDFNetC/Lib/PDFNetRuby'
include PDFNetRuby
require '../../LicenseKey/RUBY/LicenseKey'

$stdout.sync = true

#---------------------------------------------------------------------------------------
# This sample explores the structure and content of a tagged PDF document and dumps 
# the structure information to the console window.
#
# In tagged PDF documents StructTree acts as a central repository for information 
# related to a PDF document's logical structure. The tree consists of StructElement-s
# and ContentItem-s which are leaf nodes of the structure tree.
#
# The sample can be extended to access and extract the marked-content elements such 
# as text and images.
#---------------------------------------------------------------------------------------

def PrintIndent(indent)
	print "\n"
	i = 0
	while i < indent
		print "  "
		i = i + 1
	end
end
		
def ProcessStructElement(element, indent)
	if !element.IsValid
		return
	end
	
	# Print out the type and title info, if any.
	PrintIndent(indent)
	indent = indent + 1
	print "Type: " + element.GetType
	if element.HasTitle
		print ". Title:" + element.GetTitle
	end
	
	num = element.GetNumKids
	i = 0
	while i < num do
		# Check if the kid is a leaf node (i.e. it is a ContentItem)
		if element.IsContentItem(i)
			cont = element.GetAsContentItem(i)
			type = cont.GetType
			
			page = cont.GetPage
			
			PrintIndent(indent)
			print "Content Item. Part of page #" + page.GetIndex.to_s
			PrintIndent(indent)
			case type
			when ContentItem::E_MCID
				print "MCID: " + cont.GetMCID.to_s
			when ContentItem::E_MCR
				print "MCID: " + cont.GetMCID.to_s
			when ContentItem::E_OBJR
				print "OBJR "
				ref_obj = cont.GetRefObj
				if !ref_obj.nil?
					print "- Referenced Object#: " + ref_obj.GetObjNum.to_s
				end
			end
		else
			ProcessStructElement(element.GetAsStructElem(i), indent)
		end
		i = i + 1
	end
end	

# Used in code snippet 3.
def ProcessElements2(reader)
	mcid_page_map = Hash.new
	element = reader.Next
	while !element.nil? do	# Read page contents
		# In this sample we process only text, but the code can be extended
		# to handle paths, images, or other Element type.
		mcid = element.GetStructMCID
		
		if mcid>=0 and element.GetType == Element::E_text
			val = element.GetTextString
			
			if mcid_page_map.has_key?(mcid)
				mcid_page_map[mcid] = mcid_page_map[mcid].to_s + val
			else
				mcid_page_map[mcid] = val
			end
		end
		element = reader.Next
	end
	return mcid_page_map
end

# Used in code snippet 2.
def ProcessElements(reader)
	element = reader.Next
	while !element.nil? do	# Read page contents
		# In this sample we process only paths & text, but the code can be 
		# extended to handle any element type.
		type = element.GetType
		if (type == Element::E_path or
			type == Element::E_text or
			type == Element::E_path)
			case type
			when Element::E_path	# Process path ...
				print "\nPATH: "
			when Element::E_text	# Process text ...
				print "\nTEXT: " + element.GetTextString + "\n"
			when Element::E_path	# Process from XObjects
				print "\nFORM XObject: "
			end
			
			# Check if the element is associated with any structural element.
			# Content items are leaf nodes of the structure tree.
			struct_parent = element.GetParentStructElement
			if struct_parent.IsValid
				# Print out the parent structural element's type, title, and object number.
				print " Type: " + struct_parent.GetType.to_s + ", MCID: " + element.GetStructMCID.to_s
				if struct_parent.HasTitle
					print ". Title: " + struct_parent.GetTitle
				end
				print ", Obj#: " + struct_parent.GetSDFObj.GetObjNum.to_s
			end
		end
		element = reader.Next
	end
end		
		
def ProcessStructElement2(element, mcid_doc_map, indent)
	if !element.IsValid
		return
	end
	
	# Print out the type and title info, if any
	PrintIndent(indent)
	print "<" + element.GetType
	if element.HasTitle
		print " title=\"" + element.GetTitle + "\""
	end
	print ">"
	
	num = element.GetNumKids
	i = 0
	while i < num do
		if element.IsContentItem(i)
			cont = element.GetAsContentItem(i)
			if cont.GetType == ContentItem::E_MCID
				page_num = cont.GetPage.GetIndex
				if mcid_doc_map.has_key?(page_num)
					mcid_page_map = mcid_doc_map[page_num]
					mcid_key = cont.GetMCID
					if mcid_page_map.has_key?(mcid_key)
						print mcid_page_map[mcid_key]
					end
				end
			end
		else	# the kid is another StructElement node.
			ProcessStructElement2(element.GetAsStructElem(i), mcid_doc_map, indent+1)
		end 
		i = i + 1
	end
	PrintIndent(indent)
	print "</" + element.GetType + ">"		
end

	PDFNet.Initialize(PDFTronLicense.Key)
	
	# Relative path to the folder containing the test files.
	input_path = "../../TestFiles/"
	output_path = "../../TestFiles/Output/"
	
	# Extract logical structure from a PDF document
	doc = PDFDoc.new(input_path + "tagged.pdf")
	doc.InitSecurityHandler
	
	puts "____________________________________________________________"
	puts "Sample 1 - Traverse logical structure tree..."
	
	tree = doc.GetStructTree
	if tree.IsValid
		puts "Document has a StructTree root."
		
		i = 0
		while i<tree.GetNumKids do
			# Recursively get structure info for all child elements.
			ProcessStructElement(tree.GetKid(i), 0)
			i = i + 1
		end
	else
		puts "This document does not contain any logical structure."
	end
	
	puts "\nDone 1."
	
	puts "____________________________________________________________"
	puts "Sample 2 - Get parent logical structure elements from"
	puts "layout elements."
	
	reader = ElementReader.new
	itr = doc.GetPageIterator
	while itr.HasNext do
		reader.Begin(itr.Current)
		ProcessElements(reader)
		reader.End
		itr.Next
	end
	
	puts "\nDone 2."
	
	puts "____________________________________________________________"
	puts "Sample 3 - 'XML style' extraction of PDF logical structure and page content."

	# A map which maps page numbers(as Integers)
	# to page Maps(which map from struct mcid(as Integers) to
	# text Strings)

	mcid_doc_map = Hash.new
	reader = ElementReader.new
	itr = doc.GetPageIterator
	while itr.HasNext do
		reader.Begin(itr.Current)
		mcid_doc_map[itr.Current.GetIndex] = ProcessElements2(reader)
		reader.End
		itr.Next
	end
	tree = doc.GetStructTree
	if tree.IsValid
		i = 0
		while i < tree.GetNumKids do
			ProcessStructElement2(tree.GetKid(i), mcid_doc_map, 0)
			i = i + 1  
		end
	end
	puts "\nDone 3."
	doc.Save((output_path + "LogicalStructure.pdf"), SDFDoc::E_linearized)
	doc.Close
	PDFNet.Terminate
```

{% endcode %}
{% endtab %}

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

```vb
'
' Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
'

Imports System
Imports System.Collections
Imports pdftron
Imports pdftron.Common
Imports pdftron.Filters
Imports pdftron.SDF
Imports pdftron.PDF
Imports pdftron.PDF.Struct

Module LogicalStructureTestCS
    Dim pdfNetLoader As PDFNetLoader
    Sub New()
        pdfNetLoader = pdftron.PDFNetLoader.Instance()
    End Sub

    Sub PrintIndent(ByVal indent As Integer)
        Console.WriteLine()

        For i As Integer = 0 To indent - 1
            Console.Write("  ")
        Next
    End Sub

    Sub ProcessStructElement(ByVal element As SElement, ByVal indent As Integer)
        If Not element.IsValid() Then
            Return
        End If

        PrintIndent(Math.Min(System.Threading.Interlocked.Increment(indent), indent - 1))
        Console.Write("Type: " & element.[GetType]())

        If element.HasTitle() Then
            Console.Write(". Title: " & element.GetTitle())
        End If

        Dim num As Integer = element.GetNumKids()

        For i As Integer = 0 To num - 1

            If element.IsContentItem(i) Then
                Dim cont As ContentItem = element.GetAsContentItem(i)
                Dim type As ContentItem.Type = cont.[GetType]()
                Dim page As Page = cont.GetPage()
                PrintIndent(indent)
                Console.Write("Content Item. Part of page #" & page.GetIndex())
                PrintIndent(indent)

                Select Case type
                    Case ContentItem.Type.e_MCID, ContentItem.Type.e_MCR
                        Console.Write("MCID: " & cont.GetMCID())
                    Case ContentItem.Type.e_OBJR
                        Console.Write("OBJR ")
                        Dim ref_obj As Obj = cont.GetRefObj()
                        If ref_obj IsNot Nothing Then Console.Write("- Referenced Object#: " & ref_obj.GetObjNum())
                    Case Else
                End Select
            Else
                ProcessStructElement(element.GetAsStructElem(i), indent)
            End If
        Next
    End Sub

    Sub ProcessElements(ByVal reader As ElementReader)
        Dim element As Element = reader.Next()
        While Not IsNothing(element)  ' Read page contents
            Dim type As Element.Type = element.[GetType]()

            If type = element.Type.e_path OrElse type = element.Type.e_text OrElse type = element.Type.e_path Then

                Select Case type
                    Case element.Type.e_path
                        Console.WriteLine()
                        Console.Write("PATH: ")
                    Case element.Type.e_text
                        Console.WriteLine()
                        Console.WriteLine("TEXT: " & element.GetTextString())
                    Case element.Type.e_form
                        Console.WriteLine()
                        Console.Write("FORM XObject: ")
                End Select

                Dim struct_parent As SElement = element.GetParentStructElement()

                If struct_parent.IsValid() Then
                    Console.Write(" Type: " & struct_parent.[GetType]() & ", MCID: " + String.Format("{0}", element.GetStructMCID()))

                    If struct_parent.HasTitle() Then
                        Console.Write(". Title: " & struct_parent.GetTitle())
                    End If

                    Console.Write(", Obj#: " & struct_parent.GetSDFObj().GetObjNum())
                End If
            End If
            element = reader.Next()
        End While
    End Sub

    Sub ProcessElements2(ByVal reader As ElementReader, ByVal mcid_page_map As Hashtable)
        Dim element As Element = reader.Next()
        While Not IsNothing(element)  ' Read page contents
            Dim mcid As Integer = element.GetStructMCID()

            If mcid >= 0 AndAlso element.[GetType]() = element.Type.e_text Then
                Dim val As String = element.GetTextString()

                If mcid_page_map.ContainsKey(mcid) Then
                    mcid_page_map(mcid) = (CStr((mcid_page_map(mcid))) & val)
                Else
                    mcid_page_map.Add(mcid, val)
                End If
            End If
            element = reader.Next()
        End While
    End Sub

    Sub ProcessStructElement2(ByVal element As SElement, ByVal mcid_doc_map As Hashtable, ByVal indent As Integer)
        If Not element.IsValid() Then
            Return
        End If

        PrintIndent(indent)
        Console.Write("<" & element.[GetType]())

        If element.HasTitle() Then
            Console.Write(" title=""" & element.GetTitle() & """")
        End If

        Console.Write(">")
        Dim num As Integer = element.GetNumKids()

        For i As Integer = 0 To num - 1

            If element.IsContentItem(i) Then
                Dim cont As ContentItem = element.GetAsContentItem(i)

                If cont.[GetType]() = ContentItem.Type.e_MCID Then
                    Dim page_num As Integer = cont.GetPage().GetIndex()

                    If mcid_doc_map.ContainsKey(page_num) Then
                        Dim mcid_page_map As Hashtable = CType((mcid_doc_map(page_num)), Hashtable)
                        Dim mcid As Integer = cont.GetMCID()

                        If mcid_page_map.ContainsKey(mcid) Then
                            Console.Write(mcid_page_map(mcid))
                        End If
                    End If
                End If
            Else
                ProcessStructElement2(element.GetAsStructElem(i), mcid_doc_map, indent + 1)
            End If
        Next

        PrintIndent(indent)
        Console.Write("</" & element.[GetType]() & ">")
    End Sub


    Sub Main(ByVal args As String())
        PDFNet.Initialize(PDFTronLicense.Key)
        Dim input_path As String = "../../../../TestFiles/"
        Dim output_path As String = "../../../../TestFiles/Output/"

        Try

            Using doc As PDFDoc = New PDFDoc(input_path & "tagged.pdf")
                doc.InitSecurityHandler()
                Dim example1 As Boolean = True
                Dim example2 As Boolean = True
                Dim example3 As Boolean = True

                If example1 Then
                    Console.WriteLine("____________________________________________________________")
                    Console.WriteLine("Sample 1 - Traverse logical structure tree...")
                    Dim tree As STree = doc.GetStructTree()

                    If tree.IsValid() Then
                        Console.WriteLine("Document has a StructTree root.")

                        For i As Integer = 0 To tree.GetNumKids() - 1
                            ProcessStructElement(tree.GetKid(i), 0)
                        Next
                    Else
                        Console.WriteLine("This document does not contain any logical structure.")
                    End If

                    Console.WriteLine()
                    Console.WriteLine("Done 1.")
                End If

                If example2 Then
                    Console.WriteLine("____________________________________________________________")
                    Console.WriteLine("Sample 2 - Get parent logical structure elements from")
                    Console.WriteLine("layout elements.")
                    Dim reader As ElementReader = New ElementReader()
                    Dim itr As PageIterator = doc.GetPageIterator()

                    While itr.HasNext()
                        reader.Begin(itr.Current())
                        ProcessElements(reader)
                        reader.[End]()
                        itr.[Next]()
                    End While

                    Console.WriteLine()
                    Console.WriteLine("Done 2.")
                End If

                If example3 Then
                    Console.WriteLine("____________________________________________________________")
                    Console.WriteLine("Sample 3 - 'XML style' extraction of PDF logical structure and page content.")
                    Dim mcid_doc_map As Hashtable = New Hashtable()
                    Dim reader As ElementReader = New ElementReader()
                    Dim itr As PageIterator = doc.GetPageIterator()

                    While itr.HasNext()
                        Dim pg As Page = itr.Current()
                        reader.Begin(pg)
                        Dim page_mcid_map As Hashtable = New Hashtable()
                        mcid_doc_map.Add(pg.GetIndex(), page_mcid_map)
                        ProcessElements2(reader, page_mcid_map)
                        reader.[End]()
                        itr.[Next]()
                    End While

                    Dim tree As STree = doc.GetStructTree()

                    If tree.IsValid() Then

                        For i As Integer = 0 To tree.GetNumKids() - 1
                            ProcessStructElement2(tree.GetKid(i), mcid_doc_map, 0)
                        Next
                    End If

                    Console.WriteLine()
                    Console.WriteLine("Done 3.")
                End If

                doc.Save(output_path & "LogicalStructure.pdf", 0)
            End Using

        Catch e As PDFNetException
            Console.WriteLine(e.Message)
        End Try
        PDFNet.Terminate()
    End Sub

End Module
```

{% 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/core/get-started/samples/logicalstructuretest.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.
