Color Separation Showcase Demo Code Sample

Requirements
View Demo

Easily manipulate colors in PDF documents — all on the client side.

This demo lets you:

  • Process CMYK colors.
  • Upload your own PDF.
  • Show, hide, and isolate colors with ease.

Implementation steps
To add Color Separation capability 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.

License Key

1// ES6 Compliant Syntax
2// GitHub Copilot v1.0, Claude Sonnet 4, October 15, 2025
3// File: index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
8
9// CMYK Color Separation Demo
10// This code demonstrates how to show, hide and separate colors in PDF with no external dependencies or servers required.
11
12// Global variable to store the reset loading handler for proper cleanup
13let resetLoadingHandler = null;
14
15function initializeWebViewer() {
16
17 // This code initializes the WebViewer with the basic settings
18 WebViewer({
19 path: '/lib',
20 licenseKey: licenseKey,
21 enableFilePicker: true,
22 loadAsPDF: true, // Ensure files are loaded as PDF documents for best color separation support
23 }, document.getElementById('viewer')).then((instance) => {
24 // Enable the measurement toolbar so it appears with all the other tools, and disable Cloudy rectangular tool
25 const cloudyTools = [
26 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT,
27 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT2,
28 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT3,
29 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT4,
30 ];
31 instance.UI.enableFeatures([instance.UI.Feature.Measurement, instance.UI.Feature.Initials]);
32 instance.UI.disableTools(cloudyTools);
33 // Set default toolbar group to Annotate
34 instance.UI.setToolbarGroup('toolbarGroup-Annotate');
35 // Set default tool on mobile devices to Pan.
36 if (UIElements.isMobileDevice()) {
37 instance.UI.setToolMode(instance.Core.Tools.ToolNames.PAN);
38 }
39
40 instance.Core.documentViewer.addEventListener('documentUnloaded', () => {
41 if (searchParams.has('file')) {
42 searchParams.delete('file');
43 history.replaceState(null, '', '?' + searchParams.toString());
44 }
45 });
46
47 instance.Core.annotationManager.enableAnnotationNumbering();
48 instance.UI.NotesPanel.enableAttachmentPreview();
49 // Add the demo-specific functionality
50 customizeUI(instance).then(() => {
51 // Create UI controls after demo is initialized
52 UIElements.createUIControls();
53 });
54 });
55}
56
57let colorValues = [];
58let loadingColors = [];
59const searchParams = new URLSearchParams(window.location.search);
60const history = window.history || window.parent.history || window.top.history;
61
62const customizeUI = async (instance) => {
63 // Load the default document
64 await instance.UI.loadDocument('https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/op_blend_test.pdf');
65 // Set allowed file types to load
66 const allowedFileTypes = [
67 'pdf', // PDF
68 'jpg', 'jpeg', 'png', // Images
69 'doc', 'docx', 'docm', 'dotx', 'dotm', 'dot', 'xls', 'xlsx', 'xlsm', 'xlt', 'xls', 'odt', 'txt', 'rtf', 'pptx', 'ppt', 'pptm', 'pps', 'pot' // MS Office
70 ];
71 instance.Core.setAllowedFileExtensions(allowedFileTypes);
72 await initColorSeparation(instance); // Initialize color separation
73};
74
75const initColorSeparation = async (instance) => {
76 const { documentViewer } = instance.Core;
77 documentViewer.addEventListener('documentLoaded', async () => {
78 // Clear checkbox container from all previous color separations
79 const checkboxContainer = document.querySelector('.checkbox-container');
80 if (checkboxContainer) {
81 checkboxContainer.innerHTML = '';
82 }
83 // Get the document object
84 const doc = documentViewer.getDocument();
85 // Enable color separations on the document
86 doc.enableColorSeparations();
87 // Event listener for when a new color separation is added
88 doc.addEventListener('colorSeparationAdded', (colorData) => {
89 // Add a new checkbox for the added color separation
90 const checkboxContainer = document.querySelector('.checkbox-container');
91 if (checkboxContainer) {
92 // Create a wrapper for the checkbox
93 const checkboxWrapper = document.createElement('div');
94 checkboxWrapper.className = 'checkbox-wrapper';
95 // Add a checkbox for the new color separation
96 const checkbox = document.createElement('input');
97 checkbox.type = 'checkbox';
98 checkbox.id = `color-checkbox-${colorData.name}`;
99 checkbox.name = 'colorSeparation';
100 checkbox.value = colorData.name;
101 checkbox.checked = colorData.enabled;
102 checkbox.onchange = (e) => {
103 toggleColor(instance, colorData, e.target.checked);
104 }
105 checkbox.style.display = 'none'; // Hide the default checkbox
106 // Custom checkbox
107 const span = document.createElement('span');
108 span.className = 'checkbox-checkmark';
109 span.id = `checkmark-${colorData.name}`;
110 if (checkbox.checked) {
111 span.textContent = ''; // U+2713
112 }
113 span.onclick = () => {
114 checkbox.checked = !checkbox.checked;
115 checkbox.onchange({ target: checkbox });
116 span.textContent = checkbox.checked ? '' : ''; // U+2713
117 }
118 span.style.backgroundColor = rgbToString(colorData.rgb);
119 span.style.border = `3px solid ${rgbToString(colorData.rgb)}`;
120 // Label for the checkbox
121 const label = document.createElement('label');
122 label.className = 'label';
123 label.textContent = colorData.name;
124 label.onclick = () => {
125 checkbox.checked = !checkbox.checked;
126 checkbox.onchange({ target: checkbox });
127 span.textContent = checkbox.checked ? '' : ''; // U+2713
128 }
129 // Percentage text
130 const percentage = document.createElement('span');
131 percentage.id = `percentage-${colorData.name}`;
132 percentage.className = 'label';
133 percentage.textContent = `0%`;
134 // Append elements to the wrapper and then to the container
135 checkboxWrapper.appendChild(checkbox);
136 checkboxWrapper.appendChild(span);
137 checkboxWrapper.appendChild(label);
138 checkboxWrapper.appendChild(percentage);
139 checkboxContainer.appendChild(checkboxWrapper);
140 }
141 });
142 });
143
144 // Helper function to convert RGB array to CSS rgb() string
145 const rgbToString = (rgb) => {
146 const [r, g, b] = rgb;
147 return `rgb(${r}, ${g}, ${b})`;
148 };
149
150 // Function to toggle color separation
151 const toggleColor = (instance, color, checked) => {
152 const documentViewer = instance.Core.documentViewer;
153 loadingColors = [...loadingColors, color.name];
154 console.log(`Toggling color separation for ${color.name}:`, checked);
155
156 // Create handler if it doesn't exist
157 if (!resetLoadingHandler) {
158 resetLoadingHandler = () => resetLoading(instance);
159 }
160
161 documentViewer.addEventListener('pageComplete', resetLoadingHandler);
162 documentViewer.getDocument().enableSeparation(color.name, checked);
163 documentViewer.refreshAll();
164 documentViewer.updateView();
165 };
166
167 // Function to update color values on mouse move
168 documentViewer.addEventListener('mouseMove', (event) => {
169 const mouseLocation = documentViewer.getToolMode().getMouseLocation(event);
170 const displayMode = documentViewer.getDisplayModeManager().getDisplayMode();
171 const pageNumber = displayMode.getSelectedPages(mouseLocation, mouseLocation).first;
172 if (pageNumber !== null) {
173 const pageCoordinate = displayMode.windowToPage(mouseLocation, pageNumber);
174 if (pageCoordinate) {
175 const results = documentViewer.getColorSeparationsAtPoint(pageNumber, pageCoordinate.x, pageCoordinate.y);
176 colorValues = results.reduce(function (map, obj) {
177 map[obj.name] = obj.value;
178 return map;
179 }, {});
180 }
181 }
182 // Update percentage text for each color
183 const percentageElements = document.querySelectorAll('.label[id^="percentage-"]');
184 percentageElements.forEach((el) => {
185 const colorName = el.id.replace('percentage-', '');
186 if (colorValues && colorValues[colorName] !== undefined) {
187 el.textContent = `${colorValues[colorName]}%`;
188 } else {
189 el.textContent = `0%`;
190 }
191 });
192 });
193};
194
195// Function to reset which separations are loaded in order to disable/enable specific separations
196const resetLoading = (instance) => {
197 loadingColors = [];
198 instance.Core.documentViewer.removeEventListener('pageComplete', resetLoadingHandler);
199};
200
201// Cleanup function for when the demo is closed or page is unloaded
202const cleanup = (instance) => {
203 if (typeof instance !== 'undefined' && instance.UI) {
204 if (instance.Core.documentViewer.getDocument()) {
205 instance.Core.documentViewer
206 .getDocument()
207 .removeEventListener('colorSeparationAdded', setColorData);
208 instance.Core.documentViewer.removeEventListener('pageComplete', resetLoadingHandler);
209 instance.Core.documentViewer.removeEventListener('mouseMove', updateMouseMove);
210 }
211 console.log('Cleaning up color-separation demo');
212 }
213};
214
215// Register cleanup for page unload
216window.addEventListener('beforeunload', () => cleanup(instance));
217window.addEventListener('unload', () => cleanup(instance));
218
219// Helper function to load the ui-elements.js script
220function loadUIElementsScript() {
221 return new Promise((resolve, reject) => {
222 if (window.UIElements) {
223 console.log('UIElements already loaded');
224 resolve();
225 return;
226 }
227 const script = document.createElement('script');
228 script.src = '/showcase-demos/color-separation/ui-elements.js';
229 script.onload = function () {
230 console.log('✅ UIElements script loaded successfully');
231 resolve();
232 };
233 script.onerror = function () {
234 console.error('Failed to load UIElements script');
235 reject(new Error('Failed to load ui-elements.js'));
236 };
237 document.head.appendChild(script);
238 });
239}
240
241// Load UIElements script first, then initialize WebViewer
242loadUIElementsScript().then(() => {
243 initializeWebViewer();
244}).catch((error) => {
245 console.error('Failed to load UIElements:', error);
246});
247

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales