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

# PDF Layers (OCG) - Add, Show, Hide - PDFLayers

Sample code to use Apryse SDK for creating and manipulating PDF layers (also known as Optional Content Groups - OCGs); code provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Sample code to use Apryse SDK for creating and manipulating PDF layers (also known as Optional Content Groups - OCGs). These samples demonstrate how to create and extract layers, as well as to selectively render them (show, hide) in conforming PDF readers or printers. Sample code provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Learn more about our [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.
//

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

/// <summary>
//-----------------------------------------------------------------------------------
// This sample demonstrates how to create layers in PDF.
// The sample also shows how to extract and render PDF layers in documents 
// that contain optional content groups (OCGs)
//
// With the introduction of PDF version 1.5 came the concept of Layers. 
// Layers, or as they are more formally known Optional Content Groups (OCGs),
// refer to sections of content in a PDF document that can be selectively 
// viewed or hidden by document authors or consumers. This capability is useful 
// in CAD drawings, layered artwork, maps, multi-language documents etc.
//
// Notes: 
// ---------------------------------------
// - This sample is using CreateLayer() utility method to create new OCGs. 
//   CreateLayer() is relatively basic, however it can be extended to set 
//   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
//   a complete listing of possible entries in OC dictionary please refer to 
//   section 4.10 'Optional Content' in the PDF Reference Manual.
// - The sample is grouping all layer content into separate Form XObjects. 
//   Although using PDFNet is is also possible to specify Optional Content in 
//   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
//   XObjects results in PDFs that are cleaner, less-error prone, and faster 
//   to process.
//-----------------------------------------------------------------------------------
/// </summary>
namespace PDFLayersTestCS
{
	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/";

		[STAThread]
		static void Main(string[] args)
		{
			PDFNet.Initialize(PDFTronLicense.Key);

			try
			{
				using (PDFDoc doc = new PDFDoc())
				using (ElementBuilder builder = new ElementBuilder()) // ElementBuilder is used to build new Element objects
				using (ElementWriter writer = new ElementWriter()) // ElementWriter is used to write Elements to the page
				{
					// Create three layers...
					Group image_layer = CreateLayer(doc, "Image Layer");
					Group text_layer = CreateLayer(doc, "Text Layer");
					Group vector_layer = CreateLayer(doc, "Vector Layer");

					// Start a new page ------------------------------------
					Page page = doc.PageCreate();

					writer.Begin(page);	// begin writing to this page

					// Add new content to the page and associate it with one of the layers.
					Element element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()));
					writer.WriteElement(element);

					element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()));
					writer.WriteElement(element);

					// Add the text layer to the page...
					bool enableOCMD = false; // set to 'true' to enable 'ocmd' example.
					if (enableOCMD)  
					{
						// A bit more advanced example of how to create an OCMD text layer that 
						// is visible only if text, image and path layers are all 'ON'.
						// An example of how to set 'Visibility Policy' in OCMD.
						Obj ocgs = doc.CreateIndirectArray();
						ocgs.PushBack(image_layer.GetSDFObj());
						ocgs.PushBack(vector_layer.GetSDFObj());
						ocgs.PushBack(text_layer.GetSDFObj());
						OCMD text_ocmd = OCMD.Create(doc, ocgs, OCMD.VisibilityPolicyType.e_AllOn);
						element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()));
					}
					else {
						element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()));
					}
					writer.WriteElement(element);

					// Add some content to the page that does not belong to any layer...
					// In this case this is a rectangle representing the page border.
					element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight());
					element.SetPathFill(false);
					element.SetPathStroke(true);
					element.GetGState().SetLineWidth(40);
					writer.WriteElement(element);

					writer.End();  // save changes to the current page
					doc.PagePushBack(page);

					// Set the default viewing preference to display 'Layer' tab.
					PDFDocViewPrefs prefs = doc.GetViewPrefs();
					prefs.SetPageMode(PDFDocViewPrefs.PageMode.e_UseOC);

					doc.Save(output_path + "pdf_layers.pdf", SDFDoc.SaveOptions.e_linearized);
					Console.WriteLine("Done.");
				}
			}
			catch (PDFNetException e)
			{
				Console.WriteLine(e.Message);
			}

			// The following is a code snippet shows how to selectively render 
			// and export PDF layers.
			try  
			{	 
				using (PDFDoc doc = new PDFDoc(output_path + "pdf_layers.pdf"))
				{
					doc.InitSecurityHandler();

					if (!doc.HasOC()) 
					{
						Console.WriteLine("The document does not contain 'Optional Content'");
					}
					else 
					{
						Config init_cfg = doc.GetOCGConfig();
						Context ctx = new Context(init_cfg);

						using (PDFDraw pdfdraw = new PDFDraw())
						{
							pdfdraw.SetImageSize(1000, 1000);
							pdfdraw.SetOCGContext(ctx); // Render the page using the given OCG context.

							Page page = doc.GetPage(1); // Get the first page in the document.
							pdfdraw.Export(page, output_path + "pdf_layers_default.png");

							// Disable drawing of content that is not optional (i.e. is not part of any layer).
							ctx.SetNonOCDrawing(false);

							// Now render each layer in the input document to a separate image.
							Obj ocgs = doc.GetOCGs(); // Get the array of all OCGs in the document.
							if (ocgs != null) 
							{
								int i, sz = ocgs.Size();
								for (i=0; i<sz; ++i) 
								{
									Group ocg = new Group(ocgs.GetAt(i));
									ctx.ResetStates(false);
									ctx.SetState(ocg, true);
									string fname = "pdf_layers_" + ocg.GetName() + ".png";
									Console.WriteLine(fname);
									pdfdraw.Export(page, output_path + fname);
								}
							}

							// Now draw content that is not part of any layer...
							ctx.SetNonOCDrawing(true);
							ctx.SetOCDrawMode(Context.OCDrawMode.e_NoOC);
							pdfdraw.Export(page, output_path + "pdf_layers_non_oc.png");

							Console.WriteLine("Done.");
						}
					}
				}
			}
			catch (PDFNetException e)
			{
				Console.WriteLine(e.Message);
			}
			PDFNet.Terminate();

		}

		// A utility function used to add new Content Groups (Layers) to the document.
		static Group CreateLayer(PDFDoc doc, String layer_name)
		{
			Group grp = Group.Create(doc, layer_name);
			Config cfg = doc.GetOCGConfig();
			if (cfg == null) 
			{
				cfg = Config.Create(doc, true);
				cfg.SetName("Default");
			}

			// Add the new OCG to the list of layers that should appear in PDF viewer GUI.
			Obj layer_order_array = cfg.GetOrder();
			if (layer_order_array == null) 
			{
				layer_order_array = doc.CreateIndirectArray();
				cfg.SetOrder(layer_order_array);
			}
			layer_order_array.PushBack(grp.GetSDFObj());

			return grp;
		}

		// Creates some content (3 images) and associate them with the image layer
		static Obj CreateGroup1(PDFDoc doc, Obj layer)
		{
			using (ElementWriter writer = new ElementWriter())
			using (ElementBuilder builder = new ElementBuilder())
			{
				writer.Begin(doc);

				// Create an Image that can be reused in the document or on the same page.		
				Image img = Image.Create(doc.GetSDFDoc(), (input_path + "peppers.jpg"));

				Element element = builder.CreateImage(img, new Matrix2D(img.GetImageWidth()/2, -145, 20, img.GetImageHeight()/2, 200, 150));
				writer.WritePlacedElement(element);

				GState gstate = element.GetGState();	// use the same image (just change its matrix)
				gstate.SetTransform(200, 0, 0, 300, 50, 450);
				writer.WritePlacedElement(element);

				// use the same image again (just change its matrix).
				writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150));

				Obj grp_obj = writer.End();	

				// Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype","Form");
				grp_obj.Put("OC", layer);	
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000);  // Set the clip box for the content.

				// As an example of further configuration, set the image layer to
				// be visible on screen, but not visible when printed...

				// The AS entry is an auto state array consisting of one or more usage application 
				// dictionaries that specify how conforming readers shall automatically set the 
				// state of optional content groups based on external factors.
				Obj cfg = doc.GetOCGConfig().GetSDFObj();
				Obj auto_state = cfg.FindObj("AS");
				if (auto_state == null) auto_state = cfg.PutArray("AS");
				Obj print_state = auto_state.PushBackDict();
				print_state.PutArray("Category").PushBackName("Print");
				print_state.PutName("Event", "Print");
				print_state.PutArray("OCGs").PushBack(layer);

				Obj layer_usage = layer.PutDict("Usage");

				Obj view_setting = layer_usage.PutDict("View");
				view_setting.PutName("ViewState", "ON");

				Obj print_setting = layer_usage.PutDict("Print");
				print_setting.PutName("PrintState", "OFF");

				return grp_obj;
			}
		}

		// Creates some content (a path in the shape of a heart) and associate it with the vector layer
		static Obj CreateGroup2(PDFDoc doc, Obj layer)
		{
			using (ElementWriter writer = new ElementWriter())
			using (ElementBuilder builder = new ElementBuilder())
			{
				writer.Begin(doc);

				// Create a path object in the shape of a heart.
				builder.PathBegin();		// start constructing the path
				builder.MoveTo(306, 396);
				builder.CurveTo(681, 771, 399.75, 864.75, 306, 771);
				builder.CurveTo(212.25, 864.75, -69, 771, 306, 396);
				builder.ClosePath();
				Element element = builder.PathEnd(); // the path geometry is now specified.

				// Set the path FILL color space and color.
				element.SetPathFill(true);
				GState gstate = element.GetGState();
				gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK()); 
				gstate.SetFillColor(new ColorPt(1, 0, 0, 0));  // cyan

				// Set the path STROKE color space and color.
				element.SetPathStroke(true); 
				gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB()); 
				gstate.SetStrokeColor(new ColorPt(1, 0, 0));  // red
				gstate.SetLineWidth(20);

				gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300);

				writer.WriteElement(element);

				Obj grp_obj = writer.End();	


				// Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype","Form");
				grp_obj.Put("OC", layer);
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000); 	// Set the clip box for the content.

				return grp_obj;
			}
		}

		// Creates some text and associate it with the text layer
		static Obj CreateGroup3(PDFDoc doc, Obj layer)
		{
			using (ElementWriter writer = new ElementWriter())
			using (ElementBuilder builder = new ElementBuilder())
			{
				writer.Begin(doc);

				// Begin writing a block of text
				Element element = builder.CreateTextBegin(Font.Create(doc, Font.StandardType1Font.e_times_roman), 120);
				writer.WriteElement(element);

				element = builder.CreateTextRun("A text layer!");

				// Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
				Matrix2D transform = Matrix2D.RotationMatrix(-45 *  (3.1415/ 180.0));
				transform.Concat(1, 0, 0, 1, 180, 100);  
				element.SetTextMatrix(transform);

				writer.WriteElement(element);
				writer.WriteElement(builder.CreateTextEnd());

				Obj grp_obj = writer.End();	

				// Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype","Form");
				grp_obj.Put("OC", layer);
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000); 	// Set the clip box for the content.

				return grp_obj;
			}
		}
	}
}
```

{% 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"
    . "pdftron"
)

import  "pdftron/Samples/LicenseKey/GO"

//-----------------------------------------------------------------------------------
// This sample demonstrates how to create layers in PDF.
// The sample also shows how to extract and render PDF layers in documents 
// that contain optional content groups (OCGs)
//
// With the introduction of PDF version 1.5 came the concept of Layers. 
// Layers, or as they are more formally known Optional Content Groups (OCGs),
// refer to sections of content in a PDF document that can be selectively 
// viewed or hidden by document authors or consumers. This capability is useful 
// in CAD drawings, layered artwork, maps, multi-language documents etc.
// 
// Notes: 
// ---------------------------------------
// - This sample is using CreateLayer() utility method to create new OCGs. 
//   CreateLayer() is relatively basic, however it can be extended to set 
//   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
//   a complete listing of possible entries in OC dictionary please refer to 
//   section 4.10 'Optional Content' in the PDF Reference Manual.
// - The sample is grouping all layer content into separate Form XObjects. 
//   Although using PDFNet is is also possible to specify Optional Content in 
//   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
//   XObjects results in PDFs that are cleaner, less-error prone, and faster 
//   to process.
//-----------------------------------------------------------------------------------

// Relative path to the folder containing the test files.
var inputPath = "../../TestFiles/"
var outputPath = "../../TestFiles/Output/"

// A utility function used to add new Content Groups (Layers) to the document.
func CreateLayer(doc PDFDoc, layerName string) Group{
    grp := GroupCreate(doc, layerName)
    cfg := doc.GetOCGConfig()
    if ! cfg.IsValid(){
        cfg = ConfigCreate(doc, true)
        cfg.SetName("Default")
    }   
    // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
    layerOrderArray := cfg.GetOrder()
    if layerOrderArray.GetMp_obj().Swigcptr() == 0{
        layerOrderArray = doc.CreateIndirectArray()
        cfg.SetOrder(layerOrderArray)
    }
    layerOrderArray.PushBack(grp.GetSDFObj())
    return grp
}
// Creates some content (3 images) and associate them with the image layer
func CreateGroup1(doc PDFDoc, layer Obj) Obj{
    writer := NewElementWriter()
    writer.Begin(doc.GetSDFDoc())
    
    // Create an Image that can be reused in the document or on the same page.
    img := ImageCreate(doc.GetSDFDoc(), inputPath + "peppers.jpg")
    builder := NewElementBuilder()
    element := builder.CreateImage(img, NewMatrix2D(float64(img.GetImageWidth()/2), -145.0, 20.0, float64(img.GetImageHeight()/2), 200.0, 150.0))
    writer.WritePlacedElement(element)
    
    gstate := element.GetGState()    // use the same image (just change its matrix)
    gstate.SetTransform(200.0, 0.0, 0.0, 300.0, 50.0, 450.0)
    writer.WritePlacedElement(element)
    
    // use the same image again (just change its matrix).
    writer.WritePlacedElement(builder.CreateImage(img, 300.0, 600.0, 200.0, -150.0))
    
    grpObj := writer.End()
    
    // Indicate that this form (content group) belongs to the given layer (OCG).
    grpObj.PutName("Subtype","Form")
    grpObj.Put("OC", layer)
    grpObj.PutRect("BBox", 0.0, 0.0, 1000.0, 1000.0)   // Set the clip box for the content.
    
    return grpObj
}
// Creates some content (a path in the shape of a heart) and associate it with the vector layer
func CreateGroup2(doc PDFDoc, layer Obj) Obj{
    writer := NewElementWriter()
    writer.Begin(doc.GetSDFDoc())
    
    // Create a path object in the shape of a heart
    builder := NewElementBuilder()
    builder.PathBegin()     // start constructing the path
    builder.MoveTo(306.0, 396.0)
    builder.CurveTo(681.0, 771.0, 399.75, 864.75, 306.0, 771.0)
    builder.CurveTo(212.25, 864.75, -69, 771, 306.0, 396.0)
    builder.ClosePath()
    element := builder.PathEnd() // the path geometry is now specified.

    // Set the path FILL color space and color.
    element.SetPathFill(true)
    gstate := element.GetGState()
    gstate.SetFillColorSpace(ColorSpaceCreateDeviceCMYK())
    gstate.SetFillColor(NewColorPt(1.0, 0.0, 0.0, 0.0))    // cyan
    
    // Set the path STROKE color space and color
    element.SetPathStroke(true)
    gstate.SetStrokeColorSpace(ColorSpaceCreateDeviceRGB())
    gstate.SetStrokeColor(NewColorPt(1.0, 0.0, 0.0))     // red
    gstate.SetLineWidth(20)
    
    gstate.SetTransform(0.5, 0.0, 0.0, 0.5, 280.0, 300.0)
    
    writer.WriteElement(element)
    
    grpObj := writer.End()
    
    // Indicate that this form (content group) belongs to the given layer (OCG).
    grpObj.PutName("Subtype","Form")
    grpObj.Put("OC", layer)
    grpObj.PutRect("BBox", 0.0, 0.0, 1000.0, 1000.0)       // Set the clip box for the content.
    
    return grpObj
}
// Creates some text and associate it with the text layer
func CreateGroup3(doc PDFDoc, layer Obj) Obj{
    writer := NewElementWriter()
    writer.Begin(doc.GetSDFDoc())
    
    // Create a path object in the shape of a heart.
    builder := NewElementBuilder()
    
    // Begin writing a block of text
    element := builder.CreateTextBegin(FontCreate(doc.GetSDFDoc(), FontE_times_roman), 120.0)
    writer.WriteElement(element)
    
    element = builder.CreateTextRun("A text layer!")
    
    // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
    transform := Matrix2DRotationMatrix(-45 * (3.1415/ 180.0))
    transform.Concat(1.0, 0.0, 0.0, 1.0, 180.0, 100.0)
    element.SetTextMatrix(transform)
    
    writer.WriteElement(element)
    writer.WriteElement(builder.CreateTextEnd())
    
    grpObj := writer.End()
    
    // Indicate that this form (content group) belongs to the given layer (OCG).
    grpObj.PutName("Subtype","Form")
    grpObj.Put("OC", layer)
    grpObj.PutRect("BBox", 0.0, 0.0, 1000.0, 1000.0)   // Set the clip box for the content.
    
    return grpObj
}

func main(){
    PDFNetInitialize(PDFTronLicense.Key)
    
    // Create three layers...
    doc := NewPDFDoc()
    imageLayer := CreateLayer(doc, "Image Layer")
    textLayer := CreateLayer(doc, "Text Layer")
    vectorLayer := CreateLayer(doc, "Vector Layer")
    
    // Start a new page ------------------------------------
    page := doc.PageCreate()
    
    builder := NewElementBuilder()    // NewElementBuilder is used to build new Element objects
    writer := NewElementWriter()      // NewElementWriter is used to write Elements to the page
    writer.Begin(page)            // Begin writting to the page
    
    // Add new content to the page and associate it with one of the layers.
    element := builder.CreateForm(CreateGroup1(doc, imageLayer.GetSDFObj()))
    writer.WriteElement(element)
    
    element = builder.CreateForm(CreateGroup2(doc, vectorLayer.GetSDFObj()))
    writer.WriteElement(element)
    
    // Add the text layer to the page...
    if false{ // set to true to enable 'ocmd' example.
        // A bit more advanced example of how to create an OCMD text layer that 
        // is visible only if text, image and path layers are all 'ON'.
        // An example of how to set 'Visibility Policy' in OCMD.
        ocgs := doc.CreateIndirectArray()
        ocgs.PushBack(imageLayer.GetSDFObj())
        ocgs.PushBack(vectorLayer.GetSDFObj())
        ocgs.PushBack(textLayer.GetSDFObj())
        text_ocmd := OCMDCreate(doc, ocgs, OCMDE_AllOn)
        element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()))
    }else{
        element = builder.CreateForm(CreateGroup3(doc, textLayer.GetSDFObj()))
    }
    writer.WriteElement(element)
    
    // Add some content to the page that does not belong to any layer...
    // In this case this is a rectangle representing the page border.
    element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight())
    element.SetPathFill(false)
    element.SetPathStroke(true)
    element.GetGState().SetLineWidth(40)
    writer.WriteElement(element)
    
    writer.End()    // save changes to the current page
    doc.PagePushBack(page)
    // Set the default viewing preference to display 'Layer' tab
    prefs := doc.GetViewPrefs()
    prefs.SetPageMode(PDFDocViewPrefsE_UseOC)
    
    doc.Save(outputPath + "pdf_layers.pdf", uint(SDFDocE_linearized))
    doc.Close()
    fmt.Println("Done.")
    
    // The following is a code snippet shows how to selectively render 
    // and export PDF layers.
    
    doc = NewPDFDoc(outputPath + "pdf_layers.pdf")
    doc.InitSecurityHandler()
    
    if ! doc.HasOC(){
        fmt.Println("The document does not contain 'Optional Content'")
    }else{
        init_cfg := doc.GetOCGConfig()
        ctx := NewContext(init_cfg)
        
        pdfdraw := NewPDFDraw()
        pdfdraw.SetImageSize(1000, 1000)
        pdfdraw.SetOCGContext(ctx)  // Render the page using the given OCG context.
        
        page = doc.GetPage(1)   // Get the first page in the document.
        pdfdraw.Export(page, outputPath + "pdf_layers_default.png")
        
        // Disable drawing of content that is not optional (i.e. is not part of any layer).
        ctx.SetNonOCDrawing(false)
        
        // Now render each layer in the input document to a separate image.
        ocgs := doc.GetOCGs()    // Get the array of all OCGs in the document.
        if ocgs != nil{
            sz := ocgs.Size()
            i := int64(0)
            for i < sz{
                ocg := NewGroup(ocgs.GetAt(i))
                ctx.ResetStates(false)
                ctx.SetState(ocg, true)
                fname := "pdf_layers_" + ocg.GetName() + ".png"
                fmt.Println(fname)
                pdfdraw.Export(page, outputPath + fname)
                i = i + 1
            }
        }
        // Now draw content that is not part of any layer...
        ctx.SetNonOCDrawing(true)
        ctx.SetOCDrawMode(ContextE_NoOC)
        pdfdraw.Export(page, outputPath + "pdf_layers_non_oc.png")
        
        doc.Close()
        PDFNetTerminate()
        fmt.Println("Done.") 
    }
}
```

