PDFLayers

Sample Java 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 Android SDK.

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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales