Reading Driver's License Information from PDF417 with JavaScript

A few weeks ago, I wrote an article sharing how to read driver’s license information from PDF417 on Android. Compared to building an Android native camera app, building a web camera app is much easier. In this article, let’s take a glimpse at a JavaScript sample, which is implemented with just a few lines of code by using the Dynamsoft web barcode SDK.

Online Demo

https://yushulx.me/javascript-barcode-qr-code-scanner/examples/9.x/driver_license/

Dynamsoft Web Barcode SDK

The JavaScript barcode library is available for download on npmjs.org.

You can either download the package via:

npm install dynamsoft-javascript-barcode --save

or include the online JS file directly in your HTML file:

<script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.6.42/dist/dbr.js"></script>

You need to apply for a 30-day trial license to activate the SDK:

Dynamsoft.DBR.BarcodeReader.license = "LICENSE-KEY";

Building a Web Barcode Reader in Less Than 30 Seconds

The Dynamsoft JavaScript Barcode SDK, based on WebAssembly, delivers high-performance barcode scanning functionality to web developers. It also provides built-in camera module APIs. With the deeply encapsulated JavaScript SDK, you’ll find that creating an HTML5 barcode scanner with camera support has never been more convenient.

To quickly build a web barcode scanner app, copy the following code into your HTML file:

<!DOCTYPE html>
<html>

<body>
  <script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.6.42/dist/dbr.js"></script>
  <script>
    // initializes and uses the library
    Dynamsoft.DBR.BarcodeReader.license = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==";
    var scanner = null;
    (async () => {
      scanner = await Dynamsoft.DBR.BarcodeScanner.createInstance();
      scanner.onFrameRead = results => {
        if (results.length > 0) console.log(results);
      };
      scanner.onUnduplicatedRead = (txt, result) => {
        alert(txt);
      };
      await scanner.show();
    })();
  </script>
</body>

</html>

Parsing Driver’s License Information Based on the AAMVA Standard

To better balance the accuracy and performance of decoding the PDF417 symbology, it’s advisable to configure some parameters according to the online documentation:

 let runtimeSettings = await scanner.getRuntimeSettings();
 runtimeSettings.barcodeFormatIds = Dynamsoft.DBR.EnumBarcodeFormat.BF_PDF417;
 runtimeSettings.LocalizationModes = [2,8,0,0,0,0,0,0];
 runtimeSettings.deblurLevel = 7;
 await scanner.updateRuntimeSettings(runtimeSettings);

As PDF417 is decoded by the barcode library, you need to create a parser to extract driver’s license information based on the AAMVA standard:

const DLAbbrDesMap = {
    'DCA': 'Jurisdiction-specific vehicle class',
    'DBA': 'Expiry Date',
    'DCS': 'Last Name',
    'DAC': 'First Name',
    'DBD': 'Issue Date',
    'DBB': 'Birth Date',
    'DBC': 'Gender',
    'DAY': 'Eye Color',
    'DAU': 'Height',
    'DAG': 'Street',
    'DAI': 'City',
    'DAJ': 'State',
    'DAK': 'Zip',
    'DAQ': 'License Number',
    'DCF': 'Document Discriminator',
    'DCG': 'Issue Country',
    'DAH': 'Street 2',
    'DAZ': 'Hair Color',
    'DCI': 'Place of birth',
    'DCJ': 'Audit information',
    'DCK': 'Inventory Control Number',
    'DBN': 'Alias / AKA Family Name',
    'DBG': 'Alias / AKA Given Name',
    'DBS': 'Alias / AKA Suffix Name',
    'DCU': 'Name Suffix',
    'DCE': 'Physical Description Weight Range',
    'DCL': 'Race / Ethnicity',
    'DCM': 'Standard vehicle classification',
    'DCN': 'Standard endorsement code',
    'DCO': 'Standard restriction code',
    'DCP': 'Jurisdiction-specific vehicle classification description',
    'DCQ': 'Jurisdiction-specific endorsement code description',
    'DCR': 'Jurisdiction-specific restriction code description',
    'DDA': 'Compliance Type',
    'DDB': 'Card Revision Date',
    'DDC': 'HazMat Endorsement Expiration Date',
    'DDD': 'Limited Duration Document Indicator',
    'DAW': 'Weight(pounds)',
    'DAX': 'Weight(kilograms)',
    'DDH': 'Under 18 Until',
    'DDI': 'Under 19 Until',
    'DDJ': 'Under 21 Until',
    'DDK': 'Organ Donor Indicator',
    'DDL': 'Veteran Indicator',
    // old standard
    'DAA': 'Customer Full Name',
    'DAB': 'Customer Last Name',
    'DAE': 'Name Suffix',
    'DAF': 'Name Prefix',
    'DAL': 'Residence Street Address1',
    'DAM': 'Residence Street Address2',
    'DAN': 'Residence City',
    'DAO': 'Residence Jurisdiction Code',
    'DAR': 'License Classification Code',
    'DAS': 'License Restriction Code',
    'DAT': 'License Endorsements Code',
    'DAV': 'Height in CM',
    'DBE': 'Issue Timestamp',
    'DBF': 'Number of Duplicates',
    'DBH': 'Organ Donor',
    'DBI': 'Non-Resident Indicator',
    'DBJ': 'Unique Customer Identifier',
    'DBK': 'Social Security Number',
    'DBM': 'Social Security Number',
    'DCH': 'Federal Commercial Vehicle Codes',
    'DBR': 'Name Suffix',
    'PAA': 'Permit Classification Code',
    'PAB': 'Permit Expiration Date',
    'PAC': 'Permit Identifier',
    'PAD': 'Permit IssueDate',
    'PAE': 'Permit Restriction Code',
    'PAF': 'Permit Endorsement Code',
    'ZVA': 'Court Restriction Code',
    'DAD': 'Middle Name'
};

var parseDriverLicense = txt => {
    console.log(txt);
    let lines = txt.split(']n');
    let abbrs = Object.keys(DLAbbrDesMap);
    let map = {};
    lines.forEach((line, i) => {
        let abbr;
        let content;
        if(i === 1){
            abbr = 'DAQ';
            content = line.substring(line.indexOf(abbr) + 3);
        }else{
            abbr = line.substring(0, 3);
            content = line.substring(3).trim();
        } 
        if(abbrs.includes(abbr)){
            map[abbr] = {
                description: DLAbbrDesMap[abbr],
                content: content
            };
        }
    });
    return map;
};

Finally, concatenate the information and display it on the web page:

scanner.onFrameRead = results => {
    let txts = [];
    try {
        if (results.length > 0) {
            for (var i = 0; i < results.length; ++i) {
                txts.push(results[i].barcodeText);
                // Get infos
                let licenseInfo = parseDriverLicense(results[i].barcodeText);
                if (licenseInfo) {
                    let div = document.getElementById("div-licenseInfo");
                    div.innerHTML = '';
                    let p = document.createElement('p');
                    p.innerText = 'Document Type: DL';
                    div.appendChild(p);
                    for (let key in licenseInfo) {
                        let info = licenseInfo[key];
                        let p = document.createElement('p');
                        p.innerText = info.description + ': ' + info.content;
                        div.appendChild(p);
                    }
                }
                else {
                    document.getElementById("div-licenseInfo").innerHTML = '<p>No license info found.</p>';
                }
            }
        }
        else {
            document.getElementById("div-licenseInfo").innerHTML = '<p>No license info found.</p>';
        }

    } catch (e) {
        console.log(e);
    }
};

JavaScript driver's license

Source Code

https://github.com/yushulx/javascript-barcode-qr-code-scanner/tree/main/examples/9.x/driver_license