Java实现生成中间带图标的二维码
生成Base64格式的二维码,返回html渲染
package your.package;
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 javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
public class QRCodeWithLogo {
public static void main(String[] args) throws Exception {
String webUrl = "https://www.alibaba.com"; // 目标网址
String logoPath = "alipay_logo.png"; // Logo图片文件路径
int qrCodeSize = 200; // 二维码尺寸设置为300x300像素
int logoSize = 50; // 定义Logo的宽度,留白区域大小
// 调用函数生成带留白无logo的二维码图像
BufferedImage qrCodeImage = generateQRCodeImage(webUrl, qrCodeSize, qrCodeSize, logoSize);
// 调用函数在二维码中心加入支付宝logo
BufferedImage qrCodeWithLogo = addLogoToQRCode(qrCodeImage, logoPath);
// 调用函数将带有logo的二维码图像转换为Base64字符串
String base64Encoded = convertImageToBase64(qrCodeWithLogo);
System.out.println(base64Encoded); // 输出二维码的Base64编码字符串
}
/**
* 生成带有中部空白区(为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, 0); // 设置二维码边缘 空背空白(边框大小)为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 = ImageIO.read(new File(logoPath));
// 确定logo的最终权重宽度和高度 - 通常将logo缩小到原来尺寸的1/5
int logoWidth = logoImage.getWidth() / 5;
int logoHeight = logoImage.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);
// 绘制一个填满整个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编码,并返回字符串
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="data:image/png;base64,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">
</body>
</html>
效果图
总结
上述代码是一个Java函数,主要目的是将logo图片加到二维码图片的中央位置上,完成之后返回这个合成后的图像,你也改造渲染成图片下载出来。
制作这样的二维码可以增加品牌辨识度或者美观性,在实际操作时,需要满足两个条件:
- logo不能过大,以免遮挡二维码导致难以扫描识别
- logo最好有一个边界,那样可以让logo与背景二维码区分开,这同样也是为了不影响二维码的可读性。