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

# PDFLayers

Sample Obj-C 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 s

Sample Obj-C 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. Learn more about our [iOS SDK](/ios/guides.md).

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

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

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

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

PTObj * CreateGroup1(PTPDFDoc *doc, PTObj * layer);
PTObj * CreateGroup2(PTPDFDoc *doc, PTObj * layer);
PTObj * CreateGroup3(PTPDFDoc *doc, PTObj * layer);
PTGroup *CreateLayer(PTPDFDoc *doc, NSString *layer_name);

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

        @try  
        { 
            PTPDFDoc *doc = [[PTPDFDoc alloc] init];

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

            // Start a new page ------------------------------------
            PTPage *page = [doc PageCreate: [[PTPDFRect alloc] initWithX1: 0 y1: 0 x2: 612 y2:792]];

            PTElementBuilder *builder = [[PTElementBuilder alloc] init]; // ElementBuilder is used to build new Element objects
            PTElementWriter *writer= [[PTElementWriter alloc] init]; // ElementWriter is used to write Elements to the page
            [writer WriterBeginWithPage: page placement: e_ptoverlay page_coord_sys: YES compress: YES resources: NULL]; // Begin writing to the page

            // Add new content to the page and associate it with one of the layers.
            PTElement *element = [builder CreateFormWithObj: CreateGroup1(doc, [image_layer GetSDFObj])];
            [writer WriteElement: element];

            element = [builder CreateFormWithObj: CreateGroup2(doc, [vector_layer GetSDFObj])];
            [writer WriteElement: element];

            // Add the text layer to the page...
            if (NO)  // 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.
                PTObj * ocgs = [doc CreateIndirectArray];
                [ocgs PushBack: [image_layer GetSDFObj]];
                [ocgs PushBack: [vector_layer GetSDFObj]];
                [ocgs PushBack: [text_layer GetSDFObj]];
                PTOCMD *text_ocmd = [PTOCMD Create: doc ocgs: ocgs vis_policy: e_ptAllOn];
                element = [builder CreateFormWithObj: CreateGroup3(doc, [text_ocmd GetSDFObj])];
            }
            else {
                element = [builder CreateFormWithObj: 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 y: 0 width: [page GetPageWidth: e_ptcrop] height: [page GetPageHeight: e_ptcrop]];
            [element SetPathFill: NO];
            [element SetPathStroke: YES];
            [[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.
            PTPDFDocViewPrefs *prefs = [doc GetViewPrefs];
            [prefs SetPageMode: e_ptUseOC];

            [doc SaveToFile: @"../../TestFiles/Output/pdf_layers.pdf" flags: e_ptlinearized];
            NSLog(@"Done.");
        }
        @catch(NSException *e)
        {
            NSLog(@"%@", e.reason);
            ret = 1;
        }

        // The following is a code snippet shows how to selectively render 
        // and export PDF layers.
        @try  
        { 
            PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilepath: @"../../TestFiles/Output/pdf_layers.pdf"];
            [doc InitSecurityHandler];

            if (![doc HasOC]) {
            NSLog(@"The document does not contain 'Optional Content'");
            }
            else {
                PTConfig *init_cfg = [doc GetOCGConfig];
                PTContext *ctx = [[PTContext alloc] initWithConfig: init_cfg];

                PTPDFDraw *pdfdraw = [[PTPDFDraw alloc] initWithDpi: 92];
                [pdfdraw SetImageSize: 1000 height: 1000 preserve_aspect_ratio: YES];
                [pdfdraw SetOCGContext: ctx]; // Render the page using the given OCG context.

                PTPage *page = [doc GetPage: 1]; // Get the first page in the document.
                [pdfdraw  Export:page filename: @"../../TestFiles/Output/pdf_layers_default.png" format: @"PNG"];

                // Disable drawing of content that is not optional (i.e. is not part of any layer).
                [ctx SetNonOCDrawing: NO];
            
                // Now render each layer in the input document to a separate image.
                PTObj * ocgs = [doc GetOCGs]; // Get the array of all OCGs in the document.
                if (ocgs != 0) {
                    unsigned long i, sz = [ocgs Size];
                    for (i=0; i<sz; ++i) {
                        PTGroup *ocg = [[PTGroup alloc] initWithOcg: [ocgs GetAt: i]];
                        [ctx ResetStates: NO];
                        [ctx SetState: ocg state: YES];
                        NSLog(@"pdf_layers_%@.png", [ocg GetName]);
                        NSString* fname = [@"../../TestFiles/Output/pdf_layers_" stringByAppendingFormat: @"%@.png", [ocg GetName]];
                        [pdfdraw  Export:page filename: fname format: @"PNG"]; 
                    }
                }

                // Now draw content that is not part of any layer...
            [ctx SetNonOCDrawing: YES];
                [ctx SetOCDrawMode: e_ptNoOC];
                [pdfdraw  Export:page filename: @"../../TestFiles/Output/pdf_layers_non_oc.png" format: @"PNG" ];
            }

            NSLog(@"Done.");
        }
        @catch(NSException *e)
        {
            NSLog(@"%@", e.reason);
            ret = 1;
        }
        [PTPDFNet Terminate: 0];
        return ret;
    }
}


// A utility function used to add new Content Groups (Layers) to the document.
PTGroup *CreateLayer(PTPDFDoc *doc, NSString *layer_name)
{
    PTGroup *grp = [PTGroup Create: doc name: layer_name];
    PTConfig *cfg = [doc GetOCGConfig];
    if (![cfg IsValid]) {
        cfg = [PTConfig Create: doc default_config: YES];
        [cfg SetName: @"Default"];
    }

    // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
    PTObj * 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
PTObj * CreateGroup1(PTPDFDoc *doc, PTObj * layer) 
{
    PTElementWriter *writer = [[PTElementWriter alloc] init];
    [writer WriterBeginWithSDFDoc: [doc GetSDFDoc] compress: YES];

    // Create an Image that can be reused in the document or on the same page.        
    PTImage *img = [PTImage Create: [doc GetSDFDoc] filename: @"../../TestFiles/peppers.jpg"];

    PTElementBuilder *builder = [[PTElementBuilder alloc] init];
    PTElement *element = [builder CreateImageWithMatrix: img mtx: [[PTMatrix2D alloc] initWithA: [img GetImageWidth]/2 b: -145 c: 20 d: [img GetImageHeight]/2 h: 200 v: 150]];
    [writer WritePlacedElement: element];

    PTGState *gstate = [element GetGState];    // use the same image (just change its matrix)
    [gstate SetTransform: 200 b: 0 c: 0 d: 300 h: 50 v: 450];
    [writer WritePlacedElement: element];

    // use the same image again (just change its matrix).
    [writer WritePlacedElement: [builder CreateImageWithCornerAndScale: img x: 300 y: 600 hscale: 200 vscale: -150]];

    PTObj * grp_obj = [writer End];    

    // Indicate that this form (content group) belongs to the given layer (OCG).
    [grp_obj PutName: @"Subtype" name: @"Form"];
    [grp_obj Put: @"OC" obj: layer];    
    [grp_obj PutRect: @"BBox" x1: 0 y1: 0 x2: 1000 y2: 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
PTObj * CreateGroup2(PTPDFDoc *doc, PTObj * layer) 
{
    PTElementWriter *writer = [[PTElementWriter alloc] init];
    [writer WriterBeginWithSDFDoc: [doc GetSDFDoc] compress: YES];

    // Create a path object in the shape of a heart.
    PTElementBuilder *builder = [[PTElementBuilder alloc] init];
    [builder PathBegin];        // start constructing the path
    [builder MoveTo: 306 y: 396];
    [builder CurveTo: 681 cy1: 771 cx2: 399.75 cy2: 864.75 x2: 306 y2: 771];
    [builder CurveTo: 212.25 cy1: 864.75 cx2: -69 cy2: 771 x2: 306 y2: 396];
    [builder ClosePath];
    PTElement *element = [builder PathEnd]; // the path geometry is now specified.

    // Set the path FILL color space and color.
    [element SetPathFill: YES];
    PTGState *gstate = [element GetGState];
    [gstate SetFillColorSpace: [PTColorSpace CreateDeviceCMYK]]; 
    [gstate SetFillColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w: 0]];  // cyan

    // Set the path STROKE color space and color.
    [element SetPathStroke: YES]; 
    [gstate SetStrokeColorSpace: [PTColorSpace CreateDeviceRGB]]; 
    [gstate SetStrokeColorWithColorPt: [[PTColorPt alloc] initWithX: 1 y: 0 z: 0 w: 0]];  // red
    [gstate SetLineWidth: 20];

    [gstate SetTransform: 0.5 b: 0 c: 0 d: 0.5 h: 280 v: 300];

    [writer WriteElement: element];

    PTObj * grp_obj = [writer End];    

    // Indicate that this form (content group) belongs to the given layer (OCG).
    [grp_obj PutName: @"Subtype" name: @"Form"];
    [grp_obj Put: @"OC" obj: layer];
    [grp_obj PutRect: @"BBox" x1: 0 y1: 0 x2: 1000 y2: 1000];     // Set the clip box for the content.

    return grp_obj;
}

// Creates some text and associate it with the text layer
PTObj * CreateGroup3(PTPDFDoc *doc, PTObj * layer) 
{
    PTElementWriter *writer = [[PTElementWriter alloc] init];
    [writer WriterBeginWithSDFDoc: [doc GetSDFDoc] compress: YES];

    // Create a path object in the shape of a heart.
    PTElementBuilder *builder = [[PTElementBuilder alloc] init];

    // Begin writing a block of text
    PTElement *element = [builder CreateTextBeginWithFont: [PTFont Create: [doc GetSDFDoc] type: e_pttimes_roman embed: NO] font_sz: 120];
    [writer WriteElement: element];

    element = [builder CreateTextRun: @"A text layer!"];

    // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
    PTMatrix2D *transform = [PTMatrix2D RotationMatrix: -45 *  (3.1415/ 180.0)];
    [transform Concat: 1 b: 0 c: 0 d: 1 h: 180 v: 100];  
    [element SetTextMatrixWithMatrix2D: transform];

    [writer WriteElement: element];
    [writer WriteElement: [builder CreateTextEnd]];

    PTObj * grp_obj = [writer End];    

    // Indicate that this form (content group) belongs to the given layer (OCG).
    [grp_obj PutName: @"Subtype" name: @"Form"];
    [grp_obj Put: @"OC" obj: layer];
    [grp_obj PutRect: @"BBox" x1: 0 y1: 0 x2: 1000 y2: 1000];     // Set the clip box for the content.

    return grp_obj;
}
```

{% endcode %}
{% endtab %}

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

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

import PDFNet
import Foundation

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

func runPDFLayersTest() -> Int {
    return autoreleasepool {
        var ret = 0
        
        
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc()
                
                // Create three layers...
                let image_layer: PTGroup = CreateLayer(doc: doc, layer_name: "Image Layer")
                let text_layer: PTGroup = CreateLayer(doc: doc, layer_name: "Text Layer")
                let vector_layer: PTGroup = CreateLayer(doc: doc, layer_name: "Vector Layer")
                
                // Start a new page ------------------------------------
                let page: PTPage = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 792))
                
                let builder: PTElementBuilder = PTElementBuilder()    // ElementBuilder is used to build new Element objects
                let writer: PTElementWriter = PTElementWriter()      // ElementWriter is used to write Elements to the page
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true, resources: nil)    // Begin writing to the page
                
                // Add new content to the page and associate it with one of the layers.
                var element: PTElement! = builder.createForm(with: CreateGroup1(doc: doc, layer: image_layer.getSDFObj()))
                writer.write(element)
                
                element = builder.createForm(with: CreateGroup2(doc: doc, layer: vector_layer.getSDFObj()))
                writer.write(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.
//                    let ocgs: PTObj = doc.createIndirectArray()
//                    ocgs.pushBack(image_layer.getSDFObj())
//                    ocgs.pushBack(vector_layer.getSDFObj())
//                    ocgs.pushBack(text_layer.getSDFObj())
//                    let text_ocmd: PTOCMD = PTOCMD.create(doc, ocgs: ocgs, vis_policy: e_ptAllOn)
//                    element = builder.createForm(with: CreateGroup3(doc: doc, layer: text_ocmd.getSDFObj()))
                }
                else {
                    element = builder.createForm(with: CreateGroup3(doc: doc, layer: text_layer.getSDFObj()))
                }
                writer.write(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, y: 0, width: page.getWidth(e_ptcrop), height: page.getHeight(e_ptcrop))
                element.setPathFill(false)
                element.setPathStroke(true)
                element.getGState().setLineWidth(40)
                writer.write(element)
                
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)
                
                // Set the default viewing preference to display 'Layer' tab.
                let prefs: PTPDFDocViewPrefs = doc.getViewPrefs()
                prefs.setPageMode(e_ptUseOC)
                
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("pdf_layers.pdf").path, flags: e_ptlinearized.rawValue)
                print("Done.")
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        // The following is a code snippet shows how to selectively render
        // and export PDF layers.
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc(filepath: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("pdf_layers.pdf").path)
                doc.initSecurityHandler()
                
                if !doc.hasOC() {
                    print("The document does not contain 'Optional Content'")
                }
                else {
                    let init_cfg: PTConfig = doc.getOCGConfig()
                    let ctx: PTContext = PTContext(config: init_cfg)
                    
                    let pdfdraw: PTPDFDraw = PTPDFDraw(dpi: 92)
                    pdfdraw.setImageSize(1000, height: 1000, preserve_aspect_ratio: true)
                    pdfdraw.setOCGContext(ctx)    // Render the page using the given OCG context.
                    
                    let page: PTPage = doc.getPage(1)  // Get the first page in the document.
                    pdfdraw.export(page, filename: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("pdf_layers_default.png").path, format: "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.
                    if let ocgs: PTObj = doc.getOCGs() {    // Get the array of all OCGs in the document.
                        var _: UInt = 0
                        let sz: UInt = ocgs.size()
                        for i in 0..<sz {
                            let ocg: PTGroup = PTGroup(ocg: ocgs.getAt(i))
                            ctx.resetStates(false)
                            ctx.setState(ocg, state: true)
                            //let fname: String = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("pdf_layers_").path + ("\(ocg.getName()).png")
                            //pdfdraw.export(page, filename: fname, format: "PNG")
                        }
                    }
                    
                    // Now draw content that is not part of any layer...
                    ctx.setNonOCDrawing(true)
                    ctx.setOCDrawMode(e_ptNoOC)
                    //pdfdraw.export(page, filename: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("pdf_layers_non_oc.png").path, format: "PNG")
                }
                print("Done.")
            }
        } catch let e as NSError {
            print("\(e)")
            ret = 1
        }
        
        return ret
    }
}

// A utility function used to add new Content Groups (Layers) to the document.
func CreateLayer(doc: PTPDFDoc, layer_name: String) -> PTGroup {
    let grp: PTGroup = PTGroup.create(doc, name: layer_name)
    var cfg: PTConfig = doc.getOCGConfig()
    if !cfg.isValid() {
        cfg = PTConfig.create(doc, default_config: true)
        cfg.setName("Default")
    }
    
    // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
    var layer_order_array: PTObj
    if let opt_layer_order_array = cfg.getOrder() {
        layer_order_array = opt_layer_order_array
    } else {
        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
func CreateGroup1(doc: PTPDFDoc, layer: PTObj) -> PTObj {
    let writer: PTElementWriter = PTElementWriter()
    writer.writerBegin(with: doc.getSDFDoc(), compress: true)
    
    // Create an Image that can be reused in the document or on the same page.
    let img: PTImage = PTImage.create(doc.getSDFDoc(), filename: Bundle.main.path(forResource: "peppers", ofType: "jpg"))
    
    let builder: PTElementBuilder = PTElementBuilder()
    let element: PTElement = builder.createImage(withMatrix: img, mtx: PTMatrix2D(a: Double(img.getWidth() / 2), b: -145, c: 20, d: Double(img.getHeight() / 2), h: 200, v: 150))
    writer.writePlacedElement(element)
    
    let gstate: PTGState = element.getGState()    // use the same image (just change its matrix)
    gstate.setTransform(200, b: 0, c: 0, d: 300, h: 50, v: 450)
    writer.writePlacedElement(element)
    
    // use the same image again (just change its matrix).
    writer.writePlacedElement(builder.createImage(withCornerAndScale: img, x: 300, y: 600, hscale: 200, vscale: -150))
    let grp_obj: PTObj = writer.end()
    
    // Indicate that this form (content group) belongs to the given layer (OCG).
    grp_obj.putName("Subtype", name: "Form")
    grp_obj.put("OC", obj: layer)
    grp_obj.putRect("BBox", x1: 0, y1: 0, x2: 1000, y2: 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
func CreateGroup2(doc: PTPDFDoc, layer: PTObj) -> PTObj {
    let writer: PTElementWriter = PTElementWriter()
    writer.writerBegin(with: doc.getSDFDoc(), compress: true)
    
    // Create a path object in the shape of a heart.
    let builder: PTElementBuilder = PTElementBuilder()
    builder.pathBegin() // start constructing the path
    builder.move(to: 306, y: 396)
    builder.curve(to: 681, cy1: 771, cx2: 399.75, cy2: 864.75, x2: 306, y2: 771)
    builder.curve(to: 212.25, cy1: 864.75, cx2: -69, cy2: 771, x2: 306, y2: 396)
    builder.closePath()
    let element: PTElement = builder.pathEnd() // the path geometry is now specified.
    
    // Set the path FILL color space and color.
    element.setPathFill(true)
    let gstate: PTGState = element.getGState()
    gstate.setFill(PTColorSpace.createDeviceCMYK())
    gstate.setFillColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))   // cyan
    
    // Set the path STROKE color space and color.
    element.setPathStroke(true)
    
    gstate.setStroke(PTColorSpace.createDeviceRGB())
    gstate.setStrokeColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0)) // red
    gstate.setLineWidth(20)
    
    gstate.setTransform(0.5, b: 0, c: 0, d: 0.5, h: 280, v: 300)
    
    writer.write(element)
    
    let grp_obj: PTObj = writer.end()
    
    // Indicate that this form (content group) belongs to the given layer (OCG).
    grp_obj.putName("Subtype", name: "Form")
    grp_obj.put("OC", obj: layer)
    grp_obj.putRect("BBox", x1: 0, y1: 0, x2: 1000, y2: 1000)  // Set the clip box for the content.
    
    return grp_obj
}

// Creates some text and associate it with the text layer
func CreateGroup3(doc: PTPDFDoc, layer: PTObj) -> PTObj {
    let writer: PTElementWriter = PTElementWriter()
    writer.writerBegin(with: doc.getSDFDoc(), compress: true)
    
    // Create a path object in the shape of a heart.
    let builder: PTElementBuilder = PTElementBuilder()
    
    // Begin writing a block of text
    var element: PTElement = builder.createTextBegin(with: PTFont.create(doc.getSDFDoc(), type: e_pttimes_roman, embed: false), font_sz: 120)
    writer.write(element)
    
    element = builder.createTextRun("A text layer!")
    
    // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
    let transform: PTMatrix2D = PTMatrix2D.rotationMatrix(-45 * (3.1415 / 180.0))
    transform.concat(1, b: 0, c: 0, d: 1, h: 180, v: 100)
    element.setTextMatrix(with: transform)
    
    writer.write(element)
    writer.write(builder.createTextEnd())
    
    let grp_obj: PTObj = writer.end()
    
    // Indicate that this form (content group) belongs to the given layer (OCG).
    grp_obj.putName("Subtype", name: "Form")
    grp_obj.put("OC", obj: layer)
    grp_obj.putRect("BBox", x1: 0, y1: 0, x2: 1000, y2: 1000)  // Set the clip box for the content.
    
    return grp_obj
}
```

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


---

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

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

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

```
GET https://docs.apryse.com/ios/get-started/samples/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.
