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

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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales