Web Barcode Reader using B4X

In the B4X developing suite, B4J is used to develop desktop apps based on JavaFX or server apps based on Jetty. By default, it cannot be used to create web applications without a server. A third-party library, BANano, makes this possible. It can transpile everything you write in B4J to Javascript, HTML and CSS. The project can run purely in the browser.

Dynamsoft Barcode Reader is written in C++. It provides a JavaScript version which uses WebAssembly. The barcode decoding can be done on the client-side which is a good match for BANano.

Let’s try building a web barcode reader using BANano and Dynamsoft Barcode Reader.

First, we create the basic UI and then we integrate Dynamsoft Barcode Reader to decode local images and video stream. We can learn about the usage of the JavaScript version of Dynamsoft Barocde Reader by looking at the helloworld example. We may need to translate the HTML and JavaScript to B4J code.

Create a BANano Project for the Web Barcode Reader

Download BANano and extract the Skeleton demo. Use this project as the base and redesign the layout using the Abstract Designer as below.

Designer

By clicking the Load Image button, BANanoFiles is used to pick a local image and load it onto the page by converting it to base64.

Include the JavaScript library

Original HTML:

<script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@8.2.5/dist/dbr.js" data-productKeys="PRODUCT-KEYS"></script>

B4J:

BANano.Header.AddJavascriptFile("https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@8.2.3/dist/dbr.js")

A global Dynamsoft instance is created to call the APIs.

Private Dynamsoft As BANanoObject
Dynamsoft.Initialize("Dynamsoft")

The productKeys is set using this instance.

Dynamsoft.GetField("DBR").GetField("BarcodeReader").SetField("productKeys","<your license key>")

Create a Barcode Reader to Decode Local Images

Original JavaScript:

// reader for decoding picture
let reader = null;

// decode input picture
document.getElementById('ipt-file').addEventListener('change', async function(){
    try{
        reader = reader || await Dynamsoft.DBR.BarcodeReader.createInstance();
        let resultsToAlert = [];
        for(let i = 0; i < this.files.length; ++i){
            let file = this.files[i];
            resultsToAlert.push(i + '. ' + file.name + ":");
            let results = await reader.decode(file);
            console.log(results);
            for(let result of results){
                resultsToAlert.push(result.barcodeText);
            }
        }
        alert(resultsToAlert.join('\n'));
    }catch(ex){
        alert(ex.message);
        throw ex;
    }
    this.value = '';
});

We have to wrap relevant JavaScipt classes to B4J classes. We need to use BANanoObject, which is similar to JavaObject in B4A/B4J.

  1. Create a BarcodeReader class and a TextResult class.

    BarcodeReader:

     Sub Class_Globals
         Private BANano As BANano
         Public mReader As BANanoObject
     End Sub
    
     'Initializes the object. You can add parameters to this method if needed.
     Public Sub Initialize(Dynamsoft As BANanoObject)
         mReader = BANano.Await(Dynamsoft.GetField("DBR").GetField("BarcodeReader").RunMethod("createInstance",Null))
     End Sub
    
     Public Sub decode(data As Object) As TextResult()
         Dim results As List=BANano.Await(mReader.RunMethod("decode",Array(data)))
         Return ConvertTextResult(results)
     End Sub
    
     private Sub ConvertTextResult(results As List) As TextResult()
         Dim values(results.Size) As TextResult
         Dim index As Int=0
         For Each result As BANanoObject In results
             Dim tr As TextResult
             tr.Initialize(result)
             values(index)=tr
             index=index+1
         Next
         Return values
     End Sub
    

    TextResult:

     Sub Class_Globals
         Private mTextResult As BANanoObject
         Private mText As String
         Private mResultPoints(4) As Point2D
         Type Point2D(x As Int,y As Int)
     End Sub
    
     'Initializes the object. You can add parameters to this method if needed.
     Public Sub Initialize(result As Object)
         mTextResult=result
         Parse
     End Sub
    
     Private Sub Parse
         mText=mTextResult.GetField("barcodeText")
         Dim localizationResult As BANanoObject=mTextResult.GetField("localizationResult")
         For i=0 To 3
             Dim p As Point2D
             p.Initialize
             p.x=localizationResult.GetField("x"&(i+1))
             p.y=localizationResult.GetField("y"&(i+1))
             mResultPoints(i)=p
         Next
     End Sub
    
     Public Sub getObject As Object
         Return mTextResult
     End Sub
    
     Public Sub getText As String
         Return mText
     End Sub
    
     Public Sub getResultPoints As Object
         Return mResultPoints
     End Sub
    
  2. Decode an image using the Barcode Reader (the image is preloaded and converted to base64).

     Private Sub decodeButton_Click (event As BANanoEvent)
         resultContainer.Element.SetHTML("Decoding...")
         Dim results As List=BANano.Await(reader.decode(codeImage.Src))
         Dim sb As StringBuilder
         sb.Initialize
         Dim index As Int
         For Each tr As TextResult In results
             index=index+1
             sb.Append(index).Append(". ")
             sb.Append(tr.Text)
             sb.Append("<br/>")
         Next
         resultContainer.Element.SetHTML(sb.ToString)
     End Sub
    

Create a Barcode Scanner for Live Scan

Original JavaScript:

// scanner for decoding video
let scanner = null;
// decode video from camera
document.getElementById('btn-show-scanner').addEventListener('click', async () => {
    try{
        scanner = scanner || await Dynamsoft.DBR.BarcodeScanner.createInstance();
        scanner.onFrameRead = results => {
            if(results.length){
                console.log(results);
            }
        };
        scanner.onUnduplicatedRead = (txt, result) => {
            alert(result.barcodeFormatString + ': ' + txt);
        };
        await scanner.show();
    }catch(ex){
        alert(ex.message);
        throw ex;
    }
});

Here, we create a BarcodeScanner class.

Sub Class_Globals
    Private BANano As BANano
    Public mScanner As BANanoObject
    Private mEventName As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(Dynamsoft As BANanoObject,eventName As String)
    mEventName=eventName
    mScanner = BANano.Await(Dynamsoft.GetField("DBR").GetField("BarcodeScanner").RunMethod("createInstance",Null))
End Sub

private Sub ConvertTextResult(results As List) As TextResult()
    Dim values(results.Size) As TextResult
    Dim index As Int=0
    For Each result As BANanoObject In results
        Dim tr As TextResult
        tr.Initialize(result)
        values(index)=tr
        index=index+1
    Next
    Return values
End Sub

'Show the camera UI element, open the camera, and start decoding.
public Sub show
    Dim results As Object
    Dim txt As Object
    Dim result As Object
    mScanner.AddEventListenerOpen("onUnduplicatedRead",Array(txt,result))
    If SubExists(Main,mEventName&"_onUnduplicatedRead") Then
        Dim tr As TextResult
        tr.Initialize(result)
        BANano.CallSub(Main, mEventName&"_onUnduplicatedRead", Array(txt,tr))
    End If    
    mScanner.CloseEventListener
    
    mScanner.AddEventListenerOpen("onFrameRead",results)
    If SubExists(Main,mEventName&"_onFrameRead") Then
        BANano.CallSub(Main, mEventName&"_onFrameRead", Array(ConvertTextResult(results)))
    End If
    mScanner.CloseEventListener
    
    mScanner.RunMethod("show",Null)
End Sub

The scanner uses callbacks to pass decoding results. In BANano, the event listener is added using AddEventListenerOpen.

The events will be raised in the Main module if they are defined.

private Sub scanner_onUnduplicatedRead(txt As String,result As TextResult)
    resultLabel.Text=text 'display result in a modal
    SKModal1.Open
End Sub

The final result:

GIF

Source Code

https://github.com/xulihang/BANano_BarcodeReader

Search Blog Posts