Easily enable or disable layers in PDF construction drawings.
This demo lets you:
Implementation steps
To add layer separation capability to a PDF with WebViewer:
Step 1: Choose your preferred web stack
Step 2: Download any required modules listed in the Demo Dependencies section below
Step 3: Add the ES6 JavaScript sample code provided in this guide
Demo Dependencies
This sample uses the following:
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