kbarcode part 1 – JavaScript

Short story:

Github repository for kbarcode – the JavaScript part of the solution. You can use it on its own, without needing Cordova at all.

Demo of kbarcode finding a barcode and then the barcode parameters being printed out onto the image that the barcode was found in.

Long story:

There are already a few barcode readers for Cordova. The most popular one is the official Phonegap barcode plugin, which is based on the amazingly comprehensive ZXing library of algorithms.

At FieldMotion, we were using the official plugin, but it had a few short-comings that meant we had to look for a better solution:

  • When looking for a barcode, the plugin opens up an external camera application. This means that your own application stops, the external app is started, and when you find your barcode, yur app is started up again. This process is very jarring, and noticeably slow.
  • You have absolutely no say over the look of the barcode scanner.
  • If you want multiple barcodes, you are out of luck – you’re just going to have to go through the selection process manually for every one of them.

What we wanted was:

  • A small camera view to appear when we press to select a barcode.
  • To be able to style this ourselves in whatever way we want.
  • To optionally keep the scanner open after it has found a barcode, so that it can keep scanning for others if need be.
  • It must feel natural and fast.

So, we went looking.

The nearest thing to a solution that we found was a combination of two plugins – Moonware’s CameraPlus plugin, which allows the camera to be opened in the background and its photos returned to a JavaScript callback for you to handle however you wish, and Eddie Larsson’s JOB (JavaScript-only Barcode Reader).

In combination, these appear to be perfect – we could get images via CameraPlus, display them in a popup UI that could be used by the user to center the barcode, and then use JOB to scan the image and retrieve the code.

Unfortunately, this method is SLOW.

I identified two main reasons for this:

  1. Streaming images to JavaScript via a Java bridge is very slow, because the images need to be encrypted in Base64 (increasing their size), and the images also need to be in high resolution in order to give the barcode reader the best chance it can get.
  2. The method that Eddie’s algorithm uses is to find the barcode in the image, no matter where it is, which involves reading the entire image. In JavaScript. Brilliant, but slow.

After some wracking of the brain, I came up with this solution:

  • Tweak the CameraPlus plugin so it returns just a small image to be displayed, and also a 1px high gray-scale strip from the center of a higher-resolution image (in byte array format).
  • Write a barcode decryption algorithm that will find a barcode in a 1D array of gray-scale values, instead of a 2D image.

This worked wonderfully. We now have a very usable barcode reader that is not very laggy, and finds the barcodes incredibly quickly. We’re also only interested in the EAN-13 encoding, so we don’t need to check for other encodings.

The reason we chose to use a 1D strip instead of the entire image, is that if you have a UI which has a marker displayed where you want the user to put the barcode, they are psychologically inclined to do so, so you really only need to consider that single central strip, and can safely ignore the rest of the image.

It’s a Worker, so it runs in a separate thread to the rest of your code. No need to include it in your HTML file – just correct the reference to the file in the code example below.

Example usage:
[javascript]
var kbarcode=new Worker(‘kbarcode.js’);
kbarcode.addEventListener(‘message’, function(result) {
if (result.value) {
alert(result.value);
}
});
kbarcode.postMessage({
‘cmd’: ‘decode’,
‘img’: [43,43,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,39,39,39,40,39,39,39,39,38,38,38,38,38,39,39,39,40,40,40,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,40,40,40,40,39,39,39,39,39,39,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,42,42,42,42,42,43,43,43,43,43,42,42,42,42,42,42,43,43,43,43,43,44,44,44,44,44,45,45,45,45,45,46,46,46,46,46,47,47,47,47,48,48,48,48,49,49,49,49,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,55,55,56,56,57,57,58,58,59,59,59,60,61,61,62,63,63,64,65,65,65,66,66,67,68,69,70,71,71,73,74,79,115,166,191,200,202,204,204,204,205,206,206,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,206,206,206,206,206,206,206,205,205,205,205,203,198,184,145,93,97,143,178,181,156,105,90,114,162,175,152,105,83,81,80,79,80,80,81,88,121,168,176,141,99,86,85,85,85,98,159,189,196,198,200,200,200,198,194,175,123,95,113,162,189,196,196,195,186,146,98,96,135,174,177,147,100,91,116,171,189,195,195,191,173,127,85,81,80,79,79,78,78,84,107,163,174,157,107,83,79,79,79,79,78,78,78,78,78,85,116,167,175,155,108,88,104,156,180,171,124,95,100,147,186,195,199,201,201,201,201,202,201,199,193,172,120,91,104,150,186,196,198,199,200,199,197,191,168,101,84,82,82,82,98,149,178,175,132,94,90,123,169,179,155,101,89,108,154,174,170,121,88,89,135,170,171,137,90,85,120,165,176,156,111,85,80,80,80,80,81,82,91,131,179,191,193,193,188,167,115,90,102,155,174,168,126,89,82,82,82,82,82,83,84,106,161,187,193,194,193,182,142,96,95,155,183,193,193,194,190,172,117,95,115,163,186,195,198,198,198,197,196,189,159,113,91,104,161,176,171,128,96,97,138,181,192,196,198,198,199,199,199,199,196,193,182,142,99,92,130,175,189,192,192,189,173,127,87,81,81,80,79,79,80,81,110,155,171,159,118,87,81,80,79,79,79,80,85,112,161,173,158,116,94,99,141,176,187,189,189,181,154,113,93,99,152,173,168,131,97,96,131,174,191,196,198,198,198,199,200,201,201,201,201,201,201,200,199,199,199,198,196,193,181,150,101,91,124,159,169,155,116,89,82,82,82,89,133,173,187,189,189,186,167,124,96,97,131,166,167,142,103,84,79,78,77,77,77,77,85,123,161,170,150,105,89,104,146,169,164,133,92,89,126,166,182,186,187,185,171,125,93,80,78,78,77,78,78,80,107,149,166,158,120,91,94,136,169,180,183,183,181,165,125,94,81,80,79,82,134,169,182,184,184,179,163,121,88,97,138,161,159,128,93,88,129,164,179,186,187,188,188,187,180,163,124,91,79,77,77,81,128,157,159,133,97,85,105,145,157,143,106,87,97,146,174,181,183,184,184,184,182,174,132,96,82,77,76,78,106,154,158,135,99,86,105,150,172,182,185,186,186,187,188,189,189,189,189,189,189,189,189,189,189,189,189,186,185,184,184,183,183,179,167,136,100,79,70,68,66,65,64,64,62,61,60,60,60,60,59,59,59,59,58,58,58,58,57,57,57,57,56,56,55,55,54,54,53,53,53,52,52,51,50,50,49,48,47,46,45,45,44,43,43,42,41,41,40,39,39,38,38,38,38,38,38,37,37,37,37,37,37,37,37,36,36,36,37,37,37,38,38,38,38,39,39,38,37,37,37,36,35,34,33,33,32,32,32,32,32,31,31,31,31,31,31,31,31,31,31,31,32,32,32,31,31,31,31,31,30,30,29,29,29,29,29,28,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,28,28,28]
});
[/javascript]

In the next post in this series, I’ll upload the Cordova plugin we developed to use with this.

Leave a Reply