In the world of software development, integrating powerful third-party libraries into new or existing projects can significantly enhance functionality and efficiency. For Go developers needing robust barcode scanning capabilities, incorporating the Dynamsoft C/C++ Barcode SDK into a Go module offers a seamless way to leverage advanced barcode reading technology. This article guides you through the process of building a Go module with the Dynamsoft C/C++ Barcode SDK using cgo, enabling you to read barcodes from images effortlessly.
Prerequisites
- Go Environment: Ensure you have Go installed and configured on your system. You can download it from the official Go website.
- Dynamsoft C/C++ Barcode SDK v9.x: Download the SDK from the Dynamsoft website. Make sure to choose the C/C++ version of the SDK. A valid license key is required to use the SDK. You can obtain a free trial license from here.
- Development Tools: For compiling C/C++ code, ensure you have GCC or another compatible C/C++ compiler installed. On Windows, you can use mingw-w64 GCC. On Linux, you can use the default GCC.
Step 1: Creating a Go Module for Reading Barcodes and QR Codes
As outlined in the Go documentation, we create a Go module named goBarcodeQrSDK
using the terminal.
mkdir goBarcodeQrSDK
cd goBarcodeQrSDK
go mod init github.com/yushulx/goBarcodeQrSDK
Executing this command generates a go.mod
file, which is essential for tracking your code's dependencies.
module github.com/yushulx/goBarcodeQrSDK
go 1.19
Step 2: Preparing the Dynamsoft SDK for CGo Linking
The Dynamsoft C/C++ Barcode Reader SDK is compatible with multiple platforms, including Windows (x86, x64), Linux (x64, ARM32, ARM64), and macOS (x64, ARM64). For the purposes of this guide, we will focus exclusively on Windows x64 and Linux x64 configurations.
-
Within the
goBarcodeQrSDK
directory, create alib
folder. Then, copy the shared libraries and header files from the Dynamsoft C++ Barcode Reader SDK into the lib folder.
|- goBarcodeQrSDK |- lib |- windows |- DynamicPdfx64.dll |- DynamsoftBarcodeReaderx64.dll |- DynamsoftLicClientx64.dll |- DynamsoftLicenseClientx64.dll |- vcomp110.dll |- linux |- libDynamsoftLicenseClient.so |- libDynamsoftBarcodeReader.so |- libDynamicPdf.so |- libDynamLicenseClient.so |- DynamsoftBarcodeReader.h |- DynamsoftCommon.h
-
In the
goBarcodeQrSDK
directory, create two files:bridge.h
andbridge.c
. These files are designed to facilitate data conversion between the Dynamsoft Barcode SDK and Go, streamlining the integration process.bridge.h
:
#include <stdio.h> #include <stdlib.h> #include "DynamsoftBarcodeReader.h" TextResult *getTextResultPointer(TextResultArray *resultArray, int offset); LocalizationResult *getLocalizationPointer(TextResult *result); const char *getText(TextResult *result); const char *getFormatString(TextResult *result);
bridge.c
:
#include "bridge.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; } const char *getFormatString(TextResult *result) { return result->barcodeFormatString; }
Step 3: Writing the CGo Wrapper for the Dynamsoft C++ Barcode Reader SDK
CGo enables Go programs to directly call C code, facilitating the integration of C libraries. To utilize the Dynamsoft SDK from Go, you'll need to create a wrapper.
Within the goBarcodeQrSDK
directory, create a file named reader.go
. This file will house the Go functions that invoke the C functions provided by the Dynamsoft Barcode Reader SDK. To accommodate different linking paths for Windows and Linux, you can employ build constraints within your cgo comments.
package goBarcodeQrSDK
import (
"unsafe"
/*
#cgo CFLAGS: -I${SRCDIR}/lib
#cgo linux LDFLAGS: -L${SRCDIR}/lib/linux -lDynamsoftBarcodeReader -Wl,-rpath=\$$ORIGIN
#cgo windows LDFLAGS: -L${SRCDIR}/lib/windows -lDynamsoftBarcodeReaderx64
#include <stdlib.h>
#include "DynamsoftBarcodeReader.h"
#include "DynamsoftCommon.h"
#include "bridge.h"
*/
"C"
)
Note that the bridge.c
file located in your Go package directory will be compiled and linked automatically.
Develop the barcode reading functions that will be invoked from Go. These functions will serve as the interface between your Go application and the Dynamsoft Barcode Reader SDK's capabilities.
-
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 barcodes and QR codes 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
}
Step 4: Building and Testing the Go Module
Now that you have set up your CGo wrapper alongside the barcode reading functions, it's time to compile and test your module using a _test.go
file.
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)
}
}
Initially, executing go test
directly may result in an error due to the inability of the system to locate the shared library.
exit status 0xc0000135
To address this issue, prepare and utilize scripts that appropriately configure the library search path for both Windows and Linux systems.
-
PowerShell script for Windows:
$originalPath = $env:PATH $dllPath = "lib\windows" $env:PATH = "$dllPath;$originalPath" go test $env:PATH = $originalPath
-
Shell script for Linux:
#!/bin/bash LIB_DIR="lib/linux" ORIGINAL_LD_LIBRARY_PATH=$LD_LIBRARY_PATH export LD_LIBRARY_PATH=$LIB_DIR:$LD_LIBRARY_PATH go test export LD_LIBRARY_PATH=$ORIGINAL_LD_LIBRARY_PATH
After running these scripts, you should expect to see the following output:
Step5: Implementing a Go Barcode and QR Code Reader
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 := "LICENSE-KEY"
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("--------------")
}
}
Remember to substitute LICENSE-KEY
with your own license key.
Utilize the provided scripts to load the necessary libraries and execute your program on Windows and Linux.
- PowerShell script for Windows:
$originalPath = $env:PATH
$GOPATH = $(go env GOPATH)
$PACKAGE_PATH = Get-ChildItem -Path "$GOPATH\pkg\mod\github.com\yushulx" -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName
$ASSEMBLY_PATH = "$PACKAGE_PATH\lib\windows"
Write-Host "ASSEMBLY_PATH set to $ASSEMBLY_PATH"
# Update PATH to include the assembly path
$env:PATH = "$ASSEMBLY_PATH;" + $env:PATH
# Run your Go application
go run test.go test.png
$env:PATH = $originalPath
- Shell script for Linux:
#!/bin/bash
# Save the original PATH
originalPath=$LD_LIBRARY_PATH
# Get the GOPATH
GOPATH=$(go env GOPATH)
# Find the path to the shared libraries
PACKAGE_PATH=$(find "$GOPATH/pkg/mod/github.com/yushulx" -mindepth 1 -maxdepth 1 -type d | sort -r | head -n 1)
echo "PACKAGE_PATH set to $PACKAGE_PATH"
ASSEMBLY_PATH="$PACKAGE_PATH/lib/linux"
echo "ASSEMBLY_PATH set to $ASSEMBLY_PATH"
export LD_LIBRARY_PATH="$ASSEMBLY_PATH:$originalPath"
# Run your Go application
go run test.go test.png
# Restore the original PATH
export LD_LIBRARY_PATH=$originalPath
Step 6: Deploying the Go Barcode and QR Code Reader to Docker
-
Create a
Dockerfile
file in the root directory of your project. This file will define the environment for running your Go application within a Docker container.
FROM golang:1.19 COPY . /usr/src/myapp WORKDIR /usr/src/myapp/example/command-line RUN cp -r ../../lib/linux/* /usr/lib/x86_64-linux-gnu/ RUN cp template.json /usr/local/bin/ RUN go mod download RUN go build -v -o /usr/local/bin/reader CMD [ "reader"]
-
With the
Dockerfile
in place, build your Docker image using thedocker build
command. This process packages your application and its dependencies into a Docker image.
docker build -t golang-barcode-qr-reader .
-
Once your Docker container is running, you can use your Go application to read barcodes and QR codes from local image files that are mounted to the container.
docker run -it --rm -v <image-folder>:/app golang-barcode-qr-reader reader /app/<image-file> <license-key> <template-file>
Docker Image with Golang Barcode QR Reader
https://hub.docker.com/r/yushulx/golang-barcode-qr-reader
docker run -it --rm -v <image-folder>:/app yushulx/golang-barcode-qr-reader:latest reader /app/<image-file> <license-key> <template-file>