How to Scan Barcodes in an Oracle APEX Application

In the previous article, we’ve created an Oracle APEX application to scan documents. In this article, we are going to add barcode scanning function to the Oracle APEX application with Dynamsoft Barcode Reader.

You can check out the video to see what it does.

Build an Oracle APEX Application to Scan Barcodes

Let’s do this in steps.

Build an APEX Plugin for Dynamsoft Barcode Reader

In order to use Dynamsoft Barcode Reader to scan barcodes, we need to create an APEX plugin first.

New Plugin

  1. On the app’s page, click shared components. Then click plug-ins in the other components section.
  2. Create a new plugin from scratch. Here, we select the region type as we need to display something and run actions on the page.
  3. Define the f_render function in the PL/SQL code.

    function f_render (
        p_region              in apex_plugin.t_region,
        p_plugin              in apex_plugin.t_plugin,
        p_is_printer_friendly in boolean
    ) return apex_plugin.t_region_render_result is
    

    Then set Render Procedure/Function Name to F_RENDER in the callbacks section.

Next, we are going to put the plugin aside, try to create a barcode scanning web page first and then adapt it as a plugin.

Build a Barcode Scanning Web App

The web page loads the libraries of Dynamsoft Barcode Reader and Dynamsoft Camera Enhancer via CDN. It can use the camera enhancer library to open the camera and read barcodes from camera frames with the barcode reader library.

  1. Create a new HTML file with the following content.

    <!DOCTYPE html>
    <html>
    <head>
        <title>Dynamsoft Barcode Reader Sample</title>
        <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
        <script src="script.js"></script>
    </head>
    <body>
      <script>
        window.onload = function(){
        }
      </script>
    </body>
    </html>
    
  2. Create a new script.js file. Define a DBRExtension object in it.

    let DBRExtension = {}
    
  3. Add functions to load external JavaScript files.

    let DBRExtension = {
      loadLibrary: function (src,type,id,data){
        return new Promise(function (resolve, reject) {
          let scriptEle = document.createElement("script");
          scriptEle.setAttribute("type", type);
          scriptEle.setAttribute("src", src);
          if (id) {
            scriptEle.id = id;
          }
          if (data) {
            for (let key in data) {
              scriptEle.setAttribute(key, data[key]);
            }
          }
          document.body.appendChild(scriptEle);
          scriptEle.addEventListener("load", () => {
            console.log(src+" loaded")
            resolve(true);
          });
          scriptEle.addEventListener("error", (ev) => {
            console.log("Error on loading "+src, ev);
            reject(ev);
          });
        });
      }
    }
    
  4. Add a load function to load needed JavaScript files. It also sets the license for Dynamsoft Barcode Reader. You can apply for a license here.

    let DBRExtension = {
      load: async function(pConfig){
        await this.loadLibrary("https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.6.11/dist/dbr.js","text/javascript");
        await this.loadLibrary("https://cdn.jsdelivr.net/npm/dynamsoft-camera-enhancer@3.3.1/dist/dce.js","text/javascript");
        if (pConfig.license) {
          Dynamsoft.DBR.BarcodeScanner.license = pConfig.license;
        }else{
          Dynamsoft.DBR.BarcodeScanner.license = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="; // one-day trial
        }
      }
    }
    
  5. Add a function to initialize the reader and the camera enhancer. A container is appended to the body to display the camera. We can also pass parameters like the JSON template for the barcode reader and styles for the container to set via the arguments.

    let DBRExtension = {
      reader:undefined,
      enhancer:undefined,
      regionID:undefined,
      init: async function(pConfig){
        this.reader = await Dynamsoft.DBR.BarcodeScanner.createInstance();
        this.enhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance();
        await this.enhancer.setUIElement(Dynamsoft.DCE.CameraEnhancer.defaultUIElementURL);
        let container = document.createElement("div");
        container.id = "enhancerUIContainer";
        document.body.appendChild(container);
        if (pConfig.styles) {
          let styles = JSON.parse(pConfig.styles); //{width:"100%"} e.g.
          for (const key in styles) {
            container.style[key] = styles[key];
          }
        }
        if (pConfig.template) {
          await this.reader.initRuntimeSettingsWithString(pConfig.template);
        }
        container.appendChild(this.enhancer.getUIElement());
        // The following line hides the close button
        document.getElementsByClassName("dce-btn-close")[0].style.display = "none";
        container.style.display = "none";
      }
    }
    
  6. Add a function to open the camera.

    let DBRExtension = {
      open: async function(){
        document.getElementById("enhancerUIContainer").style.display = "";
        await this.enhancer.open(true);
      }
    }
    
  7. Add a function to close the camera.

    let DBRExtension = {
      close: function(){
        this.enhancer.close(true);
        document.getElementById("enhancerUIContainer").style.display = "none";
      }
    }
    
  8. Add functions to start scanning from the camera frames and stop scanning.

    let DBRExtension = {
      interval:undefined,
      processing:undefined,
      barcodeResults:undefined,
      startScanning: function(){
        this.stopScanning();
        let pThis = this;
        const captureAndDecode = async function() {
          if (!pThis.enhancer || !pThis.reader) {
            return;
          }
          if (pThis.enhancer.isOpen() === false) {
            return;
          }
          if (pThis.processing === true) {
            return;
          }
          pThis.processing = true; // set processing to true so that the next frame will be skipped if the processing has not completed.
          let frame = pThis.enhancer.getFrame();
          if (frame) {
            let results = await pThis.reader.decode(frame);
            if (results.length > 0) {
              pThis.barcodeResults = results;
            }
            console.log(results);
            pThis.processing = false;
          }
        }
        this.interval = setInterval(captureAndDecode,100); // set an interval to read barcodes
      },
      stopScanning: function(){
        if (this.interval) {
          clearInterval(this.interval);
          this.interval = undefined;
        }
        this.processing = false;
      }
    }
    
  9. Register the played event for the camera enhancer so that when the resolution or the camera is changed, restart scanning.

    this.enhancer.on("played", (playCallbackInfo) => {
      if (this.interval) {
        this.startScanning();
      }
    });
    
  10. Add the following scripts in the HTML file to start the barcode scanner.

    window.onload = async function(){
      let styles = {width:"100%",height:"100%",left:0,top:0,position:"absolute"}
      await DBRExtension.load({});
      await DBRExtension.init({styles:JSON.stringify(styles)});
      await DBRExtension.open();
      DBRExtension.startScanning();
    }
    

