目录

什么是ZXing

ZXing是一个开放源代码的多格式1D/2D条码图像处理库,采用Java实现。

Github:https://github.com/zxing/zxing

要实现的功能

  1. 输入内容生成条码或二维码。

  2. 解析图片中的二维码。

一维码生成解析

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;

/**
 * @author Flicker
 */
public class BarCode {

    private static final String FORMAT = "png";

    /**
     * 生成条形码
     * 条形码的宽度不能等于图片的宽度,否则解析不出来,如果解析不出来,请加大offset的值
     *
     * @param contents 内容
     * @param dest     条形码图片地址
     * @param width    宽度
     * @param height   高度
     * @param offset   偏移量
     * @throws WriterException
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void encode(String contents, String dest, int width, int height, int offset) throws WriterException, FileNotFoundException, IOException {
        contents = new String(contents.getBytes("UTF-8"), "ISO-8859-1");
        //这里是生成CODE-39条形码,如果生成EAN-13条形码,请将CODE_36改成EAN_13,并在生成时先校验内容。
        BitMatrix matrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_39, width - offset, height);
        MatrixToImageWriter.writeToStream(matrix, FORMAT, new FileOutputStream(new File(dest)));
    }

    /**
     * 解析条形码
     *
     * @param dest 要解码的图片地址
     * @return String 条形码内容
     * @throws IOException
     * @throws NotFoundException
     */
    public static String decode(String dest) throws IOException {
        BufferedImage image = ImageIO.read(new File(dest));
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap imageBinaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
        MultiFormatReader multiFormatReader = new MultiFormatReader();
        try {
            Result result;
            result = multiFormatReader.decode(imageBinaryBitmap);
            return result.getText();
        } catch (NotFoundException e) {
            return "图片中没有条形码";
        }
    }

    /**
     * 条形码校验
     * 参考:http://baike.baidu.com/view/13740.htm?fr=aladdin  百度百科-条形码
     *
     * @return String 校验码
     * @throws Exception
     */
    public static String checksum(String countryCode, String factoryCode, String productCode) throws Exception {
        String temp = countryCode + factoryCode + productCode;
        if (!(isNumber(countryCode) && isNumber(factoryCode) && isNumber(productCode))) {
            throw new Exception("不能含有非数字字符");
        }
        if (countryCode.length() != 3) {
            throw new Exception("国家地区代码不合规范,必须3位");
        }
        if (factoryCode.length() != 5) {
            throw new Exception("厂商代码不合规范,必须5位");
        }
        if (productCode.length() != 4) {
            throw new Exception("产品代码不合规范,必须4位");
        }
        char[] code = temp.toCharArray();

        int oddSum = 0;
        int evenSum = 0;
        for (int i = 0; i < code.length; i++) {
            if ((i + 1) % 2 == 1) {
                oddSum += Integer.valueOf(code[i] + "");
            } else {
                evenSum += Integer.valueOf(code[i] + "");
            }
        }
        int digit = (10 - ((oddSum + evenSum * 3) % 10)) % 10;

        return temp + digit;
    }

    /**
     * 校验数字
     *
     * @param number 数字
     * @return Boolean
     */
    public static boolean isNumber(String number) {
        if (null == number || "".equals(number)) {
            return false;
        }
        String regex = "[0-9]*";
        return number.matches(regex);
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //EAN-13条形码才需要校验
        //System.out.println("校验并写入:" + checksum("695", "32321", "2155"));
        BarCode.encode("QWE456789", "D:\\test\\BarCode.png", 400, 40, 20);
        System.out.println("解析结果:" + BarCode.decode("D:\\test\\BarCode.jpg"));
    }
}

二维码生成解析

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class QRCode {

    private static final String FORMAT = "png";

    /**
     * 生成二维码
     *
     * @param contents 内容
     * @param dest     生成二维码图片地址
     * @param width    宽度
     * @param height   高度
     * @throws WriterException
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void encode(String contents, String dest, int width, int height) throws WriterException, IOException {
        contents = new String(contents.getBytes("UTF-8"), "ISO-8859-1");
        QRCodeWriter writer = new QRCodeWriter();
        BitMatrix matrix = writer.encode(contents, BarcodeFormat.QR_CODE, width, height);
        MatrixToImageWriter.writeToStream(matrix, FORMAT, new FileOutputStream(new File(dest)));
    }

    /**
     * 解析二维码
     *
     * @param dest 目标地址
     * @return String 二维码信息
     * @throws IOException
     * @throws ChecksumException
     * @throws FormatException
     */
    public static String decode(String dest) throws IOException, ChecksumException, FormatException {
        QRCodeReader reader = new QRCodeReader();
        //MultiFormatReader reader = new MultiFormatReader();
        //Map<DecodeHintType, Object> tmpHintsMap = new EnumMap<>(DecodeHintType.class);
        //tmpHintsMap.put(DecodeHintType.CHARACTER_SET,"UTF-8");
        //tmpHintsMap.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        BufferedImage image = ImageIO.read(new File(dest));
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        Binarizer binarizer = new HybridBinarizer(source);
        BinaryBitmap imageBinaryBitmap = new BinaryBitmap(binarizer);
        try {
            Result result = reader.decode(imageBinaryBitmap);
//            Result result = reader.decode(imageBinaryBitmap, tmpHintsMap);
//            System.out.println("result = "+ result.toString());
//            System.out.println("resultFormat = "+ result.getBarcodeFormat());
//            System.out.println("resultText = "+ result.getText());
            return result.getText();
        } catch (NotFoundException e) {
            return "图片中未发现二维码";
        }
    }

    public static void main(String[] args) throws WriterException, IOException, NotFoundException, ChecksumException, FormatException {
        QRCode.encode("Hello World", "D:\\test\\QRCode.png", 200, 200);
        System.out.println("解析结果:" + QRCode.decode("D:\\test\\QRCode.png"));
    }

}

遇到的问题

最开始测试生成解析图片都没有问题,因为解析是直接解析生成的图片。
后面发现如果一维码在一张图片中(比如一张图片的某个角落有一条一维码),那么就解析不到一维码,二维码则没有问题。

然后开始找原因,发现还有一个带hints的解析方法。

/**
   * Decode an image using the hints provided. Does not honor existing state.
   *
   * @param image The pixel data to decode
   * @param hints The hints to use, clearing the previous state.
   * @return The contents of the image
   * @throws NotFoundException Any errors which occurred
   */
  @Override
  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {
    setHints(hints);
    return decodeInternal(image);
  }

打开DecodeHintType,发现里面原来里面还有这么些东西,就不贴代码了,自己去看吧。

然后设置了hints,就能正常解析了,如下。

/**
     * 解析条形码
     *
     * @param dest 要解码的图片地址
     * @return String 条形码内容
     * @throws IOException
     * @throws NotFoundException
     */
public static String decode(String dest) throws IOException {
        Map<DecodeHintType, Object> tmpHintsMap = new EnumMap<>(DecodeHintType.class);
        tmpHintsMap.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        tmpHintsMap.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.allOf(BarcodeFormat.class));
        tmpHintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);

        BufferedImage image = ImageIO.read(new File(dest));
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap imageBinaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
        MultiFormatReader multiFormatReader = new MultiFormatReader();
        try {
            Result result;
            result = multiFormatReader.decode(imageBinaryBitmap, tmpHintsMap);
            return result.getText();
        } catch (NotFoundException e) {
            return "图片中没有条形码";
        }
    }

参考资料