Convert a Mobile Camera into a Document Scanner | Mobile Capture
Dynamic Web TWAIN has added some exciting features related to the mobile platform since its version 16.
- Document capturing via mobile cameras, 16.0
- PDF Rasterizer mobile edition, 16.0
- LoadImageEx supports mobile platform, 16.1
- Acquiring images from a remote scanner, 16.1
- The scanDocument API which makes it easy to call a built-in document scanner to scan documents using mobile cameras in a mobile-friendly interface with features like automatic capture, document borders detection and image filter, 17.2
All of these make it possible to build a mobile document scanning and capture app in HTML5 with ease. Such a mobile document scanner can not only utilize high-resolution cameras equipped on modern mobile devices but also interact with connected scanners. Your phone can be the hub of the entire document scanning process.
Deprecation Note: the document scanning via camera feature in Dynamic Web TWAIN has been moved to a new product called Mobile Web Capture. Please use this instead.
This article is Part 1 in a 2-Part Series.
Requirements
Mobile Document Scanning Implementation
Let’s implement the scanner in steps.
Preview
Our goal is to build a web document scanner that mobile devices can use. The layout should be optimized. Here is a preview of the final result:
![]() |
![]() |
On the top is a viewer of scanned documents. Below the viewer, there is an actions tab. Since there is not much screen space for mobile devices, using a tab is a good choice if there are many actions available.
After pressing the Scan Documents
button, a built-in scanner will appear. Users can take a photo, edit the result and then save it.
![]() |
![]() |
A Minimum Implementation: Snapshot from MediaDevices Cameras
Here, we first create a minimum version which captures images with the camera and save them to image or PDF files. Scanned documents can later to exported to image or PDF files, as well. The entire HTML file is as below.
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Web TWAIN Mobile Sample</title>
<script type="text/javascript" src="Resources/dynamsoft.webtwain.initiate.js"></script>
<script type="text/javascript" src="Resources/dynamsoft.webtwain.config.js"></script>
<script type="text/javascript" src="Resources/addon/dynamsoft.webtwain.addon.camera.js"></script>
<script type="text/javascript" src="Resources/addon/dynamsoft.webtwain.addon.pdf.js"></script>
<script type="text/javascript" src="common.js"></script>
<script type="text/javascript" src="tabs.js"></script>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/skeleton.css">
<link rel="stylesheet" href="css/tabs.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div style="height:100%;width:100%;position:absolute;overflow:hidden;">
<div id="dwtcontrolContainer" style="width:100%;height:60%"></div>
<div id="action" style="height:40%;">
<div class="tab" style="display:flex;overflow-x:auto;height:50px;">
<button class="tablinks" onclick="switchTab(event, 'camera')">Camera</button>
<button class="tablinks" onclick="switchTab(event, 'save')">Save</button>
</div>
<div style="height:calc(100% - 50px);overflow-y: auto;">
<div id="camera" class="tabcontent">
<input id="scanDocumentButton" type="button" value="Scan Documents" onclick="ScanDocuments();" />
</div>
<div id="save" class="tabcontent">
Default Filename: <input type="text" id="filename" value="DynamicWebTWAIN"/>
<input onclick="SaveWithFileDialog();" type="button" value="Save">
<div>
<label>
<input type="radio" value="jpg" name="ImageType" id="imgTypejpeg" />JPEG</label>
<label>
<input type="radio" value="tif" name="ImageType" id="imgTypetiff" />TIFF</label>
<label>
<input type="radio" value="pdf" name="ImageType" id="imgTypepdf" checked="checked" />PDF</label>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
Dynamsoft.DWT.UseLocalService = false;
Dynamsoft.DWT.RegisterEvent('OnWebTwainReady', Dynamsoft_OnReady); // Register OnWebTwainReady event. This event fires as soon as Dynamic Web TWAIN is initialized and ready to be used
var DWObject;
switchTab(null, 'camera')
function Dynamsoft_OnReady() {
DWObject = Dynamsoft.DWT.GetWebTwain('dwtcontrolContainer'); // Get the Dynamic Web TWAIN object that is embeded in the div with id 'dwtcontrolContainer'
if (DWObject) {
DWObject.Viewer.width="100%";
DWObject.Viewer.height="100%";
DWObject.SetViewMode(2, 2);
}
}
function ScanDocuments() {
if (DWObject) {
let cameraContainer = document.createElement("div");
cameraContainer.className = "fullscreen";
document.body.appendChild(cameraContainer);
function funcConfirmExit () {
cameraContainer.remove();
return true;
}
let scanOptions = {};
scanOptions.element = cameraContainer
scanOptions.scannerViewer = {funcConfirmExit: funcConfirmExit}
DWObject.Addon.Camera.scanDocument(scanOptions).then(
function(){
console.log("OK");
},
function(error){
console.log(error.message);
}
);
}
}
//Callback functions for async APIs
function OnSuccess() {
console.log('successful');
}
function OnFailure(errorCode, errorString) {
alert(errorString);
}
function SaveWithFileDialog() {
if (DWObject) {
if (DWObject.HowManyImagesInBuffer > 0) {
DWObject.IfShowFileDialog = true;
var filename=document.getElementById("filename").value;
if (document.getElementById("imgTypejpeg").checked == true) {
//If the current image is B&W
//1 is B&W, 8 is Gray, 24 is RGB
if (DWObject.GetImageBitDepth(DWObject.CurrentImageIndexInBuffer) == 1)
//If so, convert the image to Gray
DWObject.ConvertToGrayScale(DWObject.CurrentImageIndexInBuffer);
//Save image in JPEG
DWObject.SaveAsJPEG(filename+".jpg", DWObject.CurrentImageIndexInBuffer);
}
else if (document.getElementById("imgTypetiff").checked == true)
DWObject.SaveAllAsMultiPageTIFF(filename+".tiff", OnSuccess, OnFailure);
else if (document.getElementById("imgTypepdf").checked == true)
DWObject.SaveAllAsPDF(filename+".pdf", OnSuccess, OnFailure);
}
}
}
</script>
</body>
</html>
Here are some points worth mentioning:
-
We use the Skeleton CSS framework and add the
viewport
meta to make the page responsive and suitable for mobile devices.<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" /> <link rel="stylesheet" href="css/normalize.css"> <link rel="stylesheet" href="css/skeleton.css"> <link rel="stylesheet" href="css/tabs.css">
- The tabs are created according to How To Create Tabs. The tab’s style is modified so that the tab is horizontally scrollable.
- The camera addon’s
scanDocument
API is used to access mobile cameras which is through MediaDevices. MediaDevices requires HTTPS. If you don’t have an HTTPS server, you can create a local server using Python 3 following this post. - You need to copy the Resources folder from
Dynamsoft\Dynamic Web TWAIN SDK <version>\Resources
to the project’s root directory.
Build the Muscle: Add More Inputs
Now we can add new functions based on the minimum implementation. Basically, there are three input sources: scanner, camera, local files.
Enable Loading Local Files
Loading local files is very simple with the LoadImageEx
method. It can load images as well as PDFs.
JavaScript:
//Callback functions for async APIs
function OnSuccess() {
console.log('successful');
}
function OnFailure(errorCode, errorString) {
alert(errorString);
}
function LoadLocal(){
DWObject.IfShowFileDialog = true;
// PDF Rasterizer Addon is used here to ensure PDF support
DWObject.Addon.PDF.SetResolution(200);
DWObject.Addon.PDF.SetConvertMode(Dynamsoft.EnumDWT_ConvertMode.CM_RENDERALL);
DWObject.LoadImageEx("", Dynamsoft.EnumDWT_ImageType.IT_ALL, OnSuccess, OnFailure);
}
HTML:
<button class="tablinks" onclick="switchTab(event, 'local')">Local</button>
......
<div id="local" class="tabcontent">
<input onclick="LoadLocal();" type="button" value="Load Images">
</div>
Interact with Scanners
Since mobile devices normally cannot connect to scanners physically, we can only use the RemoteScan capability. How to Build a Universal Document Scanning App in HTML5 explains how to do this in detail.
Build the Muscle: Image Editing
We may also need to remove, reorder and edit images. Dynamic Web TWAIN provides rich APIs to do this.
It also has a powerful image editor which can adapt to mobile devices. Functions like rotation, deskewing, flipping, mirroring, cropping and cutting are supported.
JavaScript:
function ShowImageEditor(){
var editorSettings = {
/*element: document.getElementById("imageEditor"),
width: 600,
height: 400,*/
border: '1px solid rgb(204, 204, 204);',
topMenuBorder: '',
innerBorder: '',
background: "rgb(255, 255, 255)",
promptToSaveChange: true
};
var imageEditor = DWObject.Viewer.createImageEditor(editorSettings);
imageEditor.show();
}
HTML:
<button class="tablinks" onclick="switchTab(event, 'edit')">Edit</button>
......
<div id="edit" class="tabcontent">
<input onclick="ShowImageEditor()" type="button" value="Show Editor" />
</div>
Source Code
Get the source code and have a try! The final version can run on both desktop and mobile.
https://github.com/Dynamsoft/mobile-document-scanning
Related Articles
- Ionic Document Scanner in React. This article uses the
scanDocument
API. - How to Build a PWA Document Scanner with Ionic Vue. This article uses the
scanDocument
API. - Scan with Document Cameras using Dynamic Web TWAIN. This article uses the
showVideo
API.