Create and manipulate layers (OCG) - PDFLayers

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