All right, we have finished writing the web page. Next, we are going to adapt it for the APEX plugin.

Adapt the Web Page for APEX

  1. If it is running in an APEX app, append the container to the region in the init function.

    if ('apex' in window) {
      this.regionID = pConfig.regionID;
      const region = document.getElementById(this.regionID);
      region.appendChild(container);
    }else{
      document.body.appendChild(container);
    }
    
  2. Get the page item from the pConfig argument and set its content to the text of the first barcode.

    let DBRExtension = {
      item: undefined,
      init: function(pConfig){
        //...
        if ('apex' in window) {
          this.regionID = pConfig.regionID;
          this.item = pConfig.item;
          const region = document.getElementById(this.regionID);
          region.appendChild(container);
        }
        //...
      },
      startScanning: function(){
        //...
        if (results.length > 0) {
          if ('apex' in window) {
            if (pThis.item) {
              apex.item(pThis.item).setValue(results[0].barcodeText);
            }
          }
        }
        //...
      },
    }
    
  3. On the setup page of the plugin, add several custom attributes.

    Attributes

    • styles: JSON string to set the styles.
    • template: JSON template to define the runtime settings of Dynamsoft Barcode Reader.
    • Barcode result container: page item for the barcode result.
    • license: license for Dynamsoft Barcode Reader. You can apply for a license here.
  4. Upload the script.js file and add it in the list of file URLs to load.

    Script files

  5. In the PL/SQL code, run the functions for initialization.

    begin
      apex_javascript.add_onload_code (
          p_code => '(async () => { await DBRExtension.load({' ||
              apex_javascript.add_attribute(p_name => 'license', p_value => p_region.attribute_02, p_add_comma => false ) ||
            '});
            DBRExtension.init({' ||
              apex_javascript.add_attribute(p_name => 'styles', p_value => p_region.attribute_01, p_add_comma => true ) ||
              apex_javascript.add_attribute(p_name => 'item', p_value => p_region.attribute_03, p_add_comma => true ) ||
              apex_javascript.add_attribute(p_name => 'template', p_value => p_region.attribute_04, p_add_comma => true ) ||
              apex_javascript.add_attribute(p_name => 'regionID', p_value => p_region.static_id, p_add_comma => false ) ||
            '}); })();',
          p_key  => null );
      return null;
    end;
    

Use the Plugin to Scan Barcodes

  1. Create a new barcode scanner page and in its page designer, drag the barcode scanner region into the app, add two buttons and a text field to display the barcode result. In addition, set the attributes for the region.

    Page designer

  2. Add a dynamic action for the StartScanButton button. When it is clicked, execute the following JavaScript code:

    (async () => {
      if (DBRExtension.reader) {
        await DBRExtension.open();
        DBRExtension.startScanning();
      }else{
        alert("The barcode scanner is still initializing.");
      }
    })();
    
  3. Add a dynamic action for the StopScanButton button. When it is clicked, execute the following JavaScript code:

    DBRExtension.stopScanning();
    DBRExtension.close();
    

We can now scan barcodes in an Oracle APEX app. You can find an online demo here.

Source Code

Get the source code of the plugin to have a try:

https://github.com/tony-xlh/APEX-Dynamsoft-Barcode-Reader