{% 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 <SDF/Obj.h>
#include <PDF/PDFNet.h>
#include <PDF/PDFDoc.h>
#include <PDF/PDFDraw.h>
#include <PDF/OCG/OCMD.h>
#include <PDF/ElementBuilder.h>
#include <PDF/ElementWriter.h>
#include <PDF/ElementReader.h>

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

using namespace pdftron;
using namespace Common;
using namespace SDF;
using namespace PDF;
using namespace std;

//-----------------------------------------------------------------------------------
// This sample demonstrates how to create layers in PDF.
// The sample also shows how to extract and render PDF layers in documents 
// that contain optional content groups (OCGs)
//
// With the introduction of PDF version 1.5 came the concept of Layers. 
// Layers, or as they are more formally known Optional Content Groups (OCGs),
// refer to sections of content in a PDF document that can be selectively 
// viewed or hidden by document authors or consumers. This capability is useful 
// in CAD drawings, layered artwork, maps, multi-language documents etc.
// 
// Notes: 
// ---------------------------------------
// - This sample is using CreateLayer() utility method to create new OCGs. 
//   CreateLayer() is relatively basic, however it can be extended to set 
//   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
//   a complete listing of possible entries in OC dictionary please refer to 
//   section 4.10 'Optional Content' in the PDF Reference Manual.
// - The sample is grouping all layer content into separate Form XObjects. 
//   Although using PDFNet is is also possible to specify Optional Content in 
//   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
//   XObjects results in PDFs that are cleaner, less-error prone, and faster 
//   to process.
//-----------------------------------------------------------------------------------

Obj CreateGroup1(PDFDoc& doc, Obj layer);
Obj CreateGroup2(PDFDoc& doc, Obj layer);
Obj CreateGroup3(PDFDoc& doc, Obj layer);
OCG::Group CreateLayer(PDFDoc& doc, const char* layer_name);

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

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

	try  
	{	 
		PDFDoc doc;

		// Create three layers...
		OCG::Group image_layer = CreateLayer(doc, "Image Layer");
		OCG::Group text_layer = CreateLayer(doc, "Text Layer");
		OCG::Group vector_layer = CreateLayer(doc, "Vector Layer");

		// Start a new page ------------------------------------
		Page page = doc.PageCreate();

		ElementBuilder builder;	// ElementBuilder is used to build new Element objects
		ElementWriter writer;	// ElementWriter is used to write Elements to the page	
		writer.Begin(page);		// Begin writing to the page

		// Add new content to the page and associate it with one of the layers.
		Element element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()));
		writer.WriteElement(element);

		element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()));
		writer.WriteElement(element);

		// Add the text layer to the page...
		if (false)  // set to true to enable 'ocmd' example.
		{
			// A bit more advanced example of how to create an OCMD text layer that 
			// is visible only if text, image and path layers are all 'ON'.
			// An example of how to set 'Visibility Policy' in OCMD.
			Obj ocgs = doc.CreateIndirectArray();
			ocgs.PushBack(image_layer.GetSDFObj());
			ocgs.PushBack(vector_layer.GetSDFObj());
			ocgs.PushBack(text_layer.GetSDFObj());
			OCG::OCMD text_ocmd = OCG::OCMD::Create(doc, ocgs, OCG::OCMD::e_AllOn);
			element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()));
		}
		else {
			element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()));
		}
		writer.WriteElement(element);

		// Add some content to the page that does not belong to any layer...
		// In this case this is a rectangle representing the page border.
		element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight());
		element.SetPathFill(false);
		element.SetPathStroke(true);
		element.GetGState().SetLineWidth(40);
		writer.WriteElement(element);

		writer.End();  // save changes to the current page
		doc.PagePushBack(page);

		// Set the default viewing preference to display 'Layer' tab.
		PDFDocViewPrefs prefs = doc.GetViewPrefs();
		prefs.SetPageMode(PDFDocViewPrefs::e_UseOC);

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

	// The following is a code snippet shows how to selectively render 
	// and export PDF layers.
	try  
	{	 
		PDFDoc doc((output_path + "pdf_layers.pdf").c_str());
		doc.InitSecurityHandler();

		if (!doc.HasOC()) {
			cout << "The document does not contain 'Optional Content'" << endl;
		}
		else {
			OCG::Config init_cfg = doc.GetOCGConfig();
			OCG::Context ctx(init_cfg);

			PDFDraw pdfdraw;
			pdfdraw.SetImageSize(1000, 1000);
			pdfdraw.SetOCGContext(&ctx); // Render the page using the given OCG context.

			Page page = doc.GetPage(1); // Get the first page in the document.
			pdfdraw.Export(page, (output_path + "pdf_layers_default.png").c_str());

			// Disable drawing of content that is not optional (i.e. is not part of any layer).
			ctx.SetNonOCDrawing(false);

			// Now render each layer in the input document to a separate image.
			Obj ocgs = doc.GetOCGs(); // Get the array of all OCGs in the document.
			if (ocgs != 0) {
				int i, sz = int(ocgs.Size());
				for (i=0; i<sz; ++i) {
					OCG::Group ocg(ocgs.GetAt(i));
					ctx.ResetStates(false);
					ctx.SetState(ocg, true);
					std::string fname("pdf_layers_");
					fname += ocg.GetName().ConvertToAscii();
					fname += ".png";
					cout << fname << endl;
					pdfdraw.Export(page, (output_path + fname).c_str());
				}
			}

			// Now draw content that is not part of any layer...
			ctx.SetNonOCDrawing(true);
			ctx.SetOCDrawMode(OCG::Context::e_NoOC);
			pdfdraw.Export(page, (output_path + "pdf_layers_non_oc.png").c_str());
		}

		cout << "Done." << endl;
	}
	catch(Common::Exception& e)
	{
		cout << e << endl;
		ret = 1;
	}
	catch(...)
	{
		cout << "Unknown Exception" << endl;
		ret = 1;
	}

	PDFNet::Terminate();
	return ret;
}


