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 that 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 Barcode 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.

Create Barcode Reader Project

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 into 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:

Final Result GIF

Source Code

https://github.com/xulihang/BANano_BarcodeReader



Source link

Write A Comment