java使用zxing生成二维码,可带logo和底部文字

springboot中整合zxing生成二维码

一、导入依赖

<properties>
   <zxing.version>3.4.0</zxing.version>
</properties>
 <!--二维码依赖-->
 <dependency>
   <groupId>com.google.zxing</groupId>
   <artifactId>core</artifactId>
   <version>${zxing.version}</version>
 </dependency>
 <dependency>
   <groupId>com.google.zxing</groupId>
   <artifactId>javase</artifactId>
   <version>${zxing.version}</version>
 </dependency>

二、二维码工具类封装QRCodeUtil.java,支持生成BufferedImage 和byte[]

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<EncodeHintType, Object> 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.generateQRCodeImage("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.getQRCodeImage("test",false,
"底部文字");
   response.setContentType("image/png");
   ImageIO.write(bufferedImage,"png",os);
 }

直接请求即可得到二维码图片

1、logo加文字2 、只logo3、无logo无文字4、只文字
1.png2.png3.png4.png

四、文字在linux下得展示问题

在jdk中加入指定的字体(宋体)

  1. 在jdk目录(/usr/local/jdk1.8.0_331/jre/lib/fonts)下创建目录fallback

  2. 将window下的宋体字体放到此目录下

    img

字体文件位于:C:\Windows\Fonts

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