Easily enable or disable layers in PDF construction drawings.
This demo lets you:
To add layer separation capability to a PDF with WebViewer:
Step 1: Get started with WebViewer in your preferred web stack
Step 2: Add the ES6 JavaScript sample code provided in this guide
Once you generate your license key, it will automatically be included in your sample code below.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 3.5, 2025-08-03
3// File: pdf-layers/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7const element = document.getElementById('viewer');
8let theInstance = null;
9const onLoad = async (instance) => {
10 theInstance = instance;
11 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
12 // Initialize layers when the document is loaded
13 initLayers();
14 });
15};
16
17// sample PDF with multiple layers
18const defaultLayersDoc = 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/construction_drawing-final.pdf';
19
20WebViewer(
21 {
22 path: '/lib',
23 licenseKey: 'YOUR_LICENSE_KEY',
24 initialDoc: defaultLayersDoc,
25 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
26 },
27 element
28).then((instance) => {
29 onLoad(instance);
30});
31
32// Define variable to hold layers information
33let docLayers = null;
34let flatLayers = null;
35
36// Array to hold labels, checkboxes and line breaks for layers
37// This will be used to dynamically create UI elements for each layer and remove them when needed
38let layerElements = [];
39
40// Show or hide layers based on checkbox state.
41// Set the value of the "visible" property for each layer in the layers array
42// and update the document viewer to reflect the changes.
43// The sender parameter is the actual checkbox that was changed.
44// The checkbox's "value" property contains the nesting level of the layer
45function updateLayersDisplay(sender) {
46 // check if layer has children
47 const layerLevel = +sender.value; // get the level as number
48 let startCheck = false;
49 let endCheck = false;
50 layerElements.forEach(el => {
51 if(endCheck)
52 return;
53 if(el.isSameNode(sender)){
54 console.log("sender same as el", sender);
55 startCheck = true; // found our checkbox. Start checking from next iteration
56 return; // from the forEach
57 }
58
59 if(el.type === 'checkbox' && startCheck){
60 if(+el.value <= layerLevel){ // Stop checking when we find first non-child
61 endCheck = true;
62 return;
63 }
64 // el is a child of sender. Set all children like sender
65 el.checked = sender.checked;
66 }
67 });
68 // Following code updates the layer's visibility to match the checkboxes
69 if (flatLayers && flatLayers.length > 0) {
70 let checkboxIndex = 0;
71 // loop to find the checkboxes, which are in the same order as their corresponding flat layers
72 layerElements.forEach(el => {
73 if(el.type === 'checkbox'){
74 flatLayers[checkboxIndex].visible = el.checked;
75 checkboxIndex++;
76 }
77 });
78 // reflect the layers' visibility on the document
79 const documentViewer = theInstance.Core.documentViewer;
80 const doc = documentViewer.getDocument();
81 doc.setLayersArray(flatLayers);
82 documentViewer.refreshAll();
83 documentViewer.updateView();
84 }
85}
86
87// Retrieve the layers from the document and initialize the UI.
88// This function will be called when the document is loaded and will populate
89// the layers array with the document's layers and create a checkbox for each layer
90// If no layers are found, it will suggest loading the default document
91async function initLayers() {
92 const doc = theInstance.Core.documentViewer.getDocument();
93 docLayers = await doc.getLayersArray();
94
95 // If no layers are found, suggest loading the default document
96 if(doc.getType() !== 'pdf') {
97 labelLayers.textContent = `This sample only supports PDFs. Click "${buttonDefault.textContent}" to load a sample with layers.`;
98 } else if(!docLayers || docLayers.length === 0){
99 labelLayers.textContent = `This document has no layers. Click "${buttonDefault.textContent}" to load a sample with layers.`;
100 }
101
102 // Clear existing labels and checkboxes if any exist from previous document
103 layerElements.forEach(element => element.remove());
104 layerElements = [];
105 // reset the flatLayers array
106 flatLayers = [];
107
108 if(!docLayers || docLayers.length === 0) {
109 return; // Exit if no layers are found
110 }
111
112 let currnetLevel = 0; // will be larger than zero for nested layers
113
114 // Function to create UI elements for the layers.
115 // Called recursively in case there are child (nested) layers
116 function addCheckboxes(layerArray){
117 layerArray.forEach((layer) => {
118 flatLayers.push(layer); // Add the layer to the flatLayers array
119 layer.visible = true; // Set all layers to visible by default
120 const checkbox = document.createElement('input');
121 checkbox.type = 'checkbox';
122 checkbox.value = currnetLevel; // zero if it's not for a child layer
123 // indent the child checkboxes based on their nesting level
124 checkbox.style.marginLeft = "" + (currnetLevel * 15) + "px";
125 checkbox.checked = layer.visible;
126 // display the layer's name next to its checkbox
127 const label = document.createElement('label');
128 label.className = 'label-style';
129 label.textContent = layer.name;
130 // The container has 2 lines separated by a line break
131 // Insert the labels and checkboxes before the line break
132 controlsContainer.insertBefore(label, lineBreak);
133 controlsContainer.insertBefore(checkbox, label);
134 // separate each layer's checkbox and label from the previous layer with a new line break
135 const br = document.createElement('br');
136 controlsContainer.insertBefore(br, checkbox);
137 // handle the change event for the checkboxes
138 checkbox.onchange = () => {
139 updateLayersDisplay(checkbox);
140 };
141 // Add the line-break, label and checkbox to the layer elements array for later reference
142 layerElements.push(br);
143 layerElements.push(label);
144 layerElements.push(checkbox);
145 if(layer.children && layer.children.length > 0) {
146 // Since the layer has children, recursively add checkboxes for these child layers between the brackets
147 currnetLevel++;
148 addCheckboxes(layer.children);
149 currnetLevel--;
150 }
151 });
152 }
153 // Add checkboxes for each layer in the layers array and create a flat array of layers
154 addCheckboxes(docLayers);
155
156 // Update the label to show the number of layers
157 labelLayers.textContent = `Layers found: ${flatLayers.length}`;
158}
159
160// UI section
161
162// Create a container for all controls (labels, buttons, checkboxes, etc.)
163const controlsContainer = document.createElement('div');
164
165const labelUpload = document.createElement('label');
166labelUpload.textContent = 'Use the Open File command in the WebViewer UI menu to upload a PDF file';
167
168// Create a button to open default PDF
169const buttonDefault = document.createElement('button');
170buttonDefault.textContent = 'Default Document';
171buttonDefault.onclick = async () => {
172 // load default PDF with layers
173 theInstance.UI.loadDocument(defaultLayersDoc);
174};
175
176// Label to display layer count or suggest loading default document
177const labelLayers = document.createElement('label');
178labelLayers.textContent = "";
179
180// Style the container and controls using CSS classes
181controlsContainer.className = 'control-container';
182
183buttonDefault.className = 'btn-style';
184labelUpload.className = 'label-style';
185labelLayers.className = 'label-style';
186
187// Create a break element to separate controls into two lines
188const lineBreak = document.createElement('br');
189
190// Append all controls to the container
191controlsContainer.appendChild(labelLayers);
192controlsContainer.appendChild(lineBreak);
193controlsContainer.appendChild(labelUpload);
194controlsContainer.appendChild(buttonDefault);
195// modify viewer element and its parent div to display controlsContainer with viewer side-by-side
196element.parentElement.style.display = "flex";
197element.style.display = "inline-block";
198element.style.width = "75%";
199controlsContainer.style.height = "100%";
200controlsContainer.style.display = "inline-block";
201controlsContainer.style.width = "25%";
202// Add the controls container right before to the viewer
203element.parentElement.insertBefore(controlsContainer, element);
204
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales