DEV Community

Xiao Ling
Xiao Ling

Posted on • Edited on • Originally published at dynamsoft.com

How to Build Golang Barcode QR Code Reader with Dynamsoft C++ Barcode SDK

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

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

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.

  1. Within the goBarcodeQrSDK directory, create a lib 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
    
  2. In the goBarcodeQrSDK directory, create two files: bridge.h and bridge.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"
    )
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

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:

Go test

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

cgo barcpde reader on Windows

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

cgo barcode reader on Linux

Step 6: Deploying the Go Barcode and QR Code Reader to Docker

  1. 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"]
    
  2. With the Dockerfile in place, build your Docker image using the docker build command. This process packages your application and its dependencies into a Docker image.

    docker build -t golang-barcode-qr-reader .
    
  3. 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>
    

    Golang barcode QR code reader

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

Source Code

https://github.com/yushulx/goBarcodeQrSDK

Top comments (0)