Effortlessly assemble PDF documents by dragging and dropping thumbnail pages between document viewers. Perform manipulation securely in the memory of the browser without any server-side dependencies.
This demo allows you to:
Implementation steps
To add PDF assembly capability from two viewers:
Step 1: Choose your preferred web stack
Step 2: Download any required modules listed in the Demo Dependencies section below
Step 3: Add the ES6 JavaScript sample code provided in this guide
Demo Dependencies
This sample uses the following:
1// ES6 Compliant Syntax
2// GitHub Copilot - Model: GPT-4o
3// Date: July 14, 2025
4// File: index.js
5
6import WebViewer from '@pdftron/webviewer';
7
8const licenseKey = 'YOUR_LICENSE_KEY';
9
10// List of viewers with their configurations
11// Each viewer can have its own document, alignment, size, instance, and merging features
12let viewers = [
13 {
14 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
15 style: {
16 alignment: 'Left',
17 width: '49%',
18 height: '100vh',
19 margin: '0 auto',
20 },
21 instance: null,
22 hasMerging: false, // The merging feature allows merging pages from one viewer to another
23 },
24 {
25 initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/report.docx',
26 style: {
27 alignment: 'Right',
28 width: '49%',
29 height: '100vh',
30 margin: '0 auto',
31 },
32 instance: null,
33 hasMerging: true,
34 },
35];
36
37// Customize the WebViewer UI
38// Add custom buttons for merging (if merging feature is enabled) and downloading PDFs
39function customizeUI(viewer) {
40 const { UI } = viewer.instance;
41
42 // Merge button
43 let mergeButton = null;
44 if (viewer.hasMerging) {
45 mergeButton = new UI.Components.CustomButton({
46 dataElement: 'mergeButton',
47 className: 'custom-button-class',
48 label: 'Merge Page',
49 title: 'Merge the left WebViewer\'s first page, to be inserted prior to the right WebViewer\'s first page.',
50 onClick: () => mergePage(), // Merge the pages
51 style: {
52 padding: '10px 20px',
53 backgroundColor: 'white',
54 color: 'blue',
55 border: '1px solid blue',
56 }
57 });
58 }
59
60 // Download Pdf button
61 let downloadButton = new UI.Components.CustomButton({
62 dataElement: 'downloadPdfButton',
63 className: 'custom-button-class',
64 label: 'Download as PDF',
65 onClick: () => downloadPdf(viewer.instance), // Download the PDF
66 style: {
67 padding: '10px 20px',
68 backgroundColor: 'blue',
69 color: 'white',
70 }
71 });
72
73 let defaultHeader = UI.getModularHeader('default-top-header');
74
75 // If the viewer has merging enabled, add the merge button and download button to the header
76 // Otherwise, just add the download button
77 if (viewer.hasMerging)
78 defaultHeader.setItems([...defaultHeader.items, mergeButton, downloadButton]);
79 else
80 defaultHeader.setItems([...defaultHeader.items, downloadButton]);
81};
82
83// Merge the first page from the left viewer to be located at position #1 of the right viewer
84// This function will be called when the merge button is clicked
85// This function retrieves the source document, exports annotations, and inserts page(s) into the destination document
86const mergePage = async () => {
87
88 const srcDoc = await viewers[0].instance.Core.documentViewer.getDocument();
89 const dstDoc = await viewers[1].instance.Core.documentViewer.getDocument();
90
91 // get first page as a blob
92 const xfdfString = await viewers[0].instance.Core.annotationManager.exportAnnotations();
93 const data = await srcDoc.getFileData({
94 xfdfString,
95 });
96 const arr = new Uint8Array(data);
97 const blob = new Blob([arr], { type: 'application/pdf' });
98
99 const docToInsert = await viewers[1].instance.Core.createDocument(blob, { extension: 'pdf', l: licenseKey });
100
101 dstDoc.insertPages(docToInsert, [1], 1);
102};
103
104// Download the PDF
105const downloadPdf = async (instance) => {
106
107 // Get the filename from the document
108 let filename = instance.Core.documentViewer.getDocument().getFilename();
109
110 // Ensure it ends with .pdf. If not, replace it with .pdf extension
111 if (!filename.endsWith('.pdf'))
112 filename = filename.replace(/\.[^/.]+$/, '') + '.pdf';
113
114 // Set the options for downloading the PDF
115 const options = {
116 filename: filename,
117 flags: instance.Core.SaveOptions.LINEARIZED,
118 downloadType: 'pdf'
119 };
120
121 instance.UI.downloadPdf(options);
122};
123
124// Create and initialize the WebViewer instances with their configurations,
125// as many viewers are defined in the viewers list
126function createWebViewer(viewer) {
127 const element = document.createElement('div');
128 element.id = `WebViewer${viewer.style.alignment}`;
129 element.style.width = viewer.style.width;
130 element.style.height = viewer.style.height;
131 element.style.margin = viewer.style.margin;
132 element.style.float = viewer.style.alignment;
133
134 //find 'viewer' element in the body and append the viewer element to it
135 const viewerElement = document.getElementById('viewer');
136 if (!viewerElement) {
137 console.error('Viewer element not found! Make sure the template execution area is ready.');
138 return;
139 }
140 viewerElement.appendChild(element);
141
142 WebViewer(
143 {
144 path: '/lib',
145 initialDoc: viewer.initialDoc,
146 loadAsPDF: true,
147 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
148 licenseKey: licenseKey,
149 },
150 element
151 ).then(instance => {
152
153 // Enable merging pages feature from another WebViewer
154 instance.UI.enableFeatures(instance.UI.Feature.MultipleViewerMerging);
155
156 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
157 // Open the thumbnails panel
158 instance.UI.openElements(['thumbnailsPanel']);
159
160 // Select the first page in the thumbnails panel
161 setTimeout(() => {
162 instance.UI.ThumbnailsPanel.selectPages([1]);
163 }, 500);
164
165 console.log(`✅ ${element.id} loaded successfully.`);
166
167 // Store the instance reference in the viewer object
168 viewer.instance = instance;
169
170 // customize WebViewer UI
171 customizeUI(viewer);
172 });
173 }).catch((error) => {
174 console.error(`❌ Failed to initialize ${element.id}:`, error);
175 });
176}
177
178// Create the defined viewers in the viewers list
179viewers.forEach(viewer => {
180 createWebViewer(viewer);
181});
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales