DEV Community 👩‍💻👨‍💻

Xiao Ling
Xiao Ling

Posted on • Originally published at dynamsoft.com

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

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
Enter fullscreen mode Exit fullscreen mode

The command creates a go.mod file which tracks your code's dependencies.

module github.com/yushulx/goBarcodeQrSDK

go 1.19
Enter fullscreen mode Exit fullscreen mode

Link Dynamsoft C++ Barcode Reader Libraries to Go

  1. Create a lib folder.
  2. Copy shared libraries and header files of Dynamsoft C++ Barcode Reader SDK to the lib folder. Change DynamsoftBarcodeReaderx64.dll to DynamsoftBarcodeReader.dll to make the linking work on both Windows and Linux.
  3. In your text editor, create a reader.go file.
  4. Add cgo LDFLAGS and include C++ header files in the reader.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;
    }
    
    const char *getFormatString(TextResult *result) {
        return result->barcodeFormatString;
    }
    

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

Run go test.

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
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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("--------------")
    }
}

Enter fullscreen mode Exit fullscreen mode

We use Windows and WSL to test the barcode QR code reader for Windows and Linux respectively.

go run .
Enter fullscreen mode Exit fullscreen mode

Link cgo on Windows and Linux

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
Enter fullscreen mode Exit fullscreen mode

Run cgo on Linux

Deploying Golang Barcode QR Reader to Docker

  1. 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>
    

    Golang barcode QR code reader

Published Docker Image

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>
Enter fullscreen mode Exit fullscreen mode

Source Code

https://github.com/yushulx/goBarcodeQrSDK

Top comments (0)

Take a look at this:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. 🛠