PDFLayers

Sample 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 Server SDK.

1//
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3//
4
5using System;
6using pdftron;
7using pdftron.Common;
8using pdftron.Filters;
9using pdftron.SDF;
10using pdftron.PDF;
11using pdftron.PDF.OCG;
12
13/// <summary>
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/// </summary>
39namespace PDFLayersTestCS
40{
41 class Class1
42 {
43 private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
44 static Class1() {}
45
46 // Relative path to the folder containing test files.
47 static string input_path = "../../../../TestFiles/";
48 static string output_path = "../../../../TestFiles/Output/";
49
50 [STAThread]
51 static void Main(string[] args)
52 {
53 PDFNet.Initialize(PDFTronLicense.Key);
54
55 try
56 {
57 using (PDFDoc doc = new PDFDoc())
58 using (ElementBuilder builder = new ElementBuilder()) // ElementBuilder is used to build new Element objects
59 using (ElementWriter writer = new ElementWriter()) // ElementWriter is used to write Elements to the page
60 {
61 // Create three layers...
62 Group image_layer = CreateLayer(doc, "Image Layer");
63 Group text_layer = CreateLayer(doc, "Text Layer");
64 Group vector_layer = CreateLayer(doc, "Vector Layer");
65
66 // Start a new page ------------------------------------
67 Page page = doc.PageCreate();
68
69 writer.Begin(page); // begin writing to this page
70
71 // Add new content to the page and associate it with one of the layers.
72 Element element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()));
73 writer.WriteElement(element);
74
75 element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()));
76 writer.WriteElement(element);
77
78 // Add the text layer to the page...
79 bool enableOCMD = false; // set to 'true' to enable 'ocmd' example.
80 if (enableOCMD)
81 {
82 // A bit more advanced example of how to create an OCMD text layer that
83 // is visible only if text, image and path layers are all 'ON'.
84 // An example of how to set 'Visibility Policy' in OCMD.
85 Obj ocgs = doc.CreateIndirectArray();
86 ocgs.PushBack(image_layer.GetSDFObj());
87 ocgs.PushBack(vector_layer.GetSDFObj());
88 ocgs.PushBack(text_layer.GetSDFObj());
89 OCMD text_ocmd = OCMD.Create(doc, ocgs, OCMD.VisibilityPolicyType.e_AllOn);
90 element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()));
91 }
92 else {
93 element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()));
94 }
95 writer.WriteElement(element);
96
97 // Add some content to the page that does not belong to any layer...
98 // In this case this is a rectangle representing the page border.
99 element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight());
100 element.SetPathFill(false);
101 element.SetPathStroke(true);
102 element.GetGState().SetLineWidth(40);
103 writer.WriteElement(element);
104
105 writer.End(); // save changes to the current page
106 doc.PagePushBack(page);
107
108 // Set the default viewing preference to display 'Layer' tab.
109 PDFDocViewPrefs prefs = doc.GetViewPrefs();
110 prefs.SetPageMode(PDFDocViewPrefs.PageMode.e_UseOC);
111
112 doc.Save(output_path + "pdf_layers.pdf", SDFDoc.SaveOptions.e_linearized);
113 Console.WriteLine("Done.");
114 }
115 }
116 catch (PDFNetException e)
117 {
118 Console.WriteLine(e.Message);
119 }
120
121 // The following is a code snippet shows how to selectively render
122 // and export PDF layers.
123 try
124 {
125 using (PDFDoc doc = new PDFDoc(output_path + "pdf_layers.pdf"))
126 {
127 doc.InitSecurityHandler();
128
129 if (!doc.HasOC())
130 {
131 Console.WriteLine("The document does not contain 'Optional Content'");
132 }
133 else
134 {
135 Config init_cfg = doc.GetOCGConfig();
136 Context ctx = new Context(init_cfg);
137
138 using (PDFDraw pdfdraw = new PDFDraw())
139 {
140 pdfdraw.SetImageSize(1000, 1000);
141 pdfdraw.SetOCGContext(ctx); // Render the page using the given OCG context.
142
143 Page page = doc.GetPage(1); // Get the first page in the document.
144 pdfdraw.Export(page, output_path + "pdf_layers_default.png");
145
146 // Disable drawing of content that is not optional (i.e. is not part of any layer).
147 ctx.SetNonOCDrawing(false);
148
149 // Now render each layer in the input document to a separate image.
150 Obj ocgs = doc.GetOCGs(); // Get the array of all OCGs in the document.
151 if (ocgs != null)
152 {
153 int i, sz = ocgs.Size();
154 for (i=0; i<sz; ++i)
155 {
156 Group ocg = new Group(ocgs.GetAt(i));
157 ctx.ResetStates(false);
158 ctx.SetState(ocg, true);
159 string fname = "pdf_layers_" + ocg.GetName() + ".png";
160 Console.WriteLine(fname);
161 pdfdraw.Export(page, output_path + fname);
162 }
163 }
164
165 // Now draw content that is not part of any layer...
166 ctx.SetNonOCDrawing(true);
167 ctx.SetOCDrawMode(Context.OCDrawMode.e_NoOC);
168 pdfdraw.Export(page, output_path + "pdf_layers_non_oc.png");
169
170 Console.WriteLine("Done.");
171 }
172 }
173 }
174 }
175 catch (PDFNetException e)
176 {
177 Console.WriteLine(e.Message);
178 }
179 PDFNet.Terminate();
180
181 }
182
183 // A utility function used to add new Content Groups (Layers) to the document.
184 static Group CreateLayer(PDFDoc doc, String layer_name)
185 {
186 Group grp = Group.Create(doc, layer_name);
187 Config cfg = doc.GetOCGConfig();
188 if (cfg == null)
189 {
190 cfg = Config.Create(doc, true);
191 cfg.SetName("Default");
192 }
193
194 // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
195 Obj layer_order_array = cfg.GetOrder();
196 if (layer_order_array == null)
197 {
198 layer_order_array = doc.CreateIndirectArray();
199 cfg.SetOrder(layer_order_array);
200 }
201 layer_order_array.PushBack(grp.GetSDFObj());
202
203 return grp;
204 }
205
206 // Creates some content (3 images) and associate them with the image layer
207 static Obj CreateGroup1(PDFDoc doc, Obj layer)
208 {
209 using (ElementWriter writer = new ElementWriter())
210 using (ElementBuilder builder = new ElementBuilder())
211 {
212 writer.Begin(doc);
213
214 // Create an Image that can be reused in the document or on the same page.
215 Image img = Image.Create(doc.GetSDFDoc(), (input_path + "peppers.jpg"));
216
217 Element element = builder.CreateImage(img, new Matrix2D(img.GetImageWidth()/2, -145, 20, img.GetImageHeight()/2, 200, 150));
218 writer.WritePlacedElement(element);
219
220 GState gstate = element.GetGState(); // use the same image (just change its matrix)
221 gstate.SetTransform(200, 0, 0, 300, 50, 450);
222 writer.WritePlacedElement(element);
223
224 // use the same image again (just change its matrix).
225 writer.WritePlacedElement(builder.CreateImage(img, 300, 600, 200, -150));
226
227 Obj grp_obj = writer.End();
228
229 // Indicate that this form (content group) belongs to the given layer (OCG).
230 grp_obj.PutName("Subtype","Form");
231 grp_obj.Put("OC", layer);
232 grp_obj.PutRect("BBox", 0, 0, 1000, 1000); // Set the clip box for the content.
233
234 // As an example of further configuration, set the image layer to
235 // be visible on screen, but not visible when printed...
236
237 // The AS entry is an auto state array consisting of one or more usage application
238 // dictionaries that specify how conforming readers shall automatically set the
239 // state of optional content groups based on external factors.
240 Obj cfg = doc.GetOCGConfig().GetSDFObj();
241 Obj auto_state = cfg.FindObj("AS");
242 if (auto_state == null) auto_state = cfg.PutArray("AS");
243 Obj print_state = auto_state.PushBackDict();
244 print_state.PutArray("Category").PushBackName("Print");
245 print_state.PutName("Event", "Print");
246 print_state.PutArray("OCGs").PushBack(layer);
247
248 Obj layer_usage = layer.PutDict("Usage");
249
250 Obj view_setting = layer_usage.PutDict("View");
251 view_setting.PutName("ViewState", "ON");
252
253 Obj print_setting = layer_usage.PutDict("Print");
254 print_setting.PutName("PrintState", "OFF");
255
256 return grp_obj;
257 }
258 }
259
260 // Creates some content (a path in the shape of a heart) and associate it with the vector layer
261 static Obj CreateGroup2(PDFDoc doc, Obj layer)
262 {
263 using (ElementWriter writer = new ElementWriter())
264 using (ElementBuilder builder = new ElementBuilder())
265 {
266 writer.Begin(doc);
267
268 // Create a path object in the shape of a heart.
269 builder.PathBegin(); // start constructing the path
270 builder.MoveTo(306, 396);
271 builder.CurveTo(681, 771, 399.75, 864.75, 306, 771);
272 builder.CurveTo(212.25, 864.75, -69, 771, 306, 396);
273 builder.ClosePath();
274 Element element = builder.PathEnd(); // the path geometry is now specified.
275
276 // Set the path FILL color space and color.
277 element.SetPathFill(true);
278 GState gstate = element.GetGState();
279 gstate.SetFillColorSpace(ColorSpace.CreateDeviceCMYK());
280 gstate.SetFillColor(new ColorPt(1, 0, 0, 0)); // cyan
281
282 // Set the path STROKE color space and color.
283 element.SetPathStroke(true);
284 gstate.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB());
285 gstate.SetStrokeColor(new ColorPt(1, 0, 0)); // red
286 gstate.SetLineWidth(20);
287
288 gstate.SetTransform(0.5, 0, 0, 0.5, 280, 300);
289
290 writer.WriteElement(element);
291
292 Obj grp_obj = writer.End();
293
294
295 // Indicate that this form (content group) belongs to the given layer (OCG).
296 grp_obj.PutName("Subtype","Form");
297 grp_obj.Put("OC", layer);
298 grp_obj.PutRect("BBox", 0, 0, 1000, 1000); // Set the clip box for the content.
299
300 return grp_obj;
301 }
302 }
303
304 // Creates some text and associate it with the text layer
305 static Obj CreateGroup3(PDFDoc doc, Obj layer)
306 {
307 using (ElementWriter writer = new ElementWriter())
308 using (ElementBuilder builder = new ElementBuilder())
309 {
310 writer.Begin(doc);
311
312 // Begin writing a block of text
313 Element element = builder.CreateTextBegin(Font.Create(doc, Font.StandardType1Font.e_times_roman), 120);
314 writer.WriteElement(element);
315
316 element = builder.CreateTextRun("A text layer!");
317
318 // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
319 Matrix2D transform = Matrix2D.RotationMatrix(-45 * (3.1415/ 180.0));
320 transform.Concat(1, 0, 0, 1, 180, 100);
321 element.SetTextMatrix(transform);
322
323 writer.WriteElement(element);
324 writer.WriteElement(builder.CreateTextEnd());
325
326 Obj grp_obj = writer.End();
327
328 // Indicate that this form (content group) belongs to the given layer (OCG).
329 grp_obj.PutName("Subtype","Form");
330 grp_obj.Put("OC", layer);
331 grp_obj.PutRect("BBox", 0, 0, 1000, 1000); // Set the clip box for the content.
332
333 return grp_obj;
334 }
335 }
336 }
337}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales