Barcode Scanner using ZXing, ML Kit, and Dynamsoft

Barcode has been around for more than half a century. There are various types of barcodes and reading libraries. Some libraries focus on one barcode format like libdmtx while most libraries can read all kinds of formats. In this article, we are going to implement a mobile barcode scanner with three libraries to make it easy to decide which one to use:

Steps on Building a React Native Barcode Scanner

There are many ways to build an app. We can use Java or Kotlin for Android development and Objective-C/Swift for iOS development. Here, we are going to use React Native to create a cross-platform app. Using the libraries is largely the same using different languages.

New Project

Create a new project:

npx @react-native-community/cli@latest init BarcodeScanner

Add Camera Permissions

For Android, add the following to android\app\src\main\AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

For iOS add the following to Info.plist:

<key>NSCameraUsageDescription</key>
<string>For barcode scanning</string>

Add Dependencies

  1. Install react-native-vision-camera to open the camera.

    npm install react-native-vision-camera react-native-worklets-core
    
  2. Install react-native-image-picker to pick an image.

    npm install react-native-image-picker
    
  3. Install the zxing library.

    npm install vision-camera-zxing
    
  4. Install the ML Kit library.

    npm install react-native-vision-camera-barcodes-scanner
    
  5. Install the Dynamsoft Barcode Reader library.

    npm install vision-camera-dynamsoft-barcode-reader
    

In addition, add the following to babel.conf.js:

 module.exports = {
   presets: ['module:@react-native/babel-preset'],
+  plugins: [
+    'react-native-worklets-core/plugin',
+  ],
 };

Read Barcodes from the Camera

  1. In a component, use Vision Camera to open the camera.

    const BarcodeScanner: React.FC<props> = (props: props) => {
      const [hasPermission, setHasPermission] = React.useState(false);
      const [isActive, setIsActive] = React.useState(false);
      const device = useCameraDevice("back");
      const cameraFormat = useCameraFormat(device, [
        { videoResolution: { width: 1280, height: 720 } },
        { fps: 60 }
      ])
      React.useEffect(() => {
        (async () => {
          const status = await Camera.requestCameraPermission();
          setHasPermission(status === 'granted');
          setIsActive(true);
        })();
        }
      }, []);
         
      return (
        <>
          {device &&
          hasPermission && (
          <>
            <Camera
            style={StyleSheet.absoluteFill}
            device={device}
            isActive={isActive}
            format={cameraFormat}
            frameProcessor={frameProcessor}
            resizeMode='contain'
            />
          </>
        </>
      );
    }
    
  2. Register a frame processor to read barcodes from the camera frames using the selected barcode reading engine.

    import { zxing, type Result } from 'vision-camera-zxing';
    import { useBarcodeScanner } from "react-native-vision-camera-barcodes-scanner";
    import { decode, TextResult } from 'vision-camera-dynamsoft-barcode-reader';
    
    const {scanBarcodes} = useBarcodeScanner();
    
    const frameProcessor = useFrameProcessor(frame => {
      'worklet'
      runAsync(frame, () => {
        'worklet'
        let results;
        if (engine.value === "ZXing") {
          results = zxing(frame,{multiple:true});
        }else if (engine.value === "Dynamsoft") {
          results = decode(frame,{rotateImage:false});
        }else{
          results = scanBarcodes(frame);
        }
        console.log(results);
      })
    }, [])
    

Read Barcodes from an Album Image

  1. Pick an image from the album.

    let options: ImageLibraryOptions = {
      mediaType: 'photo',
      includeBase64: true,
    }
    let response = await launchImageLibrary(options);
    
  2. Read barcodes from the image using its base64 or uri. The results are unified to one interface for representing the barcode.

    if (response && response.assets) {
      if (selectedEngine != "MLKit") {
        if (response.assets[0]!.base64) {
          if (selectedEngine === "Dynamsoft") {
            let textResults = await DBR.decodeBase64(response.assets[0]!.base64);
            let results = [];
            for (let index = 0; index < textResults.length; index++) {
              const tr = textResults[index];
              const points:Point[] = [];
              points.push({x:tr.x1,y:tr.y1});
              points.push({x:tr.x2,y:tr.y2});
              points.push({x:tr.x3,y:tr.y3});
              points.push({x:tr.x4,y:tr.y4});
              const result:Result = {
                barcodeText:tr.barcodeText,
                barcodeFormat:tr.barcodeFormat,
                barcodeBytesBase64:tr.barcodeBytesBase64,
                points:points
              }
              results.push(result);
            }
            setBarcodeResults(results);
          }else{
            let results = await decodeBase64(response.assets[0]!.base64,{multiple:true});
            setBarcodeResults(results);
          }
        }
      }else{
        if (response && response.assets[0] && response.assets[0].uri) {
          const uri = response.assets[0].uri as string;
          let results = [];
          const barcodes = await ImageScanner(uri);
          for (let index = 0; index < barcodes.length; index++) {
            const barcode = barcodes[index];
            const points:Point[] = [];
            points.push({x:barcode.left,y:barcode.top});
            points.push({x:barcode.right,y:barcode.top});
            points.push({x:barcode.right,y:barcode.bottom});
            points.push({x:barcode.left,y:barcode.bottom});
            const result:Result = {
              barcodeText:barcode.rawValue,
              barcodeFormat:"",
              barcodeBytesBase64:"",
              points:points
            }
            results.push(result);
          }
          setBarcodeResults(results);
        }
      }
    }
    

Comparison of the Libraries

After the demo is done, we can compare the libraries.

Barcode Formats

Dynamsoft has the most supported barcode formats.

ZXing ML Kit Dynamsoft
UPC-A UPC-A UPC-A
UPC-E UPC-E UPC-E
EAN-8 EAN-8 EAN-8
EAN-13 EAN-13 EAN-13
Code 39 Code 39 Code 39
Code 93 Code 93 Code 93
ITF ITF ITF
Codabar Codabar Codabar
QR Code QR Code QR Code
Aztec Aztec Aztec
Data Matrix Data Matrix Data Matrix
PDF417 PDF417 PDF417
Maxicode   Maxicode
RSS-14 (GS1 DataBar)   RSS-14 (GS1 DataBar)
    Code 11
    Interleaved 2 of 5
    Industrial 2 of 5
    GS1 Composite Code
    DotCode
    Pharmacode
    Patch Code

GS1 Composite which only Dynamsoft can read:

gs1-composite-inverted

Scan Angle

ZXing has to align the barcode with the camera to scan it.

zxing

Dynamsoft and ML Kit can read the barcodes in any angle. Dynamsoft can precisely return the coordinates of barcodes.

Dynamsoft:

dynamsoft

ML Kit:

mlkit

Barcode Details

Dynamsoft can return more details about the barcode, like its raw bytes, codewords, orientation, etc.

Images in Bad Conditions

Dynamsoft can handle more image conditions.

Type Image ZXing ML Kit Dynamsoft
Inverted inverted ×
Zero quietzone zero quietzone × ×
Curved curled × ×
Damaged damaged ×

Check out the QR code reading benchmark article to view more examples.

Settings

ZXing, ML Kit and Dynamsoft all have the ability to set which barcode formats to use while Dynamsoft has more settings like the following:

  1. Scan region.
  2. Expected barcode count.
  3. Localization methods.
  4. Binarization methods.
  5. Timeout.

Check out the docs to learn more.

Enterprise Support

Dynamsoft is an commercial SDK. It has a better support and provides customization services to guarantee the success of your business.

Source Code

Check out the source code of the demo to have a try:

https://github.com/tony-xlh/react-native-zxing-mlkit-dynamsoft