java使用zxing生成二维码,可带logo和底部文字
springboot中整合zxing生成二维码
一、导入依赖
3.4.0
com.google.zxing
core
${zxing.version}
com.google.zxing
javase
${zxing.version}
二、二维码工具类封装QRCodeUtil.java
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.SneakyThrows;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import sun.font.FontDesignMetrics;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
/**
* 二维码生成工具类
* Created by chenzan on 2022/09/19
*/
public class QRCodeUtil {
private static final int QRCODE_SIZE = 320; // 二维码尺寸,宽度和高度均是320
private static final String FORMAT_TYPE = "PNG"; // 二维码图片类型
/**
* 默认需要logo,无底部文字
* 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出
*
* @param dataStr
* @return 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出
*/
@SneakyThrows
public static BufferedImage getQRCodeImage(String dataStr) {
BufferedImage bufferedImage = getQRCodeImage(dataStr, true, null);
return bufferedImage;
}
/**
* 默认需要logo,无底部文字
*
* @param dataStr
* @return 返回字节数组
*/
@SneakyThrows
public static byte[] getQRCodeByte(String dataStr) {
BufferedImage bufferedImage = getQRCodeImage(dataStr, true, null);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream);
byte[] byteData = outputStream.toByteArray();
return byteData;
}
/**
* 默认需要logo,包含底部文字 文字为空则不显示文字
* 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出
*
* @param dataStr
* @return
*/
@SneakyThrows
public static BufferedImage getQRCodeImage(String dataStr, String bottomText) {
BufferedImage bufferedImage = getQRCodeImage(dataStr, true, bottomText);
return bufferedImage;
}
/**
* 默认需要logo,包含底部文字 文字为空则不显示文字
*
* @param dataStr
* @return 返回字节数组
*/
@SneakyThrows
public static byte[] getQRCodeByte(String dataStr, String bottomText) {
BufferedImage bufferedImage = getQRCodeImage(dataStr, true, bottomText);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream);
byte[] byteData = outputStream.toByteArray();
return byteData;
}
/**
* 获取二维码图片
*
* @param dataStr 二维码内容
* @param needLogo 是否需要添加logo
* @param bottomText 底部文字 为空则不显示
* @return
*/
@SneakyThrows
public static BufferedImage getQRCodeImage(String dataStr, boolean needLogo, String bottomText) {
if (dataStr == null) {
throw new RuntimeException("未包含任何信息");
}
HashMap hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); //定义内容字符集的编码
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); //定义纠错等级
hints.put(EncodeHintType.MARGIN, 1);
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(dataStr, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int tempHeight = height;
if (StringUtils.hasText(bottomText)) {
tempHeight = tempHeight + 12;
}
BufferedImage image = new BufferedImage(width, tempHeight, 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);
}
}
// 判断是否添加logo
if (needLogo) {
insertLogoImage(image);
}
// 判断是否添加底部文字
if (StringUtils.hasText(bottomText)) {
addFontImage(image, bottomText);
}
return image;
}
/**
* 插入logo图片
*
* @param source 二维码图片
* @throws Exception
*/
private static void insertLogoImage(BufferedImage source) throws Exception {
// 默认logo放于resource/static/image目录下
ClassPathResource classPathResource = new ClassPathResource("static/image/logo.png");
InputStream inputStream = classPathResource.getInputStream();
if (inputStream == null || inputStream.available() == 0) {
return;
}
Image src = ImageIO.read(inputStream);
int width = 30;
int height = 30;
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 = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_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();
}
private static void addFontImage(BufferedImage source, String declareText) {
//生成image
int defineWidth = QRCODE_SIZE;
int defineHeight = 20;
BufferedImage textImage = new BufferedImage(defineWidth, defineHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) textImage.getGraphics();
//开启文字抗锯齿
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setBackground(Color.WHITE);
g2.clearRect(0, 0, defineWidth, defineHeight);
g2.setPaint(Color.BLACK);
FontRenderContext context = g2.getFontRenderContext();
//部署linux需要注意 linux无此字体会显示方块
Font font = new Font("宋体", Font.BOLD, 15);
g2.setFont(font);
LineMetrics lineMetrics = font.getLineMetrics(declareText, context);
FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font);
float offset = (defineWidth - fontMetrics.stringWidth(declareText)) / 2;
float y = (defineHeight + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading()) / 2;
g2.drawString(declareText, (int) offset, (int) y);
Graphics2D graph = source.createGraphics();
//开启文字抗锯齿
graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//添加image
int width = textImage.getWidth(null);
int height = textImage.getHeight(null);
Image src = textImage;
graph.drawImage(src, 0, QRCODE_SIZE - 8, width, height, Color.WHITE, null);
graph.dispose();
}
}
三、使用
//1、生成带logo和底部文字得二维码
@SneakyThrows
@GetMapping("/getQrCode1")
public void getQrCode1(HttpServletResponse response) {
ServletOutputStream os = response.getOutputStream();
BufferedImage bufferedImage = QRCodeUtil.getQRCodeImage("test", "底部文字");
response.setContentType("image/png");
ImageIO.write(bufferedImage,"png",os);
}
//2、生成不带logo和底部文字得二维码
@SneakyThrows
@GetMapping("/getQrCode2")
public void getQrCode2(HttpServletResponse response) {
ServletOutputStream os = response.getOutputStream();
BufferedImage bufferedImage = QRCodeUtil.getQRCodeImage("test", false);
response.setContentType("image/png");
ImageIO.write(bufferedImage,"png",os);
}
//3、生成默认带logo不带底部文字得二维码
@SneakyThrows
@GetMapping("/getQrCode3")
public void getQrCode3(HttpServletResponse response) {
ServletOutputStream os = response.getOutputStream();
BufferedImage bufferedImage = QRCodeUtil.getQRCodeImage("test");
response.setContentType("image/png");
ImageIO.write(bufferedImage,"png",os);
}
//3、生成不带logo带底部文字得二维码
@SneakyThrows
@GetMapping("/getQrCode3")
public void getQrCode3(HttpServletResponse response) {
ServletOutputStream os = response.getOutputStream();
BufferedImage bufferedImage = QRCodeUtil.generateQRCodeImage("test",false,
"底部文字");
response.setContentType("image/png");
ImageIO.write(bufferedImage,"png",os);
}
直接请求即可得到二维码图片
1、logo加文字 | 2 、只logo | 3、无logo无文字 | 4、只文字 |
---|---|---|---|
|
|
|
|
四、文字在linux下得展示问题
在jdk中加入指定的字体(宋体)
在jdk目录(/usr/local/jdk1.8.0_331/jre/lib/fonts)下创建目录fallback
-
将window下的宋体字体放到此目录下
字体文件位于:C:\Windows\Fonts