How to Make HTML5 Barcode Reader with Desktop and Mobile Cameras

Recently, I noticed someone asked whether Dynamsoft Barcode Reader SDK can work for mobile devices. Because Dynamsoft Barcode Reader SDK does not support ARM yet, we can’t use it to develop native mobile apps. However, don’t be frustrated. Inspired by the article – Face detection using HTML5, javascript, webrtc, websockets, Jetty and OpenCV, I figured out the alternative solution. You can transmit the real-time camera stream to a remote server using HTML5 in Web browsers and decode the barcode image with Dynamsoft Barcode Reader SDK on the server that running on Windows, Linux or Mac. In this article, I will illustrate how to implement an HTML5 Barcode Reader with Cameras for PC and mobile devices step by step.

Read more

Implement the Game Logic | Modern Ludo

Think of the game, we know that we have 4 teams in play. At a given time, all we need to see is where these 16 planes are. For the game engine, all it needs to know is the coordinates of these 16 planes. So we need to create another map which has a different value for each and every position a plane might be placed. So here it is:

function createValueMap() {
    var mapxy = new Array();
    mapxy.push([00, 00, 00, 00, 01, 02, 03, 04, 05, 06, 07, -4, 00, 00, 00]);
    mapxy.push([00, 00, 00, 00, 52, 00, 00, 60, 00, 00, 08, 00, 00, 00, 00]);
    mapxy.push([00, 00, 00, 00, 51, 00, 00, 61, 00, 00, 09, 00, 00, 00, 00]);
    mapxy.push([-5, 00, 00, 00, 50, 00, 00, 62, 00, 00, 10, 00, 00, 00, 00]);
    mapxy.push([46, 47, 48, 49, 00, 00, 00, 63, 00, 00, 00, 11, 12, 13, 14]);
    mapxy.push([45, 00, 00, 00, 00, 00, 00, 64, 00, 00, 00, 00, 00, 00, 15]);
    mapxy.push([44, 00, 00, 00, 00, 00, 00, 65, 00, 00, 00, 00, 00, 00, 16]);
    mapxy.push([43, 90, 91, 92, 93, 94, 95, 99, 75, 74, 73, 72, 71, 70, 17]);
    mapxy.push([42, 00, 00, 00, 00, 00, 00, 85, 00, 00, 00, 00, 00, 00, 18]);
    mapxy.push([41, 00, 00, 00, 00, 00, 00, 84, 00, 00, 00, 00, 00, 00, 19]);
    mapxy.push([40, 39, 38, 37, 00, 00, 00, 83, 00, 00, 00, 23, 22, 21, 20]);
    mapxy.push([00, 00, 00, 00, 36, 00, 00, 82, 00, 00, 24, 00, 00, 00, -3]);
    mapxy.push([00, 00, 00, 00, 35, 00, 00, 81, 00, 00, 25, 00, 00, 00, 00]);
    mapxy.push([00, 00, 00, 00, 34, 00, 00, 80, 00, 00, 26, 00, 00, 00, 00]);
    mapxy.push([00, 00, 00, -2, 33, 32, 31, 30, 29, 28, 27, 00, 00, 00, 00]);
    return mapxy;
}

Initialize the routes of all planes:

var roles = ["red", "yellow", "blue", "green"];
var routs = []; var specialPos = []; 

function initiatePlaneRouts() {
    //fly over index -->17 ~ 29 **57 each~~
    var redRout = [
        46, 47, 48, 49, 50, 51, 52, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11,
        12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
        30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 90, 91, 92, 93,
        94, 95, 72];
    var yellowRout = [
        07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
        25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
        43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 01, 02, 03, 04, 60, 61, 62, 63,
        64, 65, 82];
    var blueRout = [
        20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
        38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 01, 02, 03,
        04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 70, 71, 72, 73,
        74, 75, 92];
    var greenRout = [
        33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
        51, 52, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 80, 81, 82, 83,
        84, 85, 62];
    routs.push(redRout);
    routs.push(yellowRout);
    routs.push(blueRout);
    routs.push(greenRout);
    var redSpecial = [47, 51, 03, 07, 11, 15, 19, 23, 27, 31, 35, 39];
    var yellowSpecial = [08, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52];
    var blueSpecial = [21, 25, 29, 33, 37, 41, 45, 49, 01, 05, 09, 13];
    var greenSpecial = [34, 38, 42, 46, 50, 02, 06, 10, 14, 18, 22, 26];
    specialPos.push(redSpecial);
    specialPos.push(yellowSpecial);
    specialPos.push(blueSpecial);
    specialPos.push(greenSpecial);
}

Now  it’s time to create a JS object to represent the status of the game. The ‘playStatus’ basically holds all the positions of the 16 planes and other information like the name of the player, the color of the player, whether it’s allowed to take off, etc. Read more

Placing the Airports and Planes | Modern Ludo

Compared with the regular tiles or ‘stations’ we talked about, the airport is not a regular shape. So a lot of math is involved in the creation of the airport. First of all, we want the airport to appear as a rounded rectangle. So we add a method to the canvas object. Reference: http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas.

CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
    if (w < 2 * r) r = w / 2;
    if (h < 2 * r) r = h / 2;
    this.beginPath();
    this.moveTo(x + r, y);
    this.arcTo(x + w, y, x + w, y + h, r);
    this.arcTo(x + w, y + h, x, y + h, r);
    this.arcTo(x, y + h, x, y, r);
    this.arcTo(x, y, x + w, y, r);
    this.closePath();
    return this;
}

Then we draw our first airport stroke by stroke. It’s quite some work but that’s the beauty of the Canvas element! You can draw on it just like a real canvas and it’s done in a millisecond. Read more

Draw the Board | Modern Ludo

Today we start making our Modern Ludo Game from scratch.

The first thing to do is draw the board. For this part, we’ll simply use the <Canvas> in HTML5.

The MarkUp is very simple:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>ModernLudo</title>
    <link rel="stylesheet" type="text/css" href="Styles/modernludo.css" />
</head>
<body>
    <div id="playGround" style="display:none;">
        <div id="main">
            <canvas id="gameboard"></canvas>
        </div>
    </div>
    <script type="text/javascript" src="Scripts/modernludo.js"></script>
</body>
</html>

CSS:

body {
    padding: 0;
    margin: 0 auto;
    overflow-y: hidden;
    background-color: #0D0D0D;
}
div#main {
    background-color: khaki;
    margin: 0 auto;
    padding: 0;
}

#gameboard {
    position: absolute;
    margin: 0;
    padding: 0;
    box-sizing: padding-box;
}

The important part is the JavaScript. How to actually draw the board:

  1. Placing the board in the browser window. Considering that we want to build an application that runs in web browsers, we need to make sure that our board can adjust itself to fit a browser window of any size:
    function refreshBoard() {
        canvasWidth = window.innerHeight - 10;
        canvasHeight = window.innerHeight;
        maindiv.style.width = canvasWidth + "px";
        maindiv.style.height = canvasWidth + "px";
        ctx.canvas.width = canvasWidth;
        ctx.canvas.height = canvasWidth;
    }
  2. Creating the background. Our game is Modern Ludo, so a blue sky makes for a good background:  Read more

A Simple Chrome Extension to Save Web Page Screenshots to Local Disk

A Chrome extension is a package of files including a manifest file, HTML files, JavaScript files and other resources. With extensions, we can add more functionalities to empower Chrome.

Getting Started with Chrome Extension

Anatomy of Web Page Screenshots

The manifest file:

{

    "name": "Screenshot Extension",

    "version": "1.0",

    "description": "A simple screenshot extension",

    "background": {

        "persistent": false,

        "scripts": ["background.js"]

    },

    "content_scripts": [

        {

            "matches" : ["<all_urls>"],

            "js": ["content.js"]

        }

    ],

    "browser_action": {

        "default_icon": "camera.png",

        "default_title": "Screenshot"

    },

    "permissions": ["tabs", "<all_urls>", "activeTab"],

    "manifest_version": 2

}
  • The background.js controls the logic of the extension, whereas content.js interacts with web pages. They send messages to communicate with each other.
  • If you want to make your extension work for all web sites, you have to set the permission <all_urls>

Capture the visible area of the web page when clicking the extension icon:

       chrome.browserAction.onClicked.addListener(function(tab) {

            chrome.tabs.captureVisibleTab(null, {

                format : "png",

                quality : 100

            }, function(data) {

                screenshot.data = data;

            });

        });

Message passing between the extension and web pages:

  • In background.js
                  chrome.tabs.query({

                        active : true,

                        currentWindow : true

                    }, function(tabs) {

                        chrome.tabs.sendMessage(tabs[0].id, {ready : "ready"}, function(response) {

                            if (response.download === "download") {

                            }

                        });

                    });
  • In content.js
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {

    if (msg.ready === "ready") {

        if (confirm('Do you want to capture the screen?')) {

            sendResponse({download : "download"});

        }

    }

});

Save the screenshot:

    saveScreenshot : function() {

        var image = new Image();

        image.onload = function() {

            var canvas = screenshot.content;

            canvas.width = image.width;

            canvas.height = image.height;

            var context = canvas.getContext("2d");

            context.drawImage(image, 0, 0);

            // save the image

            var link = document.createElement('a');

            link.download = "download.png";

            link.href = screenshot.content.toDataURL();

            link.click();

            screenshot.data = '';

        };

        image.src = screenshot.data;

    },

Install and Run Chrome Extension

  • Check Developer mode.
  • Click Load unpacked extension to select your project folder.load chrome extension
  • Open a web page and click the extension icon in the toolbar.
  • Confirm and save the screenshot.

Download Sample Code

Chrome_Screenshot

How to Remotely Scan Documents from PCs and Mobile Devices with HTML5 WebSocket and Dynamic .NET TWAIN

In this tutorial, I want to make a more complicated WebSocket demo, which enables me to capture images from scanners on server side, and view the images in desktop web browsers or mobile web browsers. Here is the flowchart:

 flow chart

Prerequisites

Scanning Documents through WebSocket Connection

Initialize Dynamic .NET TWAIN component:

            dynamicDotNetTwain = new Dynamsoft.DotNet.TWAIN.DynamicDotNetTwain(); // create Dynamic .NET TWAIN component
            dynamicDotNetTwain.OnPostAllTransfers += new Dynamsoft.DotNet.TWAIN.Delegate.OnPostAllTransfersHandler(this.dynamicDotNetTwain_OnPostAllTransfers); 
            dynamicDotNetTwain.MaxImagesInBuffer = 64;
            dynamicDotNetTwain.IfAppendImage = true;
            dynamicDotNetTwain.IfThrowException = true;
            dynamicDotNetTwain.IfShowUI = false;

When a client is connected to WebSocket server, query all available scanner sources on server side, and serialize them as JSON data:

            int iIndex;
            dynamicDotNetTwain.OpenSourceManager();

            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            using (JsonWriter writer = new JsonTextWriter(sw))
            {
                writer.Formatting = Formatting.Indented;

                writer.WriteStartObject();
                writer.WritePropertyName("Sources");
                writer.WriteStartArray();
                for (iIndex = 0; iIndex < dynamicDotNetTwain.SourceCount; iIndex++)
                {
                    writer.WriteValue(dynamicDotNetTwain.SourceNameItems(Convert.ToInt16(iIndex)));
                }
                writer.WriteEnd();
                writer.WriteEndObject();
            }

            String msg = sw.ToString();
            session.Send(msg);

Send the JSON data to the Web client, and de-serialize the JSON data in JavaScript:

                        var json = JSON.parse(data);
                        var jValue;
                        for (jProperty in json) {
                            jValue = json[jProperty];
                            switch (jProperty) {
                                case "Sources":
                                    showSources(jValue);
                                    break;
                                case "Draw":
                                    var w = jValue[0];
                                    var h = jValue[1];
                                    imageWidth = parseInt(w);
                                    imageHeight = parseInt(h);
                                    break;
                            }
                            break;
                        }

Show the sources in drop-down list with HTML5 elements <select> and <option>:

                var sources = document.getElementById('sources');
                var option;

                var count = values.length;
                if (count == 0) {
                    option = document.createElement('option');
                    option.text = "N/A";
                    sources.appendChild(option);
                }
                else {
                    for (var i = 0; i < 3; i++) {
                        option = document.createElement('option');
                        option.text = values[i];
                        sources.appendChild(option);
                    };
                }

Acquire document images from scanners:

        private void appServer_NewMessageReceived(WebSocketSession session, string message)
        {   
            int iIndex = Int32.Parse(message);

            try
            {
                dynamicDotNetTwain.CloseSource();
                bool success = dynamicDotNetTwain.SelectSourceByIndex(Convert.ToInt16(iIndex));
                dynamicDotNetTwain.OpenSource();
                dynamicDotNetTwain.AcquireImage();
            }
            catch (Dynamsoft.DotNet.TWAIN.TwainException exp)
            {
                String errorstr = "";
                errorstr += "Error " + exp.Code + "\r\n" + "Description: " + exp.Message + "\r\nPosition: " + exp.TargetSite + "\r\nHelp: " + exp.HelpLink + "\r\n";
                MessageBox.Show(errorstr);
            }
            catch (Exception exp)
            {
                String errorstr = "";
                errorstr += "ErrorMessage: " + exp.Message + "\r\n";
                MessageBox.Show(errorstr);
            }
        }

