How to Build Golang Barcode QR Code Reader with Dynamsoft C++ Barcode SDK
This article aims to help Go developers to build barcode QR code reader applications with Dynamsoft C++ Barcode SDK on Windows and Linux. You will see how to interoperate Golang with C++ code using cgo, as well as how to deploy the application to Docker.
Prerequisites
To build cgo on Windows, you need to install mingw-w64 GCC.
Get a free trial license from here.
Creating a Go Module for Reading Barcode and QR Code
Dynamsoft C++ Barcode Reader SDK supports Windows(x86, x64), Linux(x64, ARM32, ARM64), and macOS(x64, ARM64). Here we only focus on Windows and Linux.
Initialize a Go Module Project
According to the Go documentation, we create a Go module named goBarcodeQrSDK
in the terminal:
mkdir goBarcodeQrSDK
cd goBarcodeQrSDK
go mod init github.com/yushulx/goBarcodeQrSDK
The command creates a go.mod
file which tracks your code's dependencies.
module github.com/yushulx/goBarcodeQrSDK
go 1.19
Link Dynamsoft C++ Barcode Reader Libraries to Go
- Create a
lib
folder. - Copy shared libraries and header files of Dynamsoft C++ Barcode Reader SDK to the
lib
folder. ChangeDynamsoftBarcodeReaderx64.dll
toDynamsoftBarcodeReader.dll
to make the linking work on both Windows and Linux. - In your text editor, create a
reader.go
file. - Add
cgo LDFLAGS
and include C++ header files in thereader.go
file.
package goBarcodeQrSDK
import (
"unsafe"
// #include <stdlib.h>
// #include <lib/DynamsoftBarcodeReader.h>
// #include <lib/DynamsoftCommon.h>
// #include <lib/bridge.c>
// #cgo LDFLAGS: -L ./lib -lDynamsoftBarcodeReader -Wl,-rpath=./lib
"C"
)
Calling Dynamsoft C++ Barcode Reader Functions in Go
To implement a barcode QR code reader with Dynamsoft Barcode SDK, we need to call the following functions:
DBR_InitLicense
: Set a valid license key to activate the SDK.
func InitLicense(license string) (int, string) {
c_license := C.CString(license)
defer C.free(unsafe.Pointer(c_license))
errorBuffer := make([]byte, 256)
ret := C.DBR_InitLicense(c_license, (*C.char)(unsafe.Pointer(&errorBuffer[0])), C.int(len(errorBuffer)))
return int(ret), string(errorBuffer)
}
DBR_CreateInstance
: Create an instance of the barcode reader.
type BarcodeReader struct {
handler unsafe.Pointer
}
func CreateBarcodeReader() *BarcodeReader {
handler := C.DBR_CreateInstance()
if handler == nil {
return nil
}
return &BarcodeReader{handler: handler}
}
DBR_InitRuntimeSettingsWithFile
: Load a parameter template file for customizing the barcode scanning algorithm.
func (reader *BarcodeReader) LoadTemplateFile(params string) (int, string) {
errorBuffer := make([]byte, 256)
ret := C.DBR_InitRuntimeSettingsWithFile(reader.handler, C.CString(params), C.CM_OVERWRITE, (*C.char)(unsafe.Pointer(&errorBuffer[0])), C.int(len(errorBuffer)))
return int(ret), string(errorBuffer)
}
DBR_DecodeFile
: Read barcode QR code from an image file and return the results.
func (reader *BarcodeReader) DecodeFile(filePath string) (int, []Barcode) {
c_filePath := C.CString(filePath)
defer C.free(unsafe.Pointer(c_filePath))
template := C.CString("")
defer C.free(unsafe.Pointer(template))
var barcodes = []Barcode{}
ret := C.DBR_DecodeFile(reader.handler, c_filePath, template)
if ret != 0 {
return int(ret), barcodes
}
var resultArray *C.TextResultArray
C.DBR_GetAllTextResults(reader.handler, &resultArray)
if resultArray.resultsCount > 0 {
for i := 0; i < int(resultArray.resultsCount); i++ {
barcode := Barcode{}
result := C.getTextResultPointer(resultArray, C.int(i))
format := C.getFormatString(result)
barcode.Format = C.GoString(format)
text := C.getText(result)
barcode.Text = C.GoString(text)
localization := C.getLocalizationPointer(result)
barcode.X1 = int(localization.x1)
barcode.Y1 = int(localization.y1)
barcode.X2 = int(localization.x2)
barcode.Y2 = int(localization.y2)
barcode.X3 = int(localization.x3)
barcode.Y3 = int(localization.y3)
barcode.X4 = int(localization.x4)
barcode.Y4 = int(localization.y4)
barcodes = append(barcodes, barcode)
}
}
C.DBR_FreeTextResults(&resultArray)
return int(ret), barcodes
}
The Barcode
struct is defined as follows:
type Barcode struct {
Text string
Format string
X1 int
Y1 int
X2 int
Y2 int
X3 int
Y3 int
X4 int
Y4 int
}
To simplify data conversion between C++ and Go, we implement some C++ pointer operations in the bridge.c
file.
#include <stdio.h>
#include <stdlib.h>
#include "DynamsoftBarcodeReader.h"
TextResult *getTextResultPointer(TextResultArray *resultArray, int offset) {
return resultArray->results[offset];
}
LocalizationResult *getLocalizationPointer(TextResult *result) {
return result->localizationResult;
}
const char *getText(TextResult *result) {
return result->barcodeText;
}
Before using these Go functions, you need to test them in a _test.go
file.
Test the Go Module
We create a goBarcodeQrSDK_test.go
file to test the Go module.
package goBarcodeQrSDK
import (
"fmt"
"testing"
"time"
)
func TestInitLicense(t *testing.T) {
ret, _ := InitLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==")
if ret != 0 {
t.Fatalf(`initLicense("") = %d`, ret)
}
}
func TestCreateBarcodeReader(t *testing.T) {
obj := CreateBarcodeReader()
if obj == nil {
t.Fatalf(`Failed to create instance`)
}
}
func TestLoadTemplateFile(t *testing.T) {
obj := CreateBarcodeReader()
ret, _ := obj.LoadTemplateFile("template.json")
if ret != 0 {
t.Fatalf(`LoadTemplateFile() = %d`, ret)
}
}
func TestDecodeFile(t *testing.T) {
obj := CreateBarcodeReader()
obj.SetParameters("{\"ImageParameter\":{\"BarcodeFormatIds\":[\"BF_ONED\",\"BF_PDF417\",\"BF_QR_CODE\",\"BF_DATAMATRIX\"],\"BarcodeFormatIds_2\":null,\"Name\":\"sts\",\"RegionDefinitionNameArray\":[\"region0\"]},\"RegionDefinition\":{\"Bottom\":100,\"Left\":0,\"MeasuredByPercentage\":1,\"Name\":\"region0\",\"Right\":100,\"Top\":0}}")
ret, _ := obj.DecodeFile("test.png")
if ret != 0 {
t.Fatalf(`DecodeFile() = %d`, ret)
}
}
Run go test
.
If the test is successful, you can use the Go module in your project.
Implementing Golang Barcode QR Code Reader
Create a new module in another directory and add the goBarcodeQrSDK
module as a dependency locally.
mkdir example
go mod init example.com/test
go mod edit -replace github.com/yushulx/goBarcodeQrSDK=../
go mod tidy
The go.mod
file should look like this:
module example.com/test
go 1.19
replace github.com/yushulx/goBarcodeQrSDK => ../
require github.com/yushulx/goBarcodeQrSDK v0.0.0-00010101000000-000000000000
To use a published module, omit the replace directive and use a require directive with a tagged version number at the end.
- replace github.com/yushulx/goBarcodeQrSDK => ../
+ require github.com/yushulx/goBarcodeQrSDK v1.0.3
Create a test.go
file and add the following code:
package main
import (
"fmt"
"os"
"time"
"github.com/yushulx/goBarcodeQrSDK"
)
func main() {
filename := "test.png"
license := "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="
template := "template.json"
ret, errMsg := goBarcodeQrSDK.InitLicense(license)
if ret != 0 {
fmt.Println(`initLicense(): `, ret)
fmt.Println(errMsg)
return
}
obj := goBarcodeQrSDK.CreateBarcodeReader()
ret, errMsg = obj.LoadTemplateFile(template)
if ret != 0 {
fmt.Println(`LoadTemplateFile(): `, ret)
fmt.Println(errMsg)
}
startTime := time.Now()
ret, barcodes := obj.DecodeFile(filename)
elapsed := time.Since(startTime)
fmt.Println("DecodeFile() time cost: ", elapsed)
if ret != 0 {
fmt.Printf(`DecodeFile() = %d`, ret)
}
for i := 0; i < len(barcodes); i++ {
barcode := barcodes[i]
fmt.Println(barcode.Text)
fmt.Println(barcode.Format)
fmt.Println(barcode.X1)
fmt.Println(barcode.Y1)
fmt.Println(barcode.X2)
fmt.Println(barcode.Y2)
fmt.Println(barcode.X3)
fmt.Println(barcode.Y3)
fmt.Println(barcode.X4)
fmt.Println(barcode.Y4)
fmt.Println("--------------")
}
}
We use Windows and WSL to test the barcode QR code reader for Windows and Linux respectively.
go run .
As you can see, there is no error when running on Windows. However, when running on Linux, the dependent shared library cannot be found. Because the rpath
is set to the lib
directory, to find the shared library, we can build the executable file to the directory where the lib
folder is located.
go build -o ../reader
cd ..
./reader test.png
Deploying Golang Barcode QR Reader to Docker
- Create a
Dockerfile
file in the root directory of the project:
FROM golang:1.19
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp/example
COPY ../lib/ /usr/lib/x86_64-linux-gnu/
RUN cp test.png /usr/local/bin/
RUN cp template.json /usr/local/bin/
RUN go mod download
RUN go build -v -o /usr/local/bin/reader
CMD [ "reader"]
2. Build the Docker image:
docker build -t golang-barcode-qr-reader .
3. Read barcode and QR code from a local image file:
docker run -it --rm -v <image-folder>:/app golang-barcode-qr-reader reader /app/<image-file> <license-key> <template-file>
Published Docker Image
docker run -it --rm -v <image-folder>:/app yushulx/golang-barcode-qr-reader:latest reader /app/<image-file> <license-key> <template-file>
Source Code
Originally published at https://www.dynamsoft.com on September 28, 2022.