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.

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