PDF Layers (OCG) - Add, Show, Hide - Go Sample Code

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.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2021 by PDFTron Systems Inc. All Rights Reserved.
3// Consult LICENSE.txt regarding license information.
4//---------------------------------------------------------------------------------------
5
6package main
7import (
8 "fmt"
9 . "pdftron"
10)
11
12import "pdftron/Samples/LicenseKey/GO"
13
14//-----------------------------------------------------------------------------------
15// This sample demonstrates how to create layers in PDF.
16// The sample also shows how to extract and render PDF layers in documents
17// that contain optional content groups (OCGs)
18//
19// With the introduction of PDF version 1.5 came the concept of Layers.
20// Layers, or as they are more formally known Optional Content Groups (OCGs),
21// refer to sections of content in a PDF document that can be selectively
22// viewed or hidden by document authors or consumers. This capability is useful
23// in CAD drawings, layered artwork, maps, multi-language documents etc.
24//
25// Notes:
26// ---------------------------------------
27// - This sample is using CreateLayer() utility method to create new OCGs.
28// CreateLayer() is relatively basic, however it can be extended to set
29// other optional entries in the 'OCG' and 'OCProperties' dictionary. For
30// a complete listing of possible entries in OC dictionary please refer to
31// section 4.10 'Optional Content' in the PDF Reference Manual.
32// - The sample is grouping all layer content into separate Form XObjects.
33// Although using PDFNet is is also possible to specify Optional Content in
34// Content Streams (Section 4.10.2 in PDF Reference), Optional Content in
35// XObjects results in PDFs that are cleaner, less-error prone, and faster
36// to process.
37//-----------------------------------------------------------------------------------
38
39// Relative path to the folder containing the test files.
40var inputPath = "../../TestFiles/"
41var outputPath = "../../TestFiles/Output/"
42
43// A utility function used to add new Content Groups (Layers) to the document.
44func CreateLayer(doc PDFDoc, layerName string) Group{
45 grp := GroupCreate(doc, layerName)
46 cfg := doc.GetOCGConfig()
47 if ! cfg.IsValid(){
48 cfg = ConfigCreate(doc, true)
49 cfg.SetName("Default")
50 }
51 // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
52 layerOrderArray := cfg.GetOrder()
53 if layerOrderArray.GetMp_obj().Swigcptr() == 0{
54 layerOrderArray = doc.CreateIndirectArray()
55 cfg.SetOrder(layerOrderArray)
56 }
57 layerOrderArray.PushBack(grp.GetSDFObj())
58 return grp
59}
60// Creates some content (3 images) and associate them with the image layer
61func CreateGroup1(doc PDFDoc, layer Obj) Obj{
62 writer := NewElementWriter()
63 writer.Begin(doc.GetSDFDoc())
64
65 // Create an Image that can be reused in the document or on the same page.
66 img := ImageCreate(doc.GetSDFDoc(), inputPath + "peppers.jpg")
67 builder := NewElementBuilder()
68 element := builder.CreateImage(img, NewMatrix2D(float64(img.GetImageWidth()/2), -145.0, 20.0, float64(img.GetImageHeight()/2), 200.0, 150.0))
69 writer.WritePlacedElement(element)
70
71 gstate := element.GetGState() // use the same image (just change its matrix)
72 gstate.SetTransform(200.0, 0.0, 0.0, 300.0, 50.0, 450.0)
73 writer.WritePlacedElement(element)
74
75 // use the same image again (just change its matrix).
76 writer.WritePlacedElement(builder.CreateImage(img, 300.0, 600.0, 200.0, -150.0))
77
78 grpObj := writer.End()
79
80 // Indicate that this form (content group) belongs to the given layer (OCG).
81 grpObj.PutName("Subtype","Form")
82 grpObj.Put("OC", layer)
83 grpObj.PutRect("BBox", 0.0, 0.0, 1000.0, 1000.0) // Set the clip box for the content.
84
85 return grpObj
86}
87// Creates some content (a path in the shape of a heart) and associate it with the vector layer
88func CreateGroup2(doc PDFDoc, layer Obj) Obj{
89 writer := NewElementWriter()
90 writer.Begin(doc.GetSDFDoc())
91
92 // Create a path object in the shape of a heart
93 builder := NewElementBuilder()
94 builder.PathBegin() // start constructing the path
95 builder.MoveTo(306.0, 396.0)
96 builder.CurveTo(681.0, 771.0, 399.75, 864.75, 306.0, 771.0)
97 builder.CurveTo(212.25, 864.75, -69, 771, 306.0, 396.0)
98 builder.ClosePath()
99 element := builder.PathEnd() // the path geometry is now specified.
100
101 // Set the path FILL color space and color.
102 element.SetPathFill(true)
103 gstate := element.GetGState()
104 gstate.SetFillColorSpace(ColorSpaceCreateDeviceCMYK())
105 gstate.SetFillColor(NewColorPt(1.0, 0.0, 0.0, 0.0)) // cyan
106
107 // Set the path STROKE color space and color
108 element.SetPathStroke(true)
109 gstate.SetStrokeColorSpace(ColorSpaceCreateDeviceRGB())
110 gstate.SetStrokeColor(NewColorPt(1.0, 0.0, 0.0)) // red
111 gstate.SetLineWidth(20)
112
113 gstate.SetTransform(0.5, 0.0, 0.0, 0.5, 280.0, 300.0)
114
115 writer.WriteElement(element)
116
117 grpObj := writer.End()
118
119 // Indicate that this form (content group) belongs to the given layer (OCG).
120 grpObj.PutName("Subtype","Form")
121 grpObj.Put("OC", layer)
122 grpObj.PutRect("BBox", 0.0, 0.0, 1000.0, 1000.0) // Set the clip box for the content.
123
124 return grpObj
125}
126// Creates some text and associate it with the text layer
127func CreateGroup3(doc PDFDoc, layer Obj) Obj{
128 writer := NewElementWriter()
129 writer.Begin(doc.GetSDFDoc())
130
131 // Create a path object in the shape of a heart.
132 builder := NewElementBuilder()
133
134 // Begin writing a block of text
135 element := builder.CreateTextBegin(FontCreate(doc.GetSDFDoc(), FontE_times_roman), 120.0)
136 writer.WriteElement(element)
137
138 element = builder.CreateTextRun("A text layer!")
139
140 // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
141 transform := Matrix2DRotationMatrix(-45 * (3.1415/ 180.0))
142 transform.Concat(1.0, 0.0, 0.0, 1.0, 180.0, 100.0)
143 element.SetTextMatrix(transform)
144
145 writer.WriteElement(element)
146 writer.WriteElement(builder.CreateTextEnd())
147
148 grpObj := writer.End()
149
150 // Indicate that this form (content group) belongs to the given layer (OCG).
151 grpObj.PutName("Subtype","Form")
152 grpObj.Put("OC", layer)
153 grpObj.PutRect("BBox", 0.0, 0.0, 1000.0, 1000.0) // Set the clip box for the content.
154
155 return grpObj
156}
157
158func main(){
159 PDFNetInitialize(PDFTronLicense.Key)
160
161 // Create three layers...
162 doc := NewPDFDoc()
163 imageLayer := CreateLayer(doc, "Image Layer")
164 textLayer := CreateLayer(doc, "Text Layer")
165 vectorLayer := CreateLayer(doc, "Vector Layer")
166
167 // Start a new page ------------------------------------
168 page := doc.PageCreate()
169
170 builder := NewElementBuilder() // NewElementBuilder is used to build new Element objects
171 writer := NewElementWriter() // NewElementWriter is used to write Elements to the page
172 writer.Begin(page) // Begin writting to the page
173
174 // Add new content to the page and associate it with one of the layers.
175 element := builder.CreateForm(CreateGroup1(doc, imageLayer.GetSDFObj()))
176 writer.WriteElement(element)
177
178 element = builder.CreateForm(CreateGroup2(doc, vectorLayer.GetSDFObj()))
179 writer.WriteElement(element)
180
181 // Add the text layer to the page...
182 if false{ // set to true to enable 'ocmd' example.
183 // A bit more advanced example of how to create an OCMD text layer that
184 // is visible only if text, image and path layers are all 'ON'.
185 // An example of how to set 'Visibility Policy' in OCMD.
186 ocgs := doc.CreateIndirectArray()
187 ocgs.PushBack(imageLayer.GetSDFObj())
188 ocgs.PushBack(vectorLayer.GetSDFObj())
189 ocgs.PushBack(textLayer.GetSDFObj())
190 text_ocmd := OCMDCreate(doc, ocgs, OCMDE_AllOn)
191 element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()))
192 }else{
193 element = builder.CreateForm(CreateGroup3(doc, textLayer.GetSDFObj()))
194 }
195 writer.WriteElement(element)
196
197 // Add some content to the page that does not belong to any layer...
198 // In this case this is a rectangle representing the page border.
199 element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight())
200 element.SetPathFill(false)
201 element.SetPathStroke(true)
202 element.GetGState().SetLineWidth(40)
203 writer.WriteElement(element)
204
205 writer.End() // save changes to the current page
206 doc.PagePushBack(page)
207 // Set the default viewing preference to display 'Layer' tab
208 prefs := doc.GetViewPrefs()
209 prefs.SetPageMode(PDFDocViewPrefsE_UseOC)
210
211 doc.Save(outputPath + "pdf_layers.pdf", uint(SDFDocE_linearized))
212 doc.Close()
213 fmt.Println("Done.")
214
215 // The following is a code snippet shows how to selectively render
216 // and export PDF layers.
217
218 doc = NewPDFDoc(outputPath + "pdf_layers.pdf")
219 doc.InitSecurityHandler()
220
221 if ! doc.HasOC(){
222 fmt.Println("The document does not contain 'Optional Content'")
223 }else{
224 init_cfg := doc.GetOCGConfig()
225 ctx := NewContext(init_cfg)
226
227 pdfdraw := NewPDFDraw()
228 pdfdraw.SetImageSize(1000, 1000)
229 pdfdraw.SetOCGContext(ctx) // Render the page using the given OCG context.
230
231 page = doc.GetPage(1) // Get the first page in the document.
232 pdfdraw.Export(page, outputPath + "pdf_layers_default.png")
233
234 // Disable drawing of content that is not optional (i.e. is not part of any layer).
235 ctx.SetNonOCDrawing(false)
236
237 // Now render each layer in the input document to a separate image.
238 ocgs := doc.GetOCGs() // Get the array of all OCGs in the document.
239 if ocgs != nil{
240 sz := ocgs.Size()
241 i := int64(0)
242 for i < sz{
243 ocg := NewGroup(ocgs.GetAt(i))
244 ctx.ResetStates(false)
245 ctx.SetState(ocg, true)
246 fname := "pdf_layers_" + ocg.GetName() + ".png"
247 fmt.Println(fname)
248 pdfdraw.Export(page, outputPath + fname)
249 i = i + 1
250 }
251 }
252 // Now draw content that is not part of any layer...
253 ctx.SetNonOCDrawing(true)
254 ctx.SetOCDrawMode(ContextE_NoOC)
255 pdfdraw.Export(page, outputPath + "pdf_layers_non_oc.png")
256
257 doc.Close()
258 PDFNetTerminate()
259 fmt.Println("Done.")
260 }
261}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales