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

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

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.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、只文字
1.png
2.png
3.png
4.png

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

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

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

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

    img

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容