zxing绘制条形码总结

基本使用

首先工程中导入zxing的依赖,如下:

<dependency>
   <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.3.3</version>
</dependency>

接着编写生成条形码的工具类,代码演示如下:

package com.example.demo.utils;

import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;

import javax.imageio.ImageIO;

import org.springframework.util.StringUtils;

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.oned.Code128Writer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

/**
 * 类描述: 二维码生成工具类
 *
 * @author jianweiwang
 * @version v1.0
 * @link wjw@choicesoft.com.cn
 * @date 2019/4/1 11:00
 **/
public class BarcodeUtils {
    private static final String CHARSET = "utf-8";
    private static final String FORMAT = "JPG";
    /**
     * 条形码宽度
     */
    private static final int WIDTH = 220;
    /**
     * 条形码高度
     */
    private static final int HEIGHT = 80;

    /**
     * 字体大小
     */
    private static final int FONT_SIZE = 28;

    /**
     * 加文字 条形码
     */
    private static final int WORD_HEIGHT = HEIGHT + FONT_SIZE;

    /**
     * 二维码尺寸
     */
    private static final int QR_CODE_SIZE = 300;
    /**
     * LOGO宽度
     */
    private static final int LOGO_WIDTH = 60;
    /**
     * LOGO高度
     */
    private static final int LOGO_HEIGHT = 60;

    /**
     * 设置 条形码参数
     */
    private static Map<EncodeHintType, Object> hints = new HashMap<>(5);

    static {
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        hints.put(EncodeHintType.MARGIN, 1);
    }

    /**
     * 生成 图片缓冲
     *
     * @param vaNumber VA 码
     * @return 返回BufferedImage
     * @author fxbin
     */
    public static BufferedImage getBarCode(String vaNumber) {
        try {
            Code128Writer writer = new Code128Writer();
            // 编码内容, 编码类型, 宽度, 高度, 设置参数
            BitMatrix bitMatrix = writer.encode(vaNumber, BarcodeFormat.CODE_128, WIDTH, HEIGHT, hints);
            return MatrixToImageWriter.toBufferedImage(bitMatrix);
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 把带logo的二维码下面加上文字
     *
     * @param image 条形码图片
     * @param words 文字
     * @return 返回BufferedImage
     * @author fxbin
     */
    public static BufferedImage insertWords(BufferedImage image, String words) {
        // 新的图片,把带logo的二维码下面加上文字
        if (!StringUtils.isEmpty(words)) {
            BufferedImage outImage = new BufferedImage(WIDTH, WORD_HEIGHT, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = outImage.createGraphics();
            // 抗锯齿
            setGraphics2D(g2d);
            // 设置白色
            setColorWhite(g2d);
            // 画条形码到新的面板
            g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
            // 画文字到新的面板
            g2d.setColor(Color.BLACK);
            // 字体、字型、字号
            g2d.setFont(new Font("黑体", Font.PLAIN, FONT_SIZE));
            //设置条形码下面的文本的纵坐标
            int wordStartY = HEIGHT + 21;
            //-------------------------计算条形码的左边距,即左边空白的距离------------------------
            String content=words.substring(1,words.length()-1);
            int inputWidth = new Code128Writer().encode(content).length;
            int fullWidth = inputWidth + 1;
            int outputWidth = Math.max(WIDTH, fullWidth);
            int multiple = outputWidth / fullWidth;
            int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
            int x = leftPadding;
			//-----------------------计算条形码的左边距,即左边空白的距离----------------------

            String[] split = words.split("");
            int len = split.length;
            int step = new Long(Math.round(WIDTH - leftPadding * 2) / words.length()).intValue();
            for (int i = 0; i < len; i++) {
                g2d.drawString(split[i], x, wordStartY);
                x = x + step;
            }
            outImage.flush();
            return outImage;
        }
        return null;
    }


    /**
     * 设置 Graphics2D 属性 (抗锯齿)
     *
     * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
     */
    private static void setGraphics2D(Graphics2D g2d) {
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
        Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
        g2d.setStroke(s);
    }

    /**
     * 设置背景为白色
     *
     * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
     */
    private static void setColorWhite(Graphics2D g2d) {
        g2d.setColor(Color.WHITE);
        // 填充整个屏幕
        g2d.fillRect(0, 0, 600, 600);
        // 设置笔刷
        g2d.setColor(Color.BLACK);
    }

    private static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception {
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_SIZE,
                hints);
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        if (logoPath == null || "".equals(logoPath)) {
            return image;
        }
        // 插入图片
        BarcodeUtils.insertImage(image, logoPath, needCompress);
        return image;
    }

    /**
     * 插入LOGO
     *
     * @param source       二维码图片
     * @param logoPath     LOGO图片地址
     * @param needCompress 是否压缩
     * @throws Exception
     */
    private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws Exception {
        File file = new File(logoPath);
        if (!file.exists()) {
            throw new Exception("logo file not found.");
        }
        Image src = ImageIO.read(new File(logoPath));
        int width = src.getWidth(null);
        int height = src.getHeight(null);
        // 是否压缩LOGO
        if (needCompress) {
            if (width > LOGO_WIDTH) {
                width = LOGO_WIDTH;
            }
            if (height > LOGO_HEIGHT) {
                height = LOGO_HEIGHT;
            }
            Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics g = tag.getGraphics();
            // 绘制缩小后的图
            g.drawImage(image, 0, 0, null);
            g.dispose();
            src = image;
        }
        // 插入LOGO
        Graphics2D graph = source.createGraphics();
        int x = (QR_CODE_SIZE - width) / 2;
        int y = (QR_CODE_SIZE - height) / 2;
        graph.drawImage(src, x, y, width, height, null);
        Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
        graph.setStroke(new BasicStroke(3f));
        graph.draw(shape);
        graph.dispose();
    }

    /**
     * 生成二维码(内嵌LOGO) 二维码文件名随机,文件名可能会有重复
     *
     * @param content      内容
     * @param logoPath     LOGO地址
     * @param destPath     存放目录
     * @param needCompress 是否压缩LOGO
     */
    private static String encode(String content, String logoPath, String destPath, boolean needCompress)
            throws Exception {
        BufferedImage image = BarcodeUtils.createImage(content, logoPath, needCompress);
        mkdir(destPath);
        String fileName = new Random().nextInt(99999999) + "." + FORMAT.toLowerCase();
        ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
        return fileName;
    }

    /**
     * 生成二维码(内嵌LOGO) 调用者指定二维码文件名
     *
     * @param content      内容
     * @param logoPath     LOGO地址
     * @param destPath     存放目录
     * @param fileName     二维码文件名
     * @param needCompress 是否压缩LOGO
     * @throws Exception
     */
    private static String encode(String content, String logoPath, String destPath, String fileName,
                                 boolean needCompress) throws Exception {
        BufferedImage image = BarcodeUtils.createImage(content, logoPath, needCompress);
        mkdir(destPath);
        fileName = fileName.substring(0, fileName.indexOf(".") > 0 ? fileName.indexOf(".") : fileName.length()) + "."
                + FORMAT.toLowerCase();
        ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
        return fileName;
    }

    /**
     * 生成二维码(内嵌LOGO)
     *
     * @param content  内容
     * @param logoPath LOGO地址
     * @param destPath 存储地址
     */
    public static String encode(String content, String logoPath, String destPath) throws Exception {
        return BarcodeUtils.encode(content, logoPath, destPath, false);
    }

    /**
     * 生成二维码
     *
     * @param content  内容
     * @param destPath 存储地址
     * @throws Exception
     */
    public static String encode(String content, String destPath) throws Exception {
        return BarcodeUtils.encode(content, null, destPath, false);
    }

    /**
     * 生成二维码(内嵌LOGO)
     *
     * @param content      内容
     * @param logoPath     LOGO地址
     * @param output       输出流
     * @param needCompress 是否压缩LOGO
     */
    private static void encode(String content, String logoPath, OutputStream output, boolean needCompress)
            throws Exception {
        BufferedImage image = BarcodeUtils.createImage(content, logoPath, needCompress);
        ImageIO.write(image, FORMAT, output);
    }

    /**
     * 生成二维码
     *
     * @param content 内容
     * @param output  输出流
     */
    public static void encode(String content, OutputStream output) throws Exception {
        BarcodeUtils.encode(content, null, output, false);
    }

    /**
     * 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir. (mkdir如果父目录不存在则会抛出异常)
     *
     * @param destPath 存放目录
     */
    private static void mkdir(String destPath) {
        File file = new File(destPath);
        if (!file.exists() && !file.isDirectory()) {
            file.mkdirs();
        }
    }

    /**
     * 解析二维码
     *
     * @param file 二维码图片
     */
    private static String decode(File file) throws Exception {
        BufferedImage image;
        image = ImageIO.read(file);
        if (image == null) {
            return null;
        }
        BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Result result;
        Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
        hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
        result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

    /**
     * 解析二维码
     *
     * @param path 二维码图片地址
     */
    public static String decode(String path) throws Exception {
        return BarcodeUtils.decode(new File(path));
    }

    public static void main(String[] args) throws Exception {
        // 条形码
        BufferedImage image = insertWords(getBarCode("89607"), "*89607*");
        assert image != null;
        ImageIO.write(image, "jpg", new File("barcode.jpg"));
        // 二维码
        // String text = "http://localhost:8083/communication/distribution?qrid=54999";
        // 不含Logo
        // BarcodeUtils.encode(text, null, "C:\\\\Users\\\\ThinkPad\\\\Pictures", true);
        // 含Logo,不指定二维码图片名
        // BarcodeUtils.encode(text, "e:\\csdn.jpg", "e:\\", true);
        // 含Logo,指定二维码图片名
        // BarcodeUtils.encode(text, "C:\\Users\\ThinkPad\\Pictures\\mail-unread.png",
        // "C:\\Users\\ThinkPad\\Pictures", "qrcode", true);
    }
}

注意点

如果想要设置条形码周围的空白区域大小,需要通过参数如下:

 /**
     * hints 设置 条形码参数
     */
    private static Map<EncodeHintType, Object> hints = new HashMap<>(5);

    static {
        //设置留白大小
        hints.put(EncodeHintType.MARGIN, 1);
    }
public static BufferedImage getBarCode(String vaNumber) {
        try {
            Code128Writer writer = new Code128Writer();
            // 编码内容, 编码类型, 宽度, 高度, 设置参数
            BitMatrix bitMatrix = writer.encode(vaNumber, BarcodeFormat.CODE_128, WIDTH, HEIGHT, hints);
            return MatrixToImageWriter.toBufferedImage(bitMatrix);
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

但是这里有一坑,认为设置了留白大小之后,但是往往留白的大小会出乎意料。

下面通过分析zxing工具中的源码如下:
zxing绘制条形码总结
zxing绘制条形码总结
zxing绘制条形码总结
zxing绘制条形码总结
通过上述源码分析可知,如果想要设置条形码下方的展示内容与条形码两端对齐,需要知道变量leftPadding(条形码左边距的值),简单代码如下:

public static BufferedImage insertWords(BufferedImage image, String words) {
        // 新的图片,把带logo的二维码下面加上文字
        if (!StringUtils.isEmpty(words)) {
            BufferedImage outImage = new BufferedImage(WIDTH, WORD_HEIGHT, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = outImage.createGraphics();
            // 抗锯齿
            setGraphics2D(g2d);
            // 设置白色
            setColorWhite(g2d);
            // 画条形码到新的面板
            g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
            // 画文字到新的面板
            g2d.setColor(Color.BLACK);
            // 字体、字型、字号
            g2d.setFont(new Font("黑体", Font.PLAIN, FONT_SIZE));
            int wordStartY = HEIGHT + 21;
            String content=words.substring(1,words.length()-1);
            int inputWidth = new Code128Writer().encode(content).length;
            int fullWidth = inputWidth + 1;
            int outputWidth = Math.max(WIDTH, fullWidth);
            int multiple = outputWidth / fullWidth;
            int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
            int x = leftPadding;
            String[] split = words.split("");
            int len = split.length;
            int step = new Long(Math.round(WIDTH - leftPadding * 2) / words.length()).intValue();
            for (int i = 0; i < len; i++) {
                g2d.drawString(split[i], x, wordStartY);
                x = x + step;
            }
            outImage.flush();
            return outImage;
        }
        return null;
    }

演示效果大致如下图所示
zxing绘制条形码总结

上一篇:lintcode 5 · Kth Largest Element


下一篇:vs2019编译zxing cpp源码