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