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.
This article is Part 3 in a 3-Part Series.
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.
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@9.0.0/dist/dbr.js"></script>
<script>
Dynamsoft.DBR.BarcodeReader.license = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==";
</script>
B4J:
BANano.Header.AddJavascriptFile("https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.0.0/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.
-
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
-
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:
Source Code
https://github.com/xulihang/BANano_BarcodeReader
Disclaimer:
The wrappers and sample code on Dynamsoft Codepool are community editions, shared as-is and not fully tested. Dynamsoft is happy to provide technical support for users exploring these solutions but makes no guarantees.