> 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/elementreaderadvtest.md).

# PDF Data Extraction - Images, Text, Paths - ElementReaderAdv

Sample code for using Apryse Server SDK to extract text, paths, and images from a PDF; color conversion, image normalization, and process changes in the graphics state.  Sample code provided in Python

Sample code for using Apryse SDK to extract text, paths, and images from a PDF. The sample also shows how to do color conversion, image normalization, and process changes in the graphics state. 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.
//---------------------------------------------------------------------------------------
// A sample project illustrating some extraction capabilities of ElementReader
// in more detail
//---------------------------------------------------------------------------------------

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

namespace ElementReaderAdvTestCS
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	class Class1
	{
		private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
		static Class1() {}
		
		// Relative path to the folder containing test files.
		static string input_path =  "../../../../TestFiles/";
		static string output_path = "../../../../TestFiles/Output/";

		static string m_buf;

		static public void ProcessPath(ElementReader reader, Element path)
		{
			if (path.IsClippingPath())
			{
				Console.WriteLine("This is a clipping path");
			}

			PathData pathData = path.GetPathData();
			double[] data = pathData.points;
			int data_sz = data.Length;

			byte[] opr = pathData.operators;
			int opr_sz = opr.Length;

			int opr_itr = 0, opr_end = opr_sz;
			int data_itr = 0, data_end = data_sz;
			double x1, y1, x2, y2, x3, y3;

			// Use path.GetCTM() if you are interested in CTM (current transformation matrix).

			Console.Write(" Path Data Points := \"");
			for ( ; opr_itr < opr_end; ++opr_itr)
			{
				switch((PathData.PathSegmentType)((int)opr[opr_itr]))
				{
					case PathData.PathSegmentType.e_moveto:
						x1 = data[data_itr]; ++data_itr;
						y1 = data[data_itr]; ++data_itr;
						m_buf = string.Format("M{0:n0} {1:n0}", x1, y1);
						Console.Write(m_buf);
						break;
					case PathData.PathSegmentType.e_lineto:
						x1 = data[data_itr]; ++data_itr;
						y1 = data[data_itr]; ++data_itr;
						m_buf = string.Format(" L{0:n0} {1:n0}", x1, y1);
						Console.Write(m_buf);
						break;
					case PathData.PathSegmentType.e_cubicto:
						x1 = data[data_itr]; ++data_itr;
						y1 = data[data_itr]; ++data_itr;
						x2 = data[data_itr]; ++data_itr;
						y2 = data[data_itr]; ++data_itr;
						x3 = data[data_itr]; ++data_itr;
						y3 = data[data_itr]; ++data_itr;
						m_buf = string.Format(" C{0:n0} {1:n0} {2:n0} {3:n0} {4:n0} {5:n0}",
							new object[] {x1, y1, x2, y2, x3, y3});
						Console.Write(m_buf);
						break;
					case PathData.PathSegmentType.e_rect:
					{
						x1 = data[data_itr]; ++data_itr;
						y1 = data[data_itr]; ++data_itr;
						double w = data[data_itr]; ++data_itr;
						double h = data[data_itr]; ++data_itr;
						x2 = x1 + w;
						y2 = y1;
						x3 = x2;
						y3 = y1 + h;
						double x4 = x1; 
						double y4 = y3;
						m_buf = string.Format("M{0:n0} {1:n0} L{2:n0} {3:n0} L{4:n0} {5:n0} L{6:n0} {7:n0} Z",
							new object[] {x1, y1, x2, y2, x3, y3, x4, y4});
						Console.Write(m_buf);
						break;
					}
					case PathData.PathSegmentType.e_closepath:
						Console.WriteLine(" Close Path");
						break;
					default: 
						System.Diagnostics.Debug.Assert(false);
						break;
				}	
			}

			Console.Write("\" ");

			GState gs = path.GetGState();

			// Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
			if (path.IsStroked()) 
			{
				Console.WriteLine("Stroke path"); 

				if (gs.GetStrokeColorSpace().GetType() == ColorSpace.Type.e_pattern)
				{
					Console.WriteLine("Path has associated pattern"); 
				}
				else
				{
					// Get stroke color (you can use PDFNet color conversion facilities)
					// ColorPt rgb = new ColorPt();
					// gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor(), rgb);
				}
			}
			else 
			{
				// Do not stroke path
			}

			if (path.IsFilled())
			{
				Console.WriteLine("Fill path"); 

				if (gs.GetFillColorSpace().GetType() == ColorSpace.Type.e_pattern)
				{		
					Console.WriteLine("Path has associated pattern"); 
				}
				else
				{
					// ColorPt rgb = new ColorPt();
					// gs.GetFillColorSpace().Convert2RGB(gs.GetFillColor(), rgb);
				}        
			}
			else 
			{
				// Do not fill path
			}

			// Process any changes in graphics state  ---------------------------------

			GSChangesIterator gs_itr = reader.GetChangesIterator();
			for ( ; gs_itr.HasNext(); gs_itr.Next()) 
			{
				switch(gs_itr.Current())
				{
					case GState.GStateAttribute.e_transform :
						// Get transform matrix for this element. Unlike path.GetCTM() 
						// that return full transformation matrix gs.GetTransform() return 
						// only the transformation matrix that was installed for this element.
						//
						// gs.GetTransform();
						break;
					case GState.GStateAttribute.e_line_width :
						// gs.GetLineWidth();
						break;
					case GState.GStateAttribute.e_line_cap :
						// gs.GetLineCap();
						break;
					case GState.GStateAttribute.e_line_join :
						// gs.GetLineJoin();
						break;
					case GState.GStateAttribute.e_flatness :	
						break;
					case GState.GStateAttribute.e_miter_limit :
						// gs.GetMiterLimit();
						break;
					case GState.GStateAttribute.e_dash_pattern :
					{
						// double[] dashes;
						// gs.GetDashes(dashes);
						// gs.GetPhase()
						break;
					}
					case GState.GStateAttribute.e_fill_color:
					{
						if ( gs.GetFillColorSpace().GetType() == ColorSpace.Type.e_pattern &&
							 gs.GetFillPattern().GetType() != PatternColor.Type.e_shading)
						{	
							//process the pattern data
							reader.PatternBegin(true);
							ProcessElements(reader);
							reader.End();
						}
						break;
					}
				}
			}
			reader.ClearChangeList();
		}

		static public void ProcessText(ElementReader page_reader) 
		{
			// Begin text element
			Console.WriteLine("Begin Text Block:");

			Element element; 
			while ((element = page_reader.Next()) != null) 
			{
				switch (element.GetType())
				{
					case Element.Type.e_text_end: 
						// Finish the text block
						Console.WriteLine("End Text Block.");
						return;

					case Element.Type.e_text:
					{
						GState gs = element.GetGState();

						ColorSpace cs_fill = gs.GetFillColorSpace();
						ColorPt fill = gs.GetFillColor();

						ColorPt outc = new ColorPt();
						cs_fill.Convert2RGB(fill, outc);


						ColorSpace cs_stroke = gs.GetStrokeColorSpace();
						ColorPt stroke = gs.GetStrokeColor();

						Font font = gs.GetFont();

						Console.Write("Font Name: ");
						Console.WriteLine(font.GetName());
						// font.IsFixedWidth();
						// font.IsSerif();
						// font.IsSymbolic();
						// font.IsItalic();
						// ... 

						// double word_spacing = gs.GetWordSpacing();
						// double char_spacing = gs.GetCharSpacing();

						// Use element.GetCTM() if you are interested in the CTM 
						// (current transformation matrix).
						if (font.GetType() == Font.Type.e_Type3)
						{
							//type 3 font, process its data
							for (CharIterator itr = element.GetCharIterator(); itr.HasNext(); itr.Next()) 
							{
								page_reader.Type3FontBegin(itr.Current());
								ProcessElements(page_reader);
								page_reader.End();
							}
						}

						else
						{

							Matrix2D ctm = element.GetCTM();

							Matrix2D text_mtx = element.GetTextMatrix();

							/*
							Matrix2D mtx = ctm * text_mtx;
							double font_sz_scale_factor = System.Math.Sqrt(mtx.m_b * mtx.m_b + mtx.m_d * mtx.m_d);
							double font_size = gs.GetFontSize();
							Console.Write(" Font Size: {0:f}", font_sz_scale_factor * font_size);

							ColorPt font_color = gs.GetFillColor();
							ColorSpace cs = gs.GetFillColorSpace();

							ColorPt rgb = new ColorPt();
							cs.Convert2RGB(font_color, rgb);
							Color font_color_rgb = Color.FromArgb(255, (byte)(rgb.get_c(0)*255),
							(byte)(rgb.get_c(1)*255), (byte)(rgb.get_c(2)*255));
								

							Console.WriteLine(" Font Color(RGB): red={0:d} green={1:d} blue={2:d}", 
							(byte)(rgb.Get(0)*255),
							(byte)(rgb.Get(1)*255),
							(byte)(rgb.Get(2)*255));
							*/

							double x, y;
							int char_code; 
											
							for (CharIterator itr = element.GetCharIterator(); itr.HasNext(); itr.Next()) 
							{
								Console.Write("Character code: ");
								char_code = itr.Current().char_code;
								if (char_code >= 32 || char_code <= 127)
								{ 
									// Print if in ASCII range...
									Console.Write((char)char_code);
								}

								x = itr.Current().x;		// character positioning information
								y = itr.Current().y;

								// To get the exact character positioning information you need to 
								// concatenate current text matrix with CTM and then multiply 
								// relative positioning coordinates with the resulting matrix.
								//
								Matrix2D mtx2 = ctm * text_mtx;
								mtx2.Mult(ref x, ref y);
								// Console.WriteLine(" Position: x={0:f} y={1:f}", x, y);
							}
						}

						Console.WriteLine();
						break;
					}
				}
			}
		}

		static int image_counter = 0;

		static public void ProcessImage(Element image)  
		{
			bool image_mask = image.IsImageMask();
			bool interpolate = image.IsImageInterpolate();
			int width = image.GetImageWidth();
			int height = image.GetImageHeight();
			int out_data_sz = width * height * 3;

			Console.WriteLine("Image: width=\"{0:d}\" height=\"{1:d}\"", width, height);

			// Matrix2D mtx = image.GetCTM(); // image matrix (page positioning info)

			++image_counter;
			System.Drawing.Bitmap bmp = image.GetBitmap();
			bmp.Save(output_path + "reader_img_extract_" + image_counter.ToString() + ".png", System.Drawing.Imaging.ImageFormat.Png);

			// Alternatively you can use GetImageData to read the raw (decoded) image data
			// image.GetBitsPerComponent();	
			// image.GetImageData();	// get raw image data
			// another approach is to use Image2RGB filter that converts every image to 
			// RGB format. This could save you time since you don't need to deal with color 
			// conversions, image up-sampling, decoding etc.
			// ----------------
			//   Image2RGB img_conv = new Image2RGB(image);	// Extract and convert image to RGB 8-bpc format
			//   FilterReader reader = new FilterReader(img_conv);			//   
			//   byte[] image_data_out = new byte[out_data_sz];  // A buffer used to keep image data.
			//   reader.Read(image_data_out);  // image_data_out contains RGB image data.
			// ----------------
			// Note that you don't need to read a whole image at a time. Alternatively
			// you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
			// until the function returns 0. 
		}

	static void ProcessElements(ElementReader reader) 
	{
		Element element;

		while ((element = reader.Next()) != null)  // Read page contents
		{
			switch (element.GetType())
			{
				case Element.Type.e_path:          // Process path data...
				{
					ProcessPath(reader, element);
					break; 
				}
				case Element.Type.e_text_begin:    // Process text strings...
				{
					ProcessText(reader);
					break;
				}
				case Element.Type.e_form:          // Process form XObjects
				{
					reader.FormBegin(); 
					ProcessElements(reader);
					reader.End(); 
					break; 
				}
				case Element.Type.e_image:         // Process Images
				{
					ProcessImage(element);
					break; 
				}	
			}
		}
	}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			try
			{
				PDFNet.Initialize(PDFTronLicense.Key);

				Console.WriteLine("-------------------------------------------------");
				Console.WriteLine("Extract page element information from all ");
				Console.WriteLine("pages in the document.");

				// Open the test file
				using (PDFDoc doc = new PDFDoc(input_path + "newsletter.pdf"))
				{
					doc.InitSecurityHandler();

					int pgnum = doc.GetPageCount();
					PageIterator itr;

					using (ElementReader page_reader = new ElementReader())
					{
						for (itr = doc.GetPageIterator(); itr.HasNext(); itr.Next())		//  Read every page
						{				
							Console.WriteLine("Page {0:d}----------------------------------------",
								itr.GetPageNumber());

							Rect crop_box = itr.Current().GetCropBox();
							crop_box.Normalize();

							// Console.WriteLine(" Page Rectangle: x={0:f} y={1:f} x2={2:f} y2={3:f}", crop_box.x1, crop_box.y1, crop_box.x2, crop_box.y2);
							// Console.WriteLine(" Page Size: width={0:f} height={1:f}", crop_box.Width(), crop_box.Height());

							page_reader.Begin(itr.Current());
							ProcessElements(page_reader);
							page_reader.End(); 
						}
					}

					Console.WriteLine("Done.");
				}
			}
			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 <PDF/Element.h>
#include <PDF/Font.h>
#include <Filters/FilterReader.h>
#include <PDF/Image/Image2RGB.h>

#include <iostream>
#include <assert.h>
#include "../../LicenseKey/CPP/LicenseKey.h"

using namespace std;

using namespace pdftron;
using namespace PDF;
using namespace SDF;
using namespace Common;
using namespace Filters; 

char m_buf[4000];

void ProcessElements(ElementReader& reader);

void ProcessPath(ElementReader& reader, Element path)
{
	if (path.IsClippingPath())
	{
		cout << "This is a clipping path" << endl;
	}

	PathData d = path.GetPathData();

	const UChar* opr = &d.GetOperators().front();
	const UChar *opr_itr = opr, *opr_end = opr + d.GetOperators().size();
	const double* data = &d.GetPoints().front();
	const double *data_itr = data, *data_end = data + d.GetPoints().size();

	double x1, y1, x2, y2, x3, y3;

	// Use path.GetCTM() if you are interested in CTM (current transformation matrix).

	cout << " Path Data Points := \"";
	for (; opr_itr<opr_end; ++opr_itr)
	{
		switch(*opr_itr)
		{
		case PathData::e_moveto:
			x1 = *data_itr; ++data_itr;
			y1 = *data_itr; ++data_itr;
			sprintf(m_buf, "M%.0f %.0f", x1, y1);
			cout << m_buf;
			break;
		case PathData::e_lineto:
			x1 = *data_itr; ++data_itr;
			y1 = *data_itr; ++data_itr;
			sprintf(m_buf, " L%.0f %.0f", x1, y1);
			cout << m_buf;
			break;
		case PathData::e_cubicto:
			x1 = *data_itr; ++data_itr;
			y1 = *data_itr; ++data_itr;
			x2 = *data_itr; ++data_itr;
			y2 = *data_itr; ++data_itr;
			x3 = *data_itr; ++data_itr;
			y3 = *data_itr; ++data_itr;
			sprintf(m_buf, " C%.0f %.0f %.0f %.0f %.0f %.0f", x1, y1, x2, y2, x3, y3);
			cout << m_buf;
			break;
		case PathData::e_rect:
			{
				x1 = *data_itr; ++data_itr;
				y1 = *data_itr; ++data_itr;
				double w = *data_itr; ++data_itr;
				double h = *data_itr; ++data_itr;
				x2 = x1 + w;
				y2 = y1;
				x3 = x2;
				y3 = y1 + h;
				double x4 = x1; 
				double y4 = y3;
				sprintf(m_buf, "M%.0f %.0f L%.0f %.0f L%.0f %.0f L%.0f %.0f Z", 
					x1, y1, x2, y2, x3, y3, x4, y4);
				cout << m_buf;
			}
			break;
		case PathData::e_closepath:
			cout << " Close Path" << endl;
			break;
		default: 
			assert(false);
			break;
		}	
	}

	cout << "\" ";

	GState gs = path.GetGState();

	// Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
	if (path.IsStroked()) 
	{
		cout << "Stroke path" << endl; 

		if (gs.GetStrokeColorSpace().GetType() == ColorSpace::e_pattern)
		{
			cout << "Path has associated pattern" << endl; 
		}
		else
		{
			// Get stroke color (you can use PDFNet color conversion facilities)
			// ColorPt rgb;
			// gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor(), rgb);
		}
	}
	else 
	{
		// Do not stroke path
	}

	if (path.IsFilled())
	{
		cout << "Fill path" << endl; 

		if (gs.GetFillColorSpace().GetType() == ColorSpace::e_pattern)
		{		
			cout << "Path has associated pattern" << endl; 
		}
		else
		{
			// ColorPt rgb;
			// gs.GetFillColorSpace().Convert2RGB(gs.GetFillColor(), rgb);
		}        
	}
	else 
	{
		// Do not fill path
	}

	// Process any changes in graphics state  ---------------------------------

	GSChangesIterator gs_itr = reader.GetChangesIterator();
	for (; gs_itr.HasNext(); gs_itr.Next()) 
	{
		switch(gs_itr.Current())
		{
		case GState::e_transform :
			// Get transform matrix for this element. Unlike path.GetCTM() 
			// that return full transformation matrix gs.GetTransform() return 
			// only the transformation matrix that was installed for this element.
			//
			// gs.GetTransform();
			break;
		case GState::e_line_width :
			// gs.GetLineWidth();
			break;
		case GState::e_line_cap :
			// gs.GetLineCap();
			break;
		case GState::e_line_join :
			// gs.GetLineJoin();
			break;
		case GState::e_flatness :	
			break;
		case GState::e_miter_limit :
			// gs.GetMiterLimit();
			break;
		case GState::e_dash_pattern :
			{
				// std::vector<double> dashes;
				// gs.GetDashes(dashes);
				// gs.GetPhase()
			}
			break;
		case GState::e_fill_color:
			{
				if ( gs.GetFillColorSpace().GetType() == ColorSpace::e_pattern &&
					gs.GetFillPattern().GetType() != PatternColor::e_shading )
				{	
					//process the pattern data
					reader.PatternBegin(true);
					ProcessElements(reader);
					reader.End();
				}
			}
			break;
		}
	}
	reader.ClearChangeList();
}

void ProcessText(ElementReader& page_reader) 
{
	// Begin text element
	cout << "Begin Text Block:" << endl;

	Element element; 
	while ((element = page_reader.Next()) != 0) 
	{
		switch (element.GetType())
		{
		case Element::e_text_end: 
			// Finish the text block
			cout << "End Text Block." << endl;
			return;

		case Element::e_text:
			{
				GState gs =  element.GetGState();

				ColorSpace cs_fill = gs.GetFillColorSpace();
				ColorPt fill = gs.GetFillColor();

				ColorPt out;
				cs_fill.Convert2RGB(fill, out);


				ColorSpace cs_stroke = gs.GetStrokeColorSpace();
				ColorPt stroke = gs.GetStrokeColor();

				Font font = gs.GetFont();

				cout << "Font Name: " << font.GetName() << endl;
				// font.IsFixedWidth();
				// font.IsSerif();
				// font.IsSymbolic();
				// font.IsItalic();
				// ... 

				// double font_size = gs.GetFontSize();
				// double word_spacing = gs.GetWordSpacing();
				// double char_spacing = gs.GetCharSpacing();
				// const UString* txt = element.GetTextString();

				if ( font.GetType() == Font::e_Type3 )
				{
					//type 3 font, process its data
					for (CharIterator itr = element.GetCharIterator(); itr.HasNext(); itr.Next()) 
					{
						page_reader.Type3FontBegin(itr.Current());
						ProcessElements(page_reader);
						page_reader.End();
					}
				}

				else
				{	
					Matrix2D text_mtx = element.GetTextMatrix();
					double x, y;
					unsigned int char_code;

					for (CharIterator itr = element.GetCharIterator(); itr.HasNext(); itr.Next()) 
					{
						cout << "Character code: ";
						char_code = itr.Current().char_code;
						if (char_code>=32 || char_code<=127)
						{ 
							// Print if in ASCII range...
							cout << char(char_code);
						}

						x = itr.Current().x;		// character positioning information
						y = itr.Current().y;

						// Use element.GetCTM() if you are interested in the CTM 
						// (current transformation matrix).
						Matrix2D ctm = element.GetCTM();

						// To get the exact character positioning information you need to 
						// concatenate current text matrix with CTM and then multiply 
						// relative positioning coordinates with the resulting matrix.
						Matrix2D mtx = ctm * text_mtx;
						mtx.Mult(x, y);

						// Get glyph path...
						//vector<UChar> oprs;
						//vector<double> glyph_data;
						//font.GetGlyphPath(char_code, oprs, glyph_data, false, 0);
					}
				}

				cout << endl;
			}
			break;
		}
	}
}

void ProcessImage(Element image)  
{
	bool image_mask = image.IsImageMask();
	bool interpolate = image.IsImageInterpolate();
	int width = image.GetImageWidth();
	int height = image.GetImageHeight();
	int out_data_sz = width * height * 3;

	cout << "Image:" 
		<< " width=\"" << width << "\""
		<< " height=\"" << height << "\"" << endl;

	// Matrix2D& mtx = image->GetCTM(); // image matrix (page positioning info)

	// You can use GetImageData to read the raw (decoded) image data
	//image->GetBitsPerComponent();	
	//image->GetImageData();	// get raw image data
	// .... or use Image2RGB filter that converts every image to RGB format,
	// This should save you time since you don't need to deal with color conversions, 
	// image up-sampling, decoding etc.

	Image2RGB img_conv(image);	// Extract and convert image to RGB 8-bpc format
	FilterReader reader(img_conv);

	// A buffer used to keep image data.
	std::vector<UChar> image_data_out; 
	image_data_out.resize(out_data_sz);

	reader.Read(&image_data_out.front(), out_data_sz);
	// &image_data_out.front() contains RGB image data.

	// Note that you don't need to read a whole image at a time. Alternatively
	// you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
	// until the function returns 0. 
}

void ProcessElements(ElementReader& reader) 
{
	Element element;
	while ((element = reader.Next()) != 0) 	// Read page contents
	{
		switch (element.GetType())
		{
		case Element::e_path:						// Process path data...
			{
				ProcessPath(reader, element);
			}
			break; 
		case Element::e_text_begin: 				// Process text block...
			{
				ProcessText(reader);
			}
			break;
		case Element::e_form:						// Process form XObjects
			{
				reader.FormBegin(); 
				ProcessElements(reader);
				reader.End();
			}
			break; 
		case Element::e_image:						// Process Images
			{
				ProcessImage(element);
			}	
			break; 
		}
	}
}

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 text data from all pages in the document
	{
		cout << "-------------------------------------------------" << endl;
		cout << "Extract page element information from all " << endl;
		cout << "pages in the document." << endl;

		PDFDoc doc((input_path + "newsletter.pdf").c_str());
		doc.InitSecurityHandler();

		int pgnum = doc.GetPageCount();
		PageIterator page_begin = doc.GetPageIterator();

		ElementReader page_reader;

		PageIterator itr;
		for (itr = page_begin; itr.HasNext(); itr.Next())		//  Read every page
		{				
			cout << "Page " << itr.Current().GetIndex() << "----------------------------------------" << endl;
			page_reader.Begin(itr.Current());
			ProcessElements(page_reader);
			page_reader.End();
		}

		cout << "Done." << endl;
	}
	catch(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"
    "os"
    "strconv"
	. "pdftron"
)

import  "pdftron/Samples/LicenseKey/GO"

func ProcessPath(reader ElementReader, path Element){
    if path.IsClippingPath(){
        fmt.Println("This is a clipping path")
    }

    pathData := path.GetPathData()
    data := pathData.GetPoints()
    opr := pathData.GetOperators()

    oprIndex := 0
    oprEnd := int(opr.Size())
    dataIndex := 0
    //dataEnd := data.Size()
    
    // Use path.GetCTM() if you are interested in CTM (current transformation matrix).
    
    os.Stdout.Write([]byte("Path Data Points := \""))
    x1, x2, x3, x4 := 0.0, 0.0, 0.0, 0.0
    y1, y2, y3, y4 := 0.0, 0.0, 0.0, 0.0
    for oprIndex < oprEnd{
        if int(opr.Get(oprIndex)) == int(PathDataE_moveto){
            x1 = data.Get(dataIndex) 
            dataIndex = dataIndex + 1
            y1 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            os.Stdout.Write([]byte("M" + fmt.Sprintf("%f", x1) + " " + fmt.Sprintf("%f", y1)))
        }else if int(opr.Get(oprIndex)) == int(PathDataE_lineto){
            x1 = data.Get(dataIndex) 
            dataIndex = dataIndex + 1
            y1 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            os.Stdout.Write([]byte(" L" + fmt.Sprintf("%f", x1) + " " + fmt.Sprintf("%f", y1)))
        }else if int(opr.Get(oprIndex)) == int(PathDataE_cubicto){
            x1 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            y1 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            x2 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            y2 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            x3 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            y3 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            os.Stdout.Write([]byte(" C" + fmt.Sprintf("%f", x1) + " " + fmt.Sprintf("%f", y1) + " " + fmt.Sprintf("%f", x2) + " " + fmt.Sprintf("%f", y2) + " " + fmt.Sprintf("%f", x3) + " " + fmt.Sprintf("%f", y3)))
        }else if int(opr.Get(oprIndex)) == int(PathDataE_rect){
            x1 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            y1 = data.Get(dataIndex)
            dataIndex = dataIndex + 1
            w := data.Get(dataIndex)
            dataIndex = dataIndex + 1
            h := data.Get(dataIndex)
            dataIndex = dataIndex + 1
            x2 = x1 + w
            y2 = y1
            x3 = x2
            y3 = y1 + h
            x4 = x1
            y4 = y3
            os.Stdout.Write([]byte("M" + fmt.Sprintf("%.2f", x1) + " " + fmt.Sprintf("%.2f", y1) + " L" + fmt.Sprintf("%.2f", x2) + " " + fmt.Sprintf("%.2f", y2) + " L" + fmt.Sprintf("%.2f", x3) + " " + fmt.Sprintf("%.2f", y3) + " L" + fmt.Sprintf("%.2f", x4) + " " + fmt.Sprintf("%.2f", y4) + " Z"))
        }else if int(opr.Get(oprIndex)) == int(PathDataE_closepath){
            fmt.Println(" Close Path")
        }else{
            //
        }
        oprIndex = oprIndex + 1
    }

    os.Stdout.Write([]byte("\" "))
    gs := path.GetGState()
    
    // Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
    if path.IsStroked(){
        fmt.Println("Stroke path")
        
        if (gs.GetStrokeColorSpace().GetType() == ColorSpaceE_pattern){
            fmt.Println("Path has associated pattern")
        }else{
            // Get stroke color (you can use PDFNet color conversion facilities)
            // rgb = gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor())
        }
    }else{
        // Do not stroke path
    }

    if path.IsFilled(){
        fmt.Println("Fill path")
        
        if (gs.GetFillColorSpace().GetType() == ColorSpaceE_pattern){
            fmt.Println("Path has associated pattern")
        }else{
            // rgb = gs.GetFillColorSpace().Convert2RGB(gs.GetFillColor())
        }
    }else{
        // Do not fill path
    }

    // Process any changes in graphics state  ---------------------------------
    gsItr := reader.GetChangesIterator()
    for gsItr.HasNext(){
        if int(gsItr.Current()) == int(GStateE_transform){
            // Get transform matrix for this element. Unlike path.GetCTM() 
            // that return full transformation matrix gs.GetTransform() return 
            // only the transformation matrix that was installed for this element.
            //
            // gs.GetTransform()
            
        }else if int(gsItr.Current()) == int(GStateE_line_width){
            // gs.GetLineWidth()
            
        }else if int(gsItr.Current()) == int(GStateE_line_cap){
            // gs.GetLineCap()
            
        }else if int(gsItr.Current()) == int(GStateE_line_join){
            // gs.GetLineJoin()
            
        }else if int(gsItr.Current()) == int(GStateE_flatness){
            
        }else if int(gsItr.Current()) == int(GStateE_miter_limit){
            // gs.GetMiterLimit()
            
        }else if int(gsItr.Current()) == int(GStateE_dash_pattern){
            // dashes = gs.GetDashes()
            // gs.GetPhase()
            
        }else if int(gsItr.Current()) == int(GStateE_fill_color){
            if (int(gs.GetFillColorSpace().GetType()) == int(ColorSpaceE_pattern) && int(gs.GetFillPattern().GetType()) != int(PatternColorE_shading) ){
                // process the pattern data
                reader.PatternBegin(true)
                ProcessElements(reader)
                reader.End()
            }
        }
        gsItr.Next()
    }
    reader.ClearChangeList()
}

func ProcessText (pageReader ElementReader){
    // Begin text element
    fmt.Println("Begin Text Block:")
    
    element := pageReader.Next()
    
    for element.GetMp_elem().Swigcptr() != 0{
        etype := element.GetType()
        if etype == ElementE_text_end{
            // Finish the text block
            fmt.Println("End Text Block.")
            return
        }else if etype == ElementE_text{
            gs := element.GetGState()
            
            //csFill := gs.GetFillColorSpace()
            //fill := gs.GetFillColor()
            
            //out := csFill.Convert2RGB(fill)
            
            //csStroke := gs.GetStrokeColorSpace()
            //stroke := gs.GetStrokeColor()
            
            font := gs.GetFont()
            fmt.Println("Font Name: " + font.GetName())
            // font.IsFixedWidth()
            // font.IsSerif()
            // font.IsSymbolic()
            // font.IsItalic()
            // ... 

            // fontSize = gs.GetFontSize()
            // wordSpacing = gs.GetWordSpacing()
            // charSpacing = gs.GetCharSpacing()
            // txt := element.GetTextString()
            if font.GetType() == FontE_Type3{
                // type 3 font, process its data
                itr := element.GetCharIterator()
                for itr.HasNext(){
                    pageReader.Type3FontBegin(itr.Current())
                    ProcessElements(pageReader)
                    pageReader.End()
                }
            }else{
                text_mtx := element.GetTextMatrix()
                
                itr := element.GetCharIterator()
                for itr.HasNext(){
                    charCode := itr.Current().GetChar_data()
                    if *charCode >= 32 && *charCode <= 255 {     // Print if in ASCII range...
                        a := font.MapToUnicode(uint(*charCode))
                        os.Stdout.Write([]byte( a )) // Revisit: if sys.version_info.major < 3 else ascii(a[0]) ))
                    }    
                    pt := NewPoint()   
                    pt.SetX(itr.Current().GetX())     // character positioning information
                    pt.SetY(itr.Current().GetY())
                    
                    // Use element.GetCTM() if you are interested in the CTM 
                    // (current transformation matrix).
                    ctm := element.GetCTM()
                    
                    // To get the exact character positioning information you need to 
                    // concatenate current text matrix with CTM and then multiply 
                    // relative positioning coordinates with the resulting matrix.
                    mtx := ctm.Multiply(text_mtx)
                    mtx.Mult(pt)
                    itr.Next()
                }
            }
            fmt.Println("")
        }
        element = pageReader.Next()
    }
}

func ProcessImage (image Element){
    //imageMask := image.IsImageMask()
    //interpolate := image.IsImageInterpolate()
    width := image.GetImageWidth()
    height := image.GetImageHeight()
    outDataSz := width * height * 3
    
    fmt.Println("Image: width=\"" + fmt.Sprintf("%d", width) + "\"" + " height=\"" + fmt.Sprintf("%d", height)+ "\"" )
    
    // Matrix2D& mtx = image->GetCTM() // image matrix (page positioning info)

    // You can use GetImageData to read the raw (decoded) image data
    //image->GetBitsPerComponent()    
    //image->GetImageData()    // get raw image data
    // .... or use Image2RGB filter that converts every image to RGB format,
    // This should save you time since you don't need to deal with color conversions, 
    // image up-sampling, decoding etc.
    
    imgConv := NewImage2RGB(image)     // Extract and convert image to RGB 8-bps format
    reader := NewFilterReader(imgConv)

    //imageDataOut := reader.Read(int64(outDataSz))
    reader.Read(int64(outDataSz))
    
    // Note that you don't need to read a whole image at a time. Alternatively
    // you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
    // until the function returns 0. 
}

func ProcessElements(reader ElementReader){
    element := reader.Next()     // Read page contents
    for element.GetMp_elem().Swigcptr() != 0{
        etype := element.GetType()
        if etype == ElementE_path{      // Process path data...
            ProcessPath(reader, element)
        }else if etype == ElementE_text_begin{      // Process text block...
            ProcessText(reader)
        }else if etype == ElementE_form{    // Process form XObjects
            reader.FormBegin()
            ProcessElements(reader)
            reader.End()
        }else if etype == ElementE_image{    // Process Images
            ProcessImage(element)
        }
        element = reader.Next()
    }
}

func main(){
    PDFNetInitialize(PDFTronLicense.Key)
    
    // Relative path to the folder containing the test files.
    inputPath := "../../TestFiles/"
    //outputPath := "../../TestFiles/Output/"
    
    // Extract text data from all pages in the document
    
    fmt.Println("__________________________________________________")
    fmt.Println("Extract page element information from all ")
    fmt.Println("pages in the document.")
    
    doc := NewPDFDoc(inputPath + "newsletter.pdf")
    doc.InitSecurityHandler()
    //pgnum := doc.GetPageCount()
    pageBegin := doc.GetPageIterator()
    pageReader := NewElementReader()
    
    itr := pageBegin
    for itr.HasNext(){    // Read every page
        fmt.Println("Page " + strconv.Itoa(itr.Current().GetIndex()) + "----------------------------------------")
        pageReader.Begin(itr.Current())
        ProcessElements(pageReader)
        pageReader.End()
        itr.Next()
    }
    doc.Close()
    PDFNetTerminate()
    fmt.Println("Done.")
}
```

{% 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 com.pdftron.sdf.*;
import com.pdftron.pdf.*;
import com.pdftron.common.*;
import com.pdftron.filters.FilterReader;


public class ElementReaderAdvTest {

    static String m_buf;

    static void ProcessPath(ElementReader reader, Element path) throws PDFNetException {
        if (path.isClippingPath()) {
            System.out.println("This is a clipping path");
        }

        PathData pathData = path.getPathData();
        double[] data = pathData.getPoints();
        byte[] opr = pathData.getOperators();

        double x1, y1, x2, y2, x3, y3;
        // Use path.getCTM() if you are interested in CTM (current transformation matrix).

        System.out.print(" Path Data Points := \"");
        int data_index = 0;
        for (int opr_index = 0; opr_index < opr.length; ++opr_index) {
            switch (opr[opr_index]) {
                case PathData.e_moveto:
                    x1 = data[data_index];
                    ++data_index;
                    y1 = data[data_index];
                    ++data_index;
                    System.out.print("M" + x1 + " " + y1);
                    break;
                case PathData.e_lineto:
                    x1 = data[data_index];
                    ++data_index;
                    y1 = data[data_index];
                    ++data_index;
                    System.out.print(" L" + x1 + " " + y1);

                    break;
                case PathData.e_cubicto:
                    x1 = data[data_index];
                    ++data_index;
                    y1 = data[data_index];
                    ++data_index;
                    x2 = data[data_index];
                    ++data_index;
                    y2 = data[data_index];
                    ++data_index;
                    x3 = data[data_index];
                    ++data_index;
                    y3 = data[data_index];
                    ++data_index;
                    System.out.print(" C" + x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3);
                    break;
                case PathData.e_rect: {
                    x1 = data[data_index];
                    ++data_index;
                    y1 = data[data_index];
                    ++data_index;
                    double w = data[data_index];
                    ++data_index;
                    double h = data[data_index];
                    ++data_index;
                    x2 = x1 + w;
                    y2 = y1;
                    x3 = x2;
                    y3 = y1 + h;
                    double x4 = x1;
                    double y4 = y3;
                    System.out.print("M" + x1 + " " + y1 + " L" + x2 + " " + y2 + " L" + x3 + " " + y3 + " L" + x4 + " " + y4 + " Z");
                }
                break;
                case PathData.e_closepath:
                    System.out.println(" Close Path");
                    break;
                default:
                    throw new PDFNetException("Invalid Element Type", 0, "", "", "");
            }
        }

        System.out.print("\" ");

        GState gs = path.getGState();

        // Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
        if (path.isStroked()) {
            System.out.println("Stroke path");

            if (gs.getStrokeColorSpace().getType() == ColorSpace.e_pattern) {
                System.out.println("Path has associated pattern");
            } else {
                // Get stroke color (you can use PDFNet color conversion facilities)
                ColorPt rgb = new ColorPt();
                rgb = gs.getStrokeColor();
                double v = rgb.get(0);
                rgb = gs.getStrokeColorSpace().convert2RGB(rgb);
                v = rgb.get(0);
            }
        } else {
            // Do not stroke path
        }

        if (path.isFilled()) {
            System.out.println("Fill path");

            if (gs.getFillColorSpace().getType() == ColorSpace.e_pattern) {
                System.out.println("Path has associated pattern");
                PatternColor pat = gs.getFillPattern();
                int type = pat.getType();
                if (type == PatternColor.e_shading) {
                    System.out.println("Shading");
                    Shading shading = pat.getShading();
                    if (shading.getType() == Shading.e_function_shading) {
                        System.out.println("FUNCT");
                    } else if (shading.getType() == Shading.e_axial_shading) {
                        System.out.println("AXIAL");
                    } else if (shading.getType() == Shading.e_radial_shading) {
                        System.out.println("RADIAL");
                    }
                } else if (type == PatternColor.e_colored_tiling_pattern) {
                    System.out.println("e_colored_tiling_pattern");
                } else if (type == PatternColor.e_uncolored_tiling_pattern) {
                    System.out.println("e_uncolored_tiling_pattern");
                } else {
                    System.out.println("?");
                }
            } else {
                ColorPt rgb = new ColorPt();
                rgb = gs.getFillColor();
                double v = rgb.get(0);
                rgb = gs.getFillColorSpace().convert2RGB(rgb);
                v = rgb.get(0);
            }
        } else {
            // Do not fill path
        }

        // Process any changes in graphics state  ---------------------------------

        GSChangesIterator gs_itr = reader.getChangesIterator();
        while (gs_itr.hasNext()) {
            switch (gs_itr.next().intValue()) {
                case GState.e_transform:
                    // Get transform matrix for this element. Unlike path.GetCTM()
                    // that return full transformation matrix gs.GetTransform() return
                    // only the transformation matrix that was installed for this element.
                    //
                    //gs.getTransform();
                    break;
                case GState.e_line_width:
                    //gs.getLineWidth();
                    break;
                case GState.e_line_cap:
                    //gs.getLineCap();
                    break;
                case GState.e_line_join:
                    //gs.getLineJoin();
                    break;
                case GState.e_flatness:
                    break;
                case GState.e_miter_limit:
                    //gs.getMiterLimit();
                    break;
                case GState.e_dash_pattern: {
                    //double[] dashes;
                    //dashes=gs.getDashes();
                    //gs.getPhase();
                }
                break;
                case GState.e_fill_color: {
                    if (gs.getFillColorSpace().getType() == ColorSpace.e_pattern &&
                            gs.getFillPattern().getType() != PatternColor.e_shading) {
                        //process the pattern data
                        reader.patternBegin(true);
                        ProcessElements(reader);
                        reader.end();
                    }
                }
                break;
            }
        }
        reader.clearChangeList();
    }

    static void ProcessText(ElementReader page_reader) throws PDFNetException {
        // Begin text element
        System.out.println("Begin Text Block:");

        Element element;
        while ((element = page_reader.next()) != null) {
            switch (element.getType()) {
                case Element.e_text_end:
                    // Finish the text block
                    System.out.println("End Text Block.");
                    return;

                case Element.e_text: {
                    GState gs = element.getGState();

                    ColorSpace cs_fill = gs.getFillColorSpace();
                    ColorPt fill = gs.getFillColor();

                    ColorPt out;
                    out = cs_fill.convert2RGB(fill);


                    ColorSpace cs_stroke = gs.getStrokeColorSpace();
                    ColorPt stroke = gs.getStrokeColor();

                    Font font = gs.getFont();

                    System.out.println("Font Name: " + font.getName());
                    //font.isFixedWidth();
                    //font.isSerif();
                    //font.isSymbolic();
                    //font.isItalic();
                    // ...

                    //double font_size = gs.getFontSize();
                    //double word_spacing = gs.getWordSpacing();
                    //double char_spacing = gs.getCharSpacing();
                    //String txt = element.getTextString();

                    if (font.getType() == Font.e_Type3) {
                        //type 3 font, process its data
                        for (CharIterator itr = element.getCharIterator(); itr.hasNext(); ) {
                            page_reader.type3FontBegin(itr.next(), null);
                            ProcessElements(page_reader);
                            page_reader.end();
                        }
                    } else {
                        Matrix2D text_mtx = element.getTextMatrix();
                        double x, y;
                        long char_code;

                        for (CharIterator itr = element.getCharIterator(); itr.hasNext(); ) {
                            CharData data = itr.next();
                            char_code = data.getCharCode();
                            //System.out.print("Character code: ");

                            System.out.print(String.valueOf(char_code));

                            x = data.getGlyphX();        // character positioning information
                            y = data.getGlyphY();

                            // Use element.getCTM() if you are interested in the CTM
                            // (current transformation matrix).
                            Matrix2D ctm = element.getCTM();

                            // To get the exact character positioning information you need to
                            // concatenate current text matrix with CTM and then multiply
                            // relative positioning coordinates with the resulting matrix.
                            //
                            Matrix2D mtx = ctm.multiply(text_mtx);
                            java.awt.geom.Point2D.Double t = mtx.multPoint(x, y);
                            x = t.x;
                            y = t.y;
                            //System.out.println(" Position: x=" + x + " y=" + y );
                        }

                        System.out.println();
                    }
                }
                break;
            }
        }
    }

    static void ProcessImage(Element image) throws PDFNetException {
        boolean image_mask = image.isImageMask();
        boolean interpolate = image.isImageInterpolate();
        int width = image.getImageWidth();
        int height = image.getImageHeight();
        int out_data_sz = width * height * 3;

        System.out.println("Image: " +
                " width=\"" + width + "\""
                + " height=\"" + height);

        // Matrix2D& mtx = image->GetCTM(); // image matrix (page positioning info)

        // You can use GetImageData to read the raw (decoded) image data
        //image->GetBitsPerComponent();
        //image->GetImageData();	// get raw image data
        // .... or use Image2RGB filter that converts every image to RGB format,
        // This should save you time since you don't need to deal with color conversions,
        // image up-sampling, decoding etc.

        Image2RGB img_conv = new Image2RGB(image);    // Extract and convert image to RGB 8-bpc format
        FilterReader reader = new FilterReader(img_conv);

        // A buffer used to keep image data.
        byte[] buf = new byte[out_data_sz];
        long image_data_out = reader.read(buf);
        // &image_data_out.front() contains RGB image data.

        // Note that you don't need to read a whole image at a time. Alternatively
        // you can read a chunk at a time by repeatedly calling reader.Read(buf)
        // until the function returns 0.
    }

    static void ProcessElements(ElementReader reader) throws PDFNetException {
        Element element;
        while ((element = reader.next()) != null)    // Read page contents
        {
            switch (element.getType()) {
                case Element.e_path:                        // Process path data...
                {
                    ProcessPath(reader, element);
                }
                break;
                case Element.e_text_begin:                // Process text block...
                {
                    ProcessText(reader);
                }
                break;
                case Element.e_form:                        // Process form XObjects
                {
                    reader.formBegin();
                    ProcessElements(reader);
                    reader.end();
                }
                break;
                case Element.e_image:                        // Process Images
                {
                    ProcessImage(element);
                }
                break;
            }
        }
    }

    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/";

        System.out.println("__________________________________________________");
        System.out.println("Extract page element information from all ");
        System.out.println("pages in the document.");
        try (PDFDoc doc = new PDFDoc((input_path + "newsletter.pdf")))    // Extract text data from all pages in the document
        {
            doc.initSecurityHandler();

            int pgnum = doc.getPageCount();
            PageIterator page_begin = doc.getPageIterator();

            ElementReader page_reader = new ElementReader();

            PageIterator itr;

            for (itr = page_begin; itr.hasNext(); )        //  Read every page
            {
                Page nextPage = itr.next();
                System.out.println("Page " + nextPage.getIndex() +
                        "----------------------------------------");
                page_reader.begin(nextPage);
                ProcessElements(page_reader);
                page_reader.end();
            }
            System.out.println("Done");
        } catch (Exception e) {
            System.out.println(e);
        }

        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.
//---------------------------------------------------------------------------------------


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

((exports) => {

  exports.runElementReaderAdvTest = () => {

    const processPath = async (reader, path) => {
      if (await path.isClippingPath()) {
        console.log('This is a clipping path');
      }

      const d = await path.getPathData();

      const opr = d.operators;
      const opr_len = opr.byteLength;
      const data = d.points;
      let data_idx = 0, data_len = data.byteLength / data.BYTES_PER_ELEMENT;

      let x1, y1, x2, y2, x3, y3;

      // Use path.GetCTM() if you are interested in CTM (current transformation matrix).

      let path_str = ' Path Data Points := "';
      for (let opr_idx = 0; opr_idx < opr_len; ++opr_idx) {
        switch (opr[opr_idx]) {
          case PDFNet.Element.PathSegmentType.e_moveto:
            x1 = data[data_idx]; ++data_idx;
            y1 = data[data_idx]; ++data_idx;
            path_str += 'M' + Math.round(x1) + ' ' + Math.round(y1);
            break;
          case PDFNet.Element.PathSegmentType.e_lineto:
            x1 = data[data_idx]; ++data_idx;
            y1 = data[data_idx]; ++data_idx;
            path_str += 'L' + Math.round(x1) + ' ' + Math.round(y1);
            break;
          case PDFNet.Element.PathSegmentType.e_cubicto:
            x1 = data[data_idx]; ++data_idx;
            y1 = data[data_idx]; ++data_idx;
            x2 = data[data_idx]; ++data_idx;
            y2 = data[data_idx]; ++data_idx;
            x3 = data[data_idx]; ++data_idx;
            y3 = data[data_idx]; ++data_idx;
            path_str += 'C' + Math.round(x1) + ' ' + Math.round(y1) + ' ' + Math.round(x2)
             + ' ' + Math.round(y2) + ' ' + Math.round(x3) + ' ' + Math.round(y3);
            break;
          case PDFNet.Element.PathSegmentType.e_rect:
            x1 = data[data_idx]; ++data_idx;
            y1 = data[data_idx]; ++data_idx;
            const w = data[data_idx]; ++data_idx;
            const h = data[data_idx]; ++data_idx;
            x2 = x1 + w;
            y2 = y1;
            x3 = x2;
            y3 = y1 + h;
            const x4 = x1;
            const y4 = y3;
            path_str += 'M' + Math.round(x1) + ' ' + Math.round(y1) + ' L' + Math.round(x2) + ' ' + Math.round(y2)
             + ' L' + Math.round(x3) + ' ' + Math.round(y3) + ' L' + Math.round(x4) + ' ' + Math.round(y4) + ' Z';
            break;
          case PDFNet.Element.PathSegmentType.e_closepath:
            path_str += ' Close Path\n';
            break;
          default:
            throw ''
            break;
        }
      }

      path_str += '" ';

      const gs = await path.getGState();

      // Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
      if (await path.isStroked()) {
        console.log(path_str + 'Stroke path');
        path_str = '';

        if (await (await gs.getStrokeColorSpace()).getType() === PDFNet.ColorSpace.Type.e_pattern) {
          console.log('Path has associated pattern');
        } else {
          // Get stroke color (you can use PDFNet color conversion facilities)
          // ColorPt rgb;
          // gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor(), rgb);
        }
      } else {
        // Do not stroke path
      }

      if (await path.isFilled()) {
        console.log(path_str + 'Fill path');
        path_str = '';

        if (await (await gs.getFillColorSpace()).getType() === PDFNet.ColorSpace.Type.e_pattern) {
          console.log('Path has associated pattern');
        } else {
          // ColorPt rgb;
          // gs.GetFillColorSpace().Convert2RGB(gs.GetFillColor(), rgb);
        }
      } else {
        // Do not fill path
      }

      if (path_str) {
        console.log(path_str);
      }

      // Process any changes in graphics state  ---------------------------------

      const gs_itr = await reader.getChangesIterator();
      for (; await gs_itr.hasNext(); await gs_itr.next()) {
        switch (await gs_itr.current()) {
          case PDFNet.GState.Attribute.e_transform:
            // Get transform matrix for this element. Unlike path.GetCTM() 
            // that return full transformation matrix gs.GetTransform() return 
            // only the transformation matrix that was installed for this element.
            //
            // gs.GetTransform();
            break;
          case PDFNet.GState.Attribute.e_line_width:
            // gs.GetLineWidth();
            break;
          case PDFNet.GState.Attribute.e_line_cap:
            // gs.GetLineCap();
            break;
          case PDFNet.GState.Attribute.e_line_join:
            // gs.GetLineJoin();
            break;
          case PDFNet.GState.Attribute.e_flatness:
            break;
          case PDFNet.GState.Attribute.e_miter_limit:
            // gs.GetMiterLimit();
            break;
          case PDFNet.GState.Attribute.e_dash_pattern:
            {
              // std::vector<double> dashes;
              // gs.GetDashes(dashes);
              // gs.GetPhase()
            }
            break;
          case PDFNet.GState.Attribute.e_fill_color:
            {
              if (await (await gs.getFillColorSpace()).getType() === PDFNet.ColorSpace.Type.e_pattern &&
                await (await gs.getFillPattern()).getType() !== PDFNet.PatternColor.Type.e_shading) {
                //process the pattern data
                await reader.patternBegin(true);
                await processElements(reader);
                await reader.end();
              }
            }
            break;
        }
      }
      await reader.clearChangeList();
    };

    const processText = async (pageReader) => {
      // Begin text element
      console.log('Begin Text Block:');

      let element;
      while (element = await pageReader.next()) {
        switch (await element.getType()) {
          case PDFNet.Element.Type.e_text_end:
            // Finish the text block
            console.log('End Text Block.');
            return;

          case PDFNet.Element.Type.e_text:
            const gs = await element.getGState();

            const cs_fill = await gs.getFillColorSpace();
            const fill = await gs.getFillColor();

            const out = await cs_fill.convert2RGB(fill);


            const cs_stroke = await gs.getStrokeColorSpace();
            const stroke = await gs.getStrokeColor();

            const font = await gs.getFont();

            console.log('Font Name: ' + await font.getName());

            let outPutStr = '';
            if (await font.getType() == PDFNet.Font.Type.e_Type3) {
              //type 3 font, process its data
              for (const itr = await element.getCharIterator(); await itr.hasNext(); await itr.next()) {
                await pageReader.type3FontBegin(await itr.current());
                await processElements(pageReader);
                await pageReader.end();
              }
            } else {
              const text_mtx = await element.getTextMatrix();

              for (const itr = await element.getCharIterator(); await itr.hasNext(); await itr.next()) {
                outPutStr += 'Character code: ';
                const charData = await itr.current();
                const charCode = charData.char_code;
                if (charCode >= 32 || charCode <= 127) {
                  // Print if in ASCII range...
                  outPutStr += String.fromCharCode(charCode);
                }

                const x = charData.x;		// character positioning information
                const y = charData.y;

                // Use element.GetCTM() if you are interested in the CTM 
                // (current transformation matrix).
                const ctm = await element.getCTM();

                // To get the exact character positioning information you need to 
                // concatenate current text matrix with CTM and then multiply 
                // relative positioning coordinates with the resulting matrix.
                await ctm.multiply(text_mtx);
                await ctm.mult(x, y);
              }
            }
            console.log(outPutStr);
            break;
        }
      }
    };

    const processImage = async (image) => {
      const width = await image.getImageWidth();
      const height = await image.getImageHeight();
      const out_data_sz = await width * height * 3;

      console.log('Image: width=\'' + width + '\' height=\'' + height + '\'');

      const img_conv = await PDFNet.Filter.createImage2RGBFromElement(image);	// Extract and convert image to RGB 8-bpc format
      const reader = await PDFNet.FilterReader.create(img_conv);

      const image_data_out = await reader.read(out_data_sz);

      // Note that you don't need to read a whole image at a time. Alternatively
      // you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
      // until the function returns 0. 
    }

    const processElements = async (reader) => {
      let element;
      while (element = await reader.next()) {	// Read page contents
        switch (await element.getType()) {
          case PDFNet.Element.Type.e_path:						// Process path data...
            await processPath(reader, element);
            break;
          case PDFNet.Element.Type.e_text_begin: 				// Process text block...
            await processText(reader);
            break;
          case PDFNet.Element.Type.e_form:						// Process form XObjects
            await reader.formBegin();
            await processElements(reader);
            await reader.end();
            break;
          case PDFNet.Element.Type.e_image:						// Process Images
            await processImage(element);
            break;
        }
      }
    }

    const main = async () => {
      // Relative path to the folder containing test files.
      const inputPath = '../TestFiles/';
      try {
        console.log('-------------------------------------------------');
        console.log('Extract page element information from all ');
        console.log('pages in the document.');

        const doc = await PDFNet.PDFDoc.createFromFilePath(inputPath + 'newsletter.pdf');
        doc.initSecurityHandler();

        const pgnum = await doc.getPageCount();
        const pageBegin = await doc.getPageIterator();

        const pageReader = await PDFNet.ElementReader.create();

        for (const itr = pageBegin; await itr.hasNext(); await itr.next())		//  Read every page
        {
          const curPage = await itr.current();
          console.log('Page ' + await curPage.getIndex() + '----------------------------------------');
          await pageReader.beginOnPage(curPage);
          await processElements(pageReader);
          await pageReader.end();
        }

        console.log('Done.');
      } 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.runElementReaderAdvTest();
})(exports);
// eslint-disable-next-line spaced-comment
//# sourceURL=ElementReaderAdvTest.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");

function ProcessPath($reader, $path)
{
	if ($path->IsClippingPath())
	{
		echo nl2br("This is a clipping path\n");
	}

	$pathData = $path->GetPathData();
	$data = $pathData->GetPoints();
	$opr = $pathData->GetOperators();

	$opr_index = 0;
	$opr_end = count((array)$opr);
	$data_index = 0;
	$data_end = count($data);

	// Use path.GetCTM() if you are interested in CTM (current transformation matrix).

	echo " Path Data Points := \"";
	for (; $opr_index<$opr_end; ++$opr_index)
	{
		switch($opr[$opr_index])
		{
		case PathData::e_moveto:
			$x1 = $data[$data_index]; ++$data_index;
			$y1 = $data[$data_index]; ++$data_index;
			$m_buf = sprintf("M%.5g %.5g", $x1, $y1);
			echo $m_buf;
			break;
		case PathData::e_lineto:
			$x1 = $data[$data_index]; ++$data_index;
			$y1 = $data[$data_index]; ++$data_index;
			$m_buf = sprintf(" L%.5g %.5g", $x1, $y1);
			echo $m_buf;
			break;
		case PathData::e_cubicto:
			$x1 = $data[$data_index]; ++$data_index;
			$y1 = $data[$data_index]; ++$data_index;
			$x2 = $data[$data_index]; ++$data_index;
			$y2 = $data[$data_index]; ++$data_index;
			$x3 = $data[$data_index]; ++$data_index;
			$y3 = $data[$data_index]; ++$data_index;
			$m_buf = sprintf(" C%.5g %.5g %.5g %.5g %.5g %.5g", $x1, $y1, $x2, $y2, $x3, $y3);
			echo $m_buf;
			break;
		case PathData::e_rect:
			{
				$x1 = $data[$data_index]; ++$data_index;
				$y1 = $data[$data_index]; ++$data_index;
				$w = $data[$data_index]; ++$data_index;
				$h = $data[$data_index]; ++$data_index;
				$x2 = $x1 + $w;
				$y2 = $y1;
				$x3 = $x2;
				$y3 = $y1 + $h;
				$x4 = $x1; 
				$y4 = $y3;
				$m_buf = sprintf("M%.5g %.5g L%.5g %.5g L%.5g %.5g L%.5g %.5g Z", 
					$x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4);
				echo $m_buf;
			}
			break;
		case PathData::e_closepath:
			echo nl2br(" Close Path\n");
			break;
		default: 
			//assert(false);
			break;
		}	
	}

	echo "\" ";

	$gs = $path->GetGState();

	// Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
	if ($path->IsStroked()) 
	{
		echo nl2br("Stroke path\n"); 

		if ($gs->GetStrokeColorSpace()->GetType() == ColorSpace::e_pattern)
		{
			echo nl2br("Path has associated pattern\n"); 
		}
		else
		{
			// Get stroke color (you can use PDFNet color conversion facilities)
			// $rgb = $gs->GetStrokeColorSpace()->Convert2RGB($gs->GetStrokeColor());
		}
	}
	else 
	{
		// Do not stroke path
	}

	if ($path->IsFilled())
	{
		echo nl2br("Fill path\n"); 

		if ($gs->GetFillColorSpace()->GetType() == ColorSpace::e_pattern)
		{		
			echo nl2br("Path has associated pattern\n"); 
		}
		else
		{
			// $rgb = $gs->GetFillColorSpace()->Convert2RGB($gs->GetFillColor());
		}        
	}
	else 
	{
		// Do not fill path
	}

	// Process any changes in graphics state  ---------------------------------

	$gs_itr = $reader->GetChangesIterator();
	for (; $gs_itr->HasNext(); $gs_itr->Next()) 
	{
		switch($gs_itr->Current())
		{
		case GState::e_transform :
			// Get transform matrix for this element. Unlike path.GetCTM() 
			// that return full transformation matrix gs.GetTransform() return 
			// only the transformation matrix that was installed for this element.
			//
			// $gs->GetTransform();
			break;
		case GState::e_line_width :
			// $gs->GetLineWidth();
			break;
		case GState::e_line_cap :
			// $gs->GetLineCap();
			break;
		case GState::e_line_join :
			// $gs->GetLineJoin();
			break;
		case GState::e_flatness :	
			break;
		case GState::e_miter_limit :
			// $gs->GetMiterLimit();
			break;
		case GState::e_dash_pattern :
			{
				// $dashes = $gs->GetDashes($dashes);
				// $gs->GetPhase()
			}
			break;
		case GState::e_fill_color:
			{
				if ( $gs->GetFillColorSpace()->GetType() == ColorSpace::e_pattern &&
					$gs->GetFillPattern()->GetType() != PatternColor::e_shading )
				{	
					//process the pattern data
					$reader->PatternBegin(true);
					ProcessElements($reader);
					$reader->End();
				}
			}
			break;
		}
	}
	$reader->ClearChangeList();
}

function ProcessText($page_reader) 
{
	// Begin text element
	echo nl2br("Begin Text Block:\n");

	while (($element = $page_reader->Next()) != NULL) 
	{
		switch ($element->GetType())
		{
		case Element::e_text_end: 
			// Finish the text block
			echo nl2br("End Text Block.\n");
			return;

		case Element::e_text:
			{
				$gs = $element->GetGState();

				$cs_fill = $gs->GetFillColorSpace();
				$fill = $gs->GetFillColor();

				$out = $cs_fill->Convert2RGB($fill);

				$cs_stroke = $gs->GetStrokeColorSpace();
				$stroke = $gs->GetStrokeColor();

				$font = $gs->GetFont();

				echo nl2br("Font Name: ".$font->GetName()."\n");
				// $font->IsFixedWidth();
				// $font->IsSerif();
				// $font->IsSymbolic();
				// $font->IsItalic();
				// ... 

				// $font_size = $gs->GetFontSize();
				// $word_spacing = $gs->GetWordSpacing();
				// $char_spacing = $gs->GetCharSpacing();
				// $txt = $element->GetTextString();

				if ( $font->GetType() == Font::e_Type3 )
				{
					//type 3 font, process its data
					for ($itr = $element->GetCharIterator(); $itr->HasNext(); $itr->Next()) 
					{
						$page_reader->Type3FontBegin($itr->Current());
						ProcessElements($page_reader);
						$page_reader->End();
					}
				}

				else
				{	
					$text_mtx = $element->GetTextMatrix();
					
					for ($itr = $element->GetCharIterator(); $itr->HasNext(); $itr->Next()) 
					{
						$char_code = $itr->Current()->char_code;
						if ($char_code>=32 || $char_code<=255) { // Print if in ASCII range...
							echo chr($char_code);
						}

						$x = $itr->Current()->x;		// character positioning information
						$y = $itr->Current()->y;
						$pt = new Point($x, $y);

						// Use element.GetCTM() if you are interested in the CTM 
						// (current transformation matrix).
						$ctm = $element->GetCTM();

						// To get the exact character positioning information you need to 
						// concatenate current text matrix with CTM and then multiply 
						// relative positioning coordinates with the resulting matrix.
						$mtx = $text_mtx;
						$mtx->Concat($ctm->m_a, $ctm->m_b, $ctm->m_c, $ctm->m_d, $ctm->m_h, $ctm->m_v);
						$mtx->Mult($pt);

						// Get glyph path...
						//$glyphPath = font.GetGlyphPath($char_code, false, 0);
						//$oprs = $glyphPath->GetOperators();
						//$glyph_data = $glyphPath->GetDataPoints();
					}
				}

				echo nl2br("\n");
			}
			break;
		}
	}
}

function ProcessImage($image)  
{
	$image_mask = $image->IsImageMask();
	$interpolate = $image->IsImageInterpolate();
	$width = $image->GetImageWidth();
	$height = $image->GetImageHeight();

	$out_data_sz = $width * $height * 3;

	echo "Image: " 
		." width=\"".$width."\""
		." height=\"".$height."\n";

	// $mtx = $image->GetCTM(); // image matrix (page positioning info)

	// You can use GetImageData to read the raw (decoded) image data
	//$image->GetBitsPerComponent();	
	//$image->GetImageData();	// get raw image data
	// .... or use Image2RGB filter that converts every image to RGB format,
	// This should save you time since you don't need to deal with color conversions, 
	// image up-sampling, decoding etc.

	$img_conv = new Image2RGB($image);	// Extract and convert image to RGB 8-bpc format
	$reader = new FilterReader($img_conv);

	// A buffer used to keep image data.
	$image_data_out = $reader->Read($out_data_sz);
	// $image_data_out contains RGB image data.

	// Note that you don't need to read a whole image at a time. Alternatively
	// you can read a chuck at a time by repeatedly calling reader.Read(buf_sz) 
	// until the function returns 0. 
}
    
function ProcessElements($reader) 
{
	while (($element = $reader->Next()) != NULL) 	// Read page contents
	{
		switch ($element->GetType())
		{
		case Element::e_path:						// Process path data...
			{
				ProcessPath($reader, $element);
			}
			break; 
		case Element::e_text_begin: 				// Process text block...
			{
				ProcessText($reader);
			}
			break;
		case Element::e_form:						// Process form XObjects
			{
				$reader->FormBegin(); 
				ProcessElements($reader);
				$reader->End();
			}
			break; 
		case Element::e_image:						// Process Images
			{
				ProcessImage($element);
			}	
			break; 
		}
	}
}

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

	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 text data from all pages in the document
	echo nl2br("__________________________________________________\n");
	echo nl2br("Extract page element information from all \n");
	echo nl2br("pages in the document.\n");

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

	$pgnum = $doc->GetPageCount();
	$page_begin = $doc->GetPageIterator();

	$page_reader = new ElementReader();

	for ($itr = $page_begin; $itr->HasNext(); $itr->Next())		//  Read every page
	{				
		echo nl2br("Page ".$itr->Current()->GetIndex()."----------------------------------------\n");
		$page_reader->Begin($itr->Current());
		ProcessElements($page_reader);
		$page_reader->End();
	}
	$doc->Close();
	PDFNet::Terminate();
	echo nl2br("Done.\n");		
?>
```

{% 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

def ProcessPath(reader, path)
	if path.IsClippingPath
		puts "This is a clipping path"
	end
	
	pathData = path.GetPathData
	data = pathData.GetPoints
	opr = pathData.GetOperators

	opr_index = 0
	opr_end = opr.size
	data_index = 0
	data_end = data.size

	# Use path.GetCTM if you are interested in CTM (current transformation matrix).
	print "Path Data Points := \""
	
	while opr_index < opr_end
		case opr[opr_index].ord
		when PathData::E_moveto
			x1 = data[data_index] 
			data_index = data_index + 1
			y1 = data[data_index]
			data_index = data_index + 1
			puts "M" + x1.to_s + " " + y1.to_s
		when PathData::E_lineto
			x1 = data[data_index] 
			data_index = data_index + 1
			y1 = data[data_index]
			data_index = data_index + 1
			print " L" + x1.to_s + " " + y1.to_s
		when PathData::E_cubicto
			x1 = data[data_index]
			data_index = data_index + 1
			y1 = data[data_index]
			data_index = data_index + 1
			x2 = data[data_index]
			data_index = data_index + 1
			y2 = data[data_index]
			data_index = data_index + 1
			x3 = data[data_index]
			data_index = data_index + 1
			y3 = data[data_index]
			data_index = data_index + 1
			print " C" + x1.to_s + " " + y1.to_s + " " + x2.to_s + 
				" " + y2.to_s + " " + x3.to_s + " " + y3.to_s
		when PathData::E_rect
			x1 = data[data_index]
			data_index = data_index + 1
			y1 = data[data_index]
			data_index = data_index + 1
			w = data[data_index]
			data_index = data_index + 1
			h = data[data_index]
			data_index = data_index + 1
			x2 = x1 + w
			y2 = y1
			x3 = x2
			y3 = y1 + h
			x4 = x1
			y4 = y3
			print "M" + x1.to_s + " " + y1.to_s + " L " + x2.to_s + " " + y2.to_s + " L " + 
				x3.to_s + " " + y3.to_s + " L " + x4.to_s + " " + y4.to_s + " Z"
		when PathData::E_closepath
			puts " Close Path"
		else
			raise "Assert: false"
		end
		opr_index = opr_index + 1
	end
	
	print "\" "
	gs = path.GetGState
	
	# Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
	if path.IsStroked
		puts "Stroke path"
		
		if gs.GetStrokeColorSpace.GetType == ColorSpace::E_pattern
			puts "Path has associated pattern"
		else
			# Get stroke color (you can use PDFNet color conversion facilities)
			# rgb = gs.GetStrokeColorSpace.Convert2RGB(gs.GetStrokeColor)
		end
	else
		# Do not stroke path
	end
		
	if path.IsFilled
		puts "Fill path"
		
		if gs.GetFillColorSpace.GetType == ColorSpace::E_pattern
			puts "Path has associated pattern"
		else
			# rgb = gs.GetFillColorSpace.Convert2RGB(gs.GetFillColor)
		end
	else
		# Do not fill path
	end
	
	# Process any changes in graphics state  ---------------------------------
	gs_itr = reader.GetChangesIterator
	while gs_itr.HasNext do
		case gs_itr.Current
		when GState::E_transform
			# Get transform matrix for this element. Unlike path.GetCTM 
			# that return full transformation matrix gs.GetTransform return 
			# only the transformation matrix that was installed for this element.
			#
			# gs.GetTransform
		when GState::E_line_width
			# gs.GetLineWidth
		when GState::E_line_cap
			# gs.GetLineCap
		when GState::E_line_join
			# gs.GetLineJoin
		when GState::E_flatness
		when GState::E_miter_limit
			# gs.GetMiterLimit
		when GState::E_dash_pattern
			# dashes = gs.GetDashes
			# gs.GetPhase
		when GState::E_fill_color
			if (gs.GetFillColorSpace.GetType == ColorSpace::E_pattern and
				gs.GetFillPattern.GetType != PatternColor::E_shading )
				# process the pattern data
				reader.PatternBegin(true)
				ProcessElements(reader)
				reader.End
			end
		end
		gs_itr.Next
	end
	reader.ClearChangeList
end
	
def ProcessText (page_reader)
	# Begin text element
	puts "Begin Text Block:"
	
	element = page_reader.Next
	
	while !element.nil?
		type = element.GetType
		if type == Element::E_text_end
			# Finish the text block
			puts "End Text Block."
			return
		elsif type == Element::E_text
			gs = element.GetGState
			
			cs_fill = gs.GetFillColorSpace
			fill = gs.GetFillColor
			
			out = cs_fill.Convert2RGB(fill)
			
			cs_stroke = gs.GetStrokeColorSpace
			stroke = gs.GetStrokeColor
			
			font = gs.GetFont
			puts "Font Name: " + font.GetName
			# font.IsFixedWidth
			# font.IsSerif
			# font.IsSymbolic
			# font.IsItalic
			# ... 

			# font_size = gs.GetFontSize
			# word_spacing = gs.GetWordSpacing
			# char_spacing = gs.GetCharSpacing
			# txt = element.GetTextString
			if font.GetType == Font::E_Type3
				# type 3 font, process its data
				itr = element.GetCharIterator
				while itr.HasNext do
					page_reader.Type3FontBegin(itr.Current)
					ProcessElements(page_reader)
					page_reader.End
				end
			else
				text_mtx = element.GetTextMatrix
				
				itr = element.GetCharIterator
				while itr.HasNext do
					char_code = itr.Current.char_code
					if char_code>=32 and char_code<=255	 # Print if in ASCII range...
						a = font.MapToUnicode(char_code)
						print a[0]
					end
						
					pt = Point.new   
					pt.x = itr.Current.x	 # character positioning information
					pt.y = itr.Current.y
					
					# Use element.GetCTM if you are interested in the CTM 
					# (current transformation matrix).
					ctm = element.GetCTM
					
					# To get the exact character positioning information you need to 
					# concatenate current text matrix with CTM and then multiply 
					# relative positioning coordinates with the resulting matrix.
					mtx = ctm.Multiply(text_mtx)
					mtx.Mult(pt)
					itr.Next
				end
			end
			puts ""
		end
		element = page_reader.Next
	end
end
	
def ProcessImage (image)
	image_mask = image.IsImageMask
	interpolate = image.IsImageInterpolate
	width = image.GetImageWidth
	height = image.GetImageHeight
	out_data_sz = width * height * 3
	
	puts "Image: width=\"" + width.to_s + "\"" + " height=\"" + height.to_s
	
	# mtx = image.GetCTM # image matrix (page positioning info)

	# You can use GetImageData to read the raw (decoded) image data
	#image.GetBitsPerComponent	
	#image.GetImageData	# get raw image data
	# .... or use Image2RGB filter that converts every image to RGB format,
	# This should save you time since you don't need to deal with color conversions, 
	# image up-sampling, decoding etc.
	
	img_conv = Image2RGB.new(image)	 # Extract and convert image to RGB 8-bps format
	reader = FilterReader.new(img_conv)

	image_data_out = reader.Read(out_data_sz)
	
	# Note that you don't need to read a whole image at a time. Alternatively
	# you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
	# until the function returns 0. 
end

def ProcessElements(reader)
	element = reader.Next	 # Read page contents
	while !element.nil?
		type = element.GetType
		case type
		when Element::E_path	  # Process path data...
			ProcessPath(reader, element)
		when Element::E_text_begin	  # Process text block...
			ProcessText(reader)
		when Element::E_form	# Process form XObjects
			reader.FormBegin
			ProcessElements(reader)
			reader.End
		when Element::E_image	# Process Images
			ProcessImage(element)
		end
		element = reader.Next
	end
end

	PDFNet.Initialize(PDFTronLicense.Key)
	
	# Relative path to the folder containing the test files.
	input_path = "../../TestFiles/"
	output_path = "../../TestFiles/Output/"
	
	# Extract text data from all pages in the document
	
	puts "__________________________________________________"
	puts "Extract page element information from all "
	puts "pages in the document."
	

	doc = PDFDoc.new(input_path + "newsletter.pdf")
	doc.InitSecurityHandler
	pgnum = doc.GetPageCount
	page_begin = doc.GetPageIterator
	page_reader = ElementReader.new
	
	itr = page_begin
	while itr.HasNext do	# Read every page
		puts "Page " + itr.Current.GetIndex.to_s + "----------------------------------------"
		page_reader.Begin(itr.Current)
		ProcessElements(page_reader)
		page_reader.End
		itr.Next
	end
	doc.Close
	PDFNet.Terminate
	puts "Done."
```

{% 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 *

def ProcessPath(reader, path):
    if path.IsClippingPath():
        print("This is a clipping path")
    
    pathData = path.GetPathData()
    data = pathData.GetPoints()
    opr = pathData.GetOperators()

    opr_index = 0
    opr_end = len(opr)
    data_index = 0
    data_end = len(data)
    
    # Use path.GetCTM() if you are interested in CTM (current transformation matrix).
    
    sys.stdout.write("Path Data Points := \"")
    
    while opr_index < opr_end:
        if opr[opr_index] == PathData.e_moveto:
            x1 = data[data_index] 
            data_index = data_index + 1
            y1 = data[data_index]
            data_index = data_index + 1
            sys.stdout.write("M" + str(x1) + " " + str(y1))
        elif opr[opr_index] == PathData.e_lineto:
            x1 = data[data_index] 
            data_index = data_index + 1
            y1 = data[data_index]
            data_index = data_index + 1
            sys.stdout.write(" L" + str(x1) + " " + str(y1))
        elif opr[opr_index] == PathData.e_cubicto:
            x1 = data[data_index]
            data_index = data_index + 1
            y1 = data[data_index]
            data_index = data_index + 1
            x2 = data[data_index]
            data_index = data_index + 1
            y2 = data[data_index]
            data_index = data_index + 1
            x3 = data[data_index]
            data_index = data_index + 1
            y3 = data[data_index]
            data_index = data_index + 1
            sys.stdout.write(" C" + str(x1) + " " + str(y1) + " " + str(x2) + 
                             " " + str(y2) + " " + str(x3) + " " + str(y3))
        elif opr[opr_index] == PathData.e_rect:
            x1 = data[data_index]
            data_index = data_index + 1
            y1 = data[data_index]
            data_index = data_index + 1
            w = data[data_index]
            data_index = data_index + 1
            h = data[data_index]
            data_index = data_index + 1
            x2 = x1 + w
            y2 = y1
            x3 = x2
            y3 = y1 + h
            x4 = x1
            y4 = y3
            sys.stdout.write("M" + str(x1) + " " + str(y1) + " L" + str(x2) + " " + str(y2) + " L" + 
                             str(x3) + " " + str(y3) + " L" + str(x4) + " " + str(y4) + " Z")
        elif opr[opr_index] == PathData.e_closepath:
            print(" Close Path")
        else:
            assert(False)
        opr_index = opr_index + 1
    
    sys.stdout.write("\" ")
    gs = path.GetGState()
    
    # Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
    if path.IsStroked():
        print("Stroke path")
        
        if (gs.GetStrokeColorSpace().GetType() == ColorSpace.e_pattern):
            print("Path has associated pattern")
        else:
            # Get stroke color (you can use PDFNet color conversion facilities)
            # rgb = gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor())
            pass
    else:
        pass;
        # Do not stroke path
        
    if path.IsFilled():
        print("Fill path")
        
        if (gs.GetFillColorSpace().GetType() == ColorSpace.e_pattern):
            print("Path has associated pattern")
        else:
            # rgb = gs.GetFillColorSpace().Convert2RGB(gs.GetFillColor())
            pass
    else:
        pass
        # Do not fill path
    
    # Process any changes in graphics state  ---------------------------------
    gs_itr = reader.GetChangesIterator()
    while gs_itr.HasNext():
        if gs_itr.Current() == GState.e_transform:
            # Get transform matrix for this element. Unlike path.GetCTM() 
            # that return full transformation matrix gs.GetTransform() return 
            # only the transformation matrix that was installed for this element.
            #
            # gs.GetTransform()
            pass
        elif gs_itr.Current() == GState.e_line_width:
            # gs.GetLineWidth()
            pass
        elif gs_itr.Current() == GState.e_line_cap:
            # gs.GetLineCap()
            pass
        elif gs_itr.Current() == GState.e_line_join:
            # gs.GetLineJoin()
            pass
        elif gs_itr.Current() == GState.e_flatness:
            pass
        elif gs_itr.Current() == GState.e_miter_limit:
            # gs.GetMiterLimit()
            pass
        elif gs_itr.Current() == GState.e_dash_pattern:
            # dashes = gs.GetDashes()
            # gs.GetPhase()
            pass
        elif gs_itr.Current() == GState.e_fill_color:
            if (gs.GetFillColorSpace().GetType() == ColorSpace.e_pattern and
                gs.GetFillPattern().GetType() != PatternColor.e_shading ):
                # process the pattern data
                reader.PatternBegin(True)
                ProcessElements(reader)
                reader.End()
        gs_itr.Next()
    reader.ClearChangeList()
    
def ProcessText (page_reader):
    # Begin text element
    print("Begin Text Block:")
    
    element = page_reader.Next()
    
    while element != None:
        type = element.GetType()
        if type == Element.e_text_end:
            # Finish the text block
            print("End Text Block.")
            return
        elif type == Element.e_text:
            gs = element.GetGState()
            
            cs_fill = gs.GetFillColorSpace()
            fill = gs.GetFillColor()
            
            out = cs_fill.Convert2RGB(fill)
            
            cs_stroke = gs.GetStrokeColorSpace()
            stroke = gs.GetStrokeColor()
            
            font = gs.GetFont()
            print("Font Name: " + font.GetName())
            # font.IsFixedWidth()
            # font.IsSerif()
            # font.IsSymbolic()
            # font.IsItalic()
            # ... 

            # font_size = gs.GetFontSize()
            # word_spacing = gs.GetWordSpacing()
            # char_spacing = gs.GetCharSpacing()
            # txt = element.GetTextString()
            if font.GetType() == Font.e_Type3:
                # type 3 font, process its data
                itr = element.GetCharIterator()
                while itr.HasNext():
                    page_reader.Type3FontBegin(itr.Current())
                    ProcessElements(page_reader)
                    page_reader.End()
            else:
                text_mtx = element.GetTextMatrix()
                
                itr = element.GetCharIterator()
                while itr.HasNext():
                    char_code = itr.Current().char_code
                    if char_code>=32 and char_code<=255:     # Print if in ASCII range...
                        a = font.MapToUnicode(char_code)
                        sys.stdout.write( a[0] if sys.version_info.major < 3 else ascii(a[0]) )
                        
                    pt = Point()   
                    pt.x = itr.Current().x     # character positioning information
                    pt.y = itr.Current().y
                    
                    # Use element.GetCTM() if you are interested in the CTM 
                    # (current transformation matrix).
                    ctm = element.GetCTM()
                    
                    # To get the exact character positioning information you need to 
                    # concatenate current text matrix with CTM and then multiply 
                    # relative positioning coordinates with the resulting matrix.
                    mtx = ctm.Multiply(text_mtx)
                    mtx.Mult(pt)
                    itr.Next()
            print("")
        element = page_reader.Next()
    
def ProcessImage (image):
    image_mask = image.IsImageMask()
    interpolate = image.IsImageInterpolate()
    width = image.GetImageWidth()
    height = image.GetImageHeight()
    out_data_sz = width * height * 3
    
    print("Image: width=\"" + str(width) + "\"" + " height=\"" + str(height))
    
    # Matrix2D& mtx = image->GetCTM() # image matrix (page positioning info)

    # You can use GetImageData to read the raw (decoded) image data
    #image->GetBitsPerComponent()    
    #image->GetImageData()    # get raw image data
    # .... or use Image2RGB filter that converts every image to RGB format,
    # This should save you time since you don't need to deal with color conversions, 
    # image up-sampling, decoding etc.
    
    img_conv = Image2RGB(image)     # Extract and convert image to RGB 8-bps format
    reader = FilterReader(img_conv)

    image_data_out = reader.Read(out_data_sz)
    
    # Note that you don't need to read a whole image at a time. Alternatively
    # you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
    # until the function returns 0. 

def ProcessElements(reader):
    element = reader.Next()     # Read page contents
    while element != None:
        type = element.GetType()
        if type == Element.e_path:      # Process path data...
            ProcessPath(reader, element)
        elif type == Element.e_text_begin:      # Process text block...
            ProcessText(reader)
        elif type == Element.e_form:    # Process form XObjects
            reader.FormBegin()
            ProcessElements(reader)
            reader.End()
        elif type == Element.e_image:    # Process Images
            ProcessImage(element)
        element = reader.Next()

if __name__ == '__main__':
    PDFNet.Initialize(LicenseKey)
    
    # Relative path to the folder containing the test files.
    input_path = "../../TestFiles/"
    output_path = "../../TestFiles/Output/"
    
    # Extract text data from all pages in the document
    
    print("__________________________________________________")
    print("Extract page element information from all ")
    print("pages in the document.")
    
    doc = PDFDoc(input_path + "newsletter.pdf")
    doc.InitSecurityHandler()
    pgnum = doc.GetPageCount()
    page_begin = doc.GetPageIterator()
    page_reader = ElementReader()
    
    itr = page_begin
    while itr.HasNext():    # Read every page
        print("Page " + str(itr.Current().GetIndex()) + "----------------------------------------")
        page_reader.Begin(itr.Current())
        ProcessElements(page_reader)
        page_reader.End()
        itr.Next()
    doc.Close()
    PDFNet.Terminate()
    print("Done.")
```

{% endcode %}
{% endtab %}

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

```vb
'
' Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
'
' A sample project illustrating some extraction capabilities of ElementReader
' in more detail
'

Imports System

Imports pdftron
Imports pdftron.Common
Imports pdftron.Filters
Imports pdftron.SDF
Imports pdftron.PDF

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

    Dim m_buf As String

    Sub ProcessPath(ByRef reader As ElementReader, ByRef path As Element)
        If path.IsClippingPath() Then
            Console.WriteLine("This is a clipping path")
        End If

        Dim pathData As PathData = path.GetPathData()
        Dim data As Double() = pathData.points
        Dim data_sz As Integer = data.Length

        Dim opr As Byte() = pathData.operators
        Dim opr_sz As Integer = opr.Length

        Dim opr_itr As Integer = 0
        Dim opr_end As Integer = opr_sz
        Dim data_itr As Integer = 0
        Dim data_end As Integer = data_sz
        Dim x1, y1, x2, y2, x3, y3 As Double

        ' Use path.GetCTM() if you are interested in CTM (current transformation matrix).

        Console.Write(" Path Data Points := \")
        While opr_itr < opr_end
            'switch((Element.PathSegmentType)((int)opr[opr_itr]))
            If opr(opr_itr) = pathData.PathSegmentType.e_moveto Then
                x1 = data(data_itr)
                data_itr += 1
                y1 = data(data_itr)
                data_itr += 1
                m_buf = String.Format("M{0:g5} {1:g5}", x1, y1)
                Console.Write(m_buf)
            ElseIf opr(opr_itr) = pathData.PathSegmentType.e_lineto Then
                x1 = data(data_itr)
                data_itr += 1
                y1 = data(data_itr)
                data_itr += 1
                m_buf = String.Format(" L{0:g5} {1:g5}", x1, y1)
                Console.Write(m_buf)
            ElseIf opr(opr_itr) = pathData.PathSegmentType.e_cubicto Then
                x1 = data(data_itr)
                data_itr += 1
                y1 = data(data_itr)
                data_itr += 1
                x2 = data(data_itr)
                data_itr += 1
                y2 = data(data_itr)
                data_itr += 1
                x3 = data(data_itr)
                data_itr += 1
                y3 = data(data_itr)
                data_itr += 1
                Dim coords() As Object = New Object() {x1, y1, x2, y2, x3, y3}
                m_buf = String.Format(" C{0:g5} {1:g5} {2:g5} {3:g5} {4:g5} {5:g5}", _
                 coords)
                Console.Write(m_buf)
            ElseIf opr(opr_itr) = pathData.PathSegmentType.e_rect Then
                x1 = data(data_itr)
                data_itr += 1
                y1 = data(data_itr)
                data_itr += 1
                Dim w As Double = data(data_itr)
                data_itr += 1
                Dim h As Double = data(data_itr)
                data_itr += 1
                x2 = x1 + w
                y2 = y1
                x3 = x2
                y3 = y1 + h
                Dim x4 As Double = x1
                Dim y4 As Double = y3
                Dim coords() As Object = New Object() {x1, y1, x2, y2, x3, y3, x4, y4}
                m_buf = String.Format("M{0:g5} {1:g5} L{2:g5} {3:g5} L{4:g5} {5:g5} L{6:g5} {7:g5} Z", _
                 coords)
                Console.Write(m_buf)
            ElseIf opr(opr_itr) = pathData.PathSegmentType.e_closepath Then
                Console.WriteLine(" Close Path")
            Else
                System.Diagnostics.Debug.Assert(False)
            End If

            opr_itr += 1
        End While

        Console.Write(""" ")

        Dim gs As GState = path.GetGState()

        ' Set Path State 0 (stroke, fill, fill-rule) -----------------------------------
        If path.IsStroked() Then
            Console.WriteLine("Stroke path")
            If gs.GetStrokeColorSpace().GetType() = ColorSpace.Type.e_pattern Then
                Console.WriteLine("Path has associated pattern")
            Else
                ' Get stroke color (you can use PDFNet color conversion facilities)
                ' Dim rgb As ColorPt
                ' gs.GetStrokeColorSpace().Convert2RGB(gs.GetStrokeColor(), rgb)
            End If
        Else
            ' Do not stroke path
        End If

        If path.IsFilled() Then
            Console.WriteLine("Fill path")

            If gs.GetFillColorSpace().GetType() = ColorSpace.Type.e_pattern Then
                Console.WriteLine("Path has associated pattern")
            Else
                ' Dim rgb As ColorPt
                ' gs.GetFillColorSpace().Convert2RGB(gs.GetFillColor(), rgb)
            End If
        Else
            ' Do not fill path
        End If

        ' Process any changes in graphics state  ---------------------------------
        Dim gs_itr As GSChangesIterator = reader.GetChangesIterator()
        While gs_itr.HasNext()
            If gs_itr.Current() = GState.GStateAttribute.e_transform Then
                ' Get transform matrix for this element. Unlike path.GetCTM() 
                ' that return full transformation matrix gs.GetTransform() return 
                ' only the transformation matrix that was installed for this element.
                '
                ' gs.GetTransform()
            ElseIf gs_itr.Current() = GState.GStateAttribute.e_line_width Then
                ' gs.GetLineWidth()
            ElseIf gs_itr.Current() = GState.GStateAttribute.e_line_cap Then
                ' gs.GetLineCap()
            ElseIf gs_itr.Current() = GState.GStateAttribute.e_line_join Then
                ' gs.GetLineJoin()
            ElseIf gs_itr.Current() = GState.GStateAttribute.e_flatness Then
            ElseIf gs_itr.Current() = GState.GStateAttribute.e_miter_limit Then
                ' gs.GetMiterLimit()
            ElseIf gs_itr.Current() = GState.GStateAttribute.e_dash_pattern Then
                ' Dim dashes As Double()
                ' gs.GetDashes(dashes)
                ' gs.GetPhase()
            End If

            gs_itr.Next()
        End While
    End Sub

    Sub ProcessText(ByRef page_reader As ElementReader)
        ' Begin text element
        Console.WriteLine("Begin Text Block:")

        Dim element As Element
        element = page_reader.Next()
        While Not IsNothing(element)
            If element.GetType() = element.Type.e_text_end Then
                ' Finish the text block
                Console.WriteLine("End Text Block.")
                Return
            ElseIf element.GetType() = element.Type.e_text Then
                Dim gs As GState = element.GetGState()

                Dim cs_fill As ColorSpace = gs.GetFillColorSpace()
                Dim fill As ColorPt = gs.GetFillColor()

                Dim outc As ColorPt = New ColorPt
                cs_fill.Convert2RGB(fill, outc)

                Dim cs_stroke As ColorSpace = gs.GetStrokeColorSpace()
                Dim stroke As ColorPt = gs.GetStrokeColor()

                Dim font As Font = gs.GetFont()

                Console.Write("Font Name: ")
                Console.Write(font.GetName())
                ' font.IsFixedWidth()
                ' font.IsSerif()
                ' font.IsSymbolic()
                ' font.IsItalic()
                ' ... 

                ' Dim word_spacing As Double = gs.GetWordSpacing()
                ' Dim char_spacing As Double = gs.GetCharSpacing()

                ' Use element.GetCTM() if you are interested in the CTM 
                ' (current transformation matrix).
                Dim ctm As Matrix2D = element.GetCTM()

                Dim text_mtx As Matrix2D = element.GetTextMatrix()

                Dim mtx As Matrix2D = New Matrix2D
                mtx.Set(ctm)
                mtx.Concat(text_mtx.m_a, text_mtx.m_b, text_mtx.m_c, text_mtx.m_d, text_mtx.m_h, text_mtx.m_v)
                Dim font_sz_scale_factor As Double = System.Math.Sqrt(mtx.m_b * mtx.m_b + mtx.m_d * mtx.m_d)
                Dim font_size As Double = gs.GetFontSize()
                Console.Write(" Font Size: {0:f}", font_sz_scale_factor * font_size)

                Dim font_color As ColorPt = gs.GetFillColor()
                Dim cs As ColorSpace = gs.GetFillColorSpace()

                Dim rgb As ColorPt = New ColorPt
                cs.Convert2RGB(font_color, rgb)

                Console.WriteLine(" Font Color(RGB): red={0:d} green={1:d} blue={2:d}", _
                    CByte(rgb.Get(0) * 255), CByte(rgb.Get(1) * 255), CByte(rgb.Get(2) * 255))

                Dim x, y As Double
                Dim char_code As Integer

                Dim itr As CharIterator = element.GetCharIterator()
                While itr.HasNext()
                    Console.Write("Character code: ")
                    char_code = itr.Current().char_code
                    Console.Write(Chr(char_code))

                    x = itr.Current().x      ' character positioning information
                    y = itr.Current().y

                    ' To get the exact character positioning information you need to 
                    ' concatenate current text matrix with CTM and then multiply 
                    ' relative positioning coordinates with the resulting matrix.
                    '
                    mtx.Set(ctm)
                    mtx.Concat(text_mtx.m_a, text_mtx.m_b, text_mtx.m_c, text_mtx.m_d, text_mtx.m_h, text_mtx.m_v)
                    mtx.Mult(x, y)
                    Console.WriteLine(" Position: x={0:f} y={1:f}", x, y)
                    itr.Next()
                End While

                Console.WriteLine()
            End If
            element = page_reader.Next()
        End While
    End Sub

    Sub ProcessImage(ByRef image As Element)
        Dim image_mask As Boolean = image.IsImageMask()
        Dim interpolate As Boolean = image.IsImageInterpolate()
        Dim width As Integer = image.GetImageWidth()
        Dim height As Integer = image.GetImageHeight()
        Dim out_data_sz As Integer = width * height * 3

        Console.WriteLine("Image: width=""{0:d}"" height=""{1:d}""", width, height)

        ' Dim mtx As Matrix2D = image.GetCTM() ' image matrix (page positioning info)

        ' You can use GetImageData to read the raw (decoded) image data
        'image.GetBitsPerComponent()    
        'image.GetImageData()    ' get raw image data
        ' .... or use Image2RGB filter that converts every image to RGB format,
        ' This should save you time since you don't need to deal with color conversions, 
        ' image up-sampling, decoding etc.

        Dim img_conv As Image2RGB = New Image2RGB(image)       ' Extract and convert image to RGB 8-bpc format
        Dim reader As FilterReader = New FilterReader(img_conv)

        ' A buffer used to keep image data.
        Dim image_data_out As Byte() = Nothing       '= New Byte(out_data_sz)

        reader.Read(image_data_out)
        ' image_data_out contains RGB image data.

        ' Note that you don't need to read a whole image at a time. Alternatively
        ' you can read a chuck at a time by repeatedly calling reader.Read(buf, buf_sz) 
        ' until the function returns 0. 
    End Sub

    Sub ProcessElements(ByRef reader As ElementReader)
        Dim element As Element = reader.Next()

        element = reader.Next()
        While Not IsNothing(element)         ' Read page contents
            If element.GetType() = element.Type.e_path Then
                ' Process path data...
                ProcessPath(reader, element)
            ElseIf element.GetType() = element.Type.e_text_begin Then
                ' Process text strings...
                ProcessText(reader)
            ElseIf element.GetType() = element.Type.e_form Then
                ' Process form XObjects
                reader.FormBegin()
                ProcessElements(reader)
                reader.End()
            ElseIf element.GetType() = element.Type.e_image Then
                ' Process Images
                ProcessImage(element)
            End If
            element = reader.Next()
        End While
    End Sub

    Sub Main()

        PDFNet.Initialize(PDFTronLicense.Key)

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

        Console.WriteLine("-------------------------------------------------")
        Console.WriteLine("Extract page element information from all")
        Console.WriteLine("pages in the document.")

        ' Open the test file
        Console.WriteLine("Opening the input file...")
        Using doc As PDFDoc = New PDFDoc(input_path + "newsletter.pdf")
            doc.InitSecurityHandler()

            Dim pgnum As Integer = doc.GetPageCount()

            Dim itr As PageIterator
            Using page_reader As ElementReader = New ElementReader
                itr = doc.GetPageIterator()
                While itr.HasNext()    '  Read every page
                    Console.WriteLine("Page {0:d} ----------------------------------------", _
                     itr.GetPageNumber())

                    Dim crop_box As Rect = itr.Current().GetCropBox()
                    Console.WriteLine(" Page Rectangle: x={0:f} y={1:f} x2={2:f} y2={3:f}", crop_box.x1, crop_box.y1, crop_box.x2, crop_box.y2)
                    Console.WriteLine(" Page Size: width={0:f} height={1:f}", crop_box.Width(), crop_box.Height())

                    page_reader.Begin(itr.Current())
                    ProcessElements(page_reader)
                    page_reader.End()
                    itr.Next()
                End While
            End Using
        End Using
        PDFNet.Terminate()
        Console.WriteLine("Done.")

    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/elementreaderadvtest.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.
