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

How to Implement a Java WebSocket Server for Image Transmission with Jetty

In the previous articles, I shared how to implement a .Net WebSocket server with SuperWebSocket. Today, I’d like to continue the series WebSocket: How-to, talking about how to implement a WebSocket server in Java.

What is Jetty?

“Jetty provides a Web server andjavax.servlet container, plus support forSPDY, WebSocket, OSGi, JMX, JNDI, JAASand many other integrations.” from http://www.eclipse.org/jetty/

Quickly Setup a Java WebSocket Server

Create a class WSHandler which extends the class WebSocketHandler:

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

@WebSocket
public class WSHandler extends WebSocketHandler {

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
    }

    @OnWebSocketError
    public void onError(Throwable t) {
    }

    @OnWebSocketConnect
    public void onConnect(Session session) {
    }

    @OnWebSocketMessage
    public void onMessage(String message) {
    }

	@Override
	public void configure(WebSocketServletFactory factory) {
		// TODO Auto-generated method stub
		factory.register(WSHandler.class);
	}
}

Start the server with a port and a handler:

    public static void main(String[] args) throws Exception {
        Server server = new Server(2014);
        server.setHandler(new WSHandler());
        server.setStopTimeout(0);
        server.start();
        server.join();
    }

Done.

JavaScript Client for WebSocket Connection

We can create a simple web client for testing.

Index.htm:

<!DOCTYPE html>
<html>
    <body>
        <script src="websocket.js"></script>
    </body>
</html>

Websocket.js:

var ws = new WebSocket("ws://127.0.0.1:2014/");

ws.onopen = function() {
    alert("Opened");
    ws.send("I'm client");
};

ws.onmessage = function (evt) { 
};

ws.onclose = function() {
    alert("Closed");
};

ws.onerror = function(err) {
    alert("Error: " + err);
};

Run the app and check the message.

Image Transmission Between WebSocket Server & Web Clients

What I’m going to do: Read more