How to Digitize Documents in a Blazor Web App Using TWAIN, WIA, SANE, ICA, and eSCL Scanners

In an increasingly digital world, the ability to efficiently digitize documents is paramount for businesses and organizations aiming to streamline operations, enhance data accessibility, and improve overall productivity. To achieve comprehensive document digitization capabilities, it’s essential to support a variety of scanner protocols, including TWAIN, WIA, SANE, ICA, and eSCL. Dynamic Web TWAIN is a JavaScript-based document scanning SDK that provides cross-platform support for these scanner protocols, enabling developers to easily integrate document scanning functionality into their web applications. In this article, we’ll explore how to integrate TWAIN, WIA, SANE, ICA, and eSCL scanner protocols into a Blazor Web App using Dynamic Web TWAIN.

Blazor Document Digitization App Demo Video

Try Online Demo

https://yushulx.me/blazor-barcode-mrz-document-scanner/

Prerequisites

  • Dynamic Web TWAIN

    This package is essential for integrating scanning capabilities into your web application. It includes:

    • Cross-Platform JavaScript Libraries: Compatible with Windows, macOS, and Linux.
    • Platform-Specific Service Installers: Facilitates communication between the web app and scanner hardware.

      Dynamsoft service installer

    You can install Dynamic Web TWAIN via npm.

    Important Note:

    • Installation Prompt: When users access your document scanning app for the first time, they will be prompted to install the Dynamsoft service.

      Dynamsoft service installation prompt

    • CDN Limitations: The service installer exceeds jsDelivr’s size limits, leading to failed downloads if attempted through this method. Instead, host the service installer on your server or use the unpkg website to download the installer.

  • Dynamsoft Capture Vision Trial License

    To utilize the full capabilities of the Dynamic Web TWAIN SDK, obtain a 30-day free trial license.

Setting Up Dynamic Web TWAIN in a Blazor WebAssembly App

Integrating Dynamic Web TWAIN into your Blazor WebAssembly application enables robust document scanning capabilities across multiple platforms. Follow the steps below to set up Dynamic Web TWAIN effectively:

  1. Create a New Blazor WebAssembly Project: Scaffold a new Blazor WebAssembly project using Visual Studio.
  2. Download Dynamic Web TWAIN: Install the Dynamic Web TWAIN package via npm.

     npm install dwt
    
  3. Integrate Dynamic Web TWAIN into the Blazor Project: Copy the dist folder from the node_modules/dwt directory to the wwwroot folder in the Blazor project, and then include the JavaScript file in the index.html file.

     <script src="dist/dynamsoft.webtwain.min.js"></script>
    
  4. Create a JavaScript File for C# Interop: To facilitate communication between Blazor (C#) and JavaScript, create a jsInterop.js file in the wwwroot directory and include it in your index.html:

     <script src="dist/dynamsoft.webtwain.min.js"></script>
     <script src="jsInterop.js"></script>
    
  5. Configure Resource Paths and License Key: In the wwwroot/jsInterop.js file, define a setLicense function to configure the resource path and license key for Dynamic Web TWAIN:
     window.jsFunctions = {
         setLicense: async function setLicense(license) {
             if (isInitialized) return true;
            
             try {
                 Dynamsoft.DWT.ResourcesPath = "dist";
                 Dynamsoft.DWT.ProductKey = license;
            
                 isInitialized = true;
             } catch (e) {
                 alert(e);
                 return false;
             }
            
             return true;
         },
         ...
     };
    
  6. Create an EditForm for License Input: In Pages/Home.razor, add the following HTML and C# code to allow users to activate the SDK with a valid license key:

     @page "/"
     @inject IJSRuntime JSRuntime
        
     <PageTitle>Home</PageTitle>
        
     <p>Click <a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform" target="_blank">here</a> to obtain a Dynamsoft Capture Vision Trial License.</p>
        
     <EditForm Model="@this">
         <InputText @bind-Value="LicenseKey" placeholder="Enter your license key" />
         <button type="button" class="btn btn-primary" @onclick="SetLicenseKey">Activate the SDK</button>
     </EditForm>
        
     @code {
         Boolean initialized = false;
        
         private string LicenseKey = "LICENSE-KEY";
        
         private async Task SetLicenseKey()
         {
             initialized = await JSRuntime.InvokeAsync<Boolean>("jsFunctions.setLicense", LicenseKey);
             StateHasChanged();
         }
     }
    

Implementing Document Scanning in Blazor WebAssembly

The following steps outline how to implement document scanning functionality in a Blazor WebAssembly application using Dynamic Web TWAIN:

  1. Create a Razor Component: Add a new Razor component named WebTwain.razor to the Pages directory and reference it in the Layout/NavMenu.razor file.

     <div class="nav-item px-3">
         <NavLink class="nav-link" href="webtwain">
             <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Web TWAIN
         </NavLink>
     </div>
    
  2. Initialize a Document Container: In the WebTwain.razor file, create a container to display scanned documents:

     @page "/webtwain"
     @inject IJSRuntime JSRuntime
     @using System.Text.Json;
        
     <div id="@containerId"></div>
        
     @code {
         private string containerId = "document-container";
         private int containerWidth = 800;
         private int containerHeight = 800;
        
         protected override async Task OnAfterRenderAsync(bool firstRender)
         {
             if (firstRender)
             {
                 await JSRuntime.InvokeVoidAsync("jsFunctions.initWebTwain", containerId, containerWidth, containerHeight);
                 ...
             }
         }
     }
    

    Explanation:

    • @page "/webtwain" directive defines the route for this component.
    • initWebTwain is a JavaScript function that initializes the Web TWAIN container with a specified ID and dimensions.

        initWebTwain: async function (containerId, width, height) {
            if (!isInitialized) {
                alert("Please set the license first.");
                return;
            }
              
            try {
                await new Promise((resolve, reject) => {
                    Dynamsoft.DWT.CreateDWTObjectEx({ "WebTwainId": containerId }, (obj) => {
                        dwtObject = obj;
                        dwtObject.Viewer.bind(document.getElementById(containerId));
                        dwtObject.Viewer.width = width;
                        dwtObject.Viewer.height = height;
                        dwtObject.Viewer.show();
                        resolve();
                    }, (errorString) => {
                        console.log(errorString);
                        resolve();
                    });
                });
            }
            catch (e) {
                alert(e);
            }
        },
      
  3. List Available Scanners: Add a dropdown to list the available scanners connected to the computer:

     <select id="@selectId"></select>
    
     @code {
         protected override async Task OnAfterRenderAsync(bool firstRender)
         {
             if (firstRender)
             {
                 await JSRuntime.InvokeVoidAsync("jsFunctions.initWebTwain", containerId, containerWidth, containerHeight);
                 await JSRuntime.InvokeVoidAsync("jsFunctions.getDevices", selectId);
             }
         }
     }
    

    Explanation:

    • getDevices is a JavaScript function that retrieves the available scanners and populates the select element with the scanner names.

        getDevices: async function (selectId) {
            await new Promise((resolve, reject) => {
                if (!dwtObject) {
                    resolve();
                    return;
                }
              
                dwtObject.GetDevicesAsync(Dynamsoft.DWT.EnumDWT_DeviceType.TWAINSCANNER | Dynamsoft.DWT.EnumDWT_DeviceType.TWAINX64SCANNER | Dynamsoft.DWT.EnumDWT_DeviceType.ESCLSCANNER).then((sources) => {
                    sourceList = sources;
              
                    selectSources = document.getElementById(selectId);
                    for (let i = 0; i < sources.length; i++) {
                        let option = document.createElement("option");
                        option.text = sources[i].displayName;
                        option.value = i.toString();
                        selectSources.add(option);
                    }
              
                    resolve();
                });
            });
        },
      
  4. Scan Documents from a Selected Scanner: Add a button to initiate the document scanning process:

     <button @onclick="AcquireImage">Scan Documents</button>
        
     @code {
         public async Task AcquireImage()
         {
             var deviceConfiguration = new
             {
                 IfShowUI = false,
                 PixelType = Utils.PixelType.TWPT_RGB,
                 Resolution = 300,
                 IfFeederEnabled = true,
                 IfDuplexEnabled = false,
                 IfDisableSourceAfterAcquire = true,
                 IfGetImageInfo = true,
                 IfGetExtImageInfo = true,
                 extendedImageInfoQueryLevel = 0
             };
             var jsonString = JsonSerializer.Serialize(deviceConfiguration);
            
             await JSRuntime.InvokeVoidAsync("jsFunctions.acquireImage", jsonString);
            
         }
     }
    

    Explanation:

    • deviceConfiguration is a C# object that specifies the scanning settings.
    • acquireImage is a JavaScript function that triggers the document scanning process with the specified settings.

        acquireImage: async function (jsonString) {
            await new Promise((resolve, reject) => {
                if (!dwtObject || sourceList.length == 0) {
                    resolve();
                    return;
                }
              
                if (selectSources) {
                    dwtObject.SelectDeviceAsync(sourceList[selectSources.selectedIndex]).then(() => {
              
                        return dwtObject.OpenSourceAsync()
              
                    }).then(() => {
              
                        return dwtObject.AcquireImageAsync(JSON.parse(jsonString))
              
                    }).then(() => {
              
                        if (dwtObject) {
              
                            dwtObject.CloseSource();
              
                        }
              
                        resolve();
              
                    }).catch(
              
                        (e) => {
              
                            console.error(e);
                            resolve();
              
                        }
              
                    )
                }
                else {
                    resolve();
                }
            });
        },
      
  5. Remove a Selected Document: Add a button to remove a selected document from the container:

     <button @onclick="RemoveSelected">Remove Selected</button>
        
     @code {
         public async Task RemoveSelected()
         {
             await JSRuntime.InvokeVoidAsync("jsFunctions.removeSelected");
         }
     }
    

    Explanation:

    • removeSelected is a JavaScript function that removes the selected document from the container.

        removeSelected: async function () {
            await new Promise((resolve, reject) => {
                if (!dwtObject) {
                    resolve();
                    return;
                }
              
                dwtObject.RemoveImage(dwtObject.CurrentImageIndexInBuffer);
                resolve();
            });
              
        },
      
  6. Save Documents to a PDF File: Add a button that allows users to save the scanned documents as JPEG, TIFF, or PDF format. The file will be generated directly by the Dynamsoft service and saved to the local file system without triggering a browser download prompt.

     <button @onclick="Save">Save Documents to PDF</button>
        
     @code {
         public async Task Save()
         {
             await JSRuntime.InvokeVoidAsync("jsFunctions.save", Utils.ImageType.PDF, "test");
         }
     }
    

    Explanation:

    • save is a JavaScript function that requests the Dynamsoft service to save the scanned documents as a PDF file.

        save: async function (type, name) {
            await new Promise((resolve, reject) => {
                if (!dwtObject) {
                    resolve();
                    return;
                }
              
                if (type == 0) {
                    if (dwtObject.GetImageBitDepth(dwtObject.CurrentImageIndexInBuffer) == 1)
                        dwtObject.ConvertToGrayScale(dwtObject.CurrentImageIndexInBuffer);
                    dwtObject.SaveAsJPEG(name + ".jpg", dwtObject.CurrentImageIndexInBuffer);
                }
                else if (type == 1)
                    dwtObject.SaveAllAsMultiPageTIFF(name + ".tiff");
                else if (type == 2) { 
                    dwtObject.SaveAllAsPDF(name + ".pdf");
                }
              
                alert("Save successfully!");
                resolve();
            });
        }
      
  7. Run and Test the Application: Launch the Blazor app and test the scanning functionality. Ensure that a compatible scanner (HP, Canon, Epson, etc.) is connected.

    Blazor WebAssembly document scanning app

Source Code

https://github.com/yushulx/blazor-barcode-mrz-document-scanner