// ES6 Compliant Syntax
// GitHub Copilot v1.0 - GPT-4o model - July 24, 2025
// File: index.js
import WebViewer from '@pdftron/webviewer';
import JSZip from 'jszip';
import saveAs from 'file-saver';
const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
// extracted images array
let extractedImages = [];
// Customize the WebViewer UI
const customizeUI = (instance) => {
const { UI } = instance;
// Set the toolbar group to the annotate tools
UI.setToolbarGroup('toolbarGroup-Annotate');
// Extract images button
const extractImagesButton = new UI.Components.CustomButton({
dataElement: 'extractImagesButton',
className: 'custom-button-class',
label: 'Extract Images',
onClick: () => extractImages(instance), // extract images from the PDF
style: {
padding: '10px 10px',
backgroundColor: 'blue',
color: 'white',
}
});
const defaultHeader = UI.getModularHeader('default-top-header');
defaultHeader.setItems([...defaultHeader.items, extractImagesButton]);
};
// Extract images by traversing the display list for
// every page. With this approach it is possible to obtain
// image positioning information and DPI.
const extractImages = async (instance) => {
// Clear previously extracted images
extractedImages = [];
// PDFNet is only available with full API enabled
const { PDFNet, documentViewer } = instance.Core;
let pageLimit = 5;
try {
// Start the full workers
PDFNet.initialize().then(async () => {
const document = documentViewer.getDocument();
const doc = await document.getPDFDoc();
doc.initSecurityHandler();
const reader = await PDFNet.ElementReader.create();
const itr = await doc.getPageIterator(1);
// Read every page
for (itr; (await itr.hasNext()) && pageLimit > 0; await itr.next()) {
const page = await itr.current();
reader.beginOnPage(page);
await extractImagesFromPage(reader, PDFNet);
reader.end();
pageLimit--;
}
extractedImagesDialogBox();
});
} catch (e) {
console.error('❌ Failed to extract images:', e);
}
}
// Recursive function to extract images from a page
const extractImagesFromPage = async (reader, PDFNet) => {
let element;
while ((element = await reader.next()) !== null) {
switch (await element.getType()) {
case PDFNet.Element.Type.e_image:
case PDFNet.Element.Type.e_inline_image:
if ((await element.getType()) == PDFNet.Element.Type.e_image) {
const image = await PDFNet.Image.createFromObj(await element.getXObject());
const filter = await PDFNet.Filter.createMemoryFilter(65536, false);
const writer = await PDFNet.FilterWriter.create(filter);
await image.exportAsPngFromStream(writer);
await writer.flush();
await filter.memoryFilterSetAsInputFilter();
const reader = await PDFNet.FilterReader.create(filter);
const pngBuffer = await reader.readAllIntoBuffer();
const file = new Blob([pngBuffer], { type: 'image/png' });
extractedImages.push(file);
}
break;
case PDFNet.Element.Type.e_form: // Process form XObjects
reader.formBegin();
await extractImagesFromPage(reader, PDFNet);
reader.end();
break;
}
}
};
// Dialog box for displaying extracted images
const extractedImagesDialogBox = async () => {
// Create overlay
const overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
overlay.style.zIndex = '999';
overlay.style.display = 'flex';
overlay.style.justifyContent = 'center';
overlay.style.alignItems = 'center';
// Create dialog box
const dialogBox = document.createElement('div');
dialogBox.style.backgroundColor = 'white';
dialogBox.style.padding = '20px';
dialogBox.style.border = '2px solid #ccc';
dialogBox.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
dialogBox.style.width = '300px';
dialogBox.style.textAlign = 'center';
dialogBox.style.borderRadius = '8px';
// Message
const message = document.createElement('p');
message.style.fontFamily = 'Arial';
message.style.fontWeight = 'bold';
message.textContent = 'Extracted Images';
dialogBox.appendChild(message);
// Note
const note = document.createElement('p');
note.style.marginTop = "20px";
note.style.fontFamily = 'Arial';
note.textContent = 'For demo purposes, only the first 5 pages of the PDF will be processed.';
dialogBox.appendChild(note);
// Listbox (select element)
const listBox = document.createElement('select');
listBox.size = 6;
listBox.id = 'listBox';
listBox.style.marginTop = "20px";
listBox.style.alignItems = 'center';
listBox.style.border = '2px solid blue';
listBox.style.borderRadius = '4px';
// Add images to the listbox dynamically
extractedImages.forEach((image, index) => {
const imageFileName = `image${index + 1}.png`;
const item = document.createElement('option');
item.value = index;
item.textContent = `Download ${imageFileName}`;
item.style.color = 'blue';
item.style.cursor = 'pointer';
item.onclick = () => saveAs(image, imageFileName); // Download the image when clicked
listBox.appendChild(item);
});
dialogBox.appendChild(listBox);
// Download all images button
const downloadButton = document.createElement('button');
downloadButton.textContent = 'Download All Images (.zip)';
downloadButton.style.marginTop = "20px";
downloadButton.style.backgroundColor = 'blue';
downloadButton.style.color = 'white';
downloadButton.style.border = '1px solid blue';
downloadButton.style.borderRadius = '4px';
downloadButton.style.cursor = 'pointer';
downloadButton.onclick = () => downloadAllImagesZip();
dialogBox.appendChild(downloadButton);
// Close button
const closeButton = document.createElement('button');
closeButton.textContent = 'Close';
closeButton.style.marginLeft = '10px';
closeButton.style.color = 'blue';
closeButton.style.border = '1px solid blue';
closeButton.style.borderColor = 'blue';
closeButton.style.borderRadius = '4px';
closeButton.style.cursor = 'pointer';
closeButton.onclick = () => document.body.removeChild(overlay);
dialogBox.appendChild(closeButton);
// Append dialog to overlay and overlay to body
overlay.appendChild(dialogBox);
document.body.appendChild(overlay);
};
const downloadAllImagesZip = () => {
const zip = new JSZip();
extractedImages.forEach((image, index) => {
zip.file(`image${index + 1}.png`, image);
});
zip.generateAsync({ type: 'blob' }).then(function (content) {
saveAs(content, 'extracted.zip');
});
// Close dialog box after download
const overlay = document.body.querySelector('div[style*="position: fixed"]');
document.body.removeChild(overlay);
};
WebViewer(
{
path: '/lib',
initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/lexpress.pdf',
fullAPI: true, // Enable full API access. This will make the PDFNet namespace available on the WebViewer instance and allow accessing a PDFDoc instance from the WebViewer document object.
enableFilePicker: true, // Enable file picker to open files
licenseKey: licenseKey,
},
document.getElementById('viewer')
).then((instance) => {
// customize WebViewer UI
customizeUI(instance);
console.log('✅ WebViewer loaded successfully.');
}).catch((error) => {
console.error('❌ Failed to initialize WebViewer:', error);
});