How to Write and Read QR Code with ZXing in Java

ZXing is an open-source, 1D/2D barcode image processing library implemented in Java. The supported barcode formats include UPC-A, UPC-E, EAN-8, Code 93, Code 128, QR Code, Data Matrix, Aztec, PDF 417, etc. Besides the Java version, developers can leverage other ported projects such as QZXing, zxing-cpp, zxing_cpp.rb, python-zxing and ZXing .NET to quickly make barcode reader or writer software. According to the visualization report of Google trends, QR code seems to be the most popular barcode format since 2009. In this post, I’d like to share how to use ZXing to create QR code writer and reader for both desktop Java applications and Android mobile apps.

💡 Here is a benchmark report of ZXing and its alternatives for scanning QR Codes.

Getting ZXing Source Code

The ZXing project has moved from Google Code to GitHub. To get the source code, use the  following git command line:

git clone https://github.com/zxing/zxing

Linking ZXing to Projects

There are two ways to link ZXing:

  • Build the source code to a jar file, and then import it to Java projects.  If you want to use Android Studio instead of Eclipse, please read Time to Migrate Android Projects to Android Studio.
  • Directly import the ZXing source code to Java projects. In Eclipse, select Project Properties > Java Build Path > Source > Link Source, and specify the source folder location: **\core\src\main\java**. If the folder location is incorrect, you will see many package errors in the console. [![link zxing source folder](./img/2015/08/zxing_linked_folder.png)](./img/2015/08/zxing_linked_folder.png)

Taking a Glimpse of ZXing Source Code

Before implementing QR code writer and reader, let’s take a look at the basic Java classes in ZXing barcode library.

In Eclipse, search for the text implements Writer.

zxing barcode writer Here are the 1D/2D barcode writers. All barcode formats are defined in BarcodeFormat.java:

public enum BarcodeFormat {

  /** Aztec 2D barcode format. */
  AZTEC,

  /** CODABAR 1D format. */
  CODABAR,

  /** Code 39 1D format. */
  CODE_39,

  /** Code 93 1D format. */
  CODE_93,

  /** Code 128 1D format. */
  CODE_128,

  /** Data Matrix 2D barcode format. */
  DATA_MATRIX,

  /** EAN-8 1D format. */
  EAN_8,

  /** EAN-13 1D format. */
  EAN_13,

  /** ITF (Interleaved Two of Five) 1D format. */
  ITF,

  /** MaxiCode 2D barcode format. */
  MAXICODE,

  /** PDF417 format. */
  PDF_417,

  /** QR Code 2D barcode format. */
  QR_CODE,

  /** RSS 14 */
  RSS_14,

  /** RSS EXPANDED */
  RSS_EXPANDED,

  /** UPC-A 1D format. */
  UPC_A,

  /** UPC-E 1D format. */
  UPC_E,

  /** UPC/EAN extension format. Not a stand-alone format. */
  UPC_EAN_EXTENSION

}

The class MultiFormatWriter has covered all supported barcode writers. Instead of any specific bar code writer, we just need to use MultiFormatWriter with a specified barcode format.

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width, int height,
                          Map<EncodeHintType,?> hints) throws WriterException {

    Writer writer;
    switch (format) {
      case EAN_8:
        writer = new EAN8Writer();
        break;
      case EAN_13:
        writer = new EAN13Writer();
        break;
      case UPC_A:
        writer = new UPCAWriter();
        break;
      case QR_CODE:
        writer = new QRCodeWriter();
        break;
      case CODE_39:
        writer = new Code39Writer();
        break;
      case CODE_128:
        writer = new Code128Writer();
        break;
      case ITF:
        writer = new ITFWriter();
        break;
      case PDF_417:
        writer = new PDF417Writer();
        break;
      case CODABAR:
        writer = new CodaBarWriter();
        break;
      case DATA_MATRIX:
        writer = new DataMatrixWriter();
        break;
      case AZTEC:
        writer = new AztecWriter();
        break;
      default:
        throw new IllegalArgumentException("No encoder available for format " + format);
    }
    return writer.encode(contents, format, width, height, hints);
  }

Similarly, we can search for implements Reader:

zxing barcode reader

The MultiFormatReader will also simplify the code work.

QR Code Writer and Reader for Windows, Mac and Linux

Since ZXing is implemented in Java, it is easy to create cross-platform QR writer and reader software for Windows, Mac and Linux. To operate image data in Java, we need to use the class BufferedImage.

Writing QR Code with QRCodeWriter

public static void writeQRCode() {
		QRCodeWriter writer = new QRCodeWriter();
		int width = 256, height = 256;
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // create an empty image
		int white = 255 << 16 | 255 << 8 | 255;
		int black = 0;
		try {
			BitMatrix bitMatrix = writer.encode("https://www.dynamsoft.com/codepool/zxing-write-read-qrcode.html", BarcodeFormat.QR_CODE, width, height);
	        for (int i = 0; i < width; i++) {
	            for (int j = 0; j < height; j++) {
	            	image.setRGB(i, j, bitMatrix.get(i, j) ? black : white); // set pixel one by one
	            }
	        }

	        try {
				ImageIO.write(image, "jpg", new File("dynamsoftbarcode.jpg")); // save QR image to disk
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		} catch (WriterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

Reading QR Code with QRCodeReader

We need to use RGBLuminanceSource to wrap RGB data:

public static String readQRCode(String fileName) {
		File file = new File(fileName);
		BufferedImage image = null;
		BinaryBitmap bitmap = null;
		Result result = null;

		try {
			image = ImageIO.read(file);
			int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
			RGBLuminanceSource source = new RGBLuminanceSource(image.getWidth(), image.getHeight(), pixels);
			bitmap = new BinaryBitmap(new HybridBinarizer(source));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (bitmap == null)
			return null;

		QRCodeReader reader = new QRCodeReader();	
		try {
			result = reader.decode(bitmap);
			return result.getText();
		} catch (NotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ChecksumException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

QR Code Generator and Reader for Android

Comparing to the Java code of the desktop application, the only difference is the way of operating image bytes on Android. In Android SDK, there is no class named BufferedImage. Instead, we should use Bitmap.

Generating QR Code to ImageView on Android

    QRCodeWriter writer = new QRCodeWriter();
    try {
        int width = mImageView.getWidth();
        int height = mImageView.getHeight();
        BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height);
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                bitmap.setPixel(i, j, bitMatrix.get(i, j) ? Color.BLACK: Color.WHITE);
            }
        }
        mImageView.setImageBitmap(bitmap);
    } catch (WriterException e) {
        e.printStackTrace();
    }

Reading QR Code from Android Camera Preview

The preview data type of Android camera is NV21. So We need to use PlanarYUVLuminanceSource to wrap it.

            MultiFormatReader reader = new MultiFormatReader();            
            LuminanceSource source = new PlanarYUVLuminanceSource(yuvData, dataWidth, dataHeight, left, top, width, height, false);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            Result result;
            try {
                result = reader.decode(bitmap);
                if (result != null) {
                    mDialog.setTitle("Result");
                    mDialog.setMessage(result.getText());
                    mDialog.show();
                }
            } catch (NotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }