How to Scan Documents Inside a Shadow DOM Web Component with Dynamic Web TWAIN

Shadow DOM is a web standard designed to encapsulate HTML and CSS into web components. It allows for the creation of separate DOM trees and specifies how these trees interact with each other in a document. Polymer is a library developed by Google to make it easier to create reusable web components. It utilizes Shadow DOM as a core part of its architecture. This article shares how to integrate Dynamic Web TWAIN into a Polymer project for implementing document scanning functions in web browsers.

What you’ll build: A browser-based document scanning application that runs inside a Shadow DOM web component, using Polymer and the Dynamic Web TWAIN SDK to acquire scanned images from TWAIN-compatible scanners.

Key Takeaways

  • Dynamic Web TWAIN can be embedded inside Shadow DOM by binding the viewer to a shadowRoot element instead of the global document.
  • The CreateDWTObjectEx method with Viewer.bind(this.shadowRoot.querySelector(...)) is the key pattern for Shadow DOM integration.
  • Polymer’s ready() lifecycle hook is the correct place to initialize the Dynamic Web TWAIN object so that the Shadow DOM tree is fully rendered.
  • This approach applies to any Shadow DOM-based framework, not only Polymer — the same shadowRoot binding pattern works with Lit, vanilla Web Components, and other libraries.

Common Developer Questions

  • How do I scan documents inside a Shadow DOM web component?
  • Why does Dynamic Web TWAIN not render when used inside Shadow DOM, and how do I fix it?
  • How do I bind the Dynamic Web TWAIN viewer to a shadowRoot element instead of the main document?

Prerequisites

Step 1: Scaffold a Polymer Project

mkdir polymer-document-scan
cd polymer-document-scan
polymer init polymer-3-starter-kit
npm start

Polymer project

Step 2: Install the Dynamic Web TWAIN SDK

In the project directory, install the SDK:

npm install dwt

Step 3: Include the Dynamic Web TWAIN Script in index.html

<script src="node_modules/dwt/dist/dynamsoft.webtwain.min.js"></script>

Step 4: Add the Scanner UI to the Polymer Component Template

Add a div element and a button element to the HTML template in src/my-view1.js:

class MyView1 extends PolymerElement {
  MyView1() {
    this.dwtObj;
    this.deviceList = [];
  }
  static get template() {
    return html`
      <style include="shared-styles">
        :host {
          display: block;

          padding: 10px;
        }
      </style>

      <div class="card">
        <h1>Web Document Scan</h1>
        <select size="1" id="source" style="position: relative; width: 220px;"></select>
        <div id="dwtcontrolContainer"></div>
        <button on-click="handleClick">scan</button>
      </div>
    `;
  }
 
  handleClick() {
    if (DWObject) {
      
    }
  }
}
  • The div element with the ID dwtcontrolContainer is used to initialize the Dynamic Web TWAIN object.
  • The button element with the handleClick event triggers the document scanning process.

Step 5: Initialize Dynamic Web TWAIN Inside the Shadow DOM

Instantiate a Dynamic Web TWAIN object with a valid license key and the div element in the ready() method:

ready() {
    super.ready();
    // TODO: initialization
    this.initializeDWT();
  }

  initializeDWT() {
    if (Dynamsoft) {
      Dynamsoft.DWT.AutoLoad = true;
      Dynamsoft.DWT.UseLocalService = true;
      // https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform
      Dynamsoft.DWT.ProductKey = 'LICENSE-KEY';
      Dynamsoft.DWT.ResourcesPath = 'node_modules/dwt/dist/';
      Dynamsoft.DWT.CreateDWTObjectEx({ WebTwainId: 'Viewer' }, (obj) => {
        this.dwtObj = obj;
        obj.Viewer.bind(this.shadowRoot.querySelector('#dwtcontrolContainer'));
        obj.Viewer.width = 560;
        obj.Viewer.height = 600;
        obj.Viewer.show();

        let select = this.shadowRoot.querySelector('#source');
        let deviceList = this.deviceList = [];
        obj.GetDevicesAsync().then(function (devices) {
          for (var i = 0; i < devices.length; i++) {
            let option = document.createElement('option');
            option.value = devices[i].displayName;
            option.text = devices[i].displayName;
            deviceList.push(devices[i]);
            select.appendChild(option);
          }
        }).catch(function (exp) {
          alert(exp.message);
        });

      }, function (e) {
        console.log(e)
      });
    }
  }

Note: You must set the ResourcesPath to the path where the Dynamic Web TWAIN resources are located. The CreateDWTObjectEx method creates a Dynamic Web TWAIN object. The bindmethod binds the object to the div element. The GetDevicesAsync method is used to retrieve the list of available devices.

Step 6: Trigger Document Scanning on Button Click

Implement the handleClick method to trigger the document scanning process:

handleClick() {
    var obj = this.dwtObj;
    if (obj) {
      obj.SelectDeviceAsync(this.deviceList[this.shadowRoot.querySelector('#source').selectedIndex]).then(function () {
        return obj.AcquireImageAsync({
          IfShowUI: false,
          IfCloseSourceAfterAcquire: true
        });
      }).catch(function (exp) {
        alert(exp.message);
      });
    }
  }

Step 7: Run and Test the Scanning App

npm start

Open a web browser and navigate to http://localhost:8081/ to see the document scanning app implemented in Polymer.

Polymer Shadow DOM document scan

Common Issues and Edge Cases

  • Dynamic Web TWAIN viewer does not appear inside Shadow DOM: This happens when you use document.getElementById instead of this.shadowRoot.querySelector. Shadow DOM encapsulation prevents global DOM queries from finding elements inside the component. Always use this.shadowRoot.querySelector('#dwtcontrolContainer') when binding the viewer.
  • Scanner list is empty or GetDevicesAsync returns no devices: Ensure the Dynamsoft Service is installed and running on the client machine. Dynamic Web TWAIN requires its local service for TWAIN scanner communication. Check that UseLocalService is set to true.
  • Resource files fail to load (404 errors for .wasm or worker files): Verify that Dynamsoft.DWT.ResourcesPath points to the correct node_modules/dwt/dist/ directory relative to your served index.html. Path mismatches are the most common setup failure.

Source Code

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