// ES6 Compliant Syntax
// GitHub Copilot v1.0, Claude Sonnet 4, October 15, 2025
// File: index.js
import WebViewer from '@pdftron/webviewer';
const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
// CMYK Color Separation Demo
// This code demonstrates how to show, hide and separate colors in PDF with no external dependencies or servers required.
// Global variable to store the reset loading handler for proper cleanup
let resetLoadingHandler = null;
function initializeWebViewer() {
// This code initializes the WebViewer with the basic settings
WebViewer({
path: '/lib',
licenseKey: licenseKey,
enableFilePicker: true,
loadAsPDF: true, // Ensure files are loaded as PDF documents for best color separation support
}, document.getElementById('viewer')).then((instance) => {
// Enable the measurement toolbar so it appears with all the other tools, and disable Cloudy rectangular tool
const cloudyTools = [
instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT,
instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT2,
instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT3,
instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT4,
];
instance.UI.enableFeatures([instance.UI.Feature.Measurement, instance.UI.Feature.Initials]);
instance.UI.disableTools(cloudyTools);
// Set default toolbar group to Annotate
instance.UI.setToolbarGroup('toolbarGroup-Annotate');
// Set default tool on mobile devices to Pan.
if (UIElements.isMobileDevice()) {
instance.UI.setToolMode(instance.Core.Tools.ToolNames.PAN);
}
instance.Core.documentViewer.addEventListener('documentUnloaded', () => {
if (searchParams.has('file')) {
searchParams.delete('file');
history.replaceState(null, '', '?' + searchParams.toString());
}
});
instance.Core.annotationManager.enableAnnotationNumbering();
instance.UI.NotesPanel.enableAttachmentPreview();
// Add the demo-specific functionality
customizeUI(instance).then(() => {
// Create UI controls after demo is initialized
UIElements.createUIControls();
});
});
}
let colorValues = [];
let loadingColors = [];
const searchParams = new URLSearchParams(window.location.search);
const history = window.history || window.parent.history || window.top.history;
const customizeUI = async (instance) => {
// Load the default document
await instance.UI.loadDocument('https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/op_blend_test.pdf');
// Set allowed file types to load
const allowedFileTypes = [
'pdf', // PDF
'jpg', 'jpeg', 'png', // Images
'doc', 'docx', 'docm', 'dotx', 'dotm', 'dot', 'xls', 'xlsx', 'xlsm', 'xlt', 'xls', 'odt', 'txt', 'rtf', 'pptx', 'ppt', 'pptm', 'pps', 'pot' // MS Office
];
instance.Core.setAllowedFileExtensions(allowedFileTypes);
await initColorSeparation(instance); // Initialize color separation
};
const initColorSeparation = async (instance) => {
const { documentViewer } = instance.Core;
documentViewer.addEventListener('documentLoaded', async () => {
// Clear checkbox container from all previous color separations
const checkboxContainer = document.querySelector('.checkbox-container');
if (checkboxContainer) {
checkboxContainer.innerHTML = '';
}
// Get the document object
const doc = documentViewer.getDocument();
// Enable color separations on the document
doc.enableColorSeparations();
// Event listener for when a new color separation is added
doc.addEventListener('colorSeparationAdded', (colorData) => {
// Add a new checkbox for the added color separation
const checkboxContainer = document.querySelector('.checkbox-container');
if (checkboxContainer) {
// Create a wrapper for the checkbox
const checkboxWrapper = document.createElement('div');
checkboxWrapper.className = 'checkbox-wrapper';
// Add a checkbox for the new color separation
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `color-checkbox-${colorData.name}`;
checkbox.name = 'colorSeparation';
checkbox.value = colorData.name;
checkbox.checked = colorData.enabled;
checkbox.onchange = (e) => {
toggleColor(instance, colorData, e.target.checked);
}
checkbox.style.display = 'none'; // Hide the default checkbox
// Custom checkbox
const span = document.createElement('span');
span.className = 'checkbox-checkmark';
span.id = `checkmark-${colorData.name}`;
if (checkbox.checked) {
span.textContent = '✓'; // U+2713
}
span.onclick = () => {
checkbox.checked = !checkbox.checked;
checkbox.onchange({ target: checkbox });
span.textContent = checkbox.checked ? '✓' : ''; // U+2713
}
span.style.backgroundColor = rgbToString(colorData.rgb);
span.style.border = `3px solid ${rgbToString(colorData.rgb)}`;
// Label for the checkbox
const label = document.createElement('label');
label.className = 'label';
label.textContent = colorData.name;
label.onclick = () => {
checkbox.checked = !checkbox.checked;
checkbox.onchange({ target: checkbox });
span.textContent = checkbox.checked ? '✓' : ''; // U+2713
}
// Percentage text
const percentage = document.createElement('span');
percentage.id = `percentage-${colorData.name}`;
percentage.className = 'label';
percentage.textContent = `0%`;
// Append elements to the wrapper and then to the container
checkboxWrapper.appendChild(checkbox);
checkboxWrapper.appendChild(span);
checkboxWrapper.appendChild(label);
checkboxWrapper.appendChild(percentage);
checkboxContainer.appendChild(checkboxWrapper);
}
});
});
// Helper function to convert RGB array to CSS rgb() string
const rgbToString = (rgb) => {
const [r, g, b] = rgb;
return `rgb(${r}, ${g}, ${b})`;
};
// Function to toggle color separation
const toggleColor = (instance, color, checked) => {
const documentViewer = instance.Core.documentViewer;
loadingColors = [...loadingColors, color.name];
console.log(`Toggling color separation for ${color.name}:`, checked);
// Create handler if it doesn't exist
if (!resetLoadingHandler) {
resetLoadingHandler = () => resetLoading(instance);
}
documentViewer.addEventListener('pageComplete', resetLoadingHandler);
documentViewer.getDocument().enableSeparation(color.name, checked);
documentViewer.refreshAll();
documentViewer.updateView();
};
// Function to update color values on mouse move
documentViewer.addEventListener('mouseMove', (event) => {
const mouseLocation = documentViewer.getToolMode().getMouseLocation(event);
const displayMode = documentViewer.getDisplayModeManager().getDisplayMode();
const pageNumber = displayMode.getSelectedPages(mouseLocation, mouseLocation).first;
if (pageNumber !== null) {
const pageCoordinate = displayMode.windowToPage(mouseLocation, pageNumber);
if (pageCoordinate) {
const results = documentViewer.getColorSeparationsAtPoint(pageNumber, pageCoordinate.x, pageCoordinate.y);
colorValues = results.reduce(function (map, obj) {
map[obj.name] = obj.value;
return map;
}, {});
}
}
// Update percentage text for each color
const percentageElements = document.querySelectorAll('.label[id^="percentage-"]');
percentageElements.forEach((el) => {
const colorName = el.id.replace('percentage-', '');
if (colorValues && colorValues[colorName] !== undefined) {
el.textContent = `${colorValues[colorName]}%`;
} else {
el.textContent = `0%`;
}
});
});
};
// Function to reset which separations are loaded in order to disable/enable specific separations
const resetLoading = (instance) => {
loadingColors = [];
instance.Core.documentViewer.removeEventListener('pageComplete', resetLoadingHandler);
};
// Cleanup function for when the demo is closed or page is unloaded
const cleanup = (instance) => {
if (typeof instance !== 'undefined' && instance.UI) {
if (instance.Core.documentViewer.getDocument()) {
instance.Core.documentViewer
.getDocument()
.removeEventListener('colorSeparationAdded', setColorData);
instance.Core.documentViewer.removeEventListener('pageComplete', resetLoadingHandler);
instance.Core.documentViewer.removeEventListener('mouseMove', updateMouseMove);
}
console.log('Cleaning up color-separation demo');
}
};
// Register cleanup for page unload
window.addEventListener('beforeunload', () => cleanup(instance));
window.addEventListener('unload', () => cleanup(instance));
// Helper function to load the ui-elements.js script
function loadUIElementsScript() {
return new Promise((resolve, reject) => {
if (window.UIElements) {
console.log('UIElements already loaded');
resolve();
return;
}
const script = document.createElement('script');
script.src = '/showcase-demos/color-separation/ui-elements.js';
script.onload = function () {
console.log('✅ UIElements script loaded successfully');
resolve();
};
script.onerror = function () {
console.error('Failed to load UIElements script');
reject(new Error('Failed to load ui-elements.js'));
};
document.head.appendChild(script);
});
}
// Load UIElements script first, then initialize WebViewer
loadUIElementsScript().then(() => {
initializeWebViewer();
}).catch((error) => {
console.error('Failed to load UIElements:', error);
});