// A utility function used to add new Content Groups (Layers) to the document.
OCG::Group CreateLayer(PDFDoc& doc, const char* layer_name)
{
	OCG::Group grp = OCG::Group::Create(doc, layer_name);
	OCG::Config cfg = doc.GetOCGConfig();
	if (!cfg.IsValid()) {
		cfg = OCG::Config::Create(doc, true);
		cfg.SetName("Default");
	}

	// Add the new OCG to the list of layers that should appear in PDF viewer GUI.
	Obj layer_order_array = cfg.GetOrder();
	if (!layer_order_array) {
		layer_order_array = doc.CreateIndirectArray();
		cfg.SetOrder(layer_order_array);
	}
	layer_order_array.PushBack(grp.GetSDFObj());

	return grp;
}

// Creates some content (3 images) and associate them with the image layer
Obj CreateGroup1(PDFDoc& doc, Obj layer) 
{
	ElementWriter writer;
	writer.Begin(doc);

	// Create an Image that can be reused in the document or on the same page.		
	Image img = Image::Create(doc, (input_path + "peppers.jpg").c_str());

	ElementBuilder builder;
	Element element = builder.CreateImage(img, Common::Matrix2D(img.GetImageWidth()/2, -145, 20, img.GetImageHeight()/2, 200, 150));
	writer.WritePlacedElement(element);

	GState gstate = element.GetGState();	// use the same image (just change its matrix)
	gstate.SetTransform(200, 0, 0, 300, 50, 450);
	writer.WritePlacedElement(element);

	// use the same image again (just change its matrix).
	writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150));

	Obj grp_obj = writer.End();	

	// Indicate that this form (content group) belongs to the given layer (OCG).
	grp_obj.PutName("Subtype","Form");
	grp_obj.Put("OC", layer);	
	grp_obj.PutRect("BBox", 0, 0, 1000, 1000);  // Set the clip box for the content.

	return grp_obj;
}

// Creates some content (a path in the shape of a heart) and associate it with the vector layer
Obj CreateGroup2(PDFDoc& doc, Obj layer) 
{
	ElementWriter writer;
	writer.Begin(doc);

	// Create a path object in the shape of a heart.
	ElementBuilder builder;
	builder.PathBegin();		// start constructing the path
	builder.MoveTo(306, 396);
	builder.CurveTo(681, 771, 399.75, 864.75, 306, 771);
	builder.CurveTo(212.25, 864.75, -69, 771, 306, 396);
	builder.ClosePath();
	Element element = builder.PathEnd(); // the path geometry is now specified.

	// Set the path FILL color space and color.
	element.SetPathFill(true);
	GState gstate = element.GetGState();
	gstate.SetFillColorSpace(ColorSpace::CreateDeviceCMYK()); 
	gstate.SetFillColor(ColorPt(1, 0, 0, 0));  // cyan

	// Set the path STROKE color space and color.
	element.SetPathStroke(true); 
	gstate.SetStrokeColorSpace(ColorSpace::CreateDeviceRGB()); 
	gstate.SetStrokeColor(ColorPt(1, 0, 0));  // red
	gstate.SetLineWidth(20);

	gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300);

	writer.WriteElement(element);

	Obj grp_obj = writer.End();	

	// Indicate that this form (content group) belongs to the given layer (OCG).
	grp_obj.PutName("Subtype","Form");
	grp_obj.Put("OC", layer);
	grp_obj.PutRect("BBox", 0, 0, 1000, 1000); 	// Set the clip box for the content.

	return grp_obj;
}

// Creates some text and associate it with the text layer
Obj CreateGroup3(PDFDoc& doc, Obj layer) 
{
	ElementWriter writer;
	writer.Begin(doc);

	// Create a path object in the shape of a heart.
	ElementBuilder builder;

	// Begin writing a block of text
	Element element = builder.CreateTextBegin(Font::Create(doc, Font::e_times_roman), 120);
	writer.WriteElement(element);

	element = builder.CreateTextRun("A text layer!");

	// Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
	Matrix2D transform = Matrix2D::RotationMatrix(-45 *  (3.1415/ 180.0));
	transform *= Matrix2D(1, 0, 0, 1, 180, 100);  
	element.SetTextMatrix(transform);

	writer.WriteElement(element);
	writer.WriteElement(builder.CreateTextEnd());

	Obj grp_obj = writer.End();	

	// Indicate that this form (content group) belongs to the given layer (OCG).
	grp_obj.PutName("Subtype","Form");
	grp_obj.Put("OC", layer);
	grp_obj.PutRect("BBox", 0, 0, 1000, 1000); 	// Set the clip box for the content.

	return grp_obj;
}
```

{% 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.common.Matrix2D;
import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.*;
import com.pdftron.pdf.ocg.*;
import com.pdftron.sdf.*;


//-----------------------------------------------------------------------------------
// This sample demonstrates how to create layers in PDF.
// The sample also shows how to extract and render PDF layers in documents 
// that contain optional content groups (OCGs)
//
// With the introduction of PDF version 1.5 came the concept of Layers. 
// Layers, or as they are more formally known Optional Content Groups (OCGs),
// refer to sections of content in a PDF document that can be selectively 
// viewed or hidden by document authors or consumers. This capability is useful 
// in CAD drawings, layered artwork, maps, multi-language documents etc.
//
// Couple of notes regarding this sample: 
// ---------------------------------------
// - This sample is using CreateLayer() utility method to create new OCGs. 
//   CreateLayer() is relatively basic, however it can be extended to set 
//   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
//   a complete listing of possible entries in OC dictionary please refer to 
//   section 4.10 'Optional Content' in the PDF Reference Manual.
// - The sample is grouping all layer content into separate Form XObjects. 
//   Although using PDFNet is is also possible to specify Optional Content in 
//   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
//   XObjects results in PDFs that are cleaner, less-error prone, and faster 
//   to process.
//-----------------------------------------------------------------------------------
public class PDFLayersTest {
    // Relative path to the folder containing test files.
    static String input_path = "../../TestFiles/";
    static String output_path = "../../TestFiles/Output/";

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

        try (PDFDoc doc = new PDFDoc()) {
            // Create three layers...
            Group image_layer = createLayer(doc, "Image Layer");
            Group text_layer = createLayer(doc, "Text Layer");
            Group vector_layer = createLayer(doc, "Vector Layer");

            // Start a new page ------------------------------------
            Page page = doc.pageCreate();

            ElementBuilder builder = new ElementBuilder(); // ElementBuilder is used to build new Element objects
            ElementWriter writer = new ElementWriter(); // ElementWriter is used to write Elements to the page
            writer.begin(page);        // Begin writing to the page

            // Add new content to the page and associate it with one of the layers.
            Element element = builder.createForm(createGroup1(doc, image_layer.getSDFObj()));
            writer.writeElement(element);

            element = builder.createForm(createGroup2(doc, vector_layer.getSDFObj()));
            writer.writeElement(element);

            // Add the text layer to the page...
            if (false)  // set to true to enable 'ocmd' example.
            {
                // A bit more advanced example of how to create an OCMD text layer that
                // is visible only if text, image and path layers are all 'ON'.
                // An example of how to set 'Visibility Policy' in OCMD.
                Obj ocgs = doc.createIndirectArray();
                ocgs.pushBack(image_layer.getSDFObj());
                ocgs.pushBack(vector_layer.getSDFObj());
                ocgs.pushBack(text_layer.getSDFObj());
                OCMD text_ocmd = OCMD.create(doc, ocgs, OCMD.e_AllOn);
                element = builder.createForm(createGroup3(doc, text_ocmd.getSDFObj()));
            } else {
                element = builder.createForm(createGroup3(doc, text_layer.getSDFObj()));
            }
            writer.writeElement(element);


            // Add some content to the page that does not belong to any layer...
            // In this case this is a rectangle representing the page border.
            element = builder.createRect(0, 0, page.getPageWidth(), page.getPageHeight());
            element.setPathFill(false);
            element.setPathStroke(true);
            element.getGState().setLineWidth(40);
            writer.writeElement(element);

            writer.end();  // save changes to the current page
            doc.pagePushBack(page);

            // Set the default viewing preference to display 'Layer' tab.
            PDFDocViewPrefs prefs = doc.getViewPrefs();
            prefs.setPageMode(PDFDocViewPrefs.e_UseOC);

            doc.save(output_path + "pdf_layers.pdf", SDFDoc.SaveMode.LINEARIZED, null);
            System.out.println("Done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // The following is a code snippet shows how to selectively render
        // and export PDF layers.
        try (PDFDoc doc = new PDFDoc(output_path + "pdf_layers.pdf")) {
            doc.initSecurityHandler();

            if (doc.hasOC() == false) {
                System.out.println("The document does not contain 'Optional Content'");
            } else {
                Config init_cfg = doc.getOCGConfig();
                Context ctx = new Context(init_cfg);

                PDFDraw pdfdraw = new PDFDraw();
                pdfdraw.setImageSize(1000, 1000);
                pdfdraw.setOCGContext(ctx); // Render the page using the given OCG context.

                Page page = doc.getPage(1); // Get the first page in the document.
                pdfdraw.export(page, output_path + "pdf_layers_default.png");
                // output "pdf_layers_default.png"

                // Disable drawing of content that is not optional (i.e. is not part of any layer).
                ctx.setNonOCDrawing(false);

                // Now render each layer in the input document to a separate image.
                Obj ocgs = doc.getOCGs(); // Get the array of all OCGs in the document.
                if (ocgs != null) {
                    int i, sz = (int) ocgs.size();
                    for (i = 0; i < sz; ++i) {
                        Group ocg = new Group(ocgs.getAt(i));
                        ctx.resetStates(false);
                        ctx.setState(ocg, true);
                        String fname =  "pdf_layers_" + ocg.getName() + ".png";
                        System.out.println(fname);
                        pdfdraw.export(page, output_path + fname);
                        // output "pdf_layers_" + ocg.getName() + ".png"
                    }
                }

                // Now draw content that is not part of any layer...
                ctx.setNonOCDrawing(true);
                ctx.setOCDrawMode(Context.e_NoOC);
                pdfdraw.export(page, output_path + "pdf_layers_non_oc.png");
                // output "pdf_layers_non_oc.png"
            }

            System.out.println("Done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

        PDFNet.terminate();
    }

    // A utility function used to add new Content Groups (Layers) to the document.
    static Group createLayer(PDFDoc doc, String layer_name) throws PDFNetException {
        Group grp = Group.create(doc, layer_name);
        Config cfg = doc.getOCGConfig();
        if (cfg == null) {
            cfg = Config.create(doc, true);
            cfg.setName("Default");
        }

        // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
        Obj layer_order_array = cfg.getOrder();
        if (layer_order_array == null) {
            layer_order_array = doc.createIndirectArray();
            cfg.setOrder(layer_order_array);
        }
        layer_order_array.pushBack(grp.getSDFObj());

        return grp;
    }

    // Creates some content (3 images) and associate them with the image layer
    static Obj createGroup1(PDFDoc doc, Obj layer) throws PDFNetException {
        ElementWriter writer = new ElementWriter();
        writer.begin(doc);

        // Create an Image that can be reused in the document or on the same page.
        Image img = Image.create(doc.getSDFDoc(), (input_path + "peppers.jpg"));

        ElementBuilder builder = new ElementBuilder();
        Element element = builder.createImage(img, new Matrix2D(img.getImageWidth() / 2, -145, 20, img.getImageHeight() / 2, 200, 150));
        writer.writePlacedElement(element);

        GState gstate = element.getGState();    // use the same image (just change its matrix)
        gstate.setTransform(200, 0, 0, 300, 50, 450);
        writer.writePlacedElement(element);

        // use the same image again (just change its matrix).
        writer.writePlacedElement(builder.createImage(img, 300, 600, 200, -150));

        Obj grp_obj = writer.end();

        // Indicate that this form (content group) belongs to the given layer (OCG).
        grp_obj.putName("Subtype", "Form");
        grp_obj.put("OC", layer);
        grp_obj.putRect("BBox", 0, 0, 1000, 1000);  // Set the clip box for the content.

        return grp_obj;
    }

    // Creates some content (a path in the shape of a heart) and associate it with the vector layer
    static Obj createGroup2(PDFDoc doc, Obj layer) throws PDFNetException {
        ElementWriter writer = new ElementWriter();
        writer.begin(doc);

        // Create a path object in the shape of a heart.
        ElementBuilder builder = new ElementBuilder();
        builder.pathBegin();        // start constructing the path
        builder.moveTo(306, 396);
        builder.curveTo(681, 771, 399.75, 864.75, 306, 771);
        builder.curveTo(212.25, 864.75, -69, 771, 306, 396);
        builder.closePath();
        Element element = builder.pathEnd(); // the path geometry is now specified.

        // Set the path FILL color space and color.
        element.setPathFill(true);
        GState gstate = element.getGState();
        gstate.setFillColorSpace(ColorSpace.createDeviceCMYK());
        gstate.setFillColor(new ColorPt(1, 0, 0, 0));  // cyan

        // Set the path STROKE color space and color.
        element.setPathStroke(true);
        gstate.setStrokeColorSpace(ColorSpace.createDeviceRGB());
        gstate.setStrokeColor(new ColorPt(1, 0, 0));  // red
        gstate.setLineWidth(20);

        gstate.setTransform(0.5, 0, 0, 0.5, 280, 300);

        writer.writeElement(element);

        Obj grp_obj = writer.end();

        // Indicate that this form (content group) belongs to the given layer (OCG).
        grp_obj.putName("Subtype", "Form");
        grp_obj.put("OC", layer);
        grp_obj.putRect("BBox", 0, 0, 1000, 1000);    // Set the clip box for the content.

        return grp_obj;
    }

    // Creates some text and associate it with the text layer
    static Obj createGroup3(PDFDoc doc, Obj layer) throws PDFNetException {
        ElementWriter writer = new ElementWriter();
        writer.begin(doc);

        // Create a path object in the shape of a heart.
        ElementBuilder builder = new ElementBuilder();

        // Begin writing a block of text
        Element element = builder.createTextBegin(Font.create(doc, Font.e_times_roman), 120);
        writer.writeElement(element);

        element = builder.createTextRun("A text layer!");

        // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
        Matrix2D transform = Matrix2D.rotationMatrix(-45 * (3.1415 / 180.0));
        transform.concat(1, 0, 0, 1, 180, 100);
        element.setTextMatrix(transform);

        writer.writeElement(element);
        writer.writeElement(builder.createTextEnd());

        Obj grp_obj = writer.end();

        // Indicate that this form (content group) belongs to the given layer (OCG).
        grp_obj.putName("Subtype", "Form");
        grp_obj.put("OC", layer);
        grp_obj.putRect("BBox", 0, 0, 1000, 1000);    // Set the clip box for the content.

        return grp_obj;
    }
}
```

{% endcode %}
{% endtab %}

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

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

//-----------------------------------------------------------------------------------
// This sample demonstrates how to create layers in PDF.
// The sample also shows how to extract and render PDF layers in documents 
// that contain optional content groups (OCGs)
//
// With the introduction of PDF version 1.5 came the concept of Layers. 
// Layers, or as they are more formally known Optional Content Groups (OCGs),
// refer to sections of content in a PDF document that can be selectively 
// viewed or hidden by document authors or consumers. This capability is useful 
// in CAD drawings, layered artwork, maps, multi-language documents etc.
// 
// Notes: 
// ---------------------------------------
// - This sample is using CreateLayer() utility method to create new OCGs. 
//   CreateLayer() is relatively basic, however it can be extended to set 
//   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
//   a complete listing of possible entries in OC dictionary please refer to 
//   section 4.10 'Optional Content' in the PDF Reference Manual.
// - The sample is grouping all layer content into separate Form XObjects. 
//   Although using PDFNet is is also possible to specify Optional Content in 
//   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
//   XObjects results in PDFs that are cleaner, less-error prone, and faster 
//   to process.
//-----------------------------------------------------------------------------------

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

((exports) => {

  exports.runPDFLayersTest = () => {
    const inputPath =  '../TestFiles/';
    const outputPath = inputPath + 'Output/';

    // A utility function used to add new Content Groups (Layers) to the document.
    const CreateLayer = async(doc, layerName) => {
      await PDFNet.startDeallocateStack();
      const grp = await PDFNet.OCG.create(doc, layerName);
      let cfg = await doc.getOCGConfig();
      if (cfg == null) {
        cfg = await PDFNet.OCGConfig.create(doc, true);
        cfg.setName('Default');
      }

      // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
      let layerOrderArray = await cfg.getOrder();
      if (layerOrderArray == null) {
        layerOrderArray = await doc.createIndirectArray();
        cfg.setOrder(layerOrderArray);
      }
      const grpSDFObj = await grp.getSDFObj();
      layerOrderArray.pushBack(grpSDFObj);

      await PDFNet.endDeallocateStack();
      return grp;
    };

    // Creates some content (3 images) and associate them with the image layer
    const CreateGroup1 = async(doc, layer) => {
      await PDFNet.startDeallocateStack();
      const writer = await PDFNet.ElementWriter.create();
      writer.begin(doc);

      // Create an Image that can be reused in the document or on the same page.
      const img = await PDFNet.Image.createFromFile(doc, inputPath + 'peppers.jpg');

      const builder = await PDFNet.ElementBuilder.create();
      const imgWidth = await img.getImageWidth();
      const imgHeight = await img.getImageHeight();
      const imgMatrix = new PDFNet.Matrix2D(imgWidth / 2, -145, 20, imgHeight / 2, 200, 150);
      const element = await builder.createImageFromMatrix(img, imgMatrix);
      writer.writePlacedElement(element);

      const gstate = await element.getGState(); // use the same image (just change its matrix)
      gstate.setTransform(200, 0, 0, 300, 50, 450);
      writer.writePlacedElement(element);

      // use the same image again (just change its matrix).
      writer.writePlacedElement(await builder.createImageScaled(img, 300, 600, 200, -150));

      const grpObj = await writer.end();

      // Indicate that this form (content group) belongs to the given layer (OCG).
      grpObj.putName('Subtype', 'Form');
      grpObj.put('OC', layer);
      grpObj.putRect('BBox', 0, 0, 1000, 1000); // Set the clip box for the content.
      await PDFNet.endDeallocateStack();

      return grpObj;
    };

    const CreateGroup2 = async(doc, layer) => {
      await PDFNet.startDeallocateStack();
      const writer = await PDFNet.ElementWriter.create();
      writer.begin(doc);

      // Create a path object in the shape of a heart.
      const builder = await PDFNet.ElementBuilder.create();
      builder.pathBegin(); // start constructing the path
      builder.moveTo(306, 396);
      builder.curveTo(681, 771, 399.75, 864.75, 306, 771);
      builder.curveTo(212.25, 864.75, -69, 771, 306, 396);
      builder.closePath();
      const element = await builder.pathEnd(); // the path geometry is now specified.

      // Set the path FILL color space and color.
      element.setPathFill(true);
      const gstate = await element.getGState();
      const CMYKSpace = await PDFNet.ColorSpace.createDeviceCMYK();
      gstate.setFillColorSpace(CMYKSpace);
      const cyanColorPt = await PDFNet.ColorPt.init(1, 0, 0, 0); // CMYK
      gstate.setFillColorWithColorPt(cyanColorPt); // cyan

      // Set the path STROKE color space and color.
      element.setPathStroke(true);
      const RGBSpace = await PDFNet.ColorSpace.createDeviceRGB();
      gstate.setStrokeColorSpace(RGBSpace);
      const redColorPt = await PDFNet.ColorPt.init(1, 0, 0); // RGB
      gstate.setStrokeColorWithColorPt(redColorPt); // red
      gstate.setLineWidth(20);

      gstate.setTransform(0.5, 0, 0, 0.5, 280, 300);

      writer.writeElement(element);

      const grpObj = await writer.end();

      // Indicate that this form (content group) belongs to the given layer (OCG).
      grpObj.putName('Subtype', 'Form');
      grpObj.put('OC', layer);
      grpObj.putRect('BBox', 0, 0, 1000, 1000); // Set the clip box for the content.

      await PDFNet.endDeallocateStack();
      return grpObj;
    };

    const CreateGroup3 = async(doc, layer) => {
      await PDFNet.startDeallocateStack();
      const writer = await PDFNet.ElementWriter.create();
      writer.begin(doc);

      const builder = await PDFNet.ElementBuilder.create();

      // Begin writing a block of text
      const textFont = await PDFNet.Font.create(doc, PDFNet.Font.StandardType1Font.e_times_roman);
      let element = await builder.createTextBeginWithFont(textFont, 120);
      writer.writeElement(element);

      element = await builder.createNewTextRun('A text layer!');

      // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
      const transform = await PDFNet.Matrix2D.createRotationMatrix(-45 * (3.1415 / 180.0));
      await transform.concat(1, 0, 0, 1, 180, 100);
      await element.setTextMatrix(transform);

      await writer.writeElement(element);
      await writer.writeElement(await builder.createTextEnd());

      const grpObj = await writer.end();

      // Indicate that this form (content group) belongs to the given layer (OCG).
      grpObj.putName('Subtype', 'Form');
      grpObj.put('OC', layer);
      grpObj.putRect('BBox', 0, 0, 1000, 1000); // Set the clip box for the content.
      await PDFNet.endDeallocateStack();
      return grpObj;
    };


    const main = async() => {
      try {
        const doc = await PDFNet.PDFDoc.create();
        doc.initSecurityHandler();

        const imageLayer = await CreateLayer(doc, 'Image Layer');
        const textLayer = await CreateLayer(doc, 'Text Layer');
        const vectorLayer = await CreateLayer(doc, 'Vector Layer');

        // Start a new page ------------------------------------
        const page = await doc.pageCreate();

        const builder = await PDFNet.ElementBuilder.create();	 // ElementBuilder is used to build new Element objects
        const writer = await PDFNet.ElementWriter.create();  // ElementWriter is used to write Elements to the page
        writer.beginOnPage(page);  // Begin writing to the page

        const groupObj = await CreateGroup1(doc, (await imageLayer.getSDFObj()));
        let element = await builder.createFormFromStream(groupObj);
        writer.writeElement(element);

        const groupObj2 = await CreateGroup2(doc, (await vectorLayer.getSDFObj()));
        element = await builder.createFormFromStream(groupObj2);
        writer.writeElement(element);

        // eslint-disable-next-line no-constant-condition
        if (false) {
          // A bit more advanced example of how to create an OCMD text layer that
          // is visible only if text, image and path layers are all 'ON'.
          // An example of how to set 'Visibility Policy' in OCMD.
          const ocgs = doc.createIndirectArray();
          ocgs.pushBack(await imageLayer.getSDFObj());
          ocgs.pushBack(await vectorLayer.getSDFObj());
          ocgs.PushBack(await textLayer.getSDFObj());
          const textOcmd = await PDFNet.OCMD.create(doc, ocgs, PDFNet.OCMD.VisibilityPolicyType.e_AllOn);
          element = await builder.createFormFromStream(await CreateGroup3(doc, (await textOcmd.getSDFObj())));
        } else {
          // let SDFObj = await textLayer.getSDFObj();
          element = await builder.createFormFromStream(await CreateGroup3(doc, (await textLayer.getSDFObj())));
        }
        writer.writeElement(element);

        // Add some content to the page that does not belong to any layer...
        // In this case this is a rectangle representing the page border.
        element = await builder.createRect(0, 0, (await page.getPageWidth()), (await page.getPageHeight()));
        element.setPathFill(false);
        element.setPathStroke(true);
        const elementGState = await element.getGState();
        elementGState.setLineWidth(40);
        writer.writeElement(element);

        writer.end(); // save changes to the current page
        doc.pagePushBack(page);

        // Set the default viewing preference to display 'Layer' tab.
        const prefs = await doc.getViewPrefs();
        prefs.setPageMode(PDFNet.PDFDocViewPrefs.PageMode.e_UseOC);

        await doc.save(outputPath + 'pdf_layers.pdf', PDFNet.SDFDoc.SaveOptions.e_linearized);
        console.log('Done.');
      } catch (err) {
        // console.log(err);
        console.log(err.stack);
      }

      // The following is a code snippet shows how to selectively render 
      // and export PDF layers.
      try {
        const doc = await PDFNet.PDFDoc.createFromFilePath(outputPath + 'pdf_layers.pdf');
        doc.initSecurityHandler();

        if (!(await doc.hasOC())) {
          console.log("The document does not contain 'Optional Content'");
        } else {
          const initCfg = await doc.getOCGConfig();
          const ctx = await PDFNet.OCGContext.createFromConfig(initCfg);

          const pdfdraw = await PDFNet.PDFDraw.create();
          pdfdraw.setImageSize(1000, 1000);
          pdfdraw.setOCGContext(ctx);

          const page = await doc.getPage(1);

          await pdfdraw.export(page, outputPath + 'pdf_layers_default.png');

          // Disable drawing of content that is not optional (i.e. is not part of any layer).
          ctx.setNonOCDrawing(false);

          // Now render each layer in the input document to a separate image.
          const ocgs = await doc.getOCGs();
          if (ocgs !== null) {
            let i;
            const sz = await ocgs.size();
            for (i = 0; i < sz; ++i) {
              const ocg = await PDFNet.OCG.createFromObj(await ocgs.getAt(i));
              ctx.resetStates(false);
              await ctx.setState(ocg, true);
              let fname = 'pdf_layers_';
              fname += await ocg.getName();
              fname += '.png';
              console.log(fname);
              await pdfdraw.export(page, outputPath + fname);
            }
          }

          // Now draw content that is not part of any layer...
          ctx.setNonOCDrawing(true);
          await ctx.setOCDrawMode(PDFNet.OCGContext.OCDrawMode.e_NoOC);
          await pdfdraw.export(page, outputPath + 'pdf_layers_non_oc.png');
        }

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

{% endcode %}
{% endtab %}

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

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

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

$stdout.sync = true

#-----------------------------------------------------------------------------------
# This sample demonstrates how to create layers in PDF.
# The sample also shows how to extract and render PDF layers in documents 
# that contain optional content groups (OCGs)
#
# With the introduction of PDF version 1.5 came the concept of Layers. 
# Layers, or as they are more formally known Optional Content Groups (OCGs),
# refer to sections of content in a PDF document that can be selectively 
# viewed or hidden by document authors or consumers. This capability is useful 
# in CAD drawings, layered artwork, maps, multi-language documents etc.
# 
# Notes: 
# ---------------------------------------
# - This sample is using CreateLayer utility method to create new OCGs. 
#   CreateLayer is relatively basic, however it can be extended to set 
#   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
#   a complete listing of possible entries in OC dictionary please refer to 
#   section 4.10 'Optional Content' in the PDF Reference Manual.
# - The sample is grouping all layer content into separate Form XObjects. 
#   Although using PDFNet is is also possible to specify Optional Content in 
#   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
#   XObjects results in PDFs that are cleaner, less-error prone, and faster 
#   to process.
#-----------------------------------------------------------------------------------

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

# A utility function used to add new Content Groups (Layers) to the document.
def CreateLayer(doc, layer_name)
	grp = Group.Create(doc, layer_name)
	cfg = doc.GetOCGConfig
	if !cfg.IsValid
		cfg = PDFNetRuby::Config.Create(doc, true)
		cfg.SetName("Default")
	end
		
	# Add the new OCG to the list of layers that should appear in PDF viewer GUI.
	layer_order_array = cfg.GetOrder
	if layer_order_array.nil?
		layer_order_array = doc.CreateIndirectArray
		cfg.SetOrder(layer_order_array)
	end
	layer_order_array.PushBack(grp.GetSDFObj)
	return grp
end

# Creates some content (3 images) and associate them with the image layer
def CreateGroup1(doc, layer)
	writer = ElementWriter.new
	writer.Begin(doc.GetSDFDoc)
	
	# Create an Image that can be reused in the document or on the same page.
	img = Image.Create(doc.GetSDFDoc, $input_path + "peppers.jpg")
	builder = ElementBuilder.new
	element = builder.CreateImage(img, Matrix2D.new(img.GetImageWidth/2, -145, 20, img.GetImageHeight/2, 200, 150))
	writer.WritePlacedElement(element)
	
	gstate = element.GetGState	# use the same image (just change its matrix)
	gstate.SetTransform(200, 0, 0, 300, 50, 450)
	writer.WritePlacedElement(element)
	
	# use the same image again (just change its matrix).
	writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150))
	
	grp_obj = writer.End
	
	# Indicate that this form (content group) belongs to the given layer (OCG).
	grp_obj.PutName("Subtype","Form")
	grp_obj.Put("OC", layer)
	grp_obj.PutRect("BBox", 0, 0, 1000, 1000)   # Set the clip box for the content.
	
	return grp_obj
end

# Creates some content (a path in the shape of a heart) and associate it with the vector layer
def CreateGroup2(doc, layer)
	writer = ElementWriter.new
	writer.Begin(doc.GetSDFDoc)
	
	# Create a path object in the shape of a heart
	builder = ElementBuilder.new
	builder.PathBegin	 # start constructing the path
	builder.MoveTo(306, 396)
	builder.CurveTo(681, 771, 399.75, 864.75, 306, 771)
	builder.CurveTo(212.25, 864.75, -69, 771, 306, 396)
	builder.ClosePath
	element = builder.PathEnd # the path geometry is now specified.

	# Set the path FILL color space and color.
	element.SetPathFill(true)
	gstate = element.GetGState
	gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK)
	gstate.SetFillColor(ColorPt.new(1, 0, 0, 0))	# cyan
	
	# Set the path STROKE color space and color
	element.SetPathStroke(true)
	gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB)
	gstate.SetStrokeColor(ColorPt.new(1, 0, 0))	 # red
	gstate.SetLineWidth(20)
	
	gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300)
	
	writer.WriteElement(element)
	
	grp_obj = writer.End
	
	# Indicate that this form (content group) belongs to the given layer (OCG).
	grp_obj.PutName("Subtype","Form")
	grp_obj.Put("OC", layer)
	grp_obj.PutRect("BBox", 0, 0, 1000, 1000)	# Set the clip box for the content.
	
	return grp_obj
end

# Creates some text and associate it with the text layer
def CreateGroup3(doc, layer)
	writer = ElementWriter.new
	writer.Begin(doc.GetSDFDoc)
	
	# Create a path object in the shape of a heart.
	builder = ElementBuilder.new
	
	# Begin writing a block of text
	element = builder.CreateTextBegin(Font.Create(doc.GetSDFDoc, Font::E_times_roman), 120)
	writer.WriteElement(element)
	
	element = builder.CreateTextRun("A text layer!")
	
	# Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
	transform = Matrix2D.RotationMatrix(-45 * (3.1415/ 180.0))
	transform.Concat(1, 0, 0, 1, 180, 100)
	element.SetTextMatrix(transform)
	
	writer.WriteElement(element)
	writer.WriteElement(builder.CreateTextEnd)
	
	grp_obj = writer.End
	
	# Indicate that this form (content group) belongs to the given layer (OCG).
	grp_obj.PutName("Subtype","Form")
	grp_obj.Put("OC", layer)
	grp_obj.PutRect("BBox", 0, 0, 1000, 1000)   # Set the clip box for the content.
	
	return grp_obj
end

	PDFNet.Initialize(PDFTronLicense.Key)
	
	# Create three layers...
	doc = PDFDoc.new
	image_layer = CreateLayer(doc, "Image Layer")
	text_layer = CreateLayer(doc, "Text Layer")
	vector_layer = CreateLayer(doc, "Vector Layer")
	
	# Start a new page ------------------------------------
	page = doc.PageCreate
	
	builder = ElementBuilder.new	# ElementBuilder is used to build new Element objects
	writer = ElementWriter.new	# ElementWriter is used to write Elements to the page
	writer.Begin(page)		# Begin writting to the page
	
	# Add new content to the page and associate it with one of the layers.
	element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj))
	writer.WriteElement(element)
	
	element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj))
	writer.WriteElement(element)
	
	# Add the text layer to the page...
	if false # set to true to enable 'ocmd' example.
		# A bit more advanced example of how to create an OCMD text layer that 
		# is visible only if text, image and path layers are all 'ON'.
		# An example of how to set 'Visibility Policy' in OCMD.
		ocgs = doc.CreateIndirectArray
		ocgs.PushBack(image_layer.GetSDFObj)
		ocgs.PushBack(vector_layer.GetSDFObj)
		ocgs.PushBack(text_layer.GetSDFObj)
		text_ocmd = OCMD.Create(doc, ocgs, OCMD::E_AllOn)
		element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj))
	else
		element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj))
	end
	writer.WriteElement(element)
	
	# Add some content to the page that does not belong to any layer...
	# In this case this is a rectangle representing the page border.
	element = builder.CreateRect(0, 0, page.GetPageWidth, page.GetPageHeight)
	element.SetPathFill(false)
	element.SetPathStroke(true)
	element.GetGState.SetLineWidth(40)
	writer.WriteElement(element)
	
	writer.End	# save changes to the current page
	doc.PagePushBack(page)
	# Set the default viewing preference to display 'Layer' tab
	prefs = doc.GetViewPrefs
	prefs.SetPageMode(PDFDocViewPrefs::E_UseOC)
	
	doc.Save($output_path + "pdf_layers.pdf", SDFDoc::E_linearized)
	doc.Close
	puts "Done."
	
	# The following is a code snippet shows how to selectively render 
	# and export PDF layers.
	
	doc = PDFDoc.new($output_path + "pdf_layers.pdf")
	doc.InitSecurityHandler
	
	if !doc.HasOC
		puts "The document does not contain 'Optional Content'"
	else
		init_cfg = doc.GetOCGConfig
		ctx = Context.new(init_cfg)
		
		pdfdraw = PDFDraw.new
		pdfdraw.SetImageSize(1000, 1000)
		pdfdraw.SetOCGContext(ctx)  # Render the page using the given OCG context.
		
		page = doc.GetPage(1)   # Get the first page in the document.
		pdfdraw.Export(page, $output_path + "pdf_layers_default.png")
		
		# Disable drawing of content that is not optional (i.e. is not part of any layer).
		ctx.SetNonOCDrawing(false)
		
		# Now render each layer in the input document to a separate image.
		ocgs = doc.GetOCGs	# Get the array of all OCGs in the document.
		if !ocgs.nil?
			sz = ocgs.Size
			i = 0
			while i < sz do
				ocg = Group.new(ocgs.GetAt(i))
				ctx.ResetStates(false)
				ctx.SetState(ocg, true)
				fname = "pdf_layers_" + ocg.GetName + ".png"
				puts fname
				pdfdraw.Export(page, $output_path + fname)
				i = i + 1
			end
		end

		# Now draw content that is not part of any layer...
		ctx.SetNonOCDrawing(true)
		ctx.SetOCDrawMode(Context::E_NoOC)
		pdfdraw.Export(page, $output_path + "pdf_layers_non_oc.png")
		
		doc.Close
	end
	PDFNet.Terminate
	puts "Done."
```

{% endcode %}
{% endtab %}

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

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

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

//-----------------------------------------------------------------------------------
// This sample demonstrates how to create layers in PDF.
// The sample also shows how to extract and render PDF layers in documents 
// that contain optional content groups (OCGs)
//
// With the introduction of PDF version 1.5 came the concept of Layers. 
// Layers, or as they are more formally known Optional Content Groups (OCGs),
// refer to sections of content in a PDF document that can be selectively 
// viewed or hidden by document authors or consumers. This capability is useful 
// in CAD drawings, layered artwork, maps, multi-language documents etc.
// 
// Notes: 
// ---------------------------------------
// - This sample is using CreateLayer() utility method to create new OCGs. 
//   CreateLayer() is relatively basic, however it can be extended to set 
//   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
//   a complete listing of possible entries in OC dictionary please refer to 
//   section 4.10 'Optional Content' in the PDF Reference Manual.
// - The sample is grouping all layer content into separate Form XObjects. 
//   Although using PDFNet is is also possible to specify Optional Content in 
//   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
//   XObjects results in PDFs that are cleaner, less-error prone, and faster 
//   to process.
//-----------------------------------------------------------------------------------

// A utility function used to add new Content Groups (Layers) to the document.
function CreateLayer($doc, $layer_name)
{
	$grp = Group::Create($doc, $layer_name);
	$cfg = $doc->GetOCGConfig();
	if (!$cfg->IsValid()) {
		$cfg = OCGConfig::Create($doc, true);
		$cfg->SetName("Default");
	}

	// Add the new OCG to the list of layers that should appear in PDF viewer GUI.
	$layer_order_array = $cfg->GetOrder();
	if (!$layer_order_array) {
        	$layer_order_array = $doc->CreateIndirectArray();
		$cfg->SetOrder($layer_order_array);
	}
	$layer_order_array->PushBack($grp->GetSDFObj());

	return $grp;
}

// Creates some content (3 images) and associate them with the image layer
function CreateGroup1($doc, $layer) 
{
	$writer = new ElementWriter();
	$writer->Begin($doc->GetSDFDoc());
	global $input_path;
	// Create an Image that can be reused in the document or on the same page.		
	$img = Image::Create($doc->GetSDFDoc(), $input_path."peppers.jpg");

	$builder = new ElementBuilder();
	$element = $builder->CreateImage($img, new Matrix2D((double)$img->GetImageWidth()/2, -145.0, 20.0, 
							(double)$img->GetImageHeight()/2, 200.0, 150.0));
	$writer->WritePlacedElement($element);

	$gstate = $element->GetGState();	// use the same image (just change its matrix)
	$gstate->SetTransform(200.0, 0.0, 0.0, 300.0, 50.0, 450.0);
	$writer->WritePlacedElement($element);

	// use the same image again (just change its matrix).
	$writer->WritePlacedElement($builder->CreateImage($img, 300.0, 600.0, 200.0, -150.0));

	$grp_obj = $writer->End();	

	// Indicate that this form (content group) belongs to the given layer (OCG).
	$grp_obj->PutName("Subtype","Form");
	$grp_obj->Put("OC", $layer);	
	$grp_obj->PutRect("BBox", 0, 0, 1000, 1000);  // Set the clip box for the content.

	return $grp_obj;
}

// Creates some content (a path in the shape of a heart) and associate it with the vector layer
function CreateGroup2($doc, $layer) 
{
	$writer = new ElementWriter();
	$writer->Begin($doc->GetSDFDoc());

	// Create a path object in the shape of a heart.
	$builder = new ElementBuilder();
	$builder->PathBegin();		// start constructing the path
	$builder->MoveTo(306, 396);
	$builder->CurveTo(681, 771, 399.75, 864.75, 306, 771);
	$builder->CurveTo(212.25, 864.75, -69, 771, 306, 396);
	$builder->ClosePath();
	$element = $builder->PathEnd(); // the path geometry is now specified.

	// Set the path FILL color space and color.
	$element->SetPathFill(true);
	$gstate = $element->GetGState();
	$gstate->SetFillColorSpace(ColorSpace::CreateDeviceCMYK()); 
	$gstate->SetFillColor(new ColorPt(1.0, 0.0, 0.0, 0.0));  // cyan

	// Set the path STROKE color space and color.
	$element->SetPathStroke(true); 
	$gstate->SetStrokeColorSpace(ColorSpace::CreateDeviceRGB()); 
	$gstate->SetStrokeColor(new ColorPt(1.0, 0.0, 0.0));  // red
	$gstate->SetLineWidth(20);

	$gstate->SetTransform(0.5, 0.0, 0.0, 0.5, 280.0, 300.0);

	$writer->WriteElement($element);

	$grp_obj = $writer->End();	

	// Indicate that this form (content group) belongs to the given layer (OCG).
	$grp_obj->PutName("Subtype","Form");
	$grp_obj->Put("OC", $layer);
	$grp_obj->PutRect("BBox", 0, 0, 1000, 1000); 	// Set the clip box for the content.

	return $grp_obj;
}

// Creates some text and associate it with the text layer
function CreateGroup3($doc, $layer) 
{
	$writer = new ElementWriter();
	$writer->Begin($doc->GetSDFDoc());

	// Create a path object in the shape of a heart.
	$builder = new ElementBuilder();

	// Begin writing a block of text
	$element = $builder->CreateTextBegin(Font::Create($doc->GetSDFDoc(), Font::e_times_roman), 120.0);
	$writer->WriteElement($element);

	$element = $builder->CreateTextRun("A text layer!");

	// Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
	$transform = Matrix2D::RotationMatrix(-45 *  (3.1415/ 180.0));
	$transform->Concat(1, 0, 0, 1, 180, 100);  
	$element->SetTextMatrix($transform);

	$writer->WriteElement($element);
	$writer->WriteElement($builder->CreateTextEnd());

	$grp_obj = $writer->End();	

	// Indicate that this form (content group) belongs to the given layer (OCG).
	$grp_obj->PutName("Subtype","Form");
	$grp_obj->Put("OC", $layer);
	$grp_obj->PutRect("BBox", 0, 0, 1000, 1000); 	// Set the clip box for the content.

	return $grp_obj;
}

	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.

	$doc = new PDFDoc();

	// Create three layers...
	$image_layer = CreateLayer($doc, "Image Layer");
	$text_layer = CreateLayer($doc, "Text Layer");
	$vector_layer = CreateLayer($doc, "Vector Layer");

	// Start a new page ------------------------------------
	$page = $doc->PageCreate();

	$builder = new ElementBuilder();	// ElementBuilder is used to build new Element objects
	$writer = new ElementWriter;	// ElementWriter is used to write Elements to the page	
	$writer->Begin($page);		// Begin writing to the page

	// Add new content to the page and associate it with one of the layers.
	$element = $builder->CreateForm(CreateGroup1($doc, $image_layer->GetSDFObj()));
	$writer->WriteElement($element);

	$element = $builder->CreateForm(CreateGroup2($doc, $vector_layer->GetSDFObj()));
	$writer->WriteElement($element);

	// Add the text layer to the page...
	if (false)  // set to true to enable 'ocmd' example.
	{
		// A bit more advanced example of how to create an OCMD text layer that 
		// is visible only if text, image and path layers are all 'ON'.
		// An example of how to set 'Visibility Policy' in OCMD.
		$ocgs = $doc->CreateIndirectArray();
		$ocgs->PushBack($image_layer->GetSDFObj());
		$ocgs->PushBack($vector_layer->GetSDFObj());
		$ocgs->PushBack($text_layer->GetSDFObj());
		$text_ocmd = OCMD::Create($doc, $ocgs, OCMD::e_AllOn);
		$element = $builde->CreateForm(CreateGroup3($doc, $text_ocmd->GetSDFObj()));
	}
	else {
		$element = $builder->CreateForm(CreateGroup3($doc, $text_layer->GetSDFObj()));
	}

	$writer->WriteElement($element);

	// Add some content to the page that does not belong to any layer...
	// In this case this is a rectangle representing the page border.
	$element = $builder->CreateRect(0, 0, $page->GetPageWidth(), $page->GetPageHeight());
	$element->SetPathFill(false);
	$element->SetPathStroke(true);
	$element->GetGState()->SetLineWidth(40);
	$writer->WriteElement($element);

	$writer->End();  // save changes to the current page
	$doc->PagePushBack($page);

	// Set the default viewing preference to display 'Layer' tab.
	$prefs = $doc->GetViewPrefs();
	$prefs->SetPageMode(PDFDocViewPrefs::e_UseOC);

	$doc->Save($output_path."pdf_layers.pdf", SDFDoc::e_linearized);
	echo nl2br("Done.\n");

	// The following is a code snippet shows how to selectively render 
	// and export PDF layers.
	
	$doc = new PDFDoc($output_path."pdf_layers.pdf");
	$doc->InitSecurityHandler();

	if (!$doc->HasOC()) {
		echo nl2br("The document does not contain 'Optional Content'\n");
	}
	else {
		$init_cfg = $doc->GetOCGConfig();
		$ctx = new Context($init_cfg);

		$pdfdraw = new PDFDraw();
		$pdfdraw->SetImageSize(1000, 1000);
		$pdfdraw->SetOCGContext($ctx); // Render the page using the given OCG context.

		$page = $doc->GetPage(1); // Get the first page in the document.
		$pdfdraw->Export($page, $output_path."pdf_layers_default.png");

		// Disable drawing of content that is not optional (i.e. is not part of any layer).
		$ctx->SetNonOCDrawing(false);

		// Now render each layer in the input document to a separate image.
		$ocgs = $doc->GetOCGs(); // Get the array of all OCGs in the document.
		if ($ocgs != null) {
			$sz = $ocgs->Size();
			for ($i=0; $i<$sz; ++$i) {
				$ocg = new Group($ocgs->GetAt($i));
				$ctx->ResetStates(false);
				$ctx->SetState($ocg, true);
				$fname = "pdf_layers_".$ocg->GetName().".png";
				echo nl2br($fname."\n");
				$pdfdraw->Export($page, $output_path.$fname);
			}
		}

		// Now draw content that is not part of any layer...
		$ctx->SetNonOCDrawing(true);
		$ctx->SetOCDrawMode(Context::e_NoOC);
		$pdfdraw->Export($page, $output_path."pdf_layers_non_oc.png");
	}

	$doc->Close();
	PDFNet::Terminate();
	echo nl2br("Done.\n");	
?>
```

{% endcode %}
{% endtab %}

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

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

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

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

#-----------------------------------------------------------------------------------
# This sample demonstrates how to create layers in PDF.
# The sample also shows how to extract and render PDF layers in documents 
# that contain optional content groups (OCGs)
#
# With the introduction of PDF version 1.5 came the concept of Layers. 
# Layers, or as they are more formally known Optional Content Groups (OCGs),
# refer to sections of content in a PDF document that can be selectively 
# viewed or hidden by document authors or consumers. This capability is useful 
# in CAD drawings, layered artwork, maps, multi-language documents etc.
# 
# Notes: 
# ---------------------------------------
# - This sample is using CreateLayer() utility method to create new OCGs. 
#   CreateLayer() is relatively basic, however it can be extended to set 
#   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
#   a complete listing of possible entries in OC dictionary please refer to 
#   section 4.10 'Optional Content' in the PDF Reference Manual.
# - The sample is grouping all layer content into separate Form XObjects. 
#   Although using PDFNet is is also possible to specify Optional Content in 
#   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
#   XObjects results in PDFs that are cleaner, less-error prone, and faster 
#   to process.
#-----------------------------------------------------------------------------------

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

# A utility function used to add new Content Groups (Layers) to the document.
def CreateLayer(doc, layer_name):
    grp = Group.Create(doc, layer_name)
    cfg = doc.GetOCGConfig()
    if not cfg.IsValid():
        cfg = Config.Create(doc, True)
        cfg.SetName("Default")
        
    # Add the new OCG to the list of layers that should appear in PDF viewer GUI.
    layer_order_array = cfg.GetOrder()
    if layer_order_array is None:
        layer_order_array = doc.CreateIndirectArray()
        cfg.SetOrder(layer_order_array)
    layer_order_array.PushBack(grp.GetSDFObj())
    return grp

# Creates some content (3 images) and associate them with the image layer
def CreateGroup1(doc, layer):
    writer = ElementWriter()
    writer.Begin(doc.GetSDFDoc())
    
    # Create an Image that can be reused in the document or on the same page.
    img = Image.Create(doc.GetSDFDoc(), input_path + "peppers.jpg")
    builder = ElementBuilder()
    element = builder.CreateImage(img, Matrix2D(img.GetImageWidth()/2, -145, 20, img.GetImageHeight()/2, 200, 150))
    writer.WritePlacedElement(element)
    
    gstate = element.GetGState()    # use the same image (just change its matrix)
    gstate.SetTransform(200, 0, 0, 300, 50, 450)
    writer.WritePlacedElement(element)
    
    # use the same image again (just change its matrix).
    writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150))
    
    grp_obj = writer.End()
    
    # Indicate that this form (content group) belongs to the given layer (OCG).
    grp_obj.PutName("Subtype","Form")
    grp_obj.Put("OC", layer)
    grp_obj.PutRect("BBox", 0, 0, 1000, 1000)   # Set the clip box for the content.
    
    return grp_obj

# Creates some content (a path in the shape of a heart) and associate it with the vector layer
def CreateGroup2(doc, layer):
    writer = ElementWriter()
    writer.Begin(doc.GetSDFDoc())
    
    # Create a path object in the shape of a heart
    builder = ElementBuilder()
    builder.PathBegin()     # start constructing the path
    builder.MoveTo(306, 396)
    builder.CurveTo(681, 771, 399.75, 864.75, 306, 771)
    builder.CurveTo(212.25, 864.75, -69, 771, 306, 396)
    builder.ClosePath()
    element = builder.PathEnd() # the path geometry is now specified.

    # Set the path FILL color space and color.
    element.SetPathFill(True)
    gstate = element.GetGState()
    gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK())
    gstate.SetFillColor(ColorPt(1, 0, 0, 0))    # cyan
    
    # Set the path STROKE color space and color
    element.SetPathStroke(True)
    gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB())
    gstate.SetStrokeColor(ColorPt(1, 0, 0))     # red
    gstate.SetLineWidth(20)
    
    gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300)
    
    writer.WriteElement(element)
    
    grp_obj = writer.End()
    
    # Indicate that this form (content group) belongs to the given layer (OCG).
    grp_obj.PutName("Subtype","Form")
    grp_obj.Put("OC", layer)
    grp_obj.PutRect("BBox", 0, 0, 1000, 1000)       # Set the clip box for the content.
    
    return grp_obj

# Creates some text and associate it with the text layer
def CreateGroup3(doc, layer):
    writer = ElementWriter()
    writer.Begin(doc.GetSDFDoc())
    
    # Create a path object in the shape of a heart.
    builder = ElementBuilder()
    
    # Begin writing a block of text
    element = builder.CreateTextBegin(Font.Create(doc.GetSDFDoc(), Font.e_times_roman), 120)
    writer.WriteElement(element)
    
    element = builder.CreateTextRun("A text layer!")
    
    # Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
    transform = Matrix2D.RotationMatrix(-45 * (3.1415/ 180.0))
    transform.Concat(1, 0, 0, 1, 180, 100)
    element.SetTextMatrix(transform)
    
    writer.WriteElement(element)
    writer.WriteElement(builder.CreateTextEnd())
    
    grp_obj = writer.End()
    
    # Indicate that this form (content group) belongs to the given layer (OCG).
    grp_obj.PutName("Subtype","Form")
    grp_obj.Put("OC", layer)
    grp_obj.PutRect("BBox", 0, 0, 1000, 1000)   # Set the clip box for the content.
    
    return grp_obj

def main():
    PDFNet.Initialize(LicenseKey)
    
    # Create three layers...
    doc = PDFDoc()
    image_layer = CreateLayer(doc, "Image Layer")
    text_layer = CreateLayer(doc, "Text Layer")
    vector_layer = CreateLayer(doc, "Vector Layer")
    
    # Start a new page ------------------------------------
    page = doc.PageCreate()
    
    builder = ElementBuilder()    # ElementBuilder is used to build new Element objects
    writer = ElementWriter()      # ElementWriter is used to write Elements to the page
    writer.Begin(page)            # Begin writting to the page
    
    # Add new content to the page and associate it with one of the layers.
    element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()))
    writer.WriteElement(element)
    
    element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()))
    writer.WriteElement(element)
    
    # Add the text layer to the page...
    if False: # set to true to enable 'ocmd' example.
        # A bit more advanced example of how to create an OCMD text layer that 
        # is visible only if text, image and path layers are all 'ON'.
        # An example of how to set 'Visibility Policy' in OCMD.
        ocgs = doc.CreateIndirectArray()
        ocgs.PushBack(image_layer.GetSDFObj())
        ocgs.PushBack(vector_layer.GetSDFObj())
        ocgs.PushBack(text_layer.GetSDFObj())
        text_ocmd = OCMD.Create(doc, ocgs, OCMD.e_AllOn)
        element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()))
    else:
        element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()))
    writer.WriteElement(element)
    
    # Add some content to the page that does not belong to any layer...
    # In this case this is a rectangle representing the page border.
    element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight())
    element.SetPathFill(False)
    element.SetPathStroke(True)
    element.GetGState().SetLineWidth(40)
    writer.WriteElement(element)
    
    writer.End()    # save changes to the current page
    doc.PagePushBack(page)
    # Set the default viewing preference to display 'Layer' tab
    prefs = doc.GetViewPrefs()
    prefs.SetPageMode(PDFDocViewPrefs.e_UseOC)
    
    doc.Save(output_path + "pdf_layers.pdf", SDFDoc.e_linearized)
    doc.Close()
    print("Done.")
    
    # The following is a code snippet shows how to selectively render 
    # and export PDF layers.
    
    doc = PDFDoc(output_path + "pdf_layers.pdf")
    doc.InitSecurityHandler()
    
    if not doc.HasOC():
        print("The document does not contain 'Optional Content'")
    else:
        init_cfg = doc.GetOCGConfig()
        ctx = Context(init_cfg)
        
        pdfdraw = PDFDraw()
        pdfdraw.SetImageSize(1000, 1000)
        pdfdraw.SetOCGContext(ctx)  # Render the page using the given OCG context.
        
        page = doc.GetPage(1)   # Get the first page in the document.
        pdfdraw.Export(page, output_path + "pdf_layers_default.png")
        
        # Disable drawing of content that is not optional (i.e. is not part of any layer).
        ctx.SetNonOCDrawing(False)
        
        # Now render each layer in the input document to a separate image.
        ocgs = doc.GetOCGs()    # Get the array of all OCGs in the document.
        if ocgs is not None:
            sz = ocgs.Size()
            i = 0
            while i < sz:
                ocg = Group(ocgs.GetAt(i))
                ctx.ResetStates(False)
                ctx.SetState(ocg, True)
                fname = "pdf_layers_" + ocg.GetName() + ".png"
                print(fname)
                pdfdraw.Export(page, output_path + fname)
                i = i + 1
        # Now draw content that is not part of any layer...
        ctx.SetNonOCDrawing(True)
        ctx.SetOCDrawMode(Context.e_NoOC)
        pdfdraw.Export(page, output_path + "pdf_layers_non_oc.png")
        
        doc.Close()
        PDFNet.Terminate()
        print("Done.")                     

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

{% endcode %}
{% endtab %}

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

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

Imports System

Imports pdftron
Imports pdftron.Common
Imports pdftron.Filters
Imports pdftron.SDF
Imports PDFTRON.PDF
Imports PDFTRON.PDF.OCG


'-----------------------------------------------------------------------------------
' This sample demonstrates how to create layers in PDF.
' The sample also shows how to extract and render PDF layers in documents 
' that contain optional content groups (OCGs)
'
' With the introduction of PDF version 1.5 came the concept of Layers. 
' Layers, or as they are more formally known Optional Content Groups (OCGs),
' refer to sections of content in a PDF document that can be selectively 
' viewed or hidden by document authors or consumers. This capability is useful 
' in CAD drawings, layered artwork, maps, multi-language documents etc.
'
' Notes: 
' ---------------------------------------
' - This sample is using CreateLayer() utility method to create new OCGs. 
'   CreateLayer() is relatively basic, however it can be extended to set 
'   other optional entries in the 'OCG' and 'OCProperties' dictionary. For 
'   a complete listing of possible entries in OC dictionary please refer to 
'   section 4.10 'Optional Content' in the PDF Reference Manual.
' - The sample is grouping all layer content into separate Form XObjects. 
'   Although using PDFNet is is also possible to specify Optional Content in 
'   Content Streams (Section 4.10.2 in PDF Reference), Optional Content in  
'   XObjects results in PDFs that are cleaner, less-error prone, and faster 
'   to process.
'-----------------------------------------------------------------------------------

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

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

	Sub Main()

		PDFNet.Initialize(PDFTronLicense.Key)

		Try
			Using doc As PDFDoc = New PDFDoc
				' Create three layers...
				Dim image_layer As Group = CreateLayer(doc, "Image Layer")
				Dim text_layer As Group = CreateLayer(doc, "Text Layer")
				Dim vector_layer As Group = CreateLayer(doc, "Vector Layer")

				' Start a new page ------------------------------------
				Dim page As Page = doc.PageCreate()

				Using builder As ElementBuilder = New ElementBuilder			' ElementBuilder is used to build new Element objects
					Using writer As ElementWriter = New ElementWriter		  ' ElementWriter is used to write Elements to the page	
						writer.Begin(page)			 ' begin writing to this page           

						' Add new content to the page and associate it with one of the layers.
						Dim element As Element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()))
						writer.WriteElement(element)

						element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()))
						writer.WriteElement(element)

						element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()))
						writer.WriteElement(element)

						' Add some content to the page that does not belong to any layer...
						' In this case this is a rectangle representing the page border.
						element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight())
						element.SetPathFill(False)
						element.SetPathStroke(True)
						element.GetGState().SetLineWidth(40)
						writer.WriteElement(element)

						writer.End()			  ' save changes to the current page
						doc.PagePushBack(page)

						doc.Save(output_path + "pdf_layers.pdf", SDF.SDFDoc.SaveOptions.e_linearized)

					End Using
				End Using

			End Using
			Console.WriteLine("Done.")

		Catch ex As PDFNetException
			Console.WriteLine(ex.Message)
		Catch ex As Exception
			MsgBox(ex.Message)
		End Try


		' The following is a code snippet shows how to selectively render 
		' and export PDF layers.
		Try
			Using doc As PDFDoc = New PDFDoc(output_path + "pdf_layers.pdf")
				doc.InitSecurityHandler()

				If doc.HasOC() = False Then
					Console.WriteLine("The document does not contain 'Optional Content'")
				Else
					Dim init_cfg As Config = doc.GetOCGConfig()
					Dim ctx As Context = New Context(init_cfg)

					Using pdfdraw As PDFDraw = New PDFDraw
						pdfdraw.SetImageSize(1000, 1000)
						pdfdraw.SetOCGContext(ctx)			 ' Render the page using the given OCG context.

						Dim page As Page = doc.GetPage(1)			 ' Get the first page in the document.
						pdfdraw.Export(page, output_path + "pdf_layers_default.png")

						' Disable drawing of content that is not optional (i.e. is not part of any layer).
						ctx.SetNonOCDrawing(False)

						' Now render each layer in the input document to a separate image.
						Dim ocgs As Obj = doc.GetOCGs()			 ' Get the array of all OCGs in the document.
						If Not ocgs Is Nothing Then
							Dim i As Integer = 0
							Dim sz As Integer = ocgs.Size()
							While (i < sz)
								Dim ocg As Group = New Group(ocgs.GetAt(i))
								ctx.ResetStates(False)
								ctx.SetState(ocg, True)
								Dim fname As String = "pdf_layers_" + ocg.GetName() + ".png"
								Console.WriteLine(fname)
								pdfdraw.Export(page, output_path + fname)
								i = i + 1
							End While
						End If

						' Now draw content that is not part of any layer...
						ctx.SetNonOCDrawing(True)
						ctx.SetOCDrawMode(Context.OCDrawMode.e_NoOC)
						pdfdraw.Export(page, output_path + "pdf_layers_non_oc.png")
					End Using
					Console.WriteLine("Done.")
				End If
			End Using
		Catch ex As PDFNetException
			Console.WriteLine(ex.Message)
		Catch ex As Exception
			MsgBox(ex.Message)
		End Try
		PDFNet.Terminate()
	End Sub

	' A utility function used to add new Content Groups (Layers) to the document.
	Function CreateLayer(ByRef doc As PDFDoc, ByVal layer_name As String) As Group

		Dim grp As Group = Group.Create(doc, layer_name)
		Dim cfg As Config = doc.GetOCGConfig()
		If cfg Is Nothing Then
			cfg = Config.Create(doc, True)
			cfg.SetName("Default")
		End If

		' Add the new OCG to the list of layers that should appear in PDF viewer GUI.
		Dim layer_order_array As Obj = cfg.GetOrder()
		If layer_order_array Is Nothing Then
			layer_order_array = doc.CreateIndirectArray()
			cfg.SetOrder(layer_order_array)
		End If
		layer_order_array.PushBack(grp.GetSDFObj())

		Return grp
	End Function

	' Creates some content (3 images) and associate them with the image layer
	Function CreateGroup1(ByRef doc As PDFDoc, ByRef layer As Obj) As Obj

		Using writer As ElementWriter = New ElementWriter
			writer.Begin(doc.GetSDFDoc())

			' Create an Image that can be reused in the document or on the same page.		
			Dim img As Image = Image.Create(doc.GetSDFDoc(), (input_path + "peppers.jpg"))

			Using builder As ElementBuilder = New ElementBuilder

				Dim element As Element = builder.CreateImage(img, New Matrix2D(img.GetImageWidth() / 2, -145, 20, img.GetImageHeight() / 2, 200, 150))
				writer.WritePlacedElement(element)

				Dim gstate As GState = element.GetGState()		  ' use the same image (just change its matrix)
				gstate.SetTransform(200, 0, 0, 300, 50, 450)
				writer.WritePlacedElement(element)

				' use the same image again (just change its matrix).
				writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150))

				Dim grp_obj As Obj = writer.End()

				' Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype", "Form")
				grp_obj.Put("OC", layer)
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000)		' Set the clip box for the content.

				Return grp_obj
			End Using
		End Using
	End Function

	' Creates some content (a path in the shape of a heart) and associate it with the vector layer
	Function CreateGroup2(ByRef doc As PDFDoc, ByRef layer As Obj) As Obj

		Using writer As ElementWriter = New ElementWriter
			writer.Begin(doc.GetSDFDoc())

			Using builder As ElementBuilder = New ElementBuilder
				' Create a path object in the shape of a heart.
				builder.PathBegin()		' start constructing the path
				builder.MoveTo(306, 396)
				builder.CurveTo(681, 771, 399.75, 864.75, 306, 771)
				builder.CurveTo(212.25, 864.75, -69, 771, 306, 396)
				builder.ClosePath()
				Dim element As Element = builder.PathEnd()		  ' the path geometry is now specified.

				' Set the path FILL color space and color.
				element.SetPathFill(True)
				Dim gstate As GState = element.GetGState()
				gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK())
				gstate.SetFillColor(New ColorPt(1, 0, 0, 0))		' cyan

				' Set the path STROKE color space and color.
				element.SetPathStroke(True)
				gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB())
				gstate.SetStrokeColor(New ColorPt(1, 0, 0))		' red
				gstate.SetLineWidth(20)

				gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300)

				writer.WriteElement(element)

				Dim grp_obj As Obj = writer.End()

				' Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype", "Form")
				grp_obj.Put("OC", layer)
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000)		' Set the clip box for the content.

				Return grp_obj
			End Using
		End Using
	End Function

	' Creates some text and associate it with the text layer
	Function CreateGroup3(ByRef doc As PDFDoc, ByRef layer As Obj) As Obj

		Using writer As ElementWriter = New ElementWriter
			writer.Begin(doc.GetSDFDoc())

			Using builder As ElementBuilder = New ElementBuilder

				' Begin writing a block of text
				Dim element As Element = builder.CreateTextBegin(Font.Create(doc, Font.StandardType1Font.e_times_roman), 120)
				writer.WriteElement(element)

				element = builder.CreateTextRun("A text layer!")

				' Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
				Dim transform As Matrix2D = Matrix2D.RotationMatrix(-45 * (3.1415 / 180.0))
				transform.Concat(1, 0, 0, 1, 180, 100)
				element.SetTextMatrix(transform)

				writer.WriteElement(element)
				writer.WriteElement(builder.CreateTextEnd())

				Dim grp_obj As Obj = writer.End()

				' Indicate that this form (content group) belongs to the given layer (OCG).
				grp_obj.PutName("Subtype", "Form")
				grp_obj.Put("OC", layer)
				grp_obj.PutRect("BBox", 0, 0, 1000, 1000)		' Set the clip box for the content.

				Return grp_obj
			End Using
		End Using
	End Function

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