More languages
Some test text!
More languages
Sample Obj-C code to use PDFTron 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 Obj-C PDF Library.
Get Started Samples DownloadTo run this sample, get started with a free trial of Apryse SDK.
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2023 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;
}