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.
This article is Part 5 in a 5-Part Series.
- Part 1 - Blazor WebAssembly: Building Web Apps for Digitizing Documents with C# and .NET
- Part 2 - How to Build a .NET Blazor Application for Scanning Barcode and QR Code
- Part 3 - How to Create a Blazor Web App for Document PDF Viewing and Annotation
- Part 4 - How to Integrate MRZ Recognition into a Blazor Web Application
- Part 5 - How to Digitize Documents in a Blazor Web App Using TWAIN, WIA, SANE, ICA, and eSCL Scanners
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.
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.
-
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:
- Create a New Blazor WebAssembly Project: Scaffold a new Blazor WebAssembly project using Visual Studio.
-
Download Dynamic Web TWAIN: Install the Dynamic Web TWAIN package via npm.
npm install dwt
-
Integrate Dynamic Web TWAIN into the Blazor Project: Copy the
dist
folder from thenode_modules/dwt
directory to thewwwroot
folder in the Blazor project, and then include the JavaScript file in theindex.html
file.<script src="dist/dynamsoft.webtwain.min.js"></script>
-
Create a JavaScript File for C# Interop: To facilitate communication between Blazor (C#) and JavaScript, create a
jsInterop.js
file in thewwwroot
directory and include it in yourindex.html
:<script src="dist/dynamsoft.webtwain.min.js"></script> <script src="jsInterop.js"></script>
- Configure Resource Paths and License Key: In the
wwwroot/jsInterop.js
file, define asetLicense
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; }, ... };
-
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:
-
Create a Razor Component: Add a new Razor component named
WebTwain.razor
to thePages
directory and reference it in theLayout/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>
-
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); } },
-
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(); }); }); },
-
-
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(); } }); },
-
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(); }); },
-
-
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(); }); }
-
-
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.
Source Code
https://github.com/yushulx/blazor-barcode-mrz-document-scanner