Nick's Blog

Scanning Drivers License Barcodes on Webforms in Javascript

This weekend I wrote a Javascript AAMVA Driver License/Identification Card Parser for use with USB 2D Barcode Scanners.  The result was an interesting reading through specs for the PDF 417 and DMV websites, but I did get a rough proof of concept together to at least show it works.  I spent a fair amount of time searching the interwebs for something out there to do this, but most all the libraries I did find were more about scanning/decoding the image of the barcode itself (like with a webcam).  I didn't need any of that as I was using a hardware USB scanner that appears to the computer as a keyboard.

The convention I volunteer at has for many years used barcodes scanners to scan driver's license data to speed the import for at-con registration forms.  This greatly speeds up entry of member data (name and address).  We're using a new system this year and looking to get new scanners.  The old scanners we had were a higher end model that decoded the data in the PDF 417 and let you configure the fields and order the scanner would return, so you could basically put your cursor at the top of a form, scan the barcode and the form would get filled in just like it was being typed from a keyboard.

Looking to save some money, I found some much lower cost scanners (in the $50 range) on Amazon.
https://www.amazon.com/Bluehresy-Barcode-Scanner-Datamatrix-Handheld/dp/B075DVFLJ7/ext ste

Unboxing the scanner, it definatly is no frills.  In the box you get the scanner and a 1-sheet Quick Refernce Guide with Chinese as the first language.

Plugging it into my Mac, it showed up as a keyboard just fine.  So I fired up TextEdit and did a scan.  What I got back was a whole bunch of gibberish, but I could at least make out some information like my name and address inside there.  There was also a lot of beeping as some unknown characters were attempted to get typed into TextEdit.  So the scanner did work, but the results were a little more raw then what I was expecting having only used higher end scanners.

My next step was to write a simple HTML web form and some Javascript to see if I could get a better handle on what exectly the scanner was trying to type out.  Capturing the onkeydown event, I was able to see the thing that was most likely making TextEdit freak out was the scanner attempting to send CTRL+SHIFT+J sequences.  TextEdit didn't much like it, but Javascript was able to capture them without issue.

Knowing that I would be able to capture everything the scanner was sending in Javascript with regular onkeydown and onkeypress events, it was time to look into the spec for exactly what I was receiving.  The AAMVA DL/ID Card Standard was what I was looking for and they have a PDF available with the full spec describing the header and format of all the data.

The result of what I came up with is posted to github here: https://github.com/ketaro/LicenseParser

It contains the Javascript library along with an example HTML file with a web form and a console area where you can see the raw data being recevied.  Since the scanner acts just like a keyboard, it's typing in a bunch of characters you don't want entered into your web form.  After you initialize the library, it starting monitoring keyboard input on that web page looking for the characters that trigger the start of a PDF417 scan (@ + CR + CTRL+J).  Once the LicenseParser see that sequence, it enabled "capturing" mode.  In "capturing" mode, any characters typed on that web page will no longer be entered into whichever field has your focus (through an event.preventDefault() in the onkeypress event handler).  Instead, the data is stored in an interal stack until special data (CTRL+M) or segment terminator (CTRL+J) characters are seen.

After the first segement terminator, the stack should contain all the data in the PDF417 header.  This is then parsed out and validated.  The header must start with "ANSI ".  The header also shows version information and how many segments are in the barcode (called "the file" in the spec).  This gives us an indication so we know when we've reached the end of the data.

After the header, data is received.  Data is prefixed with a three-letter code (Element ID) that indicates what the field contains.  In the library, there's a lookup hash object called license_fields that contains the Element ID's I was interested in and what field I wanted to map that data to in my normalized results.  There are loads more ID's out there then what I'm capturing in the example library.  There's also a fair amount of overlap where multiple codes could represent the same data and that is refelected in the lookup hash.

Once the final sequence terminator is received, capturing mode is disable.  If the web page has a field that has the active focus, it probably received an @ character before capturing mode was turned on, so that is removed in Javascript.  The library then called a callback function that was registered with the library was initialized and passed it a hash object containing all the data that was decoeded from the scan.  This is your function on your page, so you know what you want do to with the data (like set the name and address fields on your web form).  The example HTML file shows just that being done.