JAVA通过google.zxing生成带logo二维码

 先看效果图:


import cn.hutool.core.img.ImgUtil;
import com.google.zxing.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.util.ResourceUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;

public class QRCodeWithLogo {

    public static void main(String[] args) throws Exception {
        //二维码携带参数
        StringBuilder sb = new StringBuilder()
                .append("https://www.baidu.com?testNum1=102329")
                .append("&testNum2=666666");
        String logoPath = "C:\\qrcode\\test-logo.png"; // Logo图片文件路径
//        String logoPath = ResourceUtils.getFile("classpath:logo/logo.jpg").getPath();
        generateQrCode(sb.toString(), "C:\\qrcode\\"+System.currentTimeMillis()+".png", logoPath);
    }

    public static void generateQrCode(String scene, String qrCodePath, String logoPath){
       
        try {
            checkFilePath(qrCodePath);
            int qrCodeSize = 430;
            int logoSize = 100;
            // 调生成二维码图像
            BufferedImage qrCodeImage = generateQRCodeImage(scene, qrCodeSize, qrCodeSize, logoSize);
            //加入logo
            BufferedImage qrCodeWithLogo = addLogoToQRCode(qrCodeImage, logoPath);
            // 保存二维码
            ImgUtil.write(qrCodeWithLogo, qrCodePath));
        } catch (Exception e) {
            System.err.println("生成带图标的二维码时出错: " + e.getMessage());
        }
    }

    /**
     * 生成带有中部空白区(为Logo预留空间)的二维码图像。
     * @param data 要编码到二维码中的数据字符串
     * @param width 生成的二维码图像宽度
     * @param height 生成的二维码图像高度
     * @param logoSize 预留给logo的空白区域的宽度与高度(假设空白区域为正方形)
     * @return 中央带有空白区域的二维码图像
     * @throws Exception 如果生成二维码时遇到任何错误,则抛出异常
     */
    private static BufferedImage generateQRCodeImage(
            String data, int width, int height, int logoSize) throws Exception {

        // 配置二维码生成参数
        HashMap<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 指定二维码的编码设置为UTF-8
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 设置二维码的纠错级别为高(H)
        hints.put(EncodeHintType.MARGIN, 2); // 设置二维码边缘 空背空白(边框大小)为0
        hints.put(EncodeHintType.QR_VERSION, 3); // 设置二维码边缘 空背空白(边框大小)为0
        // 根据数据与配置信息生成二维码的比特矩阵
        BitMatrix bitMatrix = new MultiFormatWriter().encode(
                data, BarcodeFormat.QR_CODE, width, height, hints);
        // 将二维码比特矩阵转换为BufferedImage图像,用于最终输出
        BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
        // 计算中部logo空白区域的坐标位置
        // 这确保了空间位于二维码的正中心
        int deltaHeight = image.getHeight() - logoSize;
        int deltaWidth = image.getWidth() - logoSize;
        int coordX = deltaWidth / 2;
        int coordY = deltaHeight / 2;

        // 采用图形对象在图像上绘图
        Graphics2D g2 = image.createGraphics();
        // 设置compositing 规则定为SRC,通过AlphaComposite指定透明度效果
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
        // 设置Logo区域的填充颜色为白色
        g2.setPaint(Color.WHITE);
        // 绘制一个填充过的矩形以形成预留给logo的空白区域
        g2.fillRect(coordX, coordY, logoSize, logoSize);
        g2.dispose(); // 结束图形编辑并释放系统资源
        return image; // 返回生成的二维码BufferedImage对象
    }

    /**
     * 将一个给定的logo图片添加到二维码中心
     *
     * @param qrCodeImage 二维码的BufferedImage对象
     * @param logoPath logo图片的路径
     * @return 合成后的带有logo的二维码图片
     * @throws IOException 如果读取logo文件失败
     */
    private static BufferedImage addLogoToQRCode(BufferedImage qrCodeImage, String logoPath) throws IOException {
        // 从文件中读取logo图片
        BufferedImage logoImage = null;
        try {
            logoImage = ImageIO.read(new File(logoPath));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 确定logo的最终权重宽度和高度 - 通常将logo缩小到原来尺寸的1/5
//        int logoWidth = logoImage.getWidth() / 5;
//        int logoHeight = logoImage.getHeight() / 5;
        int logoWidth = qrCodeImage.getWidth() / 5;
        int logoHeight = qrCodeImage.getHeight() / 5;

        // 创建一个新的缓冲区图像,其中包含alpha值(透明度)用来画带边界的logo
        BufferedImage logoWithBorder = new BufferedImage(logoWidth, logoHeight, BufferedImage.TYPE_INT_ARGB);
        // 使用Graphics2D绘制带边界的logo
        Graphics2D gBorder = logoWithBorder.createGraphics();

        // 设置合成规则,表示后续绘图操作将直接替换背景像素
        gBorder.setComposite(AlphaComposite.Src);

        // 设置抗锯齿属性来平滑边缘
        gBorder.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        gBorder.setClip(new RoundRectangle2D.Float(0, 0, logoWidth, logoHeight, 20, 20)); // 圆角半径
        // 绘制一个填满整个logoWithBorder尺寸的白色矩形,形成logo的边界
        gBorder.setColor(Color.WHITE);
        gBorder.fillRect(0, 0, logoWidth, logoHeight);

        // 将logo图片缩放并绘制到logoWithBorder上
        gBorder.drawImage(logoImage.getScaledInstance(logoWidth, logoHeight, Image.SCALE_SMOOTH), 0, 0, null);

        // 不再进行绘制操作,释放此图形的上下文以及它使用的任何系统资源
        gBorder.dispose();

        // 计算二维码和logo的差距,用于将logo置于二维码的中心位置
        int deltaHeight = qrCodeImage.getHeight() - logoWithBorder.getHeight();
        int deltaWidth = qrCodeImage.getWidth() - logoWithBorder.getWidth();

        // 创建一个新的缓冲区图像以容纳原始的二维码和放置其中的logo
        BufferedImage combined = new BufferedImage(qrCodeImage.getHeight(), qrCodeImage.getWidth(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) combined.getGraphics();

        // 将二维码绘制在新图片的最底层
        g.drawImage(qrCodeImage, 0, 0, null);

        // 设置合成效果来绘制logo,覆盖在之前绘制的内容之上
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));

        // 在合适的位置绘制带有白边背景框的logo(确保居中)
        g.drawImage(logoWithBorder, Math.round(deltaWidth / 2), Math.round(deltaHeight / 2), null);

        // 完成绘制,释放图形上下文使用的资源
        g.dispose();

        // 返回合成后带有logo的二维码图像
        return combined;
    }

    /**
     * 将图像转换成Base64编码的字符串
     * @param image
     * @return
     * @throws Exception
     */
    private static String convertImageToBase64(BufferedImage image) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 创建ByteArrayOutputStream对象
        ImageIO.write(image, "png", outputStream); // 把BufferedImage写入流中
        byte[] imageBytes = outputStream.toByteArray(); // 转换成对应的byte数组
        return Base64.getEncoder().encodeToString(imageBytes); // 进行Base64编码,并返回字符串
    }

    public static void checkFilePath(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        // 获取父目录路径
        Path parentDir = path.getParent();
        // 检查父目录是否存在,如果不存在则创建
        if (parentDir != null && !Files.exists(parentDir)) {
            Files.createDirectories(parentDir);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值