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 Xamarin SDK.
1//
2// Copyright (c) 2001-2021 by PDFTron Systems 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>
39using NUnit.Framework;
40
41namespace MiscellaneousSamples
42{
43	[TestFixture]
44	public class PDFLayersTest
45	{			
46		
47		// Relative path to the folder containing test files.
48		const string input_path =  "TestFiles/";
49
50		[Test]
51		public static void Sample()
52		{
53
54			try
55			{
56				using (PDFDoc doc = new PDFDoc())
57				using (ElementBuilder builder = new ElementBuilder()) // ElementBuilder is used to build new Element objects
58				using (ElementWriter writer = new ElementWriter()) // ElementWriter is used to write Elements to the page
59				{
60					// Create three layers...
61					Group image_layer = CreateLayer(doc, "Image Layer");
62					Group text_layer = CreateLayer(doc, "Text Layer");
63					Group vector_layer = CreateLayer(doc, "Vector Layer");
64
65					// Start a new page ------------------------------------
66					Page page = doc.PageCreate();
67
68					writer.Begin(page);	// begin writing to this page
69
70					// Add new content to the page and associate it with one of the layers.
71					Element element = builder.CreateForm(CreateGroup1(doc, image_layer.GetSDFObj()));
72					writer.WriteElement(element);
73
74					element = builder.CreateForm(CreateGroup2(doc, vector_layer.GetSDFObj()));
75					writer.WriteElement(element);
76
77					// Add the text layer to the page...
78					bool enableOCMD = false; // set to 'true' to enable 'ocmd' example.
79					if (enableOCMD)  
80					{
81						// A bit more advanced example of how to create an OCMD text layer that 
82						// is visible only if text, image and path layers are all 'ON'.
83						// An example of how to set 'Visibility Policy' in OCMD.
84						Obj ocgs = doc.CreateIndirectArray();
85						ocgs.PushBack(image_layer.GetSDFObj());
86						ocgs.PushBack(vector_layer.GetSDFObj());
87						ocgs.PushBack(text_layer.GetSDFObj());
88						OCMD text_ocmd = OCMD.Create(doc, ocgs, OCMD.VisibilityPolicyType.e_AllOn);
89						element = builder.CreateForm(CreateGroup3(doc, text_ocmd.GetSDFObj()));
90					}
91					else {
92						element = builder.CreateForm(CreateGroup3(doc, text_layer.GetSDFObj()));
93					}
94					writer.WriteElement(element);
95
96					// Add some content to the page that does not belong to any layer...
97					// In this case this is a rectangle representing the page border.
98					element = builder.CreateRect(0, 0, page.GetPageWidth(), page.GetPageHeight());
99					element.SetPathFill(false);
100					element.SetPathStroke(true);
101					element.GetGState().SetLineWidth(40);
102					writer.WriteElement(element);
103
104					writer.End();  // save changes to the current page
105					doc.PagePushBack(page);
106
107					// Set the default viewing preference to display 'Layer' tab.
108					PDFDocViewPrefs prefs = doc.GetViewPrefs();
109					prefs.SetPageMode(PDFDocViewPrefs.PageMode.e_UseOC);
110
111					doc.Save(Utils.CreateExternalFile("pdf_layers.pdf"), SDFDoc.SaveOptions.e_linearized);
112					Console.WriteLine("Done.");
113				}
114			}
115			catch (PDFNetException e)
116			{
117				Console.WriteLine(e.Message);
118				Assert.True(false);
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(Utils.CreateExternalFile("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, Utils.CreateExternalFile("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, Utils.CreateExternalFile(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, Utils.CreateExternalFile("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				Assert.True(false);
179			}
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(), (Utils.GetAssetTempFile(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 DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales