PDF Layers (OCG) - Add, Show, Hide - Python 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-2023 by Apryse Software Inc. All Rights Reserved.
3# Consult LICENSE.txt regarding license information.
4#---------------------------------------------------------------------------------------
5
6import site
7site.addsitedir("../../../PDFNetC/Lib")
8import sys
9from PDFNetPython import *
10
11sys.path.append("../../LicenseKey/PYTHON")
12from LicenseKey import *
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.
40input_path = "../../TestFiles/"
41output_path = "../../TestFiles/Output/"
42
43# A utility function used to add new Content Groups (Layers) to the document.
44def CreateLayer(doc, layer_name):
45 grp = Group.Create(doc, layer_name)
46 cfg = doc.GetOCGConfig()
47 if not cfg.IsValid():
48 cfg = Config.Create(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 layer_order_array = cfg.GetOrder()
53 if layer_order_array is None:
54 layer_order_array = doc.CreateIndirectArray()
55 cfg.SetOrder(layer_order_array)
56 layer_order_array.PushBack(grp.GetSDFObj())
57 return grp
58
59# Creates some content (3 images) and associate them with the image layer
60def CreateGroup1(doc, layer):
61 writer = ElementWriter()
62 writer.Begin(doc.GetSDFDoc())
63
64 # Create an Image that can be reused in the document or on the same page.
65 img = Image.Create(doc.GetSDFDoc(), input_path + "peppers.jpg")
66 builder = ElementBuilder()
67 element = builder.CreateImage(img, Matrix2D(img.GetImageWidth()/2, -145, 20, img.GetImageHeight()/2, 200, 150))
68 writer.WritePlacedElement(element)
69
70 gstate = element.GetGState() # use the same image (just change its matrix)
71 gstate.SetTransform(200, 0, 0, 300, 50, 450)
72 writer.WritePlacedElement(element)
73
74 # use the same image again (just change its matrix).
75 writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150))
76
77 grp_obj = writer.End()
78
79 # Indicate that this form (content group) belongs to the given layer (OCG).
80 grp_obj.PutName("Subtype","Form")
81 grp_obj.Put("OC", layer)
82 grp_obj.PutRect("BBox", 0, 0, 1000, 1000) # Set the clip box for the content.
83
84 return grp_obj
85
86# Creates some content (a path in the shape of a heart) and associate it with the vector layer
87def CreateGroup2(doc, layer):
88 writer = ElementWriter()
89 writer.Begin(doc.GetSDFDoc())
90
91 # Create a path object in the shape of a heart
92 builder = ElementBuilder()
93 builder.PathBegin() # start constructing the path
94 builder.MoveTo(306, 396)
95 builder.CurveTo(681, 771, 399.75, 864.75, 306, 771)
96 builder.CurveTo(212.25, 864.75, -69, 771, 306, 396)
97 builder.ClosePath()
98 element = builder.PathEnd() # the path geometry is now specified.
99
100 # Set the path FILL color space and color.
101 element.SetPathFill(True)
102 gstate = element.GetGState()
103 gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK())
104 gstate.SetFillColor(ColorPt(1, 0, 0, 0)) # cyan
105
106 # Set the path STROKE color space and color
107 element.SetPathStroke(True)
108 gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB())
109 gstate.SetStrokeColor(ColorPt(1, 0, 0)) # red
110 gstate.SetLineWidth(20)
111
112 gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300)
113
114 writer.WriteElement(element)
115
116 grp_obj = writer.End()
117
118 # Indicate that this form (content group) belongs to the given layer (OCG).
119 grp_obj.PutName("Subtype","Form")
120 grp_obj.Put("OC", layer)
121 grp_obj.PutRect("BBox", 0, 0, 1000, 1000) # Set the clip box for the content.
122
123 return grp_obj
124
125# Creates some text and associate it with the text layer
126def CreateGroup3(doc, layer):
127 writer = ElementWriter()
128 writer.Begin(doc.GetSDFDoc())
129
130 # Create a path object in the shape of a heart.
131 builder = ElementBuilder()
132
133 # Begin writing a block of text
134 element = builder.CreateTextBegin(Font.Create(doc.GetSDFDoc(), Font.e_times_roman), 120)
135 writer.WriteElement(element)
136
137 element = builder.CreateTextRun("A text layer!")
138
139 # Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
140 transform = Matrix2D.RotationMatrix(-45 * (3.1415/ 180.0))
141 transform.Concat(1, 0, 0, 1, 180, 100)
142 element.SetTextMatrix(transform)
143
144 writer.WriteElement(element)
145 writer.WriteElement(builder.CreateTextEnd())
146
147 grp_obj = writer.End()
148
149 # Indicate that this form (content group) belongs to the given layer (OCG).
150 grp_obj.PutName("Subtype","Form")
151 grp_obj.Put("OC", layer)
152 grp_obj.PutRect("BBox", 0, 0, 1000, 1000) # Set the clip box for the content.
153
154 return grp_obj
155
156def main():
157 PDFNet.Initialize(LicenseKey)
158
159 # Create three layers...
160 doc = PDFDoc()
161 image_layer = CreateLayer(doc, "Image Layer")
162 text_layer = CreateLayer(doc, "Text Layer")
163 vector_layer = CreateLayer(doc, "Vector Layer")
164
165 # Start a new page ------------------------------------
166 page = doc.PageCreate()
167
168 builder = ElementBuilder() # ElementBuilder is used to build new Element objects
169 writer = ElementWriter() # ElementWriter is used to write Elements to the page
170 writer.Begin(page) # Begin writting to the page
171
172 # Add new content to the page and associate it with one of the layers.
173 element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()))
174 writer.WriteElement(element)
175
176 element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()))
177 writer.WriteElement(element)
178
179 # Add the text layer to the page...
180 if False: # set to true to enable 'ocmd' example.
181 # A bit more advanced example of how to create an OCMD text layer that
182 # is visible only if text, image and path layers are all 'ON'.
183 # An example of how to set 'Visibility Policy' in OCMD.
184 ocgs = doc.CreateIndirectArray()
185 ocgs.PushBack(image_layer.GetSDFObj())
186 ocgs.PushBack(vector_layer.GetSDFObj())
187 ocgs.PushBack(text_layer.GetSDFObj())
188 text_ocmd = OCMD.Create(doc, ocgs, OCMD.e_AllOn)
189 element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()))
190 else:
191 element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()))
192 writer.WriteElement(element)
193
194 # Add some content to the page that does not belong to any layer...
195 # In this case this is a rectangle representing the page border.
196 element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight())
197 element.SetPathFill(False)
198 element.SetPathStroke(True)
199 element.GetGState().SetLineWidth(40)
200 writer.WriteElement(element)
201
202 writer.End() # save changes to the current page
203 doc.PagePushBack(page)
204 # Set the default viewing preference to display 'Layer' tab
205 prefs = doc.GetViewPrefs()
206 prefs.SetPageMode(PDFDocViewPrefs.e_UseOC)
207
208 doc.Save(output_path + "pdf_layers.pdf", SDFDoc.e_linearized)
209 doc.Close()
210 print("Done.")
211
212 # The following is a code snippet shows how to selectively render
213 # and export PDF layers.
214
215 doc = PDFDoc(output_path + "pdf_layers.pdf")
216 doc.InitSecurityHandler()
217
218 if not doc.HasOC():
219 print("The document does not contain 'Optional Content'")
220 else:
221 init_cfg = doc.GetOCGConfig()
222 ctx = Context(init_cfg)
223
224 pdfdraw = PDFDraw()
225 pdfdraw.SetImageSize(1000, 1000)
226 pdfdraw.SetOCGContext(ctx) # Render the page using the given OCG context.
227
228 page = doc.GetPage(1) # Get the first page in the document.
229 pdfdraw.Export(page, output_path + "pdf_layers_default.png")
230
231 # Disable drawing of content that is not optional (i.e. is not part of any layer).
232 ctx.SetNonOCDrawing(False)
233
234 # Now render each layer in the input document to a separate image.
235 ocgs = doc.GetOCGs() # Get the array of all OCGs in the document.
236 if ocgs is not None:
237 sz = ocgs.Size()
238 i = 0
239 while i < sz:
240 ocg = Group(ocgs.GetAt(i))
241 ctx.ResetStates(False)
242 ctx.SetState(ocg, True)
243 fname = "pdf_layers_" + ocg.GetName() + ".png"
244 print(fname)
245 pdfdraw.Export(page, output_path + fname)
246 i = i + 1
247 # Now draw content that is not part of any layer...
248 ctx.SetNonOCDrawing(True)
249 ctx.SetOCDrawMode(Context.e_NoOC)
250 pdfdraw.Export(page, output_path + "pdf_layers_non_oc.png")
251
252 doc.Close()
253 PDFNet.Terminate()
254 print("Done.")
255
256if __name__ == '__main__':
257 main()

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales