Some test text!
This Javascript sample shows how to render three synchronized panels where the middle panel shows pixel differences between the two documents. Learn more about our JavaScript PDF Library.
Get Started Samples DownloadTo run this sample, get started with a free trial of Apryse SDK.
JavaScript
HTML
// Workaround while WebViewer does not have an API to check if a document is loaded or not.
let isLoading = false;
(function(exports) {
const mainCore = Core;
mainCore.setWorkerPath('../../../lib/core');
const BASE_PANEL_DOC_INDEX = 0;
const OVERLAY_PANEL_DOC_INDEX = 1;
let originalScroller = null;
let scrollTimeout;
let pdfWorkerTransportPromise;
let officeWorkerTransportPromise;
const viewers = [];
const instances = {};
const PANEL_IDS = {
BASE_PANEL: 'basePanel',
MIDDLE_PANEL: 'resultPanel',
OVERLAY_PANEL: 'overlayPanel',
};
const VIEWER_IDS = [{ panel: PANEL_IDS.BASE_PANEL }, { panel: PANEL_IDS.MIDDLE_PANEL }, { panel: PANEL_IDS.OVERLAY_PANEL }];
const TRANSFORMATION_DELTA = 1;
// Normally setTimout(...., 0) will work, however since IE can be slow, use a longer timeout to give more time for WebViewer to perform operations
const timeoutForIE = 50;
function getDocumentObject(panelId) {
return instances[panelId].instance.Core.documentViewer.getDocument();
}
function getDocumentViewerObject(panelId) {
return instances[panelId].instance.Core.documentViewer;
}
function onOpacitySliderValueChanged() {
let baseDocOpacity = 1;
let overlayDocOpacity = 1;
if (exports.DiffOverlaySlider.isEnabled()) {
const value = exports.DiffOverlaySlider.getValue();
baseDocOpacity = 1 - value;
overlayDocOpacity = value;
}
const compareDocs = getDocumentObject(PANEL_IDS.MIDDLE_PANEL);
const basePanelDocCurrentPage = getDocumentViewerObject(PANEL_IDS.BASE_PANEL).getCurrentPage();
const overlayPanelDocCurrentPage = getDocumentViewerObject(PANEL_IDS.OVERLAY_PANEL).getCurrentPage();
compareDocs.setDocumentPageOpacity(BASE_PANEL_DOC_INDEX, basePanelDocCurrentPage, baseDocOpacity);
compareDocs.setDocumentPageOpacity(OVERLAY_PANEL_DOC_INDEX, overlayPanelDocCurrentPage, overlayDocOpacity);
}
function onNudgeToolPressed(nudgeDirection) {
const compareDocs = getDocumentObject(PANEL_IDS.MIDDLE_PANEL);
const currPageNumber = getDocumentViewerObject(PANEL_IDS.MIDDLE_PANEL).getCurrentPage();
switch (nudgeDirection) {
case exports.NudgeTool.TRANSFORMATION_TYPE.HORIZONTAL_TRANSLATION_INC:
compareDocs.translate('right', TRANSFORMATION_DELTA, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.HORIZONTAL_TRANSLATION_DEC:
compareDocs.translate('left', TRANSFORMATION_DELTA, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.VERTICAL_TRANSLATION_INC:
compareDocs.translate('up', TRANSFORMATION_DELTA, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.VERTICAL_TRANSLATION_DEC:
compareDocs.translate('down', TRANSFORMATION_DELTA, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.ROTATION_INC:
compareDocs.rotate((TRANSFORMATION_DELTA * Math.PI) / 180, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.ROTATION_DEC:
compareDocs.rotate((-TRANSFORMATION_DELTA * Math.PI) / 180, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.SCALE_IN:
compareDocs.scale(TRANSFORMATION_DELTA / 10, currPageNumber);
break;
case exports.NudgeTool.TRANSFORMATION_TYPE.SCALE_OUT:
compareDocs.scale(-TRANSFORMATION_DELTA / 10, currPageNumber);
break;
}
}
// Create an instance of worker transport to share among WebViewer instances
function getWorkerTransportPromise(path) {
const filename = typeof path === 'object' ? path.name : path || '';
const isOfficeFile = filename.endsWith('docx') || filename.endsWith('pptx') || filename.endsWith('xlsx');
// replace with your license key here as it needs to be passed when instantiating the worker transport promise
const licenseKey = undefined;
// Use existing workers
if (isOfficeFile && officeWorkerTransportPromise) {
return officeWorkerTransportPromise;
}
if (!isOfficeFile && pdfWorkerTransportPromise) {
return pdfWorkerTransportPromise;
}
const workerTransportPromise = Core.getDefaultBackendType().then(backendType => {
if (path && isOfficeFile) {
return Core.initOfficeWorkerTransports(backendType, {}, licenseKey);
}
// Use PDF worker by default
return Core.initPDFWorkerTransports(backendType, {}, licenseKey);
});
if (path && isOfficeFile) {
officeWorkerTransportPromise = workerTransportPromise;
} else {
pdfWorkerTransportPromise = workerTransportPromise;
}
return workerTransportPromise;
}
async function openDoc(panel, docPath) {
await getDocumentViewerObject(panel).loadDocument(docPath, {
loadAsPDF: true,
});
}
function syncZoom(zoom) {
if (!isLoading) {
viewers.forEach(item => {
const instance = instances[item.panel].instance;
if (instance.UI.getZoomLevel() !== zoom) {
instance.UI.setZoomLevel(zoom);
}
});
}
}
function syncDocumentContainerScrolls(scrollLeft, scrollTop) {
viewers.forEach(item => {
const documentContainer = instances[item.panel].documentContainer;
if (!documentContainer) {
return;
}
if (documentContainer.scrollLeft !== scrollLeft) {
documentContainer.scrollLeft = scrollLeft;
}
if (documentContainer.scrollTop !== scrollTop) {
documentContainer.scrollTop = scrollTop;
}
});
}
function syncRotation(rotation) {
viewers.forEach(item => {
const documentViewer = getDocumentViewerObject(item.panel);
if (documentViewer.getRotation() !== rotation) {
documentViewer.setRotation(rotation);
}
});
}
function syncPageNumber(pageNumber) {
viewers.forEach(item => {
const documentViewer = getDocumentViewerObject(item.panel);
if (documentViewer.getCurrentPage() !== pageNumber) {
documentViewer.setCurrentPage(pageNumber);
}
});
}
function alignLineSegments(baseDocStartPoint, baseDocEndPoint, overlayDocStartPoint, overlayDocEndPoint, baseDocPageNumber, overlayDocPageNumber) {
const compareDocs = getDocumentObject(PANEL_IDS.MIDDLE_PANEL);
compareDocs.updatePageWithAlignment(overlayDocStartPoint, overlayDocEndPoint, baseDocStartPoint, baseDocEndPoint, overlayDocPageNumber, baseDocPageNumber);
}
function setupViewer(item) {
return new Promise(resolve => {
const viewerElement = document.getElementById(item.panel);
window
.WebViewer(
{
path: '../../../lib',
css: './nudge-tool.css',
// share a single instance of the worker transport
workerTransportPromise: getWorkerTransportPromise(item.pdf),
initialDoc: item.pdf || null,
// turn on to enable snapping
// fullAPI: true,
disableVirtualDisplayMode: true,
disabledElements: ['layoutButtons', 'pageTransitionButtons', 'toolsButton', 'annotationPopup', 'panToolButton', 'linkButton', 'toolsOverlayCloseButton'],
},
viewerElement
)
.then(instance => {
instance.UI.syncNamespaces({
PDFNet: mainCore.PDFNet,
});
samplesSetup(instance);
const { UI, Core } = instance;
const { documentViewer } = Core;
const { Feature, LayoutMode, FitMode } = UI;
UI.disableFeatures([Feature.Annotations]);
UI.setToolMode('AnnotationEdit');
documentViewer.addEventListener('documentLoaded', () => {
UI.setLayoutMode(LayoutMode.Single);
UI.setFitMode(FitMode.FitWidth);
if (item.panel !== PANEL_IDS.MIDDLE_PANEL) {
documentViewer.getDocument().addEventListener('layersUpdated', () => {
const midPanelDocumentViewer = getDocumentViewerObject(PANEL_IDS.MIDDLE_PANEL);
if (getDocumentObject(PANEL_IDS.MIDDLE_PANEL)) {
midPanelDocumentViewer.refreshAll();
midPanelDocumentViewer.updateView();
}
});
}
const panelNode = document.getElementById(`wc-${item.panel}`);
const documentContainer = panelNode ? panelNode.shadowRoot.querySelector('.DocumentContainer') : viewerElement.querySelector('iframe').contentDocument.querySelector('.DocumentContainer');
// Sync all WebViewer instances when scroll changes
documentContainer.onscroll = () => {
if (!exports.DiffAlignmentTool.isInAlignmentMode()) {
if (!originalScroller || originalScroller === documentContainer) {
originalScroller = documentContainer;
syncDocumentContainerScrolls(documentContainer.scrollLeft, documentContainer.scrollTop);
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
originalScroller = null;
}, timeoutForIE);
}
}
};
// Update zoom value of the WebViewer instances
documentViewer.addEventListener('zoomUpdated', zoom => {
// zoom events will also trigger a scroll event
// set the original scroll to be the same panel that first triggers the zoom event
// so that scroll events are handled properly and in the correct order
// some browsers such as Chrome do not respect the scroll event ordering correctly
if (!originalScroller) {
originalScroller = documentContainer;
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
originalScroller = null;
}, timeoutForIE);
}
if (!exports.DiffAlignmentTool.isInAlignmentMode()) {
syncZoom(zoom);
}
});
documentViewer.addEventListener('rotationUpdated', rotation => {
syncRotation(rotation);
});
documentViewer.addEventListener('pageNumberUpdated', pageNumber => {
if (!exports.DiffAlignmentTool.isInAlignmentMode()) {
syncPageNumber(pageNumber);
} else {
// we are in alignment mode
// sync up left and middle panel as they are the same document
if (item.panel !== PANEL_IDS.OVERLAY_PANEL) {
viewers.forEach(viewerItem => {
if (getDocumentViewerObject(viewerItem.panel).getCurrentPage() !== pageNumber && viewerItem.panel !== PANEL_IDS.OVERLAY_PANEL) {
getDocumentViewerObject(viewerItem.panel).setCurrentPage(pageNumber);
}
});
}
}
});
instances[item.panel].documentContainer = documentContainer;
});
viewers.push(item);
instances[item.panel] = {
instance,
};
resolve();
});
});
}
function initializeWebViewerInstances(array, callback) {
Promise.all(array.map(setupViewer)).then(() => {
const instance = instances[PANEL_IDS.MIDDLE_PANEL].instance;
instance.UI.disableElements(['leftPanelButton', 'searchButton', 'searchPanel', 'searchOverlay']);
return callback();
});
}
async function initialize(basePDFRelativePath, overlayPDFRelativePath) {
isLoading = true;
await openDoc(PANEL_IDS.BASE_PANEL, basePDFRelativePath);
await openDoc(PANEL_IDS.OVERLAY_PANEL, overlayPDFRelativePath);
exports.DiffAlignmentTool.initializeDiffAlignmentToolHandlers(
instances[PANEL_IDS.BASE_PANEL].instance,
instances[PANEL_IDS.OVERLAY_PANEL].instance,
alignLineSegments,
() => {
// do nothing on enter
},
() => {
// on leave
const currPageNumber = getDocumentViewerObject(PANEL_IDS.MIDDLE_PANEL).getCurrentPage();
syncPageNumber(currPageNumber);
}
);
const baseDocument = instances[PANEL_IDS.BASE_PANEL].instance.Core.documentViewer.getDocument();
const overlayDocument = instances[PANEL_IDS.OVERLAY_PANEL].instance.Core.documentViewer.getDocument();
const files = [
// needed to pass in Core that belongs to that document for annotation diff-ing
{ document: baseDocument, Core: instances[PANEL_IDS.BASE_PANEL].instance.Core },
{ document: overlayDocument, Core: instances[PANEL_IDS.OVERLAY_PANEL].instance.Core },
];
await exports.CompareDocumentManager.initialize(instances[PANEL_IDS.MIDDLE_PANEL].instance.Core, files, {
extension: 'pdf',
resetInitialPageRotations: true,
// do not allow diffing if in IE11 else it won't work
diffAnnotations: !(exports.navigator.userAgent.indexOf('MSIE') !== -1 || exports.navigator.appVersion.indexOf('Trident/') > -1),
});
isLoading = false;
}
initializeWebViewerInstances(VIEWER_IDS, async () => {
await initialize(`${window.location.href}../../../samples/files/test_doc_1.pdf`, `${window.location.href}../../../samples/files/test_doc_2.pdf`);
exports.NudgeTool.initializeNudgeTool(nudgeDirection => {
onNudgeToolPressed(nudgeDirection);
});
document.getElementById('enable-snap-mode').disabled = !instances[PANEL_IDS.BASE_PANEL].instance.Core.isFullPDFEnabled() && !instances[PANEL_IDS.OVERLAY_PANEL].instance.Core.isFullPDFEnabled();
});
document.getElementById('selectControl').onclick = async e => {
e.preventDefault();
const select1 = document.getElementById('select1');
const basePDF = select1.options[select1.selectedIndex].value;
const select2 = document.getElementById('select2');
const pdfToOverlay = select2.options[select2.selectedIndex].value;
await initialize(window.location.href + basePDF, window.location.href + pdfToOverlay);
};
document.getElementById('url-form').onsubmit = async e => {
e.preventDefault();
const basePDF = document.getElementById('url').value;
const pdfToOverlay = document.getElementById('url2').value;
await initialize(basePDF, pdfToOverlay);
};
document.getElementById('file-picker-form').onsubmit = async e => {
e.preventDefault();
const basePDF = document.forms['file-picker-form'][0].files[0];
const pdfToOverlay = document.forms['file-picker-form'][1].files[0];
await initialize(basePDF, pdfToOverlay);
};
// use change event over input as its supported in IE11
document.getElementById('show-difference').addEventListener('change', function() {
const shouldDiff = this.checked;
const compareDocs = getDocumentObject(PANEL_IDS.MIDDLE_PANEL);
compareDocs.shouldDiff(shouldDiff);
});
document.getElementById('enable-opacity-slider').addEventListener('change', function() {
const isEnabled = this.checked;
document.getElementById('opacity-slider').disabled = !isEnabled;
exports.DiffOverlaySlider.enable(isEnabled);
const sliderValue = document.getElementById('opacity-slider').value;
exports.DiffOverlaySlider.setValue(+sliderValue / 100);
onOpacitySliderValueChanged();
});
document.getElementById('opacity-slider').addEventListener('change', () => {
const sliderValue = document.getElementById('opacity-slider').value;
exports.DiffOverlaySlider.setValue(+sliderValue / 100);
onOpacitySliderValueChanged();
});
})(window);