How to Build a PWA Document Scanner: Capture, Edit and Upload as PDF

In many industries, a document scanner app is essential for capturing, editing, and uploading documents such as invoices and receipts to the cloud. By leveraging the Dynamsoft Document Viewer SDK, you can build a Progressive Web App (PWA) document scanner that enables users to capture images, crop them, combine multiple pages into a single document, and convert the scanned documents to PDF format for easy sharing and storage. This tutorial will guide you through the process of creating a PWA document scanner using the Dynamsoft Document Viewer SDK.

PWA Document Scanner Demo Video

Prerequisites

  • Dynamsoft Document Viewer: This package provides JavaScript APIs for viewing and annotating a wide range of document formats, including PDFs and images like JPEG, PNG, TIFF, and BMP. Key features include PDF rendering, page navigation, image quality enhancement, and document saving capabilities. You can find the SDK on npm.

  • Dynamsoft Capture Vision Trial License: A 30-day free trial license that provides access to all features of the Dynamsoft SDKs.

Creating a Web Server for Uploading PDF Files

Let’s create a Node.js/Express server to receive a Base64 string and save it as a PDF file to the local disk.

Install Dependencies

  1. Create a folder for your server:

     mkdir server
     cd server
    
  2. Initialize a Node.js project:

     npm init -y
    
  3. Install Express and cors:

     npm install express cors
    

    Explanation

    • Express simplifies the creation of a web server.
    • CORS (Cross-Origin Resource Sharing) is middleware that allows cross-origin requests.

Create the Server Code (index.js)

  1. Create an index.js file with the following code:

     const express = require('express');
     const cors = require('cors');
     const fs = require('fs');
     const path = require('path');
        
     const app = express();
     const PORT = 3000;
        
     app.use(cors());
     app.use(express.json({ limit: '10mb' }));
        
     app.post('/upload', (req, res) => {
         const { image } = req.body;
        
         if (!image) {
             return res.status(400).json({ error: 'No image provided.' });
         }
        
         const buffer = Buffer.from(image, 'base64');
        
         // Save the image to disk
         const filename = `image_${Date.now()}.pdf`;
         const filepath = path.join(__dirname, 'uploads', filename);
        
         // Ensure the uploads directory exists
         if (!fs.existsSync('uploads')) {
             fs.mkdirSync('uploads');
         }
        
         fs.writeFile(filepath, buffer, (err) => {
             if (err) {
                 console.error('Failed to save image:', err);
                 return res.status(500).json({ error: 'Failed to save image.' });
             }
        
             console.log('Image saved:', filename);
             res.json({ message: 'Image uploaded successfully!', filename });
         });
     });
        
     // Start the server
     app.listen(PORT, () => {
         console.log(`Server is running on http://localhost:${PORT}`);
     });
    
  2. Run the web server:

     node index.js
    

Implementing a PWA Document Scanner with Dynamsoft Document Viewer

To get started with the Dynamsoft Document Viewer, download the official sample code from the GitHub repository: https://github.com/Dynamsoft/mobile-web-capture/tree/master/samples/complete-document-capturing-workflow. This sample demonstrates how to capture, crop, and combine multiple images into a single document using the Dynamsoft Document Viewer SDK.

Based on the project, we will add the following features:

  • Support for PWA.
  • Upload scanned documents as PDF files to a server.

Making a Web Project PWA-Compatible

  1. Create a folder for your PWA project:

     mkdir client
     cd client
    
  2. Copy the sample code into the client folder.
  3. Create a manifest.json file in the root directory of your project with the following content:

     {
         "short_name": "MyPWA",
         "name": "My Progressive Web App",
         "icons": [
             {
                 "src": "icon.png",
                 "sizes": "192x192",
                 "type": "image/png"
             }
         ],
         "start_url": "/",
         "display": "standalone",
         "background_color": "#ffffff",
         "theme_color": "#000000"
     }
    
  4. Create a sw.js file in the root directory of your project with the following content:

     const CACHE_NAME = 'pwa-cache-v1';
     const urlsToCache = [
         '/',
         '/index.css',
         '/manifest.json',
     ];
        
     self.addEventListener('install', (event) => {
         event.waitUntil(
             caches.open(CACHE_NAME).then((cache) => {
                 console.log('Opened cache');
                 return cache.addAll(urlsToCache);
             })
         );
     });
        
     self.addEventListener('fetch', (event) => {
         event.respondWith(
             caches.match(event.request).then((response) => {
                 return response || fetch(event.request);
             })
         );
     });
    
  5. Register the service worker in the index.html file:

     <script>
         if ('serviceWorker' in navigator) {
             window.addEventListener('load', () => {
                 navigator.serviceWorker
                     .register('/sw.js')
                     .then((registration) => {
                         console.log('Service Worker registered with scope:', registration.scope);
                     })
                     .catch((error) => {
                         console.error('Service Worker registration failed:', error);
                     });
             });
         }
     </script>
    

Uploading Scanned Documents as PDF Files

  1. In uiConfig.js, add a customized download button with a click event named save:

     {
         type: Dynamsoft.DDV.Elements.Button,
         className: "ddv-button ddv-button-download",
         events: {
             click: "save",
         }
     }
    
  2. In index.html, implement the save event. After saving the document as a PDF, convert the blob to a Base64 string and upload it to the server:

     async function uploadImage(base64String) {
         return fetch('http://localhost:3000/upload', {
             method: 'POST',
             headers: {
                 'Content-Type': 'application/json',
             },
             body: JSON.stringify({ image: base64String }),
         });
     }
    
     editViewer.on("save", async () => {
         // https://www.dynamsoft.com/document-viewer/docs/api/interface/idocument/index.html#savetopdf
         const pdfSettings = {
             saveAnnotation: "annotation",
         };
    
         let blob = await editViewer.currentDocument.saveToPdf(pdfSettings);
    
         // convert blob to base64
         let reader = new FileReader();
         reader.readAsDataURL(blob);
         reader.onloadend = async function () {
             let base64data = reader.result;
    
             try {
                 const response = await uploadImage(base64data.split(',')[1]);
    
                 if (response.ok) {
                     alert('Upload successful!');
                 } else {
                     alert('Upload failed.');
                 }
             } catch (error) {
                 console.error(error);
    
             }
    
         }
    
     });
    

Running the PWA Document Scanner

  1. Start a web server in the root directory of your project:

     python -m http.server
    
  2. Visit http://localhost:8000 in your web browser.

    PWA Document Scanner

Source Code

https://github.com/yushulx/web-twain-document-scan-management/tree/main/examples/pwa