PDF Layers (OCG) - Add, Show, Hide - Java 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-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6import com.pdftron.common.Matrix2D;
7import com.pdftron.common.PDFNetException;
8import com.pdftron.pdf.*;
9import com.pdftron.pdf.ocg.*;
10import com.pdftron.sdf.*;
11
12
13//-----------------------------------------------------------------------------------
14// This sample demonstrates how to create layers in PDF.
15// The sample also shows how to extract and render PDF layers in documents
16// that contain optional content groups (OCGs)
17//
18// With the introduction of PDF version 1.5 came the concept of Layers.
19// Layers, or as they are more formally known Optional Content Groups (OCGs),
20// refer to sections of content in a PDF document that can be selectively
21// viewed or hidden by document authors or consumers. This capability is useful
22// in CAD drawings, layered artwork, maps, multi-language documents etc.
23//
24// Couple of notes regarding this sample:
25// ---------------------------------------
26// - This sample is using CreateLayer() utility method to create new OCGs.
27// CreateLayer() is relatively basic, however it can be extended to set
28// other optional entries in the 'OCG' and 'OCProperties' dictionary. For
29// a complete listing of possible entries in OC dictionary please refer to
30// section 4.10 'Optional Content' in the PDF Reference Manual.
31// - The sample is grouping all layer content into separate Form XObjects.
32// Although using PDFNet is is also possible to specify Optional Content in
33// Content Streams (Section 4.10.2 in PDF Reference), Optional Content in
34// XObjects results in PDFs that are cleaner, less-error prone, and faster
35// to process.
36//-----------------------------------------------------------------------------------
37public class PDFLayersTest {
38 // Relative path to the folder containing test files.
39 static String input_path = "../../TestFiles/";
40 static String output_path = "../../TestFiles/Output/";
41
42 public static void main(String[] args) {
43 PDFNet.initialize(PDFTronLicense.Key());
44
45 try (PDFDoc doc = new PDFDoc()) {
46 // Create three layers...
47 Group image_layer = createLayer(doc, "Image Layer");
48 Group text_layer = createLayer(doc, "Text Layer");
49 Group vector_layer = createLayer(doc, "Vector Layer");
50
51 // Start a new page ------------------------------------
52 Page page = doc.pageCreate();
53
54 ElementBuilder builder = new ElementBuilder(); // ElementBuilder is used to build new Element objects
55 ElementWriter writer = new ElementWriter(); // ElementWriter is used to write Elements to the page
56 writer.begin(page); // Begin writing to the page
57
58 // Add new content to the page and associate it with one of the layers.
59 Element element = builder.createForm(createGroup1(doc, image_layer.getSDFObj()));
60 writer.writeElement(element);
61
62 element = builder.createForm(createGroup2(doc, vector_layer.getSDFObj()));
63 writer.writeElement(element);
64
65 // Add the text layer to the page...
66 if (false) // set to true to enable 'ocmd' example.
67 {
68 // A bit more advanced example of how to create an OCMD text layer that
69 // is visible only if text, image and path layers are all 'ON'.
70 // An example of how to set 'Visibility Policy' in OCMD.
71 Obj ocgs = doc.createIndirectArray();
72 ocgs.pushBack(image_layer.getSDFObj());
73 ocgs.pushBack(vector_layer.getSDFObj());
74 ocgs.pushBack(text_layer.getSDFObj());
75 OCMD text_ocmd = OCMD.create(doc, ocgs, OCMD.e_AllOn);
76 element = builder.createForm(createGroup3(doc, text_ocmd.getSDFObj()));
77 } else {
78 element = builder.createForm(createGroup3(doc, text_layer.getSDFObj()));
79 }
80 writer.writeElement(element);
81
82
83 // Add some content to the page that does not belong to any layer...
84 // In this case this is a rectangle representing the page border.
85 element = builder.createRect(0, 0, page.getPageWidth(), page.getPageHeight());
86 element.setPathFill(false);
87 element.setPathStroke(true);
88 element.getGState().setLineWidth(40);
89 writer.writeElement(element);
90
91 writer.end(); // save changes to the current page
92 doc.pagePushBack(page);
93
94 // Set the default viewing preference to display 'Layer' tab.
95 PDFDocViewPrefs prefs = doc.getViewPrefs();
96 prefs.setPageMode(PDFDocViewPrefs.e_UseOC);
97
98 doc.save(output_path + "pdf_layers.pdf", SDFDoc.SaveMode.LINEARIZED, null);
99 System.out.println("Done.");
100 } catch (Exception e) {
101 e.printStackTrace();
102 }
103
104 // The following is a code snippet shows how to selectively render
105 // and export PDF layers.
106 try (PDFDoc doc = new PDFDoc(output_path + "pdf_layers.pdf")) {
107 doc.initSecurityHandler();
108
109 if (doc.hasOC() == false) {
110 System.out.println("The document does not contain 'Optional Content'");
111 } else {
112 Config init_cfg = doc.getOCGConfig();
113 Context ctx = new Context(init_cfg);
114
115 PDFDraw pdfdraw = new PDFDraw();
116 pdfdraw.setImageSize(1000, 1000);
117 pdfdraw.setOCGContext(ctx); // Render the page using the given OCG context.
118
119 Page page = doc.getPage(1); // Get the first page in the document.
120 pdfdraw.export(page, output_path + "pdf_layers_default.png");
121 // output "pdf_layers_default.png"
122
123 // Disable drawing of content that is not optional (i.e. is not part of any layer).
124 ctx.setNonOCDrawing(false);
125
126 // Now render each layer in the input document to a separate image.
127 Obj ocgs = doc.getOCGs(); // Get the array of all OCGs in the document.
128 if (ocgs != null) {
129 int i, sz = (int) ocgs.size();
130 for (i = 0; i < sz; ++i) {
131 Group ocg = new Group(ocgs.getAt(i));
132 ctx.resetStates(false);
133 ctx.setState(ocg, true);
134 String fname = "pdf_layers_" + ocg.getName() + ".png";
135 System.out.println(fname);
136 pdfdraw.export(page, output_path + fname);
137 // output "pdf_layers_" + ocg.getName() + ".png"
138 }
139 }
140
141 // Now draw content that is not part of any layer...
142 ctx.setNonOCDrawing(true);
143 ctx.setOCDrawMode(Context.e_NoOC);
144 pdfdraw.export(page, output_path + "pdf_layers_non_oc.png");
145 // output "pdf_layers_non_oc.png"
146 }
147
148 System.out.println("Done.");
149 } catch (Exception e) {
150 e.printStackTrace();
151 }
152
153 PDFNet.terminate();
154 }
155
156 // A utility function used to add new Content Groups (Layers) to the document.
157 static Group createLayer(PDFDoc doc, String layer_name) throws PDFNetException {
158 Group grp = Group.create(doc, layer_name);
159 Config cfg = doc.getOCGConfig();
160 if (cfg == null) {
161 cfg = Config.create(doc, true);
162 cfg.setName("Default");
163 }
164
165 // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
166 Obj layer_order_array = cfg.getOrder();
167 if (layer_order_array == null) {
168 layer_order_array = doc.createIndirectArray();
169 cfg.setOrder(layer_order_array);
170 }
171 layer_order_array.pushBack(grp.getSDFObj());
172
173 return grp;
174 }
175
176 // Creates some content (3 images) and associate them with the image layer
177 static Obj createGroup1(PDFDoc doc, Obj layer) throws PDFNetException {
178 ElementWriter writer = new ElementWriter();
179 writer.begin(doc);
180
181 // Create an Image that can be reused in the document or on the same page.
182 Image img = Image.create(doc.getSDFDoc(), (input_path + "peppers.jpg"));
183
184 ElementBuilder builder = new ElementBuilder();
185 Element element = builder.createImage(img, new Matrix2D(img.getImageWidth() / 2, -145, 20, img.getImageHeight() / 2, 200, 150));
186 writer.writePlacedElement(element);
187
188 GState gstate = element.getGState(); // use the same image (just change its matrix)
189 gstate.setTransform(200, 0, 0, 300, 50, 450);
190 writer.writePlacedElement(element);
191
192 // use the same image again (just change its matrix).
193 writer.writePlacedElement(builder.createImage(img, 300, 600, 200, -150));
194
195 Obj grp_obj = writer.end();
196
197 // Indicate that this form (content group) belongs to the given layer (OCG).
198 grp_obj.putName("Subtype", "Form");
199 grp_obj.put("OC", layer);
200 grp_obj.putRect("BBox", 0, 0, 1000, 1000); // Set the clip box for the content.
201
202 return grp_obj;
203 }
204
205 // Creates some content (a path in the shape of a heart) and associate it with the vector layer
206 static Obj createGroup2(PDFDoc doc, Obj layer) throws PDFNetException {
207 ElementWriter writer = new ElementWriter();
208 writer.begin(doc);
209
210 // Create a path object in the shape of a heart.
211 ElementBuilder builder = new ElementBuilder();
212 builder.pathBegin(); // start constructing the path
213 builder.moveTo(306, 396);
214 builder.curveTo(681, 771, 399.75, 864.75, 306, 771);
215 builder.curveTo(212.25, 864.75, -69, 771, 306, 396);
216 builder.closePath();
217 Element element = builder.pathEnd(); // the path geometry is now specified.
218
219 // Set the path FILL color space and color.
220 element.setPathFill(true);
221 GState gstate = element.getGState();
222 gstate.setFillColorSpace(ColorSpace.createDeviceCMYK());
223 gstate.setFillColor(new ColorPt(1, 0, 0, 0)); // cyan
224
225 // Set the path STROKE color space and color.
226 element.setPathStroke(true);
227 gstate.setStrokeColorSpace(ColorSpace.createDeviceRGB());
228 gstate.setStrokeColor(new ColorPt(1, 0, 0)); // red
229 gstate.setLineWidth(20);
230
231 gstate.setTransform(0.5, 0, 0, 0.5, 280, 300);
232
233 writer.writeElement(element);
234
235 Obj grp_obj = writer.end();
236
237 // Indicate that this form (content group) belongs to the given layer (OCG).
238 grp_obj.putName("Subtype", "Form");
239 grp_obj.put("OC", layer);
240 grp_obj.putRect("BBox", 0, 0, 1000, 1000); // Set the clip box for the content.
241
242 return grp_obj;
243 }
244
245 // Creates some text and associate it with the text layer
246 static Obj createGroup3(PDFDoc doc, Obj layer) throws PDFNetException {
247 ElementWriter writer = new ElementWriter();
248 writer.begin(doc);
249
250 // Create a path object in the shape of a heart.
251 ElementBuilder builder = new ElementBuilder();
252
253 // Begin writing a block of text
254 Element element = builder.createTextBegin(Font.create(doc, Font.e_times_roman), 120);
255 writer.writeElement(element);
256
257 element = builder.createTextRun("A text layer!");
258
259 // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
260 Matrix2D transform = Matrix2D.rotationMatrix(-45 * (3.1415 / 180.0));
261 transform.concat(1, 0, 0, 1, 180, 100);
262 element.setTextMatrix(transform);
263
264 writer.writeElement(element);
265 writer.writeElement(builder.createTextEnd());
266
267 Obj grp_obj = writer.end();
268
269 // Indicate that this form (content group) belongs to the given layer (OCG).
270 grp_obj.putName("Subtype", "Form");
271 grp_obj.put("OC", layer);
272 grp_obj.putRect("BBox", 0, 0, 1000, 1000); // Set the clip box for the content.
273
274 return grp_obj;
275 }
276}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales