Layers (OCG) Manipulation - Sample Code

Requirements
View Demo

Sample JavaScript 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 working with OCGs and our Web SDK.

Implementation steps

To create, extract, show and hide PDF Layers (OCGs) in JavaScript with WebViewer:
Step 1: Follow get started in your preferred web stack for WebViewer
Step 2: Enable the full API by passing the fullAPI option into the WebViewer constructor
Step 3: Add the sample code provided in this guide

This full sample is one of many included in the manual download of WebViewer.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5(exports => {
6 exports.runPDFLayersTest = () => {
7 const PDFNet = exports.Core.PDFNet;
8
9 // A utility function used to add new Content Groups (Layers) to the document.
10 const CreateLayer = async (doc, layerName) => {
11 await PDFNet.startDeallocateStack();
12 const grp = await PDFNet.OCG.create(doc, layerName);
13 let cfg = await doc.getOCGConfig();
14 if (cfg == null) {
15 cfg = await PDFNet.OCGConfig.create(doc, true);
16 cfg.setName('Default');
17 }
18
19 // Add the new OCG to the list of layers that should appear in PDF viewer GUI.
20 let layerOrderArray = await cfg.getOrder();
21 if (layerOrderArray == null) {
22 layerOrderArray = await doc.createIndirectArray();
23 cfg.setOrder(layerOrderArray);
24 }
25 const grpSDFObj = await grp.getSDFObj();
26 layerOrderArray.pushBack(grpSDFObj);
27
28 await PDFNet.endDeallocateStack();
29 return grp;
30 };
31
32 // Creates some content (3 images) and associate them with the image layer
33 const CreateGroup1 = async (doc, layer) => {
34 await PDFNet.startDeallocateStack();
35 const writer = await PDFNet.ElementWriter.create();
36 writer.begin(doc);
37
38 // Create an Image that can be reused in the document or on the same page.
39 const nullEncoderHints = new PDFNet.Obj('0');
40 const img = await PDFNet.Image.createFromURL(doc, '../TestFiles/peppers.jpg', nullEncoderHints);
41
42 const builder = await PDFNet.ElementBuilder.create();
43 const imgWidth = await img.getImageWidth();
44 const imgHeight = await img.getImageHeight();
45 const imgMatrix = new PDFNet.Matrix2D(imgWidth / 2, -145, 20, imgHeight / 2, 200, 150);
46 const element = await builder.createImageFromMatrix(img, imgMatrix);
47 writer.writePlacedElement(element);
48
49 const gstate = await element.getGState(); // use the same image (just change its matrix)
50 gstate.setTransform(200, 0, 0, 300, 50, 450);
51 writer.writePlacedElement(element);
52
53 // use the same image again (just change its matrix).
54 writer.writePlacedElement(await builder.createImageScaled(img, 300, 600, 200, -150));
55
56 const grpObj = await writer.end();
57
58 // Indicate that this form (content group) belongs to the given layer (OCG).
59 grpObj.putName('Subtype', 'Form');
60 grpObj.put('OC', layer);
61 grpObj.putRect('BBox', 0, 0, 1000, 1000); // Set the clip box for the content.
62 await PDFNet.endDeallocateStack();
63
64 return grpObj;
65 };
66
67 const CreateGroup2 = async (doc, layer) => {
68 await PDFNet.startDeallocateStack();
69 const writer = await PDFNet.ElementWriter.create();
70 writer.begin(doc);
71
72 // Create a path object in the shape of a heart.
73 const builder = await PDFNet.ElementBuilder.create();
74 builder.pathBegin(); // start constructing the path
75 builder.moveTo(306, 396);
76 builder.curveTo(681, 771, 399.75, 864.75, 306, 771);
77 builder.curveTo(212.25, 864.75, -69, 771, 306, 396);
78 builder.closePath();
79 const element = await builder.pathEnd(); // the path geometry is now specified.
80
81 // Set the path FILL color space and color.
82 element.setPathFill(true);
83 const gstate = await element.getGState();
84 const CMYKSpace = await PDFNet.ColorSpace.createDeviceCMYK();
85 gstate.setFillColorSpace(CMYKSpace);
86 const cyanColorPt = await PDFNet.ColorPt.init(1, 0, 0, 0); // CMYK
87 gstate.setFillColorWithColorPt(cyanColorPt); // cyan
88
89 // Set the path STROKE color space and color.
90 element.setPathStroke(true);
91 const RGBSpace = await PDFNet.ColorSpace.createDeviceRGB();
92 gstate.setStrokeColorSpace(RGBSpace);
93 const redColorPt = await PDFNet.ColorPt.init(1, 0, 0); // RGB
94 gstate.setStrokeColorWithColorPt(redColorPt); // red
95 gstate.setLineWidth(20);
96
97 gstate.setTransform(0.5, 0, 0, 0.5, 280, 300);
98
99 writer.writeElement(element);
100
101 const grpObj = await writer.end();
102
103 // Indicate that this form (content group) belongs to the given layer (OCG).
104 grpObj.putName('Subtype', 'Form');
105 grpObj.put('OC', layer);
106 grpObj.putRect('BBox', 0, 0, 1000, 1000); // Set the clip box for the content.
107
108 await PDFNet.endDeallocateStack();
109 return grpObj;
110 };
111
112 const CreateGroup3 = async (doc, layer) => {
113 await PDFNet.startDeallocateStack();
114 const writer = await PDFNet.ElementWriter.create();
115 writer.begin(doc);
116
117 const builder = await PDFNet.ElementBuilder.create();
118
119 // Begin writing a block of text
120 const textFont = await PDFNet.Font.create(doc, PDFNet.Font.StandardType1Font.e_times_roman);
121 let element = await builder.createTextBeginWithFont(textFont, 120);
122 writer.writeElement(element);
123
124 element = await builder.createNewTextRun('A text layer!');
125
126 // Rotate text 45 degrees, than translate 180 pts horizontally and 100 pts vertically.
127 const transform = await PDFNet.Matrix2D.createRotationMatrix(-45 * (3.1415 / 180.0));
128 await transform.concat(1, 0, 0, 1, 180, 100);
129 await element.setTextMatrix(transform);
130
131 await writer.writeElement(element);
132 await writer.writeElement(await builder.createTextEnd());
133
134 const grpObj = await writer.end();
135
136 // Indicate that this form (content group) belongs to the given layer (OCG).
137 grpObj.putName('Subtype', 'Form');
138 grpObj.put('OC', layer);
139 grpObj.putRect('BBox', 0, 0, 1000, 1000); // Set the clip box for the content.
140 await PDFNet.endDeallocateStack();
141 return grpObj;
142 };
143
144 const main = async () => {
145 console.log('Beginning Test');
146 let doc = null;
147 // Here we output a pdf document with layers.
148 try {
149 doc = await PDFNet.PDFDoc.create();
150 doc.initSecurityHandler();
151 doc.lock();
152 console.log('PDFNet and PDF document initialized and locked');
153
154 const imageLayer = await CreateLayer(doc, 'Image Layer');
155 const textLayer = await CreateLayer(doc, 'Text Layer');
156 const vectorLayer = await CreateLayer(doc, 'Vector Layer');
157
158 const page = await doc.pageCreate();
159
160 const builder = await PDFNet.ElementBuilder.create();
161 const writer = await PDFNet.ElementWriter.create();
162 writer.beginOnPage(page);
163
164 const groupObj = await CreateGroup1(doc, await imageLayer.getSDFObj());
165 let element = await builder.createFormFromStream(groupObj);
166 writer.writeElement(element);
167
168 const groupObj2 = await CreateGroup2(doc, await vectorLayer.getSDFObj());
169 element = await builder.createFormFromStream(groupObj2);
170 writer.writeElement(element);
171
172 // eslint-disable-next-line no-constant-condition
173 if (false) {
174 // A bit more advanced example of how to create an OCMD text layer that
175 // is visible only if text, image and path layers are all 'ON'.
176 // An example of how to set 'Visibility Policy' in OCMD.
177 const ocgs = doc.createIndirectArray();
178 ocgs.pushBack(await imageLayer.getSDFObj());
179 ocgs.pushBack(await vectorLayer.getSDFObj());
180 ocgs.PushBack(await textLayer.getSDFObj());
181 const textOcmd = await PDFNet.OCMD.create(doc, ocgs, PDFNet.OCMD.VisibilityPolicyType.e_AllOn);
182 element = await builder.createFormFromStream(await CreateGroup3(doc, await textOcmd.getSDFObj()));
183 } else {
184 // let SDFObj = await textLayer.getSDFObj();
185 element = await builder.createFormFromStream(await CreateGroup3(doc, await textLayer.getSDFObj()));
186 }
187 writer.writeElement(element);
188
189 // Add some content to the page that does not belong to any layer...
190 // In this case this is a rectangle representing the page border.
191 element = await builder.createRect(0, 0, await page.getPageWidth(), await page.getPageHeight());
192 element.setPathFill(false);
193 element.setPathStroke(true);
194 const elementGState = await element.getGState();
195 elementGState.setLineWidth(40);
196 writer.writeElement(element);
197
198 writer.end(); // save changes to the current page
199 doc.pagePushBack(page);
200
201 // Set the default viewing preference to display 'Layer' tab.
202 const prefs = await doc.getViewPrefs();
203 prefs.setPageMode(PDFNet.PDFDocViewPrefs.PageMode.e_UseOC);
204
205 const docbuf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);
206 saveBufferAsPDFDoc(docbuf, 'pdf_layers.pdf');
207 console.log('done example 1');
208 } catch (err) {
209 console.log(err.stack);
210 }
211
212 // Here we output the individual layers as png files.
213 try {
214 // we are still using the doc from the previous section.
215 if (!(await doc.hasOC())) {
216 console.log("The document does not contain 'Optional Content'");
217 } else {
218 const initCfg = await doc.getOCGConfig();
219 const ctx = await PDFNet.OCGContext.createFromConfig(initCfg);
220
221 const pdfdraw = await PDFNet.PDFDraw.create();
222 pdfdraw.setImageSize(1000, 1000);
223 pdfdraw.setOCGContext(ctx);
224
225 const page = await doc.getPage(1);
226
227 const firstPageBuffer = await pdfdraw.exportStream(page);
228 saveBufferAsPNG(firstPageBuffer, 'pdf_layers_default.png');
229
230 ctx.setNonOCDrawing(false);
231
232 const ocgs = await doc.getOCGs();
233 if (ocgs !== null) {
234 let i;
235 const sz = await ocgs.size();
236 for (i = 0; i < sz; ++i) {
237 const ocg = await PDFNet.OCG.createFromObj(await ocgs.getAt(i));
238 ctx.resetStates(false);
239 ctx.setState(ocg, true);
240 let fname = 'pdf_layers_';
241 fname += await ocg.getName();
242 fname += '.png';
243 const pageBuffer = await pdfdraw.exportStream(page);
244 saveBufferAsPNG(pageBuffer, fname);
245 }
246 }
247
248 // Now draw content that is not part of any layer...
249 ctx.setNonOCDrawing(true);
250 ctx.setOCDrawMode(PDFNet.OCGContext.OCDrawMode.e_NoOC);
251 const nonLayerBuffer = await pdfdraw.exportStream(page);
252 saveBufferAsPNG(nonLayerBuffer, 'pdf_layers_non_oc.png');
253 }
254
255 console.log('done');
256 } catch (err) {
257 console.log(err.stack);
258 }
259 };
260 // add your own license key as the second parameter, e.g. PDFNet.runWithCleanup(main, 'YOUR_LICENSE_KEY')
261 PDFNet.runWithCleanup(main);
262 };
263})(window);
264// eslint-disable-next-line spaced-comment
265//# sourceURL=PDFLayersTest.js

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales
PDF Layers (OCG): Add, Show, Hide - Sample Code - WebViewer JavaScript SDK | Apryse documentation