DEV Community

Xiao Ling
Xiao Ling

Posted on • Originally published at dynamsoft.com

Decoding 1D/2D Barcodes from Multi-Page PDFs Using C++ and Node.js

While many barcode SDKs can decode barcodes from images, only a few excel at extracting 1D and 2D barcodes from multi-page PDF files. This is where the Dynamsoft Barcode Reader SDK stands out, offering robust capabilities to accurately scan and decode barcodes across multiple pages within PDFs. In this tutorial, we will start by using Dynamsoft C++ Barcode SDK v10.x to tackle the challenges of PDF barcode decoding and then integrate the functionality into a Node.js addon, helping developers streamline barcode extraction from complex PDF documents.

Node.js Barcode Reader Demo Video

Prerequisites

A Simple C++ Program to Decode Barcodes from Images

To get started with the C++ API of the Dynamsoft Barcode Reader SDK, you can refer to the ReadAnImage.cpp file provided in the official repository.

#include <iostream>
#include <string>

#include "../../../Include/DynamsoftCaptureVisionRouter.h"

using namespace std;
using namespace dynamsoft::license;
using namespace dynamsoft::cvr;
using namespace dynamsoft::dbr;
using namespace dynamsoft::basic_structures;
#if defined(_WIN64) || defined(_WIN32)
#ifdef _WIN64
#pragma comment(lib, "../../../Distributables/Lib/Windows/x64/DynamsoftLicensex64.lib")
#pragma comment(lib, "../../../Distributables/Lib/Windows/x64/DynamsoftCaptureVisionRouterx64.lib")
#else
#pragma comment(lib, "../../../Distributables/Lib/Windows/x86/DynamsoftLicensex86.lib")
#pragma comment(lib, "../../../Distributables/Lib/Windows/x86/DynamsoftCaptureVisionRouterx86.lib")
#endif
#endif

int main()
{
    int errorCode = 1;
    char errorMsg[512];

    errorCode = CLicenseManager::InitLicense("LICENSE-KEY", errorMsg, 512);

    if (errorCode != ErrorCode::EC_OK && errorCode != ErrorCode::EC_LICENSE_CACHE_USED)
    {
        cout << "License initialization failed: ErrorCode: " << errorCode << ", ErrorString: " << errorMsg << endl;
    }
    else
    {
        CCaptureVisionRouter *cvr = new CCaptureVisionRouter;

        string imageFile = "../../../Images/GeneralBarcodes.png";
        CCapturedResult *result = cvr->Capture(imageFile.c_str(), CPresetTemplate::PT_READ_BARCODES);

        if (result->GetErrorCode() != 0)
        {
            cout << "Error: " << result->GetErrorCode() << "," << result->GetErrorString() << endl;
        }
        CDecodedBarcodesResult *barcodeResult = result->GetDecodedBarcodesResult();
        if (barcodeResult == nullptr || barcodeResult->GetItemsCount() == 0)
        {
            cout << "No barcode found." << endl;
        }
        else
        {
            int barcodeResultItemCount = barcodeResult->GetItemsCount();
            cout << "Decoded " << barcodeResultItemCount << " barcodes" << endl;

            for (int j = 0; j < barcodeResultItemCount; j++)
            {
                const CBarcodeResultItem *barcodeResultItem = barcodeResult->GetItem(j);
                cout << "Result " << j + 1 << endl;
                cout << "Barcode Format: " << barcodeResultItem->GetFormatString() << endl;
                cout << "Barcode Text: " << barcodeResultItem->GetText() << endl;
            }
        }
        if (barcodeResult)
            barcodeResult->Release();
        if (result)
            result->Release();
        delete cvr, cvr = NULL;
    }
    cout << "Press any key to quit..." << endl;
    cin.ignore();
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

The primary methods used in the code snippet above are InitLicense() and Capture():

  • InitLicense() initializes the SDK with your license key.
  • Capture() processes the specified image file to decode barcodes and returns the results.

Note: Supported image formats include BMP, JPEG, PNG, single-page TIFF and single-page PDF. For multi-page TIFF/PDF files, use the CFileFetcher class to handle page-by-page processing.

Reading Barcodes from Multi-Page PDFs

Let's modify the implementation to read barcodes from multi-page PDF files.

  1. Create a MyImageSourceStateListener class that inherits from CImageSourceStateListener. This class listens for image source states and stops capturing when the image source is exhausted.

    class MyImageSourceStateListener : public CImageSourceStateListener
    {
    private:
        CCaptureVisionRouter *m_router;
    
    public:
        MyImageSourceStateListener(CCaptureVisionRouter *router)
        {
            m_router = router;
        }
    
        virtual void OnImageSourceStateReceived(ImageSourceState state)
        {
            if (state == ISS_EXHAUSTED)
                m_router->StopCapturing();
        }
    };
    
  2. Create a MyCapturedResultReceiver class that inherits from CCapturedResultReceiver. This class receives decoded barcode results from any image source. The fileTag->GetPageNumber() method retrieves the page number of the image in the PDF or TIFF file.

    class MyCapturedResultReceiver : public CCapturedResultReceiver
    {
    
        virtual void OnDecodedBarcodesReceived(CDecodedBarcodesResult *pResult) override
        {
            if (pResult->GetErrorCode() != EC_OK)
            {
                cout << "Error: " << pResult->GetErrorString() << endl;
            }
            else
            {
                auto tag = pResult->GetOriginalImageTag();
                if (tag)
                {
                    cout << "ImageID:" << tag->GetImageId() << endl;
                    CFileImageTag *fileTag = (CFileImageTag *)tag;
                    cout << "Page number:" << fileTag->GetPageNumber() << endl;
                }
                int count = pResult->GetItemsCount();
                cout << "Decoded " << count << " barcodes" << endl;
                for (int i = 0; i < count; i++)
                {
                    const CBarcodeResultItem *barcodeResultItem = pResult->GetItem(i);
                    if (barcodeResultItem != NULL)
                    {
                        cout << "Result " << i + 1 << endl;
                        cout << "Barcode Format: " << barcodeResultItem->GetFormatString() << endl;
                        cout << "Barcode Text: " << barcodeResultItem->GetText() << endl;
                    }
                }
            }
    
            cout << endl;
        }
    };
    
  3. Instantiate the CFileFetcher class and set the PDF file path.

    CFileFetcher *fileFetcher = new CFileFetcher();
    cvr->SetInput(fileFetcher);
    
  4. Read barcodes from the PDF file. Setting the second parameter of StartCapturing() to true blocks the main thread until the capturing process is complete.

    cvr->SetInput(fileFetcher);
    cvr->AddResultReceiver(capturedReceiver);
    cvr->AddImageSourceStateListener(listener);
    
    char errorMsg[512] = {0};
    int errorCode = cvr->StartCapturing(CPresetTemplate::PT_READ_BARCODES, true, errorMsg, 512);
    

The Complete C++ Program

#include <stdio.h>
#include <string>
#include <vector>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <conio.h>
#include <io.h>
#else
#include <cstring>
#include <dirent.h>
#include <sys/time.h>
#endif

#include <fstream>
#include <streambuf>
#include <iostream>
#include <sstream>

#include "DynamsoftCaptureVisionRouter.h"
#include "DynamsoftUtility.h"
#include "template.h"

using namespace std;
using namespace dynamsoft::license;
using namespace dynamsoft::cvr;
using namespace dynamsoft::dbr;
using namespace dynamsoft::utility;
using namespace dynamsoft::basic_structures;

class MyCapturedResultReceiver : public CCapturedResultReceiver
{

    virtual void OnDecodedBarcodesReceived(CDecodedBarcodesResult *pResult) override
    {
        if (pResult->GetErrorCode() != EC_OK)
        {
            cout << "Error: " << pResult->GetErrorString() << endl;
        }
        else
        {
            auto tag = pResult->GetOriginalImageTag();
            if (tag)
            {
                cout << "ImageID:" << tag->GetImageId() << endl;
                CFileImageTag *fileTag = (CFileImageTag *)tag;
                cout << "Page number:" << fileTag->GetPageNumber() << endl;
            }
            int count = pResult->GetItemsCount();
            cout << "Decoded " << count << " barcodes" << endl;
            for (int i = 0; i < count; i++)
            {
                const CBarcodeResultItem *barcodeResultItem = pResult->GetItem(i);
                if (barcodeResultItem != NULL)
                {
                    cout << "Result " << i + 1 << endl;
                    cout << "Barcode Format: " << barcodeResultItem->GetFormatString() << endl;
                    cout << "Barcode Text: " << barcodeResultItem->GetText() << endl;
                }
            }
        }

        cout << endl;
    }
};

class MyImageSourceStateListener : public CImageSourceStateListener
{
private:
    CCaptureVisionRouter *m_router;

public:
    MyImageSourceStateListener(CCaptureVisionRouter *router)
    {
        m_router = router;
    }

    virtual void OnImageSourceStateReceived(ImageSourceState state)
    {
        if (state == ISS_EXHAUSTED)
            m_router->StopCapturing();
    }
};

bool GetImagePath(char *pImagePath)
{
    std::string input;
    while (true)
    {
        std::cout << "\n>> Step 1: Input your image file's full path:\n";
        std::getline(std::cin, input);

        input.erase(0, input.find_first_not_of(" \t\n\r\"\'")); // Trim leading
        input.erase(input.find_last_not_of(" \t\n\r\"\'") + 1); // Trim trailing

        if (input == "q" || input == "Q")
        {
            return true; 
        }

        std::strncpy(pImagePath, input.c_str(), 511);
        pImagePath[511] = '\0'; 

        std::ifstream file(pImagePath);
        if (file.good())
        {
            file.close();
            return false; 
        }

        std::cout << "Please input a valid path.\n";
    }
}

int main(int argc, char *argv[])
{
    printf("*************************************************\r\n");
    printf("Welcome to Dynamsoft Barcode Demo\r\n");
    printf("*************************************************\r\n");
    printf("Hints: Please input 'Q' or 'q' to quit the application.\r\n");

    int iRet = -1;
    char szErrorMsg[256];
    iRet = CLicenseManager::InitLicense(LICENSE-KEY, szErrorMsg, 256);
    if (iRet != EC_OK)
    {
        cout << szErrorMsg << endl;
    }
    int errorCode = 1;
    char errorMsg[512] = {0};

    CCaptureVisionRouter *cvr = new CCaptureVisionRouter;
    errorCode = cvr->InitSettings(jsonString.c_str(), errorMsg, 512);
    if (errorCode != EC_OK)
    {
        cout << "error:" << errorMsg << endl;
        return -1;
    }

    char pszImageFile[512] = {0};
    bool bExit = false;
    CImageSourceStateListener *listener = new MyImageSourceStateListener(cvr);
    CFileFetcher *fileFetcher = new CFileFetcher();
    CCapturedResultReceiver *capturedReceiver = new MyCapturedResultReceiver;

    cvr->SetInput(fileFetcher);
    cvr->AddResultReceiver(capturedReceiver);
    cvr->AddImageSourceStateListener(listener);

    while (1)
    {
        bExit = GetImagePath(pszImageFile);
        if (bExit)
            break;
        float costTime = 0.0;
        int errorCode = 0;

        string path = string(pszImageFile);
        fileFetcher->SetFile(pszImageFile);
        errorCode = cvr->StartCapturing(CPresetTemplate::PT_READ_BARCODES, true, errorMsg, 512);
    }

    delete cvr, cvr = NULL, listener, capturedReceiver, fileFetcher;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Integrating C++ Barcode Decoding Functionality into a Node.js Addon

We have an existing Node.js barcode SDK project, barcode4nodejs, built with Dynamsoft C++ Barcode SDK v9.x. The following steps outline how to upgrade the underlying C++ barcode decoding functionality to v10.x with minimal changes: no modifications are needed for the JavaScript code, while only a few adjustments are required for the C++ part.

  1. Update the shared libraries in the platforms folder for Windows, Linux and macOS.
  2. Modify the binding.gyp to link the new shared libraries.

    {
        ...
        "targets": [
            {
                ...
                "conditions": [
                    ["OS=='linux'", {
                        "defines": ["LINUX_DBR"],
                        "cflags": ["-std=c++11", "-DNAPI_CPP_EXCEPTIONS", "-fexceptions"],
                        "cflags_cc": ["-std=c++11", "-DNAPI_CPP_EXCEPTIONS", "-fexceptions"],
                        "ldflags": ["-Wl,-rpath,'$$ORIGIN'"],
                        "libraries": [
                            "-lDynamsoftCore", "-lDynamsoftLicense", "-lDynamsoftCaptureVisionRouter", "-lDynamsoftUtility", "-L../platforms/linux/<(arch)"
                        ],
                        "copies": [
                            {
                                "destination": "build/Release/",
                                "files": [
                                    "./platforms/linux/<(arch)/libDynamicImage.so",
                                    "./platforms/linux/<(arch)/libDynamicPdf.so",
                                    "./platforms/linux/<(arch)/libDynamicPdfCore.so",
                                    "./platforms/linux/<(arch)/libDynamsoftBarcodeReader.so",
                                    "./platforms/linux/<(arch)/libDynamsoftCaptureVisionRouter.so",
                                    "./platforms/linux/<(arch)/libDynamsoftCore.so",
                                    "./platforms/linux/<(arch)/libDynamsoftImageProcessing.so",
                                    "./platforms/linux/<(arch)/libDynamsoftLicense.so",
                                    "./platforms/linux/<(arch)/libDynamsoftUtility.so",
                                ]
                            }
                        ]
                    }],
                    ["OS=='win'", {
                        "defines": ["WINDOWS_DBR", "NAPI_CPP_EXCEPTIONS"],
                        "libraries": [
                            "-l../platforms/windows/DynamsoftCorex64.lib", "-l../platforms/windows/DynamsoftLicensex64.lib", "-l../platforms/windows/DynamsoftCaptureVisionRouterx64.lib", "-l../platforms/windows/DynamsoftUtilityx64.lib"
                        ],
                        "copies": [
                            {
                                "destination": "build/Release/",
                                "files": [
                                    "./platforms/windows/DynamicImagex64.dll",
                                    "./platforms/windows/DynamicPdfCorex64.dll",
                                    "./platforms/windows/DynamicPdfx64.dll",
                                    "./platforms/windows/DynamsoftBarcodeReaderx64.dll",
                                    "./platforms/windows/DynamsoftCaptureVisionRouterx64.dll",
                                    "./platforms/windows/DynamsoftCorex64.dll",
                                    "./platforms/windows/DynamsoftImageProcessingx64.dll",
                                    "./platforms/windows/DynamsoftLicensex64.dll",
                                    "./platforms/windows/DynamsoftUtilityx64.dll",
                                    "./platforms/windows/vcomp140.dll"
                                ]
                            }
                        ]
    
                    }],
                    ["OS=='mac'", {
                        "defines": ["MAC_DBR"],
                        "cflags": ["-std=c++11", "-DNAPI_CPP_EXCEPTIONS"],
                        "cflags_cc": ["-std=c++11", "-DNAPI_CPP_EXCEPTIONS"],
                        "ldflags": ["-Wl,-rpath,@loader_path"],
                        "libraries": [
                            "-lDynamsoftCore", "-lDynamsoftLicense", "-lDynamsoftCaptureVisionRouter", "-lDynamsoftUtility", "-L../platforms/macos"
                        ],
                        "copies": [
                            {
                                "destination": "build/Release/",
                                "files": [
                                    "./platforms/macos/libDynamicImagex64.dylib",
                                    "./platforms/macos/libDynamicPdf.dylib",
                                    "./platforms/macos/libDynamicPdfCore.dylib",
                                    "./platforms/macos/libDynamsoftBarcodeReader.dylib",
                                    "./platforms/macos/libDynamsoftCaptureVisionRouter.dylib",
                                    "./platforms/macos/libDynamsoftCore.dylib",
                                    "./platforms/macos/libDynamsoftImageProcessing.dylib",
                                    "./platforms/macos/libDynamsoftLicense.dylib",
                                    "./platforms/macos/libDynamsoftUtility.dylib",
                                ]
                            }
                        ]
                    }]
                ]
            }
        ]
    }
    
  3. Copy the necessary header files to the src folder.

    DynamsoftBarcodeReader.h
    DynamsoftCaptureVisionRouter.h
    DynamsoftCodeParser.h
    DynamsoftCore.h
    DynamsoftDocumentNormalizer.h
    DynamsoftImageProcessing.h
    DynamsoftLabelRecognizer.h
    DynamsoftLicense.h
    DynamsoftUtility.h
    template.h
    
  4. Add CCaptureVisionRouter *, CFileFetcher *, CImageSourceStateListener *, and MyCapturedResultReceiver * pointers as members of the BarcodeReader class in dbr.h.

    #ifndef DBR_H
    #define DBR_H
    
    #include <napi.h>
    #include <string>
    #include <uv.h>
    #include <vector>
    #include "DynamsoftCaptureVisionRouter.h"
    #include "DynamsoftUtility.h"
    
    using namespace std;
    using namespace dynamsoft::license;
    using namespace dynamsoft::cvr;
    using namespace dynamsoft::dbr;
    using namespace dynamsoft::utility;
    using namespace dynamsoft::basic_structures;
    
    class MyCapturedResultReceiver : public CCapturedResultReceiver
    {
    public:
        vector<CDecodedBarcodesResult *> results;
    
    public:
        virtual void OnDecodedBarcodesReceived(CDecodedBarcodesResult *pResult) override;
    };
    
    typedef enum
    {
        NO_BUFFER,
        FILE_STREAM,
        YUYV_BUFFER,
        BASE64,
        RGB_BUFFER,
    } BufferType;
    
    struct BarcodeWorker
    {
        uv_work_t request;                         
        Napi::FunctionReference callback;          
        int iFormat;                               
        std::string filename;                      
        vector<CDecodedBarcodesResult *> pResults; 
        unsigned char *buffer;
        int size;              
        int errorCode;         
        int width;             
        int height;            
        BufferType bufferType; 
        bool useTemplate;
        int stride;               
        std::string base64string; 
        std::string templateContent;
        int elapsedTime;
        CCaptureVisionRouter *handler;
        MyCapturedResultReceiver *capturedReceiver;
        CFileFetcher *fileFetcher;
    };
    
    class MyImageSourceStateListener : public CImageSourceStateListener
    {
    private:
        CCaptureVisionRouter *m_router;
        BarcodeWorker *m_worker;
    
    public:
        MyImageSourceStateListener(CCaptureVisionRouter *router);
        virtual void OnImageSourceStateReceived(ImageSourceState state) override;
    };
    
    class BarcodeReader : public Napi::ObjectWrap<BarcodeReader>
    {
    ...
    
    private:
        CCaptureVisionRouter *handler;
        CFileFetcher *fileFetcher;
        CImageSourceStateListener *listener;
        MyCapturedResultReceiver *capturedReceiver;
    
        static Napi::FunctionReference constructor;
    
        ...
    };
    
    #endif // DBR_H
    
  5. Update C++ implementation in dbr.cc:

    • Instantiate CCaptureVisionRouter, MyImageSourceStateListener, CFileFetcher, and MyCapturedResultReceiver in the BarcodeReader constructor.

      BarcodeReader::BarcodeReader(const Napi::CallbackInfo &info) : Napi::ObjectWrap<BarcodeReader>(info)
      {
          Napi::Env env = info.Env();
          handler = new CCaptureVisionRouter;
          char errorMsgBuffer[256];
          int ret = handler->InitSettings(jsonString.c_str(), errorMsgBuffer, 256);
          if (ret)
          {
              printf("InitSettings: %s\n", errorMsgBuffer);
          }
      
          listener = new MyImageSourceStateListener(handler);
          fileFetcher = new CFileFetcher();
          handler->SetInput(fileFetcher);
      
          capturedReceiver = new MyCapturedResultReceiver;
          handler->AddResultReceiver(capturedReceiver);
          handler->AddImageSourceStateListener(listener);
      }
      
    • Implement the callback functions to track the image processing lifecycle and store barcode results for later use. Use Retain() to prevent auto-release of results.

      void MyCapturedResultReceiver::OnDecodedBarcodesReceived(CDecodedBarcodesResult *pResult)
      {
          pResult->Retain();
          results.push_back(pResult);
      }
      
      MyImageSourceStateListener::MyImageSourceStateListener(CCaptureVisionRouter *router)
      {
          m_router = router;
      }
      
      void MyImageSourceStateListener::OnImageSourceStateReceived(ImageSourceState state)
      {
          if (state == ISS_EXHAUSTED)
          {
              m_router->StopCapturing();
          }
      }
      
    • Call StartCapturing() in the ProcessImage() function to initiate barcode detection on the built-in thread pool. The main thread will remain blocked until all barcode decoding tasks are complete.

      void BarcodeReader::ProcessImage(BarcodeWorker *worker)
      {
          CCaptureVisionRouter *handler = worker->handler;
          CCapturedResult *result = NULL;
          if (!worker->useTemplate)
          {
              SimplifiedCaptureVisionSettings pSettings = {};
              handler->GetSimplifiedSettings("", &pSettings);
              pSettings.barcodeSettings.barcodeFormatIds = worker->iFormat <= 0 ? BF_ALL : worker->iFormat;
      
              char szErrorMsgBuffer[256];
              handler->UpdateSettings("", &pSettings, szErrorMsgBuffer, 256);
          }
          else
          {
              char errorMessage[256];
              int ret = handler->InitSettings(worker->templateContent.c_str(), errorMessage, 256);
              if (ret)
              {
                  printf("Returned value: %d, error message: %s\n", ret, errorMessage);
              }
          }
      
          int starttime = gettime();
          int ret = 0;
          switch (worker->bufferType)
          {
          case FILE_STREAM:
              if (worker->buffer)
              {
                  worker->fileFetcher->SetFile(worker->buffer, worker->size);
              }
              break;
          case YUYV_BUFFER:
              if (worker->buffer)
              {
                  int width = worker->width, height = worker->height;
                  int size = width * height;
                  int index = 0;
                  unsigned char *data = new unsigned char[size];
                  for (int i = 0; i < size; i++)
                  {
                      data[i] = worker->buffer[index];
                      index += 2;
                  }
                  CImageData *imageData = new CImageData(size, data, width, height, width, IPF_GRAYSCALED);
                  worker->fileFetcher->SetFile(imageData);
                  delete imageData, imageData = NULL;
                  delete[] data, data = NULL;
              }
              break;
          case BASE64:
              if (worker->base64string != "")
              {
                  worker->fileFetcher->SetFile(worker->buffer, worker->size);
              }
              break;
          case RGB_BUFFER:
              if (worker->buffer)
              {
                  int width = worker->width, height = worker->height, stride = worker->stride;
                  ImagePixelFormat format = IPF_RGB_888;
                  if (width == stride)
                  {
                      format = IPF_GRAYSCALED;
                  }
                  else if (width * 3 == stride)
                  {
                      format = IPF_RGB_888;
                  }
                  else if (width * 4 == stride)
                  {
                      format = IPF_ARGB_8888;
                  }
                  CImageData *imageData = new CImageData(stride * height, worker->buffer, width, height, stride, format);
                  worker->fileFetcher->SetFile(imageData);
                  delete imageData, imageData = NULL;
              }
              break;
          default:
              worker->fileFetcher->SetFile(worker->filename.c_str());
          }
      
          char errorMsg[512] = {0};
          int errorCode = worker->handler->StartCapturing("", true, errorMsg, 512);
          if (errorCode != 0)
          {
              printf("StartCapturing: %s\n", errorMsg);
          }
      
          int endtime = gettime();
          int elapsedTime = endtime - starttime;
      
          worker->pResults = worker->capturedReceiver->results;
          worker->errorCode = ret;
          worker->elapsedTime = elapsedTime;
      }
      
    • Use Napi::Object to encapsulate barcode results, making them accessible in the JavaScript layer.

      void BarcodeReader::WrapResults(BarcodeWorker *worker, Napi::Env env, Napi::Object &result)
      {
          vector<CDecodedBarcodesResult *> pResults = worker->pResults;
          Napi::Array barcodeResults = Napi::Array::New(env);
          for (int j = 0; j < pResults.size(); j++)
          {
              CDecodedBarcodesResult *barcodeResult = pResults[j];
      
              if (barcodeResult)
              {
                  CFileImageTag *fileTag = (CFileImageTag *)barcodeResult->GetOriginalImageTag();
                  int count = barcodeResult->GetItemsCount();
      
                  for (int i = 0; i < count; i++)
                  {
                      const CBarcodeResultItem *barcodeResultItem = barcodeResult->GetItem(i);
                      CPoint *points = barcodeResultItem->GetLocation().points;
      
                      Napi::Object res = Napi::Object::New(env);
                      res.Set("format", barcodeResultItem->GetFormatString());
                      res.Set("value", barcodeResultItem->GetText());
                      res.Set("x1", Napi::Number::New(env, points[0][0]));
                      res.Set("y1", Napi::Number::New(env, points[0][1]));
                      res.Set("x2", Napi::Number::New(env, points[1][0]));
                      res.Set("y2", Napi::Number::New(env, points[1][1]));
                      res.Set("x3", Napi::Number::New(env, points[2][0]));
                      res.Set("y3", Napi::Number::New(env, points[2][1]));
                      res.Set("x4", Napi::Number::New(env, points[3][0]));
                      res.Set("y4", Napi::Number::New(env, points[3][1]));
                      res.Set("page", Napi::Number::New(env, fileTag->GetPageNumber()));
                      res.Set("time", Napi::Number::New(env, worker->elapsedTime));
                      res.Set("angle", Napi::Number::New(env, barcodeResultItem->GetAngle()));
                      res.Set("isMirrored", Napi::Number::New(env, barcodeResultItem->IsMirrored()));
                      barcodeResults.Set(i + j * count, res);
                  }
      
                  barcodeResult->Release();
              }
          }
      
          result = barcodeResults;
          worker->pResults.clear();
      }
      

Build the Node.js Addon

Use node-gyp to build the Node.js barcode SDK module.

node-gyp configure
node-gyp build
Enter fullscreen mode Exit fullscreen mode

On macOS, you'll need to run the following commands to ensure the .dylib files are accessible:

    install_name_tool -change @rpath/libDynamsoftCore.dylib @loader_path/libDynamsoftCore.dylib build/Release/dbr.node
    install_name_tool -change @rpath/libDynamsoftLicense.dylib @loader_path/libDynamsoftLicense.dylib build/Release/dbr.node
    install_name_tool -change @rpath/libDynamsoftCaptureVisionRouter.dylib @loader_path/libDynamsoftCaptureVisionRouter.dylib build/Release/dbr.node
    install_name_tool -change @rpath/libDynamsoftUtility.dylib @loader_path/libDynamsoftUtility.dylib build/Release/dbr.node
    install_name_tool -change @rpath/libDynamsoftImageProcessing.dylib @loader_path/libDynamsoftImageProcessing.dylib build/Release/libDynamsoftUtility.dylib
Enter fullscreen mode Exit fullscreen mode

Reading Barcodes from Multi-Page PDFs in Node.js

  1. Create a test.js file with the following code:

    var dbr = require('barcode4nodejs');
    var barcodeTypes = dbr.barcodeTypes;
    var readline = require('readline');
    var fs = require('fs');
    
    dbr.initLicense("LICENSE-KEY");
    
    function decodeFileStreamAsync(fileName) {
      let stats = fs.statSync(fileName);
      let fileSize = stats['size'];
    
      fs.open(fileName, 'r', function (status, fd) {
        if (status) {
          console.log(status.message);
          return;
        }
        let buffer = Buffer.alloc(fileSize);
        fs.read(fd, buffer, 0, fileSize, 0, function (err, bytesRead, data) {
          (async function () {
            try {
              var result = await dbr.decodeFileStreamAsync(buffer, fileSize, barcodeTypes, "");
              console.log(result);
              setTimeout(() => {
                console.log('terminated');
              }, 1000);
            } catch (error) {
              console.log(error);
            }
          })();
        });
      });
    }
    
    let args = process.argv;
    if (args.includes('-f')) {
      let fIndex = args.indexOf('-f');
      if (args[fIndex + 1]) {
        decodeFileStreamAsync(args[fIndex + 1]);
      } else {
        console.log('Please add a file.');
      }
    
    }
    

    You need to replace LICENSE-KEY with your own.

  2. Run the script with the PDF file created earlier.

    node test.js -f multipage.pdf
    

    read barcodes from multipage PDF in Node.js

Source Code

Top comments (0)