How to Build a Document Scanning Component with LitElement
Lit is a simple library for building fast, lightweight web components. At Lit’s core is a boilerplate-killing component base class that provides reactive state, scoped styles, and a declarative template system that’s tiny, fast and expressive.
In this article, we are going to build a web component with LitElement to scan documents in an HTML website based on Dynamic Web TWAIN.
New Project
Although Lit can be used without a build system, in this article, we are going to use webpack with the following template (clone with git):
git clone https://github.com/wbkd/webpack-starter
Install Dependencies
-
Install Lit.
npm install lit
-
Install Dynamic Web TWAIN.
npm install dwt
In addition, we need to copy the resources of Dynamic Web TWAIN to the public folder.
-
Install
ncp
.npm install --save-dev ncp
-
Modify
package.json
to copy the resources for the build and start commands."scripts": { "lint": "npm run lint:styles; npm run lint:scripts", "lint:styles": "stylelint src", "lint:scripts": "eslint src", - "build": "cross-env NODE_ENV=production webpack --config webpack/webpack.config.prod.js", - "start": "webpack serve --config webpack/webpack.config.dev.js" + "build": "ncp node_modules/dwt/dist public/dwt-resources && cross-env NODE_ENV=production webpack --config webpack/webpack.config.prod.js", + "start": "ncp node_modules/dwt/dist public/dwt-resources && webpack serve --config webpack/webpack.config.dev.js" },
-
Modify
webpack.common.js
to copy the files in thepublic
folder to the output folder instead of thepublic
folder inside the output folder.new CopyWebpackPlugin({ - patterns: [{ from: Path.resolve(__dirname, '../public'), to: 'public' }], + patterns: [{ from: Path.resolve(__dirname, '../public'), to: '' }], }),
-
Write a Document Scanner Component
-
Create a new
documentscanner.js
file undersrc\scripts
with the following template.import {LitElement, html, css} from 'lit'; export class DocumentScanner extends LitElement { static properties = { }; DWObject; static styles = css` :host { display: block; } `; constructor() { super(); } render() { return html``; } } customElements.define('document-scanner', DocumentScanner);
-
Add a
div
element as the container for the controls of Dynamic Web TWAIN (mainly to view documents).render() { return html`<div id="dwtcontrolContainer"></div>`; }
-
Configure Dynamic Web TWAIN in the
constructor
. You need a license to use Dynamic Web TWAIN. You can apply for a license here.import Dynamsoft from 'dwt'; export class DocumentScanner extends LitElement { constructor() { super(); Dynamsoft.DWT.AutoLoad = false; Dynamsoft.DWT.ResourcesPath = "/dwt/dist"; Dynamsoft.DWT.ProductKey = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="; //one-day trial } }
-
Initialize Dynamic Web TWAIN in the
firstUpdated
lifecycle after the dom is first updated and bind it to the container in the previous step. In addition, dispatch a custom event with the object of Dynamic Web TWAIN so that we can control it in a parent node.DWObject; firstUpdated() { let pThis = this; let dwtContainer = this.renderRoot.getElementById("dwtcontrolContainer"); Dynamsoft.DWT.CreateDWTObjectEx( { WebTwainId: 'dwtcontrol' }, function(obj) { pThis.DWObject = obj; pThis.DWObject.Viewer.bind(dwtContainer); pThis.DWObject.Viewer.show(); pThis.DWObject.Viewer.width = "100%"; pThis.DWObject.Viewer.height = "100%"; const event = new CustomEvent('initialized', { detail: { DWObject: pThis.DWObject } }); pThis.dispatchEvent(event); }, function(err) { console.log(err); } ); }
-
Add one button to scan documents and one button to save the document images as a PDF file.
render() { return html` <div class="buttons"> <button @click=${this.scan}>Scan</button> <button @click=${this.save}>Save</button> </div> <div id="dwtcontrolContainer"></div>`; } scan(){ let pThis = this; if (pThis.DWObject) { pThis.DWObject.SelectSource(function () { pThis.DWObject.OpenSource(); pThis.DWObject.AcquireImage(); }, function () { console.log("SelectSource failed!"); } ); } } save(){ if (this.DWObject) { this.DWObject.SaveAllAsPDF("Scanned.pdf"); } }
-
Add a reactive property named
total
to reflect how many documents are scanned. We can update its value in theOnBufferChanged
event of Web TWAIN.export class DocumentScanner extends LitElement { static properties = { total: {}, }; constructor() { super(); this.total = 0; //... } render() { return html` <div class="buttons"> <button @click=${this.scan}>Scan</button> <button @click=${this.save}>Save</button> </div> <div id="dwtcontrolContainer"></div> <div class="status">Total: ${this.total}</div>`; } firstUpdated() { //... Dynamsoft.DWT.CreateDWTObjectEx( { WebTwainId: 'dwtcontrol' }, function(obj) { //... pThis.DWObject.RegisterEvent('OnBufferChanged',function () { pThis.total = pThis.DWObject.HowManyImagesInBuffer; }); //... }, function(err) { console.log(err); } ); } }
-
Set the styles for the component.
static styles = css` :host { display: block; } .buttons { height: 25px; } #dwtcontrolContainer { width: 100%; height: calc(100% - 50px); } .status { height: 25px; } `;
Use the Document Scanner Component
-
Import the component in the
index.js
file.import { DocumentScanner } from './documentscanner'; // eslint-disable-line
-
Add the component in the
index.html
.<document-scanner style="width:320px;height:480px;" ></document-scanner>
All right, we can now scan documents from the browser.
Source Code
Get the source code of the demo to have a try: