How to Build a Command-Line Barcode Reader with Rust and C++ Barcode SDK

Rust‘s popularity is increasing rapidly. This article aims to integrate Rust with the Dynamsoft C++ Barcode Reader SDK. We will walk through the process of building a command-line barcode reader for Windows and Linux.

Prerequisites

  • Rust: A systems programming language renowned for speed, safety, and concurrency.
  • bindgen: A Rust tool that generates Rust FFI bindings to C and C++ libraries. You can install it with the following command:

    cargo install bindgen-cli
    
  • Dynamsoft Barcode Reader Trial License: You will receive a 30-day free trial license by email.

  • Dynamsoft C++ Barcode SDK v9.x: A ZIP package that contains the shared library and header files for Windows and Linux.

Step 1: Setting Up the Rust Project

  1. Use Cargo to initialize the project:

    cargo new barcode_reader
    cd barcode_reader
    
  2. Modify Cargo.toml to include the required dependencies:

    [package]
    name = "hello_world"
    version = "0.1.0"
    edition = "2018" [build-dependencies]
    cc = "1.0"
    walkdir = "2.5.0"
    

    The cc crate is used to compile the C++ code. The walkdir crate is used to traverse the directory to find the shared library.

Step 2: Configuring the C++ Barcode SDK

  1. Extract the downloaded Dynamsoft C++ Barcode SDK, and copy the headers and platform-specific libraries to the Rust project directory structure as follows:

    |- include |- DynamsoftBarcodeReader.h |- DynamsoftCommon.h
    |- platforms |- linux |- libDynamicPdf.so |- libDynamsoftLicenseClient.so |- libDynamsoftBarcodeReader.so |- win |- bin |- DynamicPdfx64.dll |- DynamsoftBarcodeReaderx64.dll |- DynamsoftLicenseClientx64.dll |- vcomp110.dll |- lib |- DBRx64.lib 
  2. Create a lib directory within your project. In the lib folder, create two files: bridge.cpp and bridge.h. These files will handle communication between Rust and the C++ SDK.

  3. Edit build.rs to build the C++ code and link the shared libraries.

    1. Determine the target operating system (Windows/Linux). When running cargo build, the println!() function won’t output anything to the console unless you add cargo:warning to the message.

      use std::env;
      use cc::Build; use std::fs;
      use walkdir::WalkDir;
      use std::path::{Path, PathBuf}; fn main() { // Determine the target operating system let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); println!("cargo:warning=OS: {}..............................................", target_os);
      }
      
    2. Link the shared libraries based on the target operating system, and copy the shared libraries to the output path.

      fn get_out_dir() -> PathBuf { let out_dir = env::var("OUT_DIR").unwrap(); let debug_offset = out_dir.find("debug").unwrap_or(0); let release_offset = out_dir.find("release").unwrap_or(0); let mut path = String::from(""); if debug_offset > 0 { println!(">>> where is debug {}", debug_offset); path.push_str(&format!("{}", &out_dir[..debug_offset])); path.push_str("debug"); println!("{}", path); } if release_offset > 0 { println!(">>> where is release {}", release_offset); path.push_str(&format!("{}", &out_dir[..release_offset])); path.push_str("release"); println!("{}", path); } PathBuf::from(path)
      } fn copy_shared_libs_from_dir_to_out_dir(src_dir: &Path, out_dir: &Path, extension: &str) { for entry in WalkDir::new(src_dir).into_iter().filter_map(|e| e.ok()) { if entry.path().extension().and_then(|ext| ext.to_str()) == Some(extension) { let lib_path = entry.path(); let file_name = lib_path.file_name().unwrap(); let dest_path = out_dir.join(file_name); fs::copy(lib_path, dest_path.clone()).expect("Failed to copy shared library"); println!("Copied {} to {}", lib_path.display(), dest_path.display()); } }
      } match target_os.as_str() { "windows" => { // Link Dynamsoft Barcode Reader for Windows println!("cargo:rustc-link-search=../../../platforms/win/lib"); println!("cargo:rustc-link-lib=static=DBRx64"); // Copy *.dll files to the output path for Windows let src_dir = Path::new("../../../platforms/win/bin"); copy_shared_libs_from_dir_to_out_dir(src_dir, &get_out_dir(), "dll"); }, "linux" => { // Link Dynamsoft Barcode Reader for Linux println!("cargo:rustc-link-search=../../../platforms/linux"); println!("cargo:rustc-link-lib=dylib=DynamsoftBarcodeReader"); // Set rpath for Linux println!("cargo:rustc-link-arg=-Wl,-rpath,../../../platforms/linux"); // Copy *.so files to the output path for Linux let src_dir = Path::new("../../../platforms/linux/bin"); copy_shared_libs_from_dir_to_out_dir(src_dir, &get_out_dir(), "so"); },
      }
      
    3. Compile the C++ code that exposes some C functions to Rust.

      Build::new()
      .cpp(true)
      .include("../../../include")
      .file("lib/bridge.cpp")
      .compile("bridge"); println!("cargo:rustc-link-lib=static=bridge"); println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap());
      

Step3: Implementing the C/C++ Bridging Code

In this step, we will create the bridging code to enable Rust to interact with the C++ SDK. We will declare and implement the necessary structures and functions in C/C++.

Declaring Structures and Functions in bridge.h

In the directory, create a file named bridge.h and declare the C structures and functions that will be called by Rust.

#ifndef BRIDGE_H
#define BRIDGE_H

#include "DynamsoftBarcodeReader.h"

#ifdef __cplusplus
extern "C"
{
#endif
 typedef struct { const char *barcode_type; const char *barcode_value; int x1; int y1; int x2; int y2; int x3; int y3; int x4; int y4; } Barcode; typedef struct { Barcode *barcodes; int count; } BarcodeResults; Barcode *create_barcode(const char *type, const char *value, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); BarcodeResults *decode_barcode_file(void *instance, const char *filename); void free_barcode(BarcodeResults *results); int init_license(const char *license); #ifdef __cplusplus
}
#endif

#endif // BRIDGE_H

Enter fullscreen mode
Exit fullscreen mode
  • The Barcode structure represents the barcode information.
  • The BarcodeResults structure contains an array of Barcode structures.
  • The create_barcode function creates a Barcode structure.
  • The decode_barcode_file function decodes barcodes from an image file.
  • The free_barcode function releases the memory allocated for the BarcodeResults structure.
  • The init_license function initializes the license.

Implementing the Functions in bridge.cpp

In the bridge.cpp file, implement the functions declared in bridge.h.

#include "bridge.h"
#include <cstring>
#include <cstdlib>

Barcode *create_barcode(const char *type, const char *value, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
{ Barcode *barcode = (Barcode *)std::malloc(sizeof(Barcode)); barcode->barcode_type = strdup(type); barcode->barcode_value = strdup(value); barcode->x1 = x1; barcode->y1 = y1; barcode->x2 = x2; barcode->y2 = y2; barcode->x3 = x3; barcode->y3 = y3; barcode->x4 = x4; barcode->y4 = y4; return barcode;
} void free_barcode(BarcodeResults *results)
{ for (int i = 0; i < results->count; i++) { std::free((void *)results->barcodes[i].barcode_type); std::free((void *)results->barcodes[i].barcode_value); } std::free(results->barcodes); std::free(results);
} int init_license(const char *license)
{ char errorMsgBuffer[512]; // Click https://www.dynamsoft.com/customer/license/trialLicense/?product=dbr to get a trial license. int ret = DBR_InitLicense(license, errorMsgBuffer, 512); return ret;
} BarcodeResults *decode_barcode_file(void *instance, const char *filename)
{ char errorMsgBuffer[512]; TextResultArray *pResults = NULL; BarcodeResults *all_barcodes = NULL; int ret = DBR_DecodeFile(instance, filename, ""); DBR_GetAllTextResults(instance, &pResults); if (pResults->resultsCount > 0) { all_barcodes = (BarcodeResults *)std::malloc(sizeof(BarcodeResults)); all_barcodes->count = pResults->resultsCount; all_barcodes->barcodes = (Barcode *)std::malloc(sizeof(Barcode) * pResults->resultsCount); for (int iIndex = 0; iIndex < pResults->resultsCount; iIndex++) { LocalizationResult *localizationResult = pResults->results[iIndex]->localizationResult; Barcode *barcode = create_barcode(pResults->results[iIndex]->barcodeFormatString, pResults->results[iIndex]->barcodeText, localizationResult->x1, localizationResult->y1, localizationResult->x2, localizationResult->y2, localizationResult->x3, localizationResult->y3, localizationResult->x4, localizationResult->y4); all_barcodes->barcodes[iIndex] = *barcode; } } DBR_FreeTextResults(&pResults); return all_barcodes;
}
Enter fullscreen mode
Exit fullscreen mode

Step4: Generating Rust Bindings for C/C++ Code

To invoke the C/C++ functions from Rust, we need to generate Rust bindings for the C/C++ code. We can either write the bindings manually or use the bindgen tool to generate them automatically as follows:

bindgen ./lib/bridge.h -o bindings.rs
Enter fullscreen mode
Exit fullscreen mode

In addition to the methods implemented in bridge.cpp, we add two more functions contained in the C++ SDK: DBR_CreateInstance and DBR_DestroyInstance. The full bindings.rs file is as follows:

use std::ffi::c_void;
use std::os::raw::c_char;
use std::os::raw::c_int; #[repr(C)]
pub struct Barcode { pub barcode_type: *const c_char, pub barcode_value: *const c_char, pub x1: c_int, pub y1: c_int, pub x2: c_int, pub y2: c_int, pub x3: c_int, pub y3: c_int, pub x4: c_int, pub y4: c_int,
} #[repr(C)]
pub struct BarcodeResults { pub barcodes: *mut Barcode, pub count: c_int,
} extern "C" { // Bridge functions pub fn free_barcode(barcode: *mut BarcodeResults); pub fn init_license(license: *const c_char) -> c_int; pub fn decode_barcode_file(instance: *mut c_void, filename: *const c_char) -> *mut BarcodeResults; // Dynamsoft C++ Barcode Reader SDK functions pub fn DBR_CreateInstance() -> *mut c_void; pub fn DBR_DestroyInstance(barcodeReader: *mut c_void); } 
Enter fullscreen mode
Exit fullscreen mode

Step5: Writing Rust Code

The final step is to write Rust code in the main.rs file to implement the command-line barcode reader.

  1. Import the generated bindings and other necessary libraries.

    mod bindings;
    use std::io::{self, Write};
    use std::ffi::CString;
    use bindings::*;
    
  2. Activate the license of Dynamsoft Barcode Reader:

    let license = "LICENSE-KEY"; let ret = unsafe { let license = CString::new(license).expect("CString::new failed"); init_license(license.as_ptr())
    }; println!("InitLicense: {}", ret);
    
  3. Create an instance of Dynamsoft Barcode Reader:

    let reader_ptr = unsafe { DBR_CreateInstance() };
    if reader_ptr.is_null() { panic!("Failed to create barcode reader instance");
    }
    
  4. Prompt the user to enter a file name in a loop. If the user types exit, the program will exit.

    loop { print!("Please enter the file name (or type 'exit' to quit): "); io::stdout().flush().unwrap(); let mut file_name = String::new(); io::stdin().read_line(&mut file_name).expect("Failed to read line"); let file_name = file_name.trim(); if file_name.to_lowercase() == "exit" { break; } println!("Processing file: {}", file_name); let path = CString::new(file_name).expect("CString::new failed");
    }
    
  5. Decode barcodes from the image file and print the results.

    unsafe { let results_ptr = decode_barcode_file(reader_ptr, path.as_ptr()); if results_ptr.is_null() { println!("No barcodes found."); } else { let results = &*results_ptr; let barcodes = std::slice::from_raw_parts(results.barcodes, results.count as usize); for (i, barcode) in barcodes.iter().enumerate() { let barcode_type = std::ffi::CStr::from_ptr(barcode.barcode_type).to_string_lossy(); let barcode_value = std::ffi::CStr::from_ptr(barcode.barcode_value).to_string_lossy(); println!("Barcode {}: type = {}, value = {}", i + 1, barcode_type, barcode_value); println!( "Coordinates: ({}, {}), ({}, {}), ({}, {}), ({}, {})", barcode.x1, barcode.y1, barcode.x2, barcode.y2, barcode.x3, barcode.y3, barcode.x4, barcode.y4 ); } free_barcode(results_ptr); }
    }
    
  6. Run the program.

    cargo clean
    cargo run
    

    Rust barcode reader

Source Code

https://github.com/yushulx/cmake-cpp-barcode-qrcode/tree/main/examples/9.x/rust


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top
You desire dependable loan repayment schedules if you wish to control your debt. Free 2025 jamb questions and answers cbt answers jamb / jamb 2025 answers cbt answers jamb.