Building a Go Module with C API of Dynamsoft Barcode SDK v9.x
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 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 C API of Dynamsoft Barcode SDK v9.x using cgo, enabling you to read barcodes from images effortlessly.
This article is Part 1 in a 4-Part Series.
- Part 1 - Building a Go Module with C API of Dynamsoft Barcode SDK v9.x
- Part 2 - Integrating Dynamsoft's C++ Barcode SDK v10 into Go Module with a C Wrapper
- Part 3 - Linking and Loading *.dylib Files in Go Barcode Reader Modules on macOS
- Part 4 - Developing a Web Application for Reading Multiple Barcodes with Go and HTML5
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 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/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 namedreader.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("LICENSE-KEY")
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>
Source Code
https://github.com/yushulx/goBarcodeQrSDK
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.