When the image data is ready, send it to the Web client:

private void dynamicDotNetTwain_OnPostAllTransfers()
        {
            if (dynamicDotNetTwain.MaxImagesInBuffer < 1)
            {
                MessageBox.Show("no image");
                return;
            }

            Image img = dynamicDotNetTwain.GetImage(0);

            ImageData imageData = load_image(img);

            /* send width & height in JSON */
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            using (JsonWriter writer = new JsonTextWriter(sw))
            {
                writer.Formatting = Formatting.Indented;

                writer.WriteStartObject();
                writer.WritePropertyName("Draw");
                writer.WriteStartArray();
                writer.WriteValue(imageData.Width);
                writer.WriteValue(imageData.Height);
                writer.WriteEnd();
                writer.WriteEndObject();
            }
            String msg = sw.ToString();

            IEnumerable<WebSocketSession> sessions = appServer.GetAllSessions();
            foreach (WebSocketSession session in sessions) 
            {
                session.Send(msg);
                session.Send(imageData.Data, 0, imageData.Data.Length);
            }

            imageData = null;
        }

Finally, I can view the captured document images in Windows Chrome:

scan document with websocket

Based on HTML5, the solution is platform-independent, which means it supports not only Windows, but also Linux, Mac OS, iOS, Android and so forth. Thus I can also access the page and remotely operate scanners in Android Chrome:


android_scan_sources

android_scan_document

Now, you can try to make your own cloud scanner application with Dynamic .NET TWAIN and HTML5 WebSocket.

Source Code

WebSocketScanDocument

 

Image Transmission between a HTML5 WebSocket Server and a Web Client

HTML5 WebSocket facilitates the communication between web browsers and local/remote servers. If you want to learn a simple websocket example, creating a WebSocket Server in C# and a Web client in JavaScript, you can refer to SuperWebSocket, which is a .NET implementation of Web Socket Server.

In this article, I would like to share how I implemented a simple WebSocket solution for image transmission based on the basic sample code of SuperWebSocket and Dynamic .NET TWAIN, especially what problem I have solved.

Here is a quick look of the WebSocket server and the HTML5 & JavaScript client.

WebSocket ServerWebSocket Client

Prerequisites

Create a .NET WebSocket Server

Create a new WinForms project, and add the following references which are located at the folder of SuperWebSocket.

WebSocket References

Add the required namespaces:

using Dynamsoft.DotNet.TWAIN;

using SuperSocket.SocketBase;
using SuperWebSocket;

In the sample code, the server is launched with port 2012, no IP specified by default. You can specify the IP address for remote access. For example:

if (!appServer.Setup("192.168.8.84", 2012)) //Setup with listening port
{
     MessageBox.Show("Failed to setup!");
     return;
}

You can use Dynamic Web TWAIN component to do some image operation. With two lines of code, I can load an image file:

bool isLoad = dynamicDotNetTwain.LoadImage("dynamsoft_logo_black.png"); // load an image
Image img = dynamicDotNetTwain.GetImage(0);

Be careful of the image format. It is PNG. If you want to display it in a Web browser, you need to convert the format from PNG to BMP:

            byte[] result;
            using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
            {
                img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);   // convert png to bmp
                result = stream.GetBuffer();
            }

It’s not done yet. The byte array contains some extra image information, which is 54 bytes in length. So the actual data length is:

int iRealLen = result.Length - 54;
byte[] image = new byte[iRealLen];

Here are some tricky things that if you just send the subset of the byte array to your Web browser, you will find the displayed image is upside-down, and the color is also incorrect.

To fix the position issue, you need to sort the bytes of original data array from bottom to top. As to the color, exchange the position of blue and red. See the code:

            int iIndex = 0;
            int iRowIndex = 0;
            int iWidth = width * 4;
            for (int i = height - 1; i >= 0; --i)
            {
                iRowIndex = i * iWidth;
                for (int j = 0; j < iWidth; j += 4)
                {
                    // RGB to BGR
                    image[iIndex++] = result[iRowIndex + j + 2 + 54]; // B
                    image[iIndex++] = result[iRowIndex + j + 1 + 54]; // G
                    image[iIndex++] = result[iRowIndex + j + 54];     // R
                    image[iIndex++] = result[iRowIndex + j + 3 + 54]; // A
                }   
            }

Now, you can send the data via:

session.Send(imageData.Data, 0, imageData.Data.Length);

Create a JavaScript Client

To receive the image as ArrayBuffer on the client side, you have to specify the binaryType after creating a WebSocket:

ws.binaryType = "arraybuffer";

Once the image data is received, draw all bytes onto a new canvas, and finally create an image element to display the canvas data:

                var imageWidth = 73, imageHeight = 73; // hardcoded width & height. 
                var byteArray = new Uint8Array(data);

                var canvas = document.createElement('canvas');
                canvas.height = imageWidth;
                canvas.width = imageHeight;
                var ctx = canvas.getContext('2d');

                var imageData = ctx.getImageData(0, 0, imageWidth, imageHeight); // total size: imageWidth * imageHeight * 4; color format BGRA
                var dataLen = imageData.data.length;
                for (var i = 0; i < dataLen; i++)
                {
                    imageData.data[i] = byteArray[i];
                }
                ctx.putImageData(imageData, 0, 0);

                // create a new element and add it to div
                var image = document.createElement('img');
                image.width = imageWidth;
                image.height = imageHeight;
                image.src = canvas.toDataURL();

                var div = document.getElementById('img');
                div.appendChild(image);

Download Sample

WebSocketImageSendSource

How to Upload HTML Canvas Data to PHP Server

Last time, I shared my experience about how to use JavaScript saving HTML canvas data to local disk in Chrome. In this tutorial, I would like to share how to save HTML canvas data to remote service with JavaScript and PHP.

Dynamsoft Barcode Reader SDK
Ads Powered by Dynamsoft

Uploading canvas data is a little bit complicated comparing to file upload. If you want to learn how to upload a file, you can read the tutorial PHP File Upload.

Let’s get started to create a project, which includes two files upload.html and upload_data.php. In addition, you need to create a folder upload for receiving files.

upload.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>Upload Canvas Data to PHP Server</title>
	</head>
	<body>
		<h1>Upload Canvas Data to PHP Server</h1>
		<canvas width="80" height="80" id="canvas">canvas</canvas>
		<script type="text/javascript">
			window.onload = function() {
				var canvas = document.getElementById("canvas");
				var context = canvas.getContext("2d");
				context.rect(0, 0, 80, 80);
				context.fillStyle = 'yellow';
				context.fill();
			}
		</script>

		<div>
			<input type="button" onclick="uploadEx()" value="Upload" />
		</div>

		<form method="post" accept-charset="utf-8" name="form1">
			<input name="hidden_data" id='hidden_data' type="hidden"/>
		</form>

		<script>
			function uploadEx() {
				var canvas = document.getElementById("canvas");
				var dataURL = canvas.toDataURL("image/png");
				document.getElementById('hidden_data').value = dataURL;
				var fd = new FormData(document.forms["form1"]);

				var xhr = new XMLHttpRequest();
				xhr.open('POST', 'upload_data.php', true);

				xhr.upload.onprogress = function(e) {
					if (e.lengthComputable) {
						var percentComplete = (e.loaded / e.total) * 100;
						console.log(percentComplete + '% uploaded');
						alert('Succesfully uploaded');
					}
				};

				xhr.onload = function() {

				};
				xhr.send(fd);
			};
		</script>
	</body>
</html>
  • Create a canvas tag, and draw a rectangle on canvas when the whole page has been loaded.
  • Create a button for triggering uploading event.
  • Create a form with a hidden input tag, which is used to save the data URL.
  • Use XMLHttpRequest to asynchronously upload form data to PHP service.

upload_data.php:

<?php
$upload_dir = "upload/";
$img = $_POST['hidden_data'];
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
$file = $upload_dir . mktime() . ".png";
$success = file_put_contents($file, $data);
print $success ? $file : 'Unable to save the file.';
?>

Now deploy the whole project to a web server, such as nginx, Apache and so forth. Visit localhost:port/upload.html, click the button, and open the file folder to check whether the image is there.

Anything unclear? Please feel free to contact me at {desmond at Dynamsoft dot com}.