How to Read Binary Data from a Barcode

A barcode or bar code is a method of representing data in a visual, machine-readable form. There are all kinds of barcodes and mainly, they can be divided into 1D barcodes and 2D barcodes.

We can get binary data from barcodes and there are different ways to interpret the data. For example, in EAN-13, code 1110010 in the right hand represents the digit 0. As for QR code, which is two-dimensional and can store more data, has several modes to represent data:

Input mode Mode indicator Max. characters Possible characters, default encoding
Numeric only 1 7,089 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Alphanumeric 2 4,296 0–9, A–Z (upper-case only), space, $, %, *, +, -, ., /, :
Binary/byte 4 2,953 ISO/IEC 8859-1
Kanji/kana 8 1,817 Shift JIS X 0208
Structured Append 3 unlimited Not specific

PS: structured append is a mode in which the data is divided into several barcodes.

In this article, we are going to create a JavaScript library for reading the binary data of a barcode, with a special focus on QR code as this format has multiple modes.

Dynamsoft Barcode Reader is used for reading the barcodes.

Getting started with Dynamsoft Barcode Reader

New Project

Create a new project with Vite and the typescript template:

npm create vite@latest BarcodeDataReader -- --template vanilla-ts

Write Definitions

Define several interfaces and an enum for reading the binary data of barcodes.

  • Define a BarcodeDetail interface:

     export interface BarcodeDetail {
       mode?:number; //The data encoding mode
       model?:number; //Number of models
       errorCorrectionLevel?:number;
       columns?:number; //The column count
       rows?:number; //The row count
       page?:number; //Position of the particular code in structured append codes
       totalPage?:number; //Total number of structured append codes
       parityData?:number; //A value obtained by XORing byte by byte the ASCII/JIS values of all the original input data before division into symbol blocks.
       version?:number; //The version of the code.
     }
    
  • Define a Barcode interface:

     export interface Barcode {
       bytes:Uint8Array;
       details:BarcodeDetail;
       barcodeType?:string;
     }
    

    The binary data is stored as Uint8Array.

  • Define a ReadingResult interface:

     export interface ReadingResult {
       text?:string;
       img?:HTMLImageElement;
       blob?:Blob;
     }
    

    We can get the result as plain text, an HTML image element or blob.

    The type is decided by the DataType passed to the binary data reader.

     export enum DataType {
       text = 0,
       image = 1,
       unknown = 2
     }
    

Create a Class to Read Binary Data

  1. Create a new BarcodeDataReader.ts file with the following template:

    export class BarcodeDataReader{
      async read(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{
        if (barcodes.length == 0) {
          throw new Error("No barcodes given");
        }
        let results:ReadingResult[] = [];
        return results;
      }
    }
    
  2. Read the binary data based on different modes.

    async read(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{
      if (barcodes.length == 0) {
        throw new Error("No barcodes given");
      }
      let results:ReadingResult[] = [];
      const mode = barcodes[0].details.mode;
      if (mode == 1) {
        results = this.readNumericBarcodes(barcodes);
      }else if (mode == 2) {
        results = this.readAlphaNumericBarcodes(barcodes);
      }else if (mode == 4) {
        results = await this.readByteEncodingBarcodes(barcodes,dataType);
      }else if (mode == 8) {
        results = this.readKanjiBarcodes(barcodes);
      }else if (mode == 3) {
        results = await this.readStructuredAppendBarcodes(barcodes,dataType);
      }else {
        results = await this.readByteEncodingBarcodes(barcodes,DataType.text);
      }
      return results;
    }
    
  3. Implement reading the data in numeric, alphanumeric and Kanji modes. The three modes’ data is plain text and they can share a similar logic.

    private readAlphaNumericBarcodes(barcodes:Barcode[]):ReadingResult[]{
      return this.decodeText(barcodes,"ASCII");
    }
    
    private readNumericBarcodes(barcodes:Barcode[]):ReadingResult[]{
      return this.decodeText(barcodes,"ASCII");
    }
    
    private readKanjiBarcodes(barcodes:Barcode[]):ReadingResult[]{
      return this.decodeText(barcodes,"SHIFT-JIS");
    }
    
    private decodeText(barcodes:Barcode[],encoding:string){
      let results:ReadingResult[] = [];
      for (let index = 0; index < barcodes.length; index++) {
        const barcode = barcodes[index];
        const decoder = new TextDecoder(encoding);
        const text = decoder.decode(barcode.bytes);
        let result = {
          text:text
        }
        results.push(result);
      }
      return results;
    }
    
  4. Implement reading the data in byte mode.

    As the data type is unknown in byte mode, we have to get the reading results based on the user-specified data type.

    If the data type is text, we have to detect the encoding. The chardet library is used here for detection.

    private async readByteEncodingBarcodes(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{
      let results:ReadingResult[] = [];
      for (let index = 0; index < barcodes.length; index++) {
        const barcode = barcodes[index];
        let result:ReadingResult = await this.getResultBasedOnDataType(barcode.bytes,dataType);
        results.push(result);
      }
      return results;
    }
    
    async getResultBasedOnDataType(data:Uint8Array,dataType:DataType) {
      let result:ReadingResult;
      if (dataType == DataType.text) {
        const charset = chardet.analyse(data);
        const decoder = new TextDecoder(charset[0].name);
        const text = decoder.decode(data);
        result = {
          text:text
        }
      }else if (dataType == DataType.image) {
        const img = await this.getImageFromUint8Array(data);
        result = {
          img:img
        }
      }else{
        result = {
          blob:this.getBlobFromUint8Array(data)
        }
      }
      return result;
    }
       
    getBlobFromUint8Array(data:Uint8Array) {
      return new Blob([data]);
    }
    getImageFromUint8Array(data:Uint8Array):Promise<HTMLImageElement>{
      return new Promise<HTMLImageElement>((resolve, reject) => {
        const img = document.createElement("img");  
        const blob = this.getBlobFromUint8Array(data);
        img.onload = function(){
          resolve(img);
        }
        img.onerror = function(error) {
          console.error(error);
          reject(error);
        }
        img.src = URL.createObjectURL(blob);
        console.log(img.src)
      })
    }
    
  5. Implement reading the data in structured append mode.

    We have to sort the barcodes based on their page numbers, merge the bytes into one and then get the result.

    private async readStructuredAppendBarcodes(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{
      let results:ReadingResult[] = [];
      barcodes.sort((a, b) => (a.details.page ?? 0) - (b.details.page ?? 0))
      let concatedData:Uint8Array = new Uint8Array();
      for (let index = 0; index < barcodes.length; index++) {
        const barcode = barcodes[index];
        let merged = new Uint8Array(barcode.bytes.length + concatedData.length);
        merged.set(concatedData);
        merged.set(barcode.bytes, concatedData.length);
        concatedData = merged;
      }
      let result = await this.getResultBasedOnDataType(concatedData,dataType);
      results.push(result);
      return results;
    }
    

Read QR Codes to Have a Test

We can then update index.html to use Dynamsoft Barcode Reader to read QR codes and this library to read the binary data.

Here is the basic code:

let router = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();
//read barcodes from an image
let result = await router.capture(document.getElementById("image"),"ReadBarcodes_Balance");
let dataType = document.getElementById("dataTypeSelect").selectedIndex;
let barcodes = [];
for (let index = 0; index < result.items.length; index++) {
  const item = result.items[index];
  if (item.type === Dynamsoft.Core.EnumCapturedResultItemType.CRIT_BARCODE) {
    let data = new Uint8Array(item.bytes.length);
    data.set(item.bytes);
    let barcode = {
      bytes:data,
      details:item.details
    }
    barcodes.push(barcode);
  }
}

let results = await dataReader.read(barcodes,dataType)
console.log(results);

You can visit this online demo to have a try.

If you don’t have QR codes at hand, you can use the QR codes and the QR code generator in this article.

Here is a screenshot of a test of reading two QR codes which have an image encoded:

Sample

Package as a Library

We can publish it as a library onto NPM for ease of use.

  1. Install devDependencies:

    npm install -D @types/node vite-plugin-dts
    
  2. Create a new vite.config.ts file:

    // vite.config.ts
    import { resolve } from 'path';
    import { defineConfig } from 'vite';
    import dts from 'vite-plugin-dts';
    // https://vitejs.dev/guide/build.html#library-mode
    export default defineConfig({
      build: {
        lib: {
          entry: resolve(__dirname, 'src/index.ts'),
          name: 'barcode-data-reader',
          fileName: 'barcode-data-reader',
        },
      },
      plugins: [dts()],
    });
    
  3. Add the entry points of our package to package.json.

    {
      "main": "./dist/barcode-data-reader.umd.cjs",
      "module": "./dist/barcode-data-reader.js",
      "types": "./dist/index.d.ts",
      "exports": {
        "import": {
          "types": "./dist/index.d.ts",
          "default": "./dist/barcode-data-reader.js"
        },
        "require": {
          "types": "./dist/index.d.ts",
          "default": "./dist/barcode-data-reader.umd.cjs"
        }
      },
      "files": [
        "dist/*.css",
        "dist/*.js",
        "dist/*.cjs",
        "dist/*.d.ts"
      ]
    }
    

Run npm run build. Then, we can have the packaged files in the dist.

Source Code

Get the source code of the library to have a try:

https://github.com/tony-xlh/barcode-data-reader