解决 zxing二维码白边大 zxing二维码白边过大 zxing二维码白边太大 Java生成二维码 生成条形码二维码超多内容Java 生成条形码 Java生成二维码

解决 zxing二维码白边大 zxing二维码白边过大 zxing二维码白边太大 Java生成二维码 生成条形码二维码超多内容

原本使用 hutool的二维码工具类是可以实现我的需求,但是后期二维码内容逐渐庞大了起来 弊端就出来了,白边大、内容字符超过1700就异常了,最后还是改为 zxing实现

相关依赖

hutool

有部分功能使用到了 hutool工具

        <!--Java工具库-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.14</version>
        </dependency>

zxing

        <!-- 二维码工具-->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.3</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.3</version>
        </dependency>

具体实现二维码

二维码配置类以及生成工具类

目录结构

在这里插入图片描述

conf包

QrCodeConfig.java

import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.mh.jishi.util.qrcode.QrCodeGenWrapper;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 二维吗配置信息
 */
@Getter
@Setter
@ToString
public class QrCodeConfig {
    
    
    /**
     * 塞入二维码的信息
     */
    private String msg;


    /**
     * 二维码中间的logo
     */
    private String logo;


    /**
     * 生成二维码的宽
     */
    private Integer w;


    /**
     * 生成二维码的高
     */
    private Integer h;


    /**
     * 生成二维码的颜色
     */
    private MatrixToImageConfig matrixToImageConfig;


    private Map<EncodeHintType, Object> hints;


    /**
     * 生成二维码图片的格式 png, jpg
     */
    private String picType;


    @ToString
    public static class QrCodeConfigBuilder {
    
    
        private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();

        /**
         * The message to put into QrCode
         */
        private String msg;


        /**
         * qrcode center logo
         */
        private String logo;


        /**
         * qrcode image width
         */
        private Integer w;


        /**
         * qrcode image height
         */
        private Integer h;


        /**
         * qrcode bgcolor, default white
         */
        private Integer offColor;


        /**
         * qrcode msg color, default black
         */
        private Integer onColor;


        /**
         * qrcode message's code, default UTF-8
         */
        private String code;


        /**
         * 0 - 4
         */
        private Integer padding;


        /**
         * error level, default H
         */
        private ErrorCorrectionLevel errorCorrection;


        /**
         * output qrcode image type, default png
         */
        private String picType;


        public String getMsg() {
    
    
            return msg;
        }

        public QrCodeConfigBuilder setMsg(String msg) {
    
    
            this.msg = msg;
            return this;
        }

        public String getLogo() {
    
    
            return logo;
        }

        public QrCodeConfigBuilder setLogo(String logo) {
    
    
            this.logo = logo;
            return this;
        }

        public Integer getW() {
    
    
            return w == null ? (h == null ? 200 : h) : w;
        }

        public QrCodeConfigBuilder setW(Integer w) {
    
    
            if (w != null && w < 0) {
    
    
                throw new IllegalArgumentException("???????????0");
            }
            this.w = w;
            return this;
        }

        public Integer getH() {
    
    
            if (w != null && w < 0) {
    
    
                throw new IllegalArgumentException("???????????0");
            }
            return h == null ? (w == null ? 200 : w) : h;
        }

        public QrCodeConfigBuilder setH(Integer h) {
    
    
            this.h = h;
            return this;
        }

        public Integer getOffColor() {
    
    
            return offColor == null ? MatrixToImageConfig.WHITE : offColor;
        }

        public QrCodeConfigBuilder setOffColor(Integer offColor) {
    
    
            this.offColor = offColor;
            return this;
        }

        public Integer getOnColor() {
    
    
            return onColor == null ? MatrixToImageConfig.BLACK : onColor;
        }

        public QrCodeConfigBuilder setOnColor(Integer onColor) {
    
    
            this.onColor = onColor;
            return this;
        }

        public String getCode() {
    
    
            return code == null ? "UTF-8" : code;
        }

        public QrCodeConfigBuilder setCode(String code) {
    
    
            this.code = code;
            return this;
        }

        public Integer getPadding() {
    
    
            if (padding == null) {
    
    
                return 1;
            }

            if (padding < 0) {
    
    
                return 0;
            }

            if (padding > 4) {
    
    
                return 4;
            }

            return padding;
        }

        public QrCodeConfigBuilder setPadding(Integer padding) {
    
    
            this.padding = padding;
            return this;
        }


        public String getPicType() {
    
    
            return picType == null ? "png" : picType;
        }

        public QrCodeConfigBuilder setPicType(String picType) {
    
    
            this.picType = picType;
            return this;
        }

        public ErrorCorrectionLevel getErrorCorrection() {
    
    
            return errorCorrection == null ? ErrorCorrectionLevel.H : errorCorrection;
        }

        public QrCodeConfigBuilder setErrorCorrection(ErrorCorrectionLevel errorCorrection) {
    
    
            this.errorCorrection = errorCorrection;
            return this;
        }

        private void validate() {
    
    
            if (msg == null || msg.length() == 0) {
    
    
                throw new IllegalArgumentException("????????????!");
            }
        }


        private QrCodeConfig create() {
    
    
            this.validate();

            QrCodeConfig qrCodeConfig = new QrCodeConfig();
            qrCodeConfig.setMsg(getMsg());
            qrCodeConfig.setH(getH());
            qrCodeConfig.setW(getW());
            qrCodeConfig.setLogo(getLogo());
            qrCodeConfig.setPicType(getPicType());

            Map<EncodeHintType, Object> hints = new HashMap<>(3);
            hints.put(EncodeHintType.ERROR_CORRECTION, this.getErrorCorrection());
            hints.put(EncodeHintType.CHARACTER_SET, this.getCode());
            hints.put(EncodeHintType.MARGIN, this.getPadding());
            qrCodeConfig.setHints(hints);


            MatrixToImageConfig config;
            if (getOnColor() == MatrixToImageConfig.BLACK
                    && getOffColor() == MatrixToImageConfig.WHITE) {
    
    
                config = DEFAULT_CONFIG;
            } else {
    
    
                config = new MatrixToImageConfig(getOnColor(), getOffColor());
            }
            qrCodeConfig.setMatrixToImageConfig(config);


            return qrCodeConfig;
        }

        /**
         * create qrcodeConfig
         *
         * @return 返回构造的 QrCodeConfig 对象
         */
        public QrCodeConfig build() {
    
    
            return create();
        }


        public BufferedImage asBufferedImage() throws IOException, WriterException {
    
    
            return QrCodeGenWrapper.asBufferedImage(create());
        }


        public boolean asFile(String absFileName) throws IOException, WriterException {
    
    
            return QrCodeGenWrapper.asFile(create(), absFileName);
        }
    }
}

util包

FileUtil.java 文件工具类

import java.io.File;
import java.io.FileNotFoundException;

/**
 * 文件工具类
 */
public class FileUtil {
    
    
    /**
     * 递归生成目录, file 为根据相对路径创建时, 会抛npe
     * @param file {@link  File}file对象
     */
    public static void mkDir(File file) throws FileNotFoundException {
    
    
        if (file.getParentFile() == null) {
    
    
            file = file.getAbsoluteFile();
        }

        if (file.getParentFile().exists()) {
    
    
            if (!file.exists() && !file.mkdir()) {
    
    
                throw new FileNotFoundException();
            }
        } else {
    
    
            mkDir(file.getParentFile());
            if (!file.exists() && !file.mkdir()) {
    
    
                throw new FileNotFoundException();
            }
        }
    }


    /**
     * 创建文件
     *
     * @param filename 文件名称
     * @return {@link File} file对象
     */
    public static File createFile(String filename) throws FileNotFoundException {
    
    
        if (filename == null || "".equals(filename)) {
    
    
            return null;
        }

        int index = filename.lastIndexOf('/');
        if (index <= 0) {
    
    
            return new File(filename);
        }

        String path = filename.substring(0, index);
        mkDir(new File(path));
        return new File(filename);
    }
}

ImageUtil.java 图片工具类

import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * Image工具类
 */
public class ImageUtil {
    
    

    /**
     * 在图片中间,插入圆角的logo
     *
     * @param qrCode 原图
     * @param logo   logo地址
     */
    public static void insertLogo(BufferedImage qrCode, String logo) throws IOException {
    
    
        int QRCODE_WIDTH = qrCode.getWidth();
        int QRCODE_HEIGHT = qrCode.getHeight();

        // 获取logo图片
        BufferedImage bf = getImageByPath(logo);
        int size = bf.getWidth() > QRCODE_WIDTH * 2 / 10 ? QRCODE_WIDTH * 2 / 50 : bf.getWidth() / 5;
        bf = ImageUtil.makeRoundBorder(bf, 60, size, Color.WHITE); // 边距为二维码图片的1/10

        // logo的宽高
        int w = Math.min(bf.getWidth(), QRCODE_WIDTH * 2 / 10);
        int h = Math.min(bf.getHeight(), QRCODE_HEIGHT * 2 / 10);

        // 插入LOGO
        Graphics2D graph = qrCode.createGraphics();

        int x = (QRCODE_WIDTH - w) / 2;
        int y = (QRCODE_HEIGHT - h) / 2;

        graph.drawImage(bf, x, y, w, h, null);
        graph.dispose();
        bf.flush();
    }


    /**
     * 根据路径图片图片
     *
     * @param path 本地路径 or 网络地址
     * @return 图片
     */
    public static BufferedImage getImageByPath(String path) throws IOException {
    
    
        if (path.startsWith("http")) {
    
     // 从网络获取logo
            return ImageIO.read(new URL(path));
        } else if (path.startsWith("/")) {
    
     // 绝对地址获取logo
            return ImageIO.read(new File(path));
        } else {
    
     // 从资源目录下获取logo
            return ImageIO.read(new File(path));
        }
    }


    /**
     * 生成圆角图片 & 圆角边框
     *
     * @param image        原图
     * @param cornerRadius 圆角的角度
     * @param size         边框的边距
     * @param color        边框的颜色
     * @return 返回带边框的圆角图
     */
    public static BufferedImage makeRoundBorder(BufferedImage image, int cornerRadius, int size, Color color) {
    
    
        // 将图片变成圆角
        image = makeRoundedCorner(image, cornerRadius);

        int borderSize = size << 1;
        int w = image.getWidth() + borderSize;
        int h = image.getHeight() + borderSize;
        BufferedImage output = new BufferedImage(w, h,
                BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2 = output.createGraphics();
        g2.setComposite(AlphaComposite.Src);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(color == null ? Color.WHITE : color);
        g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius,
                cornerRadius));

        // ... then compositing the image on top,
        // using the white shape from above as alpha source
        g2.setComposite(AlphaComposite.SrcAtop);
        g2.drawImage(image, size, size, null);
        g2.dispose();

        return output;
    }


    /**
     * 生成圆角图片
     *
     * @param image        原始图片
     * @param cornerRadius 圆角的弧度
     * @return 返回圆角图
     */
    public static BufferedImage makeRoundedCorner(BufferedImage image,
                                                  int cornerRadius) {
    
    
        int w = image.getWidth();
        int h = image.getHeight();
        BufferedImage output = new BufferedImage(w, h,
                BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2 = output.createGraphics();

        // This is what we want, but it only does hard-clipping, i.e. aliasing
        // g2.setClip(new RoundRectangle2D ...)

        // so instead fake soft-clipping by first drawing the desired clip shape
        // in fully opaque white with antialiasing enabled...
        g2.setComposite(AlphaComposite.Src);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.WHITE);
        g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius,
                cornerRadius));

        // ... then compositing the image on top,
        // using the white shape from above as alpha source
        g2.setComposite(AlphaComposite.SrcAtop);
        g2.drawImage(image, 0, 0, null);

        g2.dispose();

        return output;
    }

}


MatrixToImageUtil.java Matrix转Image工具类

package com.mh.jishi.util.qrcode.util;

import com.google.zxing.common.BitMatrix;
import com.mh.jishi.util.qrcode.conf.QrCodeConfig;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * Matrix转工具类
 */
public class MatrixToImageUtil {
    
    

    /**
     * 根据二维码配置 & 二维码矩阵生成二维码图片
     *
     * @param qrCodeConfig {@link QrCodeConfig}配置
     * @param bitMatrix {@link BitMatrix}
     * @return {@link BufferedImage}
     */
    public static BufferedImage toBufferedImage(QrCodeConfig qrCodeConfig, BitMatrix bitMatrix) throws IOException {
    
    
        int qrCodeWidth = bitMatrix.getWidth();
        int qrCodeHeight = bitMatrix.getHeight();
        BufferedImage qrCode = new BufferedImage(qrCodeWidth, qrCodeHeight, BufferedImage.TYPE_INT_RGB);

        for (int x = 0; x < qrCodeWidth; x++) {
    
    
            for (int y = 0; y < qrCodeHeight; y++) {
    
    
                qrCode.setRGB(x, y,
                        bitMatrix.get(x, y) ?
                                qrCodeConfig.getMatrixToImageConfig().getPixelOnColor() :
                                qrCodeConfig.getMatrixToImageConfig().getPixelOffColor());
            }
        }

        // 插入logo
        if (!(qrCodeConfig.getLogo() == null || "".equals(qrCodeConfig.getLogo()))) {
    
    
            ImageUtil.insertLogo(qrCode, qrCodeConfig.getLogo());
        }

        // 若二维码的实际宽高和预期的宽高不一致, 则缩放
        int realQrCodeWidth = qrCodeConfig.getW();
        int realQrCodeHeight = qrCodeConfig.getH();
        if (qrCodeWidth != realQrCodeWidth || qrCodeHeight != realQrCodeHeight) {
    
    
            BufferedImage tmp = new BufferedImage(realQrCodeWidth, realQrCodeHeight, BufferedImage.TYPE_INT_RGB);
            tmp.getGraphics().drawImage(
                    qrCode.getScaledInstance(realQrCodeWidth, realQrCodeHeight,
                            Image.SCALE_SMOOTH), 0, 0, null);
            qrCode = tmp;
        }

        return qrCode;
    }



}

主目录

QrCodeGenWrapper.java 生成工具类

import cn.hutool.core.img.ImgUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;
import com.mh.jishi.util.qrcode.conf.QrCodeConfig;
import com.mh.jishi.util.qrcode.util.FileUtil;
import com.mh.jishi.util.qrcode.util.MatrixToImageUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Map;

/**
 * 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题,原参考 <a href="https://my.oschina.net/u/566591/blog/872770">...</a>
 */
public class QrCodeGenWrapper {
    
    
    private static final Logger logger = LoggerFactory.getLogger(QrCodeGenWrapper.class);

    private static final int QUIET_ZONE_SIZE = 4;


    /**
     * 构造 二维吗配置信息
     * @return QrCodeConfig
     */
    public static QrCodeConfig.QrCodeConfigBuilder createQrCodeConfig() {
    
    
        return new QrCodeConfig.QrCodeConfigBuilder();
    }

    /**
     * 生成base64格式二维吗
     * @param content 二维吗内容
     * @param width 宽度 默认 300
     * @param height 高度 默认 300
     * @param imageType 图片类型默认 ong
     * @return 返回base64格式二维码信息
     */
    public static String generateAsBase64(String content, Integer width, Integer height, String imageType) throws IOException, WriterException {
    
    
        QrCodeConfig qrConfig = QrCodeGenWrapper.createQrCodeConfig()
                .setMsg(content)
                .setH(width == null ? 300 : width)
                .setW(height == null ? 300 : height)
                .setPadding(1)
                .setErrorCorrection(ErrorCorrectionLevel.L)
                .build();
        BufferedImage img = asBufferedImage(qrConfig);
        return ImgUtil.toBase64DataUri(img, StringUtils.isBlank(imageType) ? "png" : imageType);
    }



    public static BufferedImage asBufferedImage(QrCodeConfig qrCodeConfig) throws WriterException, IOException {
    
    
        BitMatrix bitMatrix = encode(qrCodeConfig);
        return MatrixToImageUtil.toBufferedImage(qrCodeConfig, bitMatrix);
    }



    public static boolean asFile(QrCodeConfig qrCodeConfig, String absFileName) throws WriterException, IOException {
    
    
        File file = FileUtil.createFile(absFileName);
        if (file == null) {
    
    
            throw new IllegalArgumentException("file not exists! absFile: " + absFileName);
        }

        BufferedImage bufferedImage = asBufferedImage(qrCodeConfig);
        if (!ImageIO.write(bufferedImage, qrCodeConfig.getPicType(), file)) {
    
    
            throw new IOException("save qrcode image error!");
        }
        return true;
    }


    /**
     * 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题
     * <p/>
     * 源码参考 {@link com.google.zxing.qrcode.QRCodeWriter#encode(String, BarcodeFormat, int, int, Map)}
     */
    private static BitMatrix encode(QrCodeConfig qrCodeConfig) throws WriterException {
    
    
        ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
        int quietZone = 1;
        if (qrCodeConfig.getHints() != null) {
    
    
            if (qrCodeConfig.getHints().containsKey(EncodeHintType.ERROR_CORRECTION)) {
    
    
                errorCorrectionLevel = ErrorCorrectionLevel.valueOf(qrCodeConfig.getHints().get(EncodeHintType.ERROR_CORRECTION).toString());
            }
            if (qrCodeConfig.getHints().containsKey(EncodeHintType.MARGIN)) {
    
    
                quietZone = Integer.parseInt(qrCodeConfig.getHints().get(EncodeHintType.MARGIN).toString());
            }

            if (quietZone > QUIET_ZONE_SIZE) {
    
    
                quietZone = QUIET_ZONE_SIZE;
            } else if (quietZone < 0) {
    
    
                quietZone = 0;
            }
        }

        QRCode code = Encoder.encode(qrCodeConfig.getMsg(), errorCorrectionLevel, qrCodeConfig.getHints());
        return renderResult(code, qrCodeConfig.getW(), qrCodeConfig.getH(), quietZone);
    }


    /**
     * 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题
     * <p/>
     * 源码参考
     *
     * @param code {@link QRCode}
     * @param width 高
     * @param height 宽
     * @param quietZone 取值 [0, 4]
     * @return {@link BitMatrix}
     */
    private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
    
    
        ByteMatrix input = code.getMatrix();
        if (input == null) {
    
    
            throw new IllegalStateException();
        }

        // xxx 二维码宽高相等, 即 qrWidth == qrHeight
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int qrWidth = inputWidth + (quietZone * 2);
        int qrHeight = inputHeight + (quietZone * 2);


        // 白边过多时, 缩放
        int minSize = Math.min(width, height);
        int scale = calculateScale(qrWidth, minSize);
        if (scale > 0) {
    
    
            if (logger.isDebugEnabled()) {
    
    
                logger.debug("qrCode scale enable! scale: {}, qrSize:{}, expectSize:{}x{}", scale, qrWidth, width, height);
            }

            int padding, tmpValue;
            // 计算边框留白
            padding = (minSize - qrWidth * scale) / QUIET_ZONE_SIZE * quietZone;
            tmpValue = qrWidth * scale + padding;
            if (width == height) {
    
    
                width = tmpValue;
                height = tmpValue;
            } else if (width > height) {
    
    
                width = width * tmpValue / height;
                height = tmpValue;
            } else {
    
    
                height = height * tmpValue / width;
                width = tmpValue;
            }
        }

        int outputWidth = Math.max(width, qrWidth);
        int outputHeight = Math.max(height, qrHeight);

        int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
        int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
        int topPadding = (outputHeight - (inputHeight * multiple)) / 2;

        BitMatrix output = new BitMatrix(outputWidth, outputHeight);

        for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
    
    
            // Write the contents of this row of the barcode
            for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
    
    
                if (input.get(inputX, inputY) == 1) {
    
    
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
            }
        }

        return output;
    }


    /**
     * 如果留白超过15% , 则需要缩放
     * (15% 可以根据实际需要进行修改)
     *
     * @param qrCodeSize 二维码大小
     * @param expectSize 期望输出大小
     * @return 返回缩放比例, <= 0 则表示不缩放, 否则指定缩放参数
     */
    private static int calculateScale(int qrCodeSize, int expectSize) {
    
    
        if (qrCodeSize >= expectSize) {
    
    
            return 0;
        }

        int scale = expectSize / qrCodeSize;
        int abs = expectSize - scale * qrCodeSize;
        if (abs < expectSize * 0.15) {
    
    
            return 0;
        }

        return scale;
    }
}


Demo生成二维码测试

    @Test
    public void qrCodeStrTest() throws IOException, WriterException {
    
    
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < 150; i ++){
    
    
            sb.append("12345678910").append("\r\n");
        }

        System.out.printf("二维码输出: %s%n \r 二维码内容字符数量: %s%n", QrCodeGenWrapper.generateAsBase64(sb.toString(), null, null, null), sb.length());

    }

在这里插入图片描述
在这里插入图片描述

条形码

条形码工具类

BarCodeUtils .java 条形码工具类

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.pdf417.PDF417Writer;
import com.mh.jishi.constants.RedisKeyPrefix;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * <h2>条形码工具类</h2>
 * <p>
 * <a href="https://blog.51cto.com/u_15127597/4390771"> 原参考 </a>
 * </p>
 *
 * @author Evan <[email protected]>
 * @since 2022年06月09日 16:09
 */
public class BarCodeUtils {
    
    
    /**
     * 默认图片宽度
     */
    private static final int DEFAULT_PICTURE_WIDTH = 270;

    /**
     * 默认图片高度
     */
    private static final int DEFAULT_PICTURE_HEIGHT = 130;

    /**
     * 默认条形码宽度
     */
    private static final int DEFAULT_BAR_CODE_WIDTH = 310;

    /**
     * 默认条形码高度
     */
    private static final int DEFAULT_BAR_CODE_HEIGHT = 80;

    /**
     * 默认字体大小
     */
    private static final int DEFAULT_FONT_SIZE = 15;

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


    static {
    
    
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
    }

    /**
     * 获取条形码图片
     *
     * @param codeValue 条形码内容
     * @return 条形码图片
     */
    public static BufferedImage getBarCodeImage(String codeValue) {
    
    
        return getBarCodeImage(codeValue, DEFAULT_BAR_CODE_WIDTH, DEFAULT_BAR_CODE_HEIGHT);
    }

    /**
     * 获取条形码图片
     *
     * @param codeValue 条形码内容
     * @param width     宽度
     * @param height    高度
     * @return 条形码图片
     */
    public static BufferedImage getBarCodeImage(String codeValue, int width, int height) {
    
    
        // CODE_128是最常用的条形码格式
        return getBarCodeImage(codeValue, width, height, BarcodeFormat.CODE_128);
    }

    /**
     * 获取条形码图片
     *
     * @param codeValue     条形码内容
     * @param width         宽度
     * @param height        高度
     * @param barcodeFormat 条形码编码格式
     * @return 条形码图片
     */
    public static BufferedImage getBarCodeImage(String codeValue, int width, int height, BarcodeFormat barcodeFormat) {
    
    
        Writer writer;
        switch (barcodeFormat) {
    
    
            case PDF_417:
                // 支持中文的条形码格式
                writer = new PDF417Writer();
                break;
            case CODE_128:

                // 最常见的条形码,但是不支持中文
                // 如果使用到其他格式,可以在这里添加
            default:
                writer = new Code128Writer();
        }

        // 编码内容, 编码类型, 宽度, 高度, 设置参数
        BitMatrix bitMatrix;
        try {
    
    
            bitMatrix = writer.encode(codeValue, barcodeFormat, width, height, hints);
        } catch (WriterException e) {
    
    
            throw new RuntimeException("条形码内容写入失败");
        }
        return MatrixToImageWriter.toBufferedImage(bitMatrix);
    }

    /**
     * 获取条形码
     *
     * @param codeValue 条形码内容
     * @param bottomStr 底部文字
     * @return
     */
    public static BufferedImage getBarCodeWithWords(String codeValue, String bottomStr) {
    
    
        return getBarCodeWithWords(codeValue, bottomStr, "", "");
    }

    /**
     * 获取条形码
     *
     * @param codeValue   条形码内容
     * @param bottomStr   底部文字
     * @param topLeftStr  左上角文字
     * @param topRightStr 右上角文字
     * @return
     */
    public static BufferedImage getBarCodeWithWords(String codeValue, String bottomStr, String topLeftStr,
                                                    String topRightStr) {
    
    
        return getCodeWithWords(getBarCodeImage(codeValue), bottomStr, null, topLeftStr, topRightStr,
            DEFAULT_PICTURE_WIDTH, DEFAULT_PICTURE_HEIGHT, 0, 0, 0, 0, 0, 0, DEFAULT_FONT_SIZE);
    }

    /**
     * 获取条形码,返回字符串base64图片
     *
     * @param codeValue   条形码内容
     * @param topStr      顶部文字
     * @param bottomStr   底部文字
     * @param topLeftStr  左上角文字
     * @param topRightStr 右上角文字
     * @return base64格式条形码图片
     */
    public static String getBarCodeWithWordsAsBase64(String codeValue, String topStr, String bottomStr,
                                                     String topLeftStr, String topRightStr) throws IOException {
    
    
        String result;
        BufferedImage image = getCodeWithWords(getBarCodeImage(codeValue), bottomStr, topStr, topLeftStr, topRightStr,
            DEFAULT_PICTURE_WIDTH, DEFAULT_PICTURE_HEIGHT, 0, 0, 0, 0, 0, 0, DEFAULT_FONT_SIZE);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "jpeg", outputStream);
        String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());
        result = "data:image/jpeg;base64," + base64.replaceAll("\r\n", "");
        return result;
    }

    /**
     * 获取条形码,返回字符串base64图片, 可以缓存
     *
     * @param codeValue   条形码内容
     * @param bottomStr   底部文字
     * @param topLeftStr  左上角文字
     * @param topRightStr 右上角文字
     * @param cache       是否使用缓存
     */
    public static String getBarCodeWithWordsAsBase64ByCache(String codeValue, String topStr, String bottomStr,
                                                            String topLeftStr, String topRightStr, boolean cache)
        throws IOException {
    
    

        String result = getBarCodeWithWordsAsBase64(codeValue, topStr, bottomStr, topLeftStr, topRightStr)
        return result;
    }

    /**
     * 获取条形码
     *
     * @param codeImage       条形码图片
     * @param bottomStr       底部文字
     * @param topStr          顶部文字
     * @param topLeftStr      左上角文字
     * @param topRightStr     右上角文字
     * @param pictureWidth    图片宽度
     * @param pictureHeight   图片高度
     * @param codeOffsetX     条形码宽度
     * @param codeOffsetY     条形码高度
     * @param topLeftOffsetX  左上角文字X轴偏移量
     * @param topLeftOffsetY  左上角文字Y轴偏移量
     * @param topRightOffsetX 右上角文字X轴偏移量
     * @param topRightOffsetY 右上角文字Y轴偏移量
     * @param fontSize        字体大小
     * @return 条形码图片
     */
    public static BufferedImage getCodeWithWords(BufferedImage codeImage, String bottomStr, String topStr,
                                                 String topLeftStr, String topRightStr, int pictureWidth,
                                                 int pictureHeight, int codeOffsetX,
                                                 int codeOffsetY, int topLeftOffsetX, int topLeftOffsetY,
                                                 int topRightOffsetX, int topRightOffsetY,
                                                 int fontSize) {
    
    
        BufferedImage picImage = new BufferedImage(pictureWidth, pictureHeight, BufferedImage.TYPE_INT_RGB);

        Graphics2D g2d = picImage.createGraphics();
        // 抗锯齿
        setGraphics2D(g2d);
        // 设置白色
        setColorWhite(g2d, picImage.getWidth(), picImage.getHeight());

        // 条形码默认居中显示
        int codeStartX = (pictureWidth - codeImage.getWidth()) / 2 + codeOffsetX;
        int codeStartY = (pictureHeight - codeImage.getHeight()) / 2 + codeOffsetY;
        // 画条形码到新的面板
        g2d.drawImage(codeImage, codeStartX, codeStartY, codeImage.getWidth(), codeImage.getHeight(), null);

        // 画文字到新的面板
        g2d.setColor(Color.BLACK);
        // 字体、字型、字号
        g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));
        // 文字与条形码之间的间隔
        int wordAndCodeSpacing = 3;

        if (StringUtils.isNotEmpty(bottomStr)) {
    
    
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(bottomStr);
            // 文字X轴开始坐标,这里是居中
            int strStartX = codeStartX + (codeImage.getWidth() - strWidth) / 2;
            // 文字Y轴开始坐标
            int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing;
            // 画文字
            g2d.drawString(bottomStr, strStartX, strStartY);
        }

        if (StringUtils.isNotEmpty(topStr)) {
    
    
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(topStr);
            // 文字X轴开始坐标,这里是居中
            int strStartX = codeStartX + (codeImage.getWidth() - strWidth) / 2;
            // 文字Y轴开始坐标
            int strStartY = codeStartY + topLeftOffsetY - wordAndCodeSpacing;
            // 画文字
            g2d.drawString(topStr, strStartX, strStartY);
        }

        if (StringUtils.isNotEmpty(topLeftStr)) {
    
    
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(topLeftStr);
            // 文字X轴开始坐标
            int strStartX = codeStartX + topLeftOffsetX;
            // 文字Y轴开始坐标
            int strStartY = codeStartY + topLeftOffsetY - wordAndCodeSpacing;
            // 画文字
            g2d.drawString(topLeftStr, strStartX, strStartY);
        }

        if (StringUtils.isNotEmpty(topRightStr)) {
    
    
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(topRightStr);
            // 文字X轴开始坐标,这里是居中
            int strStartX = codeStartX + codeImage.getWidth() - strWidth + topRightOffsetX;
            // 文字Y轴开始坐标
            int strStartY = codeStartY + topRightOffsetY - wordAndCodeSpacing;
            // 画文字
            g2d.drawString(topRightStr, strStartX, strStartY);
        }

        g2d.dispose();
        picImage.flush();

        return picImage;
    }

    /**
     * 设置 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, int width, int height) {
    
    
        g2d.setColor(Color.WHITE);
        // 填充整个屏幕
        g2d.fillRect(0, 0, width, height);
        // 设置笔刷
        g2d.setColor(Color.BLACK);
    }
}

Demo生成条形码测试

    @Test
    public void test1() throws IOException {
    
    
        String base64Image = BarCodeUtils.getBarCodeWithWordsAsBase64("123123", "我是树哥", "CF 墨心", null, null);
        System.out.println(base64Image);
    }

猜你喜欢

转载自blog.csdn.net/qq_40739917/article/details/128091947