对zxing的简单封装,记录一下

本封装采用建造者模式来开发,支持生成base64,文件。logo文件支持本地和线上。最后的调用是这样的。可以使用的前提是要依赖zxing。

String string = new QrCodeGenWrapper.Builder()
        .msg("123456")
        .logo("http://shop.szschina.cn/images/logo.png")
        .build()
        .asString();
System.out.println("=======string======="+string);

上图是表示生成了一个base64的图片

贴代码(生成类):

public class QrCodeGenWrapper {
    /**
     * 生成二维里面的内容
     */
    private String msg;
    /**
     * 二维码中间的logo
     */
    private String logo;
    /**
     * logo的样式, 目前支持圆角+普通
     */
    private LogoStyle logoStyle;
    /**
     * 生成二维码的宽
     */
    private Integer width;
    /**
     * 生成二维码的高
     */
    private Integer height;
    /**
     * 生成二维码的颜色
     */
    private MatrixToImageConfig matrixToImageConfig;
    /**
     * 识别度
     */
    private Map<EncodeHintType, Object> hints;
    /**
     * 0 - 4
     */
    private Integer padding;
    /**
     * 生成二维码图片的格式 png, jpg
     */
    private String picType;

    public QrCodeGenWrapper() {
        this(new Builder());
    }

    QrCodeGenWrapper(Builder builder) {
        this.logo = builder.logo;
        this.width = builder.width;
        this.height = builder.height;
        this.logoStyle = builder.logoStyle;
        this.hints = builder.hints;
        this.matrixToImageConfig = builder.matrixToImageConfig;
        this.picType = builder.picType;
        if (Strings.isNullOrEmpty(builder.msg)) {
            throw new NullPointerException("msg is not null");
        }
        this.msg = builder.msg;
    }

    public String msg() {
        return msg;
    }

    public String logo() {
        return logo;
    }

    public LogoStyle logoStyle() {
        return logoStyle;
    }

    public Integer width() {
        return width;
    }

    public Integer height() {
        return height;
    }

    public Integer padding() {
        return padding;
    }

    public MatrixToImageConfig matrixToImageConfig() {
        return matrixToImageConfig;
    }

    public Map<EncodeHintType, Object> hints() {
        return hints;
    }

    public String picType() {
        return picType;
    }

    public Builder newBuilder() {
        return new Builder(this);
    }

    private BufferedImage asBufferedImage() {
        BitMatrix bitMatrix = QrCodeUtil.encode(this);
        return QrCodeUtil.toBufferedImage(this, bitMatrix);
    }

    public String asString() {
        BufferedImage bufferedImage = asBufferedImage();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferedImage, this.picType(), outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64Util.encode(outputStream);
    }

    public boolean asFile(String absFileName) {
        File file = new File(absFileName);
        BufferedImage bufferedImage = asBufferedImage();
        try {
            if (!ImageIO.write(bufferedImage, this.picType(), file)) {
                throw new IOException("save qrcode image error!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }


    public static class Builder {
        private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();
        /**
         * qrcode message's code, default UTF-8
         */
        private String code = "utf-8";
        private String msg;
        private String logo;
        private LogoStyle logoStyle = LogoStyle.ROUND;
        private Integer width;
        private Integer height;
        private MatrixToImageConfig matrixToImageConfig;
        private Map<EncodeHintType, Object> hints;
        private Integer padding;
        private String picType = "png";

        public Builder() {
            this.msg = "";
            this.logoStyle = LogoStyle.ROUND;
            this.width = 200;
            this.height = 200;
            this.logo = null;
            this.matrixToImageConfig = DEFAULT_CONFIG;
            this.padding = 1;
            Map<EncodeHintType, Object> hints = new HashMap<>(3);
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            hints.put(EncodeHintType.CHARACTER_SET, code);
            hints.put(EncodeHintType.MARGIN, this.padding);
            this.hints = hints;
        }

        Builder(QrCodeGenWrapper wrapper) {
            this.logo = wrapper.logo;
            this.width = wrapper.width;
            this.height = wrapper.height;
            this.logoStyle = wrapper.logoStyle;
            this.hints = wrapper.hints;
            this.matrixToImageConfig = wrapper.matrixToImageConfig;
            this.picType = wrapper.picType;
            if (Strings.isNullOrEmpty(wrapper.msg)) {
                throw new NullPointerException("msg is not null");
            }
            this.msg = wrapper.msg;
        }

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

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

        public Builder logoStyle(LogoStyle logoStyle) {
            this.logoStyle = logoStyle;
            return this;
        }

        public Builder width(Integer width) {
            this.width = width;
            return this;
        }

        public Builder height(Integer height) {
            this.height = height;
            return this;
        }

        public Builder matrixToImageConfig(MatrixToImageConfig matrixToImageConfig) {
            this.matrixToImageConfig = matrixToImageConfig;
            return this;
        }

        public Builder hints(Map<EncodeHintType, Object> hints) {
            this.hints = hints;
            return this;
        }

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

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

        public QrCodeGenWrapper build() {
            return new QrCodeGenWrapper(this);
        }
    }

}

生成二维码的工具类:

@Slf4j
public class QrCodeUtil {
    private static final int QUIET_ZONE_SIZE = 4;

    public static BitMatrix encode(QrCodeGenWrapper wrapper) {
        if (Strings.isNullOrEmpty(wrapper.msg())) {
            throw new NullPointerException("msg Is Not Null");
        }
        ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
        int quietZone = 1;
        if (wrapper.hints() != null) {
            if (wrapper.hints().containsKey(EncodeHintType.ERROR_CORRECTION)) {
                errorCorrectionLevel = ErrorCorrectionLevel.valueOf(wrapper.hints().get(EncodeHintType.ERROR_CORRECTION).toString());
            }
            if (wrapper.hints().containsKey(EncodeHintType.MARGIN)) {
                quietZone = Integer.parseInt(wrapper.hints().get(EncodeHintType.MARGIN).toString());
            }

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

        try {
            QRCode code = Encoder.encode(wrapper.msg(), errorCorrectionLevel, wrapper.hints());
            return renderResult(code, wrapper.width(), wrapper.height(), quietZone);
        } catch (WriterException e) {
            e.printStackTrace();
            return null;
        }
    }

    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 (log.isDebugEnabled()) {
                log.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;
    }

    /**
     * 根据二维码配置 & 二维码矩阵生成二维码图片
     *
     * @param bitMatrix
     * @return
     * @throws IOException
     */
    public static BufferedImage toBufferedImage(QrCodeGenWrapper wrapper, BitMatrix bitMatrix) {
        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) ?
                                wrapper.matrixToImageConfig().getPixelOnColor() :
                                wrapper.matrixToImageConfig().getPixelOffColor());
            }
        }

        // 插入logo
        if (!(Strings.isNullOrEmpty(wrapper.logo()))) {
            try {
                ImageUtil.insertLogo(qrCode, wrapper.logo(), wrapper.logoStyle());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 若二维码的实际宽高和预期的宽高不一致, 则缩放
        int realQrCodeWidth = wrapper.width();
        int realQrCodeHeight = wrapper.height();
        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;
    }

}

base64转换工具类:

public class Base64Util {
    public static String encode(ByteArrayOutputStream outputStream) {
        return Base64.getEncoder().encodeToString(outputStream.toByteArray());
    }
}

logo处理工具类:

public class ImageUtil {
    /**
     * 在图片中间,插入圆角的logo
     *
     * @param qrCode 原图
     * @param logo   logo地址
     * @throws IOException
     */
    public static void insertLogo(BufferedImage qrCode, String logo, LogoStyle logoStyle) 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, logoStyle, size, Color.WHITE); // 边距为二维码图片的1/10

        // logo的宽高
        int w = bf.getWidth() > QRCODE_WIDTH * 2 / 10 ? QRCODE_WIDTH * 2 / 10 : bf.getWidth();
        int h = bf.getHeight() > QRCODE_HEIGHT * 2 / 10 ? QRCODE_HEIGHT * 2 / 10 : bf.getHeight();

        // 插入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 图片
     * @throws IOException
     */
    public static BufferedImage getImageByPath(String path) throws IOException {
        if (path.startsWith("http")) { // 从网络获取logo
            return ImageIO.read(new URL(path));
//            return ImageIO.read(HttpUtil.downFile(path));
        } else if (path.startsWith("/")) { // 绝对地址获取logo
            return ImageIO.read(new File(path));
        } else { // 从资源目录下获取logo
            return ImageIO.read(ImageUtil.class.getClassLoader().getResourceAsStream(path));
        }
    }


    /**
     * fixme 边框的计算需要根据最终生成logo图片的大小来定义,这样才不会出现不同的logo原图,导致边框不一致的问题
     * <p>
     * 生成圆角图片 & 圆角边框
     *
     * @param image     原图
     * @param logoStyle 圆角的角度
     * @param size      边框的边距
     * @param color     边框的颜色
     * @return 返回带边框的圆角图
     */
    public static BufferedImage makeRoundBorder(BufferedImage image, LogoStyle logoStyle, int size, Color color) {
        // 将图片变成圆角
        int cornerRadius = 0;
        if (logoStyle == LogoStyle.ROUND) {
            cornerRadius = 30;
            image = makeRoundedCorner(image, cornerRadius);
        }

        int borderSize = size;
        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, -5, -5, null);

        g2.dispose();

        return output;
    }
}

除生成类,其它都是工具类。

就到这儿吧!

还差一个logoStyle的eNum

public enum LogoStyle {
    ROUND, NORMAL;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值