Document Web Scanning in HTML5 and Java

In this tutorial, I am about to make a more complicated solution – Web-based document imaging capture, combining the implementation of Java TWAIN and Java WebSocket with Dynamic .NET TWAIN library, C#, JNI, Java Swing, Jetty and HTML5 WebSocket. Here is the workflow:

Java Web TWAIN workflow

Prerequisites

You need to read the following articles first and try relevant sample code.

How to Run

  1. In Eclipse, run the project as Java Application, and select the class UIMain.jtwain server

    When you see the window, the WebSocket server has already been initialized.

    •  The button Load is used to load images from the local disk.
    • The button Send is used to push images from the WebSocket server to Web clients.
    • The button Scan is used to scan documents locally and automatically send captured images to remote Web clients.
  2. Open a Web client in Chrome, and select a source to scan.jtwain source
  3. Display the captured image.jtwain scan

The Anatomy of Web-based Document Imaging Capture

Import all relevant libraries to the new project.

jtwain libs

Modify the source code of Java TWAIN and Java Websocket to make them parts of the new project.

Create a class named SourceManager, and add all methods from Java TWAIN project:

package com.data;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;

import javatwain.DotNetScanner;
import javatwain.IJavaProxy;
import javatwain.INativeProxy;

import javax.swing.Timer;

import com.server.WSHandler;
import com.util.ImageUtil;

import net.sf.jni4net.Bridge;

public class SourceManager implements INativeProxy {
	private IJavaProxy mScanner;
	private String[] mSources;
	private ScanAction mScanAction;

    public SourceManager() {
		initTWAIN();
		mScanAction = new ScanAction();
    }

	private void initTWAIN() {
		try {
			Bridge.init();
			Bridge.LoadAndRegisterAssemblyFrom(new java.io.File("libs\\jni\\JavaTwain.j4n.dll"));
		}
		catch (Exception e) {
            e.printStackTrace();
        }

		mScanner = new DotNetScanner();
		mScanner.RegisterListener(this);
		mSources = mScanner.GetSources();
	}

	public String[]	getSources() {
		return mSources;
	}

	public synchronized void acquireImage(int index) {
		mScanAction.setIndex(index);
		mScanAction.start();
	}

    @Override
	public boolean Notify(String message, String value) {
    	ArrayList<WSHandler> sessions = WSHandler.getAllSessions();
    	for (WSHandler session : sessions) {
    		session.sendImage(ImageUtil.getImageBytes(new File(value)));
    	}

        return true;
    }

    public class ScanAction {
    	private int mIndex;
    	private int mDelay = 1;
    	private Timer mTimer;

    	public ScanAction() {
    		mTimer = new Timer(mDelay, mTaskPerformer);
    		mTimer.setRepeats(false);
    	}

    	private ActionListener mTaskPerformer = new ActionListener() {
            @Override
    		public void actionPerformed(ActionEvent evt) {
        		mScanner.AcquireImage(mIndex);
            	ActionListener taskPerformer = new ActionListener() {
                    @Override
        			public void actionPerformed(ActionEvent evt) {
        				mScanner.CloseSource();
                    }
                };
        		int delay = 1; 
                Timer timer = new Timer(delay, taskPerformer);
                timer.setRepeats(false);
                timer.start();
            }
        };

        public void setIndex(int index) {
        	mIndex = index;
        }

        public void start() {
        	mTimer.start();
        }
    }
}

The format of the message transferred between Java WebSocket Server and JavaScript client is JSON.

Send JSON data in Java:

        JsonObject jsonObj = new JsonObject();
        JsonArray jsonArray = new JsonArray();

        String[] sources = mSourceManager.getSources();
        if (sources != null) {
        	for (String source : sources) {
        		jsonArray.add(new JsonPrimitive(source));
        	}
        }

        jsonObj.add(Msg.MSG_SOURCES, jsonArray);

        String s = jsonObj.toString();

        try {
			session.getRemote().sendString(s);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

Parse and read JSON data in Java:

public void onMessage(String message) {
    	JsonParser parser = new JsonParser();
    	boolean isJSON = true;
    	JsonElement element = null;
    	try {
    		element =  parser.parse(message);
    	}
    	catch (JsonParseException e) {
    		System.out.println("exception: " + e);
    		isJSON = false;
    	}

        if (isJSON && element != null) {
        	JsonObject obj = element.getAsJsonObject();
        	element = obj.get(Msg.MSG_MESSAGE);
        	if (element != null) {
        		switch (element.getAsString()) {
        		case Msg.MSG_SOURCE:
        			int index = obj.get(Msg.MSG_INDEX).getAsInt();
        			mSourceManager.acquireImage(index);
        			break;
        		}
        	}
        }

        System.out.println("Message: " + message);
    }

Send JSON data in JavaScript:

var json = {};
json.Message = MSG_SOURCE;
json.Index = i;
var msg = JSON.stringify(json);
ws.send(msg);

Parse and read JSON data in JavaScript:

ws.onmessage = function (evt) { 
    var data = evt.data;
    	var json = JSON.parse(data);
    	var value = json[MSG_SOURCES];
    	showSources(value);
    }

Create a class ScanAction which includes a Timer in UI thread. The Timer is used to trigger image capture event.

public class ScanAction {
    	private int mIndex;
    	private int mDelay = 1;
    	private Timer mTimer;

    	public ScanAction() {
    		mTimer = new Timer(mDelay, mTaskPerformer);
    		mTimer.setRepeats(false);
    	}

    	private ActionListener mTaskPerformer = new ActionListener() {
            @Override
    		public void actionPerformed(ActionEvent evt) {
        		mScanner.AcquireImage(mIndex);
            	ActionListener taskPerformer = new ActionListener() {
                    @Override
        			public void actionPerformed(ActionEvent evt) {
        				mScanner.CloseSource();
                    }
                };
        		int delay = 1; 
                Timer timer = new Timer(delay, taskPerformer);
                timer.setRepeats(false);
                timer.start();
            }
        };

        public void setIndex(int index) {
        	mIndex = index;
        }

        public void start() {
        	mTimer.start();
        }
    }

When the image data is available, send it to all Web clients:

public boolean Notify(String message, String value) {
    	ArrayList<WSHandler> sessions = WSHandler.getAllSessions();
    	for (WSHandler session : sessions) {
    		session.sendImage(ImageUtil.getImageBytes(new File(value)));
    	}

        return true;
    }

Source Code

JavaWebTWAIN