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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales