Diff documents

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 Web SDK.

1(async exports => {
2 const Core = exports.Core;
3 const PDFNet = exports.Core.PDFNet;
4 Core.setWorkerPath('../../../lib/core');
5 Core.enableFullPDF();
6 await PDFNet.initialize();
7
8 const parentDoc = window.parent.window.document;
9
10 // Replace with your license key here as it needs to be passed when instantiating the worker transport promise
11 const licenseKey = undefined;
12
13 // Shared worker
14 const workerTransportPromise = Core.initPDFWorkerTransports('pdf', {}, licenseKey);
15
16 const containers = ['viewer1', 'viewer2'];
17 const instances = [];
18 const initialFiles = ['/samples/files/semantic_test_doc_1.pdf', '/samples/files/semantic_test_doc_2.pdf'];
19
20 let scrollTimeout = null;
21 let originalScroller = null;
22
23 const initializeViewer = containerName => {
24 return new Promise(resolve => {
25 WebViewer(
26 {
27 path: '../../../lib',
28 // Use shared worker
29 workerTransportPromise,
30 fullAPI: true,
31 },
32 document.getElementById(containerName)
33 ).then(instance => {
34 instance.Core.syncNamespaces({ PDFNet });
35
36 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
37 const scrollView = instance.Core.documentViewer.getScrollViewElement();
38 scrollView.onscroll = function() {
39 if (!originalScroller || originalScroller === scrollView) {
40 originalScroller = scrollView;
41 const leftPercentage = scrollView.scrollLeft / scrollView.scrollWidth;
42 const topPercentage = scrollView.scrollTop / scrollView.scrollHeight;
43 syncDocumentContainerScrolls(leftPercentage, topPercentage);
44 clearTimeout(scrollTimeout);
45 scrollTimeout = setTimeout(() => {
46 originalScroller = null;
47 }, 50);
48 }
49 };
50
51 // Update zoom value of the WebViewer instances
52 instance.Core.documentViewer.addEventListener('zoomUpdated', zoom => {
53 // zoom events will also trigger a scroll event
54 // set the original scroll to be the same panel that first triggers the zoom event
55 // so that scroll events are handled properly and in the correct order
56 // some browsers such as Chrome do not respect the scroll event ordering correctly
57 if (!originalScroller) {
58 originalScroller = scrollView;
59 clearTimeout(scrollTimeout);
60 scrollTimeout = setTimeout(() => {
61 originalScroller = null;
62 }, 50);
63 }
64 syncZoom(zoom);
65 });
66
67 instance.Core.documentViewer.addEventListener('rotationUpdated', rotation => {
68 syncRotation(rotation);
69 });
70 });
71
72 instances.push(instance);
73 resolve(instance);
74 });
75 });
76 };
77
78 const main = async () => {
79 const initTasks = containers.map(containerName => initializeViewer(containerName));
80
81 const docTasks = initialFiles.map(initialFile => PDFNet.PDFDoc.createFromURL(initialFile));
82
83 await Promise.all([...initTasks, ...docTasks]);
84 const docs = await Promise.all(docTasks);
85
86 await compareDoc(docs[0], docs[1]);
87
88 parentDoc.getElementById('fileUpload1').disabled = false;
89 parentDoc.getElementById('fileUpload2').disabled = false;
90 const compareButton = parentDoc.getElementById('compareButton');
91 compareButton.addEventListener('click', async () => {
92 const doc1 = uploadedDoc[0];
93 const doc2 = uploadedDoc[1];
94 disableCompareButton();
95 await compareDoc(doc1, doc2);
96 });
97 };
98
99 main();
100
101 const uploadedDoc = [null, null];
102 const recentDiffs = [];
103
104 const syncDocumentContainerScrolls = (scrollLeftPercentage, scrollTopPercentage) => {
105 instances.forEach(instance => {
106 const scrollView = instance.Core.documentViewer.getScrollViewElement();
107 if (!scrollView) {
108 return;
109 }
110 const currentLeftPosition = scrollView.scrollLeft / scrollView.scrollWidth;
111 const currentTopPosition = scrollView.scrollTop / scrollView.scrollHeight;
112 if (currentLeftPosition !== scrollLeftPercentage) {
113 scrollView.scrollLeft = scrollView.scrollWidth * scrollLeftPercentage;
114 }
115 if (currentTopPosition !== scrollTopPercentage) {
116 scrollView.scrollTop = scrollView.scrollHeight * scrollTopPercentage;
117 }
118 });
119 };
120
121 const syncZoom = zoom => {
122 instances.forEach(instance => {
123 if (instance.UI.getZoomLevel() !== zoom) {
124 instance.UI.setZoomLevel(zoom);
125 }
126 });
127 };
128
129 const syncRotation = rotation => {
130 instances.forEach(instance => {
131 const documentViewer = instance.Core.documentViewer;
132 if (documentViewer.getRotation() !== rotation) {
133 documentViewer.setRotation(rotation);
134 }
135 });
136 };
137
138 const compareDoc = async (doc1, doc2) => {
139 const leftPageCount = await doc1.getPageCount();
140 const rightPageCount = await doc2.getPageCount();
141
142 const leftDoc = await PDFNet.PDFDoc.create();
143 leftDoc.lock();
144
145 const rightDoc = await PDFNet.PDFDoc.create();
146 rightDoc.lock();
147
148 const newDoc = await PDFNet.PDFDoc.create();
149 newDoc.lock();
150
151 await newDoc.appendTextDiffDoc(doc1, doc2);
152
153 const totalPageCount = await newDoc.getPageCount();
154 const isLeftDocLarger = leftPageCount > rightPageCount;
155 const smallerCount = leftPageCount > rightPageCount ? leftPageCount : rightPageCount;
156
157 for (let i = 0; i < totalPageCount; i++) {
158 const page = await newDoc.getPage(i + 1);
159 if (i === smallerCount) {
160 if (isLeftDocLarger) {
161 await leftDoc.pagePushBack(page);
162 } else {
163 await rightDoc.pagePushBack(page);
164 }
165 continue;
166 }
167 if (i % 2 === 0) {
168 await leftDoc.pagePushBack(page);
169 } else {
170 await rightDoc.pagePushBack(page);
171 }
172 }
173
174 await newDoc.unlock();
175 await leftDoc.unlock();
176 await rightDoc.unlock();
177
178 instances[0].UI.loadDocument(leftDoc);
179 instances[1].UI.loadDocument(rightDoc);
180
181 recentDiffs.push([...uploadedDoc]);
182
183 // Skip default comparison
184 if (recentDiffs.length === 1) {
185 return;
186 }
187
188 const recentElement = parentDoc.getElementById('recentFiles');
189 const comparisonElement = document.createElement('button');
190 comparisonElement.innerText = `Compare ${doc1.fileName} (A) & ${doc2.fileName} (B)`;
191 comparisonElement.classList.add('link');
192 comparisonElement.onclick = onClickRecentLink.bind({ idx: recentDiffs.length - 1 });
193 recentElement.appendChild(comparisonElement);
194 };
195
196 const enableCompareButton = async () => {
197 const compareButton = parentDoc.getElementById('compareButton');
198
199 if (!compareButton.classList.contains('disabled')) {
200 return;
201 }
202
203 compareButton.classList.remove('disabled');
204 };
205
206 const disableCompareButton = async () => {
207 const compareButton = parentDoc.getElementById('compareButton');
208
209 if (compareButton.classList.contains('disabled')) {
210 return;
211 }
212
213 compareButton.classList.add('disabled');
214 };
215
216 const getPDFDocFromUpload = async (file, fileIndex) => {
217 const newDoc = await Core.createDocument(file, {});
218 uploadedDoc[fileIndex] = await newDoc.getPDFDoc();
219 uploadedDoc[fileIndex].fileName = file.name;
220 if (uploadedDoc[1] !== null && uploadedDoc[0] !== null) {
221 enableCompareButton();
222 }
223 };
224
225 parentDoc.getElementById('fileUpload1').addEventListener('change', e => {
226 getPDFDocFromUpload(e.target.files[0], 0);
227 });
228
229 parentDoc.getElementById('fileUpload2').addEventListener('change', e => {
230 getPDFDocFromUpload(e.target.files[0], 1);
231 });
232
233 const onClickRecentLink = async function() {
234 await compareDoc(recentDiffs[this.idx][0], recentDiffs[this.idx][1]);
235 };
236})(window);
237// eslint-disable-next-line spaced-comment
238//# sourceURL=config.js

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales