Java 二维码QRCode生成与解析

QR Code

QR码Quick Response Code,快速矩阵图码)是二维码的一种,于1994年由日本汽车零组件大厂电装公司原昌宏发明。QR码使用四种标准化编码模式(数字、字母数字、字节(二进制)和日文(Shift_JIS))来存储数据。QR码原创于日本,现已在世界各国广泛运用于手机读码操作。QR码比普通条形码具有快速读取和更大的存储资料容量,也无需要像一维条码般在扫描时需要直线对准扫描仪,应用范围包括产品跟踪、物品识别、文档管理、库存营销等等。

QR码最大资料容量(对于版本40)
数字最多7,089字符
字母最多4,296字符
二进制数(8 bit)最多2,953 字节
日文汉字/片假名最多1,817字符(采用Shift JIS)
中文汉字最多984字符(采用UTF-8)
最多1,800字符(采用BIG5/GB2312)
错误修正容量
L等级可修正7%的字码
M等级可修正15%的字码
Q等级可修正25%的字码
H等级可修正30%的字码

对于我们来说QR码并不陌生,不管是网购剁手,还是日常面对面交易或信息交互,都有它的身影。

Java实现QR码生成和解析

Maven引用zxing

通过修改pom.xml文件,引入zxing核心com.google.zxing.core及其javase扩展com.google.zxing.javase两个依赖。写这篇文章的时候使用的是3.5.3版本

  <properties>
    ...
    <zxing.version>3.5.3</zxing.version>
    ...
  </properties>
  <dependencies>
    ...
    <!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>core</artifactId>
      <version>${zxing.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>javase</artifactId>
      <version>${zxing.version}</version>
    </dependency>
    ...
  </dependencies>

若Maven编译报错:[ERROR] 不再支持源选项 5。请使用 7 或更高版本。[ERROR] 不再支持目标选项 5。请使用 7 或更高版本。加上源和目标编译器设置:

    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>

maven-archetype-quickstart脚手架搭建的maven项目为例,完整的pom.xml如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.humbunklung</groupId>
  <artifactId>quick-response-code</artifactId>
  <version>1.0-SNAPSHOT</version>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <source>8</source>
          <target>8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <packaging>jar</packaging>

  <name>quick-response-code</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <zxing.version>3.5.3</zxing.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>core</artifactId>
      <version>${zxing.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>javase</artifactId>
      <version>${zxing.version}</version>
    </dependency>
  </dependencies>
</project>

QR码图片生成

QR码编码,通过zxingMultiFormatWriterencode方法完成,将需要编码的内容按照用户设置的参数形成比特矩阵。

    /**
     * 按照自定义内容、宽度、高度、纠错等级、字符集、编剧创建QR码比特矩阵。
     *
     * @param content 二维码内容
     * @param width 二维码宽度
     * @param height 二维码高度
     * @param errCorrLevel 纠错等级,可以是<code>L, M, Q, H</code>
     * @param charSet 字符集
     * @param margin 边距
     * @return BitMatrix 二维码比特矩阵
     * @throws WriterException
     */
    public static BitMatrix createQRCodeBitMatrix(String content, int width, int height,
                                                  ErrorCorrectionLevel errCorrLevel,
                                                  String charSet, int margin) throws WriterException {
        if (width <= 0) {
            width = DEFAULT_QR_CODE_WIDTH;
        }
        if (height <= 0) {
            height = DEFAULT_QR_CODE_HEIGHT;
        }
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.ERROR_CORRECTION, errCorrLevel); // 设置纠错等级
        hints.put(EncodeHintType.CHARACTER_SET, charSet); // 设置编码方式
        hints.put(EncodeHintType.MARGIN, margin); // 设置边距
        return new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
    }

为了使用方便,我们可以将生成的比特矩阵BitMatrix转换成字节数组、base64字节数组甚至base64字符串:

    /**
     * 创建二维码,返回字节数组
     *
     * @param content 二维码内容
     * @param imageFormat 二维码图像格式
     * @param width 宽度
     * @param height 高度
     * @param errCorrLevel 纠错等级
     * @param charSet 字符集
     * @param margin 边距
     * @return byte[]
     * @throws WriterException
     * @throws IOException
     */
    public static byte[] createQRCode(String content, String imageFormat, int width, int height,
                                      ErrorCorrectionLevel errCorrLevel, String charSet, int margin)
            throws WriterException, IOException {
        if (imageFormat == null || imageFormat.equals("")){
            imageFormat = DEFAULT_FORMAT_NAME;
        }
        BitMatrix bitMatrix = createQRCodeBitMatrix(content, width, height, errCorrLevel, charSet, margin);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, os);
        return os.toByteArray();
    }

    /**
     * 创建二维码,返回base64字节流
     *
     * @param content 二维码里的内容
     * @param imageFormat 图片后缀名
     * @param width 二维码宽度
     * @param height 二维码高度
     * @return byte[] base64字节数组
     */
    public static byte[] createQRCodeBase64Bytes(String content , String imageFormat , int width , int height)
            throws WriterException, IOException {
        byte[] bytes = createQRCode(content , imageFormat , width, height);
        return Base64.getEncoder().encode(bytes);
    }  

    /**
     * 创建二维码,返回base64字符串
     *
     * @param content 二维码里的内容
     * @param imageFormat 图片后缀名
     * @param width 二维码宽度
     * @param height 二维码高度
     * @return String base64字符串
     */
    public static String createQRCodeBase64(String content , String imageFormat , int width , int height,
                                            String encoding) throws WriterException, IOException {
        return new String(createQRCodeBase64Bytes(content, imageFormat, width, height), encoding);
    }      

为了使用方便,也可以将BitMatrix转换为java.awt.BufferedImage供后续使用:

    /**
     * 转换为BufferedImage
     *
     * @param bitMatrix 比特矩阵
     * @param onColor 条码颜色
     * @param offColor 背景颜色
     * @return java.awt.image.BufferedImage
     */
    public static BufferedImage toBufferedImage(BitMatrix bitMatrix, int onColor, int offColor) {
        MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(onColor, offColor);
        return MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
    }

其中,通过查询zxing文档,我们可以知道MatrxiToImageConfig中的onColoroffColor分别是QR码的二维码颜色和背景色,当使用无参数构造方法时,默认为黑、白。

接下来,我们不妨用一个简单的方法来整合以上方法,供主程序调用:

    /**
     * 根据内容和设置生成二维码图片到指定路径
     *
     * @param content 二维码里的内容
     * @param imagePath 生成图片路径
     * @param imageFormat 图片格式
     * @param onColor 二维码颜色
     * @param offColor 背景颜色
     * @param width 二维码宽度
     * @param height 二维码高度
     * @param errorCorrectionLevel 纠错等级
     * @param charSet 字符集
     * @param margin 边距
     * @throws WriterException
     * @throws IOException
     */
    public static void encodeQRCode(String content, String imagePath,
                                    String imageFormat, int onColor, int offColor, int width, int height,
                                    ErrorCorrectionLevel errorCorrectionLevel, String charSet, int margin)
            throws WriterException, IOException {
        if (imageFormat == null || imageFormat.equals("")){
            imageFormat = DEFAULT_FORMAT_NAME;
        }
        BufferedImage bufferedImage = encodeQRCode(content, onColor, offColor,
                width, height, errorCorrectionLevel, charSet, margin);
        ImageIO.write(bufferedImage, imageFormat, new File(imagePath));
    }

通过在调用以上代码:

    public static void encodeDemo() {
        // 当前目录
        String currentDirectory = System.getProperty("user.dir");
        // 获取当前时间并设置格式,用于生成的文件名
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        // 输出目录
        String output = String.format("%s/data/%s.png", currentDirectory, now.format(dateTimeFormatter));
        String imagePath = new File(output).toPath().toString();
        try {
            QRCodeHelper.encodeQRCode(DEFAULT_TEST_CONTENT, imagePath, "png", 0xff5683ed, 0xffffffff,
                    500, 500, ErrorCorrectionLevel.H, "UTF-8", 2);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (WriterException e) {
            e.printStackTrace();
        }
    }

可生成如下的二维码:

基础二维码

QR码图片解码

QR码的解码相对而言显得“朴实无华”,它通过MultiFormatReaderdecode方法完成:

    /**
     * 解码二维码
     *
     * @param image 二维码BufferedImage
     * @return java.lang.String
     */
    public static String decodeQRCode(BufferedImage image) throws Exception {
        if (image == null) return "";
        BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
        hints.put(DecodeHintType.CHARACTER_SET, DEFAULT_CHAR_SET);
        Result result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

QR码嵌入LOGO

在日常生活中,我们常常见到有些二维码嵌入了LOGO,因此我们也可以在上述基础编码的基础上嵌入LOGO,因LOGO会对二维码构成遮挡,那么,前面所述的错误纠正等级,就可以起作用了,考虑不同的纠正等级和大小的二维码,我们可以适当控制嵌入图像的大小。实现代码举例如下:

    /**
     * 给BufferedImage添加LOGO
     *
     * @param bufferedImage
     * @param logoFile
     * @param xPos LOGO在二维码中的x坐标
     * @param yPos LOGO在二维码中的y坐标
     * @param logoWidth LOGO在二维码中的宽度
     * @param logoHeight LOGO在二维码中的高度
     * @param strokeWidth
     * @param strokeCap
     * @param strokeJoin
     * @param arcWidth
     * @param arcHeight
     * @return
     * @throws IOException
     */
    public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
                                                       int xPos, int yPos, int logoWidth, int logoHeight,
                                                       float strokeWidth, int strokeCap, int strokeJoin,
                                                       int arcWidth, int arcHeight) throws IOException {
        BufferedImage clone = deepCopy(bufferedImage); // 深复制BufferedImage避免污染原图
        Graphics2D graphics = clone.createGraphics();

        // 读取logo图片文件
        BufferedImage logo = ImageIO.read(logoFile);

        // 开始绘制图片
        graphics.drawImage(logo, xPos, yPos, logoWidth, logoHeight, null);
        graphics.setStroke(new BasicStroke(strokeWidth, strokeCap, strokeJoin));
        graphics.setColor(Color.white);
        // 绘制圆角矩形
        graphics.drawRoundRect(xPos, yPos, logoWidth, logoHeight, arcWidth, arcHeight);
        graphics.dispose();
        clone.flush();
        return clone;
    }


    /**
     * 在QR码图像中央加入一个1/5大小的LOGO
     * @param bufferedImage QR码图像
     * @param logoFile LOGO文件
     * @param strokeWidth LOGO圆角笔宽
     * @param strokeCap
     * @param strokeJoin
     * @param arcWidth 圆角宽,0则不圆
     * @param arcHeight 圆角高,0则不圆
     * @return
     * @throws IOException
     */
    public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
                                                       float strokeWidth, int strokeCap, int strokeJoin,
                                                       int arcWidth, int arcHeight)
            throws IOException {
        int matrixWidth = bufferedImage.getWidth();
        int matrixHeight = bufferedImage.getHeight();
        // 读取logo图片文件
        BufferedImage logo = ImageIO.read(logoFile);
        int logoWidth = logo.getWidth();
        int logoHeight = logo.getHeight();
        //  计算logo放置位置
        int x = matrixWidth  / 5*2;
        int y = matrixHeight / 5*2;
        int width = matrixWidth / 5;
        int height = matrixHeight / 5;
        return addLogoToBufferedImage(bufferedImage, logoFile, x, y, width, height,
                strokeWidth, strokeCap, strokeJoin, arcWidth, arcHeight);
    }

调用实例如下:

    public static void encodeWithLogoDemo() {
        // 当前目录
        String currentDirectory = System.getProperty("user.dir");
        // 获取当前时间并设置格式,用于生成的文件名
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        // 输出目录
        String output = String.format("%s/data/%s.png", currentDirectory, now.format(dateTimeFormatter));
        String imagePath = new File(output).toPath().toString();
        File logoFile = new File(String.format("%s/data/logo.jpg", currentDirectory));
        try {
            BufferedImage qr = QRCodeHelper.encodeQRCode(DEFAULT_TEST_CONTENT, 0xff5683ed, 0xffffffff,
                    500, 500, ErrorCorrectionLevel.H, "UTF-8", 2);
            BufferedImage qrWithLogo = QRCodeHelper.addLogoToBufferedImage(qr, logoFile, 5.0F,
                    1, 1, 20, 20);
            ImageIO.write(qr, "png", new File(imagePath));
            ImageIO.write(qrWithLogo, "png",
                    new File(imagePath.replace(".png", "-logo.png")));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (WriterException e) {
            e.printStackTrace();
        }
    }

生成的二维码如下图:

带LOGO的二维码

还有时候,我们会遇到一些像水印一样覆盖到基础QR码中的二维码图像:

LOGO覆盖

LOGO像水印一样嵌入到二维码中,这种方式同样要考虑大小、透明度的问题,过大的LOGO如果不甚透明,是无法识别的,代码可以这样实现:

    /**
     * 给QR码的BufferedImage添加可透明的LOGO
     *
     * @param qrcode
     * @param overlay
     * @param overlayToQRCodeRatio
     * @param overlayTransparency
     * @return
     */
    public static BufferedImage getQRCodeWithOverlay(BufferedImage qrcode, BufferedImage overlay,
                                                     float overlayToQRCodeRatio, float overlayTransparency) {
        BufferedImage scaledOverlay = scaleOverlay(qrcode, overlay,overlayToQRCodeRatio);

        Integer deltaHeight = qrcode.getHeight() - scaledOverlay.getHeight();
        Integer deltaWidth  = qrcode.getWidth()  - scaledOverlay.getWidth();

        BufferedImage combined = new BufferedImage(qrcode.getWidth(), qrcode.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = (Graphics2D)combined.getGraphics();
        g2.drawImage(qrcode, 0, 0, null);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, overlayTransparency));
        g2.drawImage(scaledOverlay, Math.round(deltaWidth/2), Math.round(deltaHeight/2), null);
        return combined;
    }

    /**
     * 按比例调整LOGO
     *
     * @param qrcode
     * @param overlay
     * @param overlayToQRCodeRatio
     * @return
     */
    private static BufferedImage scaleOverlay(BufferedImage qrcode, BufferedImage overlay, float overlayToQRCodeRatio) {
        Integer scaledWidth = Math.round(qrcode.getWidth() * overlayToQRCodeRatio);
        Integer scaledHeight = Math.round(qrcode.getHeight() * overlayToQRCodeRatio);

        BufferedImage imageBuff = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics g = imageBuff.createGraphics();
        g.drawImage(overlay.getScaledInstance(scaledWidth, scaledHeight, BufferedImage.SCALE_SMOOTH),
                0, 0, new Color(0,0,0), null);
        g.dispose();
        return imageBuff;
    }

调用方法如下,我们不妨设置该LOGO占二维码80%大小,透明度只有0.1,即差不多全透明了,实测不影响扫码:

    public static void encodeWithOverlayDemo() {
        // 当前目录
        String currentDirectory = System.getProperty("user.dir");
        // 获取当前时间并设置格式,用于生成的文件名
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        // 输出目录
        String output = String.format("%s/data/%s.png", currentDirectory, now.format(dateTimeFormatter));
        String imagePath = new File(output).toPath().toString();
        File logoFile = new File(String.format("%s/data/logo.jpg", currentDirectory));
        try {
            BufferedImage qr = QRCodeHelper.encodeQRCode(DEFAULT_TEST_CONTENT, 0xff5683ed, 0xffffffff,
                    500, 500, ErrorCorrectionLevel.H, "UTF-8", 2);
            BufferedImage qrWithLogo = QRCodeHelper.getQRCodeWithOverlay(qr, ImageIO.read(logoFile),
                    0.8F, 0.1F);
            ImageIO.write(qr, "png", new File(imagePath));
            ImageIO.write(qrWithLogo, "png",
                    new File(imagePath.replace(".png", "-logo.png")));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (WriterException e) {
            e.printStackTrace();
        }
    }

后记

二维码丰富了我们的生活,通过zxing等成熟的库,我们可以写出各类二维码应用。也通过这个库,完成了工作当中需要生成二维码的应用开发,文章中的代码和思路,吸收了网络上的一些资源,并作了修改,在此也向原作者表示感谢。最后修改的QRCodeHelper类如下:

package org.humbunklung;

import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * QRCodeHelper
 *
 * @author yesy
 */
public class QRCodeHelper {
    // 默认编码方式
    private static final String DEFAULT_CHAR_SET = "UTF-8";
    // 默认二维码图片格式
    private static final String DEFAULT_FORMAT_NAME = "PNG";


    // 默认二维码宽度
    private static final int DEFAULT_QR_CODE_WIDTH = 300;
    // 默认二维码高度
    private static final int DEFAULT_QR_CODE_HEIGHT = 300;

    public static BitMatrix createBitMatrix(String content) throws  WriterException {
        return createBitMatrix(content, DEFAULT_QR_CODE_WIDTH, DEFAULT_QR_CODE_WIDTH);
    }

    /**
     * 创建BitMatrix比特矩阵
     *
     * @param content 二维码里的内容
     * @param width 二维码宽度
     * @param height 二维码高度
     * @return com.google.zxing.common.BitMatrix 二维码比特矩阵
     * @throws WriterException
     */
    public static BitMatrix createBitMatrix(String content , int width , int height) throws WriterException {
        if (width <= 0) {
            width = DEFAULT_QR_CODE_WIDTH;
        }
        if (height <= 0) {
            height = DEFAULT_QR_CODE_HEIGHT;
        }

        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 纠错等级L,M,Q,H
        hints.put(EncodeHintType.CHARACTER_SET, DEFAULT_CHAR_SET);// 编码utf-8
        hints.put(EncodeHintType.MARGIN, 1);  // 边距

        // 创建比特矩阵
        return new MultiFormatWriter().encode(content,
                BarcodeFormat.QR_CODE, width, height, hints);
    }

    /**
     * 按照自定义内容、宽度、高度、纠错等级、字符集、编剧创建QR码比特矩阵。
     *
     * @param content 二维码内容
     * @param width 二维码宽度
     * @param height 二维码高度
     * @param errCorrLevel 纠错等级,可以是<code>L, M, Q, H</code>
     * @param charSet 字符集
     * @param margin 边距
     * @return BitMatrix 二维码比特矩阵
     * @throws WriterException
     */
    public static BitMatrix createQRCodeBitMatrix(String content, int width, int height,
                                                  ErrorCorrectionLevel errCorrLevel,
                                                  String charSet, int margin) throws WriterException {
        if (width <= 0) {
            width = DEFAULT_QR_CODE_WIDTH;
        }
        if (height <= 0) {
            height = DEFAULT_QR_CODE_HEIGHT;
        }
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.ERROR_CORRECTION, errCorrLevel); // 设置纠错等级
        hints.put(EncodeHintType.CHARACTER_SET, charSet); // 设置编码方式
        hints.put(EncodeHintType.MARGIN, margin); // 设置边距
        return new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
    }

    /**
     * 创建二维码,返回字节数组
     *
     * @param content 二维码里的内容
     * @param imageFormat 图片后缀名
     * @param width 二维码宽度
     * @param height 二维码高度
     * @return byte[] 二维码字节流
     */
    public static byte[] createQRCode(String content , String imageFormat , int width , int height)
            throws WriterException, IOException {
        if (imageFormat == null || imageFormat.equals("")){
            imageFormat = DEFAULT_FORMAT_NAME;
        }
        BitMatrix bitMatrix = createBitMatrix(content , width, height);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, os);
        return os.toByteArray();
    }

    /**
     * 创建二维码,返回字节数组
     *
     * @param content 二维码内容
     * @param imageFormat 二维码图像格式
     * @param width 宽度
     * @param height 高度
     * @param errCorrLevel 纠错等级
     * @param charSet 字符集
     * @param margin 边距
     * @return byte[]
     * @throws WriterException
     * @throws IOException
     */
    public static byte[] createQRCode(String content, String imageFormat, int width, int height,
                                      ErrorCorrectionLevel errCorrLevel, String charSet, int margin)
            throws WriterException, IOException {
        if (imageFormat == null || imageFormat.equals("")){
            imageFormat = DEFAULT_FORMAT_NAME;
        }
        BitMatrix bitMatrix = createQRCodeBitMatrix(content, width, height, errCorrLevel, charSet, margin);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, os);
        return os.toByteArray();
    }

    /**
     * 创建二维码,返回base64字节流
     *
     * @param content 二维码里的内容
     * @param imageFormat 图片后缀名
     * @param width 二维码宽度
     * @param height 二维码高度
     * @return byte[] base64字节数组
     */
    public static byte[] createQRCodeBase64Bytes(String content , String imageFormat , int width , int height)
            throws WriterException, IOException {
        byte[] bytes = createQRCode(content , imageFormat , width, height);
        return Base64.getEncoder().encode(bytes);
    }

    /**
     * 创建二维码,返回base64字符串
     *
     * @param content 二维码里的内容
     * @param imageFormat 图片后缀名
     * @param width 二维码宽度
     * @param height 二维码高度
     * @return String base64字符串
     */
    public static String createQRCodeBase64(String content , String imageFormat , int width , int height,
                                            String encoding) throws WriterException, IOException {
        return new String(createQRCodeBase64Bytes(content, imageFormat, width, height), encoding);
    }

    /**
     * 转换为BufferedImage
     *
     * @param bitMatrix 比特矩阵
     * @param onColor 条码颜色
     * @param offColor 背景颜色
     * @return java.awt.image.BufferedImage
     */
    public static BufferedImage toBufferedImage(BitMatrix bitMatrix, int onColor, int offColor) {
        MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(onColor, offColor);
        return MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
    }

    /**
     * 转换为BufferedImage
     *
     * @param bitMatrix 比特矩阵
     * @return java.awt.image.BufferedImage
     */
    public static BufferedImage toBufferedImage(BitMatrix bitMatrix) {
//        MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
        MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig();    // 无参数为白底黑码
        return MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
    }

    /**
     * QR码编码
     *
     * @param content 编码内容
     * @param onColor 二维码颜色
     * @param offColor 背景颜色
     * @param width 宽度
     * @param height 高度
     * @param errCorrLevel 纠错等级
     * @param charSet 字符集
     * @param margin 边距
     * @return BufferedImage
     * @throws WriterException
     */
    public static BufferedImage encodeQRCode(String content, int onColor, int offColor, int width, int height,
                                             ErrorCorrectionLevel errCorrLevel, String charSet, int margin)
            throws WriterException {
        BitMatrix bitMatrix = createQRCodeBitMatrix(content, width, height, errCorrLevel, charSet, margin);
        return toBufferedImage(bitMatrix, onColor, offColor);
    }

    /**
     * QR码编码
     *
     * @param content 编码内容
     * @param width 宽度
     * @param height 高度
     * @return BufferedImage
     * @throws WriterException
     */
    public static BufferedImage encodeQRCode(String content, int width, int height) throws WriterException {
        BitMatrix bitMatrix = createBitMatrix(content, width, height);
        return toBufferedImage(bitMatrix);
    }

    /**
     * QR码编码
     *
     * @param content 要编码的内容
     * @return BufferedImage
     * @throws WriterException
     */
    public static BufferedImage encodeQRCode(String content) throws WriterException {
        return toBufferedImage(createBitMatrix(content));
    }

    /**
     * 根据内容和设置生成二维码图片到指定路径
     *
     * @param content 二维码里的内容
     * @param imagePath 生成图片路径
     * @param imageFormat 图片格式
     * @param onColor 二维码颜色
     * @param offColor 背景颜色
     * @param width 二维码宽度
     * @param height 二维码高度
     * @param errorCorrectionLevel 纠错等级
     * @param charSet 字符集
     * @param margin 边距
     * @throws WriterException
     * @throws IOException
     */
    public static void encodeQRCode(String content, String imagePath,
                                    String imageFormat, int onColor, int offColor, int width, int height,
                                    ErrorCorrectionLevel errorCorrectionLevel, String charSet, int margin)
            throws WriterException, IOException {
        if (imageFormat == null || imageFormat.equals("")){
            imageFormat = DEFAULT_FORMAT_NAME;
        }
        BufferedImage bufferedImage = encodeQRCode(content, onColor, offColor,
                width, height, errorCorrectionLevel, charSet, margin);
        ImageIO.write(bufferedImage, imageFormat, new File(imagePath));
    }

    /**
     * 解码二维码
     *
     * @param image 二维码BufferedImage
     * @return java.lang.String
     */
    public static String decodeQRCode(BufferedImage image) throws Exception {
        if (image == null) return "";
        BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
        hints.put(DecodeHintType.CHARACTER_SET, DEFAULT_CHAR_SET);
        Result result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

    /**
     * 给BufferedImage添加LOGO
     *
     * @param bufferedImage
     * @param logoFile
     * @param xPos LOGO在二维码中的x坐标
     * @param yPos LOGO在二维码中的y坐标
     * @param logoWidth LOGO在二维码中的宽度
     * @param logoHeight LOGO在二维码中的高度
     * @param strokeWidth
     * @param strokeCap
     * @param strokeJoin
     * @param arcWidth
     * @param arcHeight
     * @return
     * @throws IOException
     */
    public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
                                                       int xPos, int yPos, int logoWidth, int logoHeight,
                                                       float strokeWidth, int strokeCap, int strokeJoin,
                                                       int arcWidth, int arcHeight) throws IOException {
        BufferedImage clone = deepCopy(bufferedImage); // 深复制BufferedImage避免污染原图
        Graphics2D graphics = clone.createGraphics();

        // 读取logo图片文件
        BufferedImage logo = ImageIO.read(logoFile);

        // 开始绘制图片
        graphics.drawImage(logo, xPos, yPos, logoWidth, logoHeight, null);
        graphics.setStroke(new BasicStroke(strokeWidth, strokeCap, strokeJoin));
        graphics.setColor(Color.white);
        // 绘制圆角矩形
        graphics.drawRoundRect(xPos, yPos, logoWidth, logoHeight, arcWidth, arcHeight);
        graphics.dispose();
        clone.flush();
        return clone;
    }


    /**
     * 在QR码图像中央加入一个1/5大小的LOGO
     * @param bufferedImage QR码图像
     * @param logoFile LOGO文件
     * @param strokeWidth LOGO圆角笔宽
     * @param strokeCap
     * @param strokeJoin
     * @param arcWidth 圆角宽,0则不圆
     * @param arcHeight 圆角高,0则不圆
     * @return
     * @throws IOException
     */
    public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
                                                       float strokeWidth, int strokeCap, int strokeJoin,
                                                       int arcWidth, int arcHeight)
            throws IOException {
        int matrixWidth = bufferedImage.getWidth();
        int matrixHeight = bufferedImage.getHeight();
        // 读取logo图片文件
        BufferedImage logo = ImageIO.read(logoFile);
        int logoWidth = logo.getWidth();
        int logoHeight = logo.getHeight();
        //  计算logo放置位置
        int x = matrixWidth  / 5*2;
        int y = matrixHeight / 5*2;
        int width = matrixWidth / 5;
        int height = matrixHeight / 5;
        return addLogoToBufferedImage(bufferedImage, logoFile, x, y, width, height,
                strokeWidth, strokeCap, strokeJoin, arcWidth, arcHeight);
    }

    /**
     * 给BufferedImage添加LOGO,该方法选择自网络,修改了原来按引用传递带来的副作用(会改变原来的BufferedImage)。
     *
     * @deprecated
     * @param bufferedImage
     * @param logoFile
     * @return
     * @throws IOException
     */
    public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile)
            throws IOException {
        BufferedImage clone = deepCopy(bufferedImage); // 深复制BufferedImage避免污染原图
        Graphics2D graphics = clone.createGraphics();
        int matrixWidth = clone.getWidth();
        int matrixHeight = clone.getHeight();

        // 读取logo图片文件
        BufferedImage logo = ImageIO.read(logoFile);
        int logoWidth = logo.getWidth();
        int logoHeight = logo.getHeight();

        //  计算logo放置位置
        int x = clone.getWidth()  / 5*2;
        int y = clone.getHeight() / 5*2;
        int width = matrixWidth / 5;
        int height = matrixHeight / 5;

        // 开始绘制图片
        graphics.drawImage(logo, x, y, width, height, null);
        graphics.setStroke(new BasicStroke(5.0F, 1, 1));
        graphics.setColor(Color.white);
        graphics.drawRoundRect(x, y, width, height, 15, 15);

        graphics.dispose();
        clone.flush();
        return clone;
    }

    /**
     * 给QR码的BufferedImage添加可透明的LOGO
     *
     * @param qrcode
     * @param overlay
     * @param overlayToQRCodeRatio
     * @param overlayTransparency
     * @return
     */
    public static BufferedImage getQRCodeWithOverlay(BufferedImage qrcode, BufferedImage overlay,
                                                     float overlayToQRCodeRatio, float overlayTransparency) {
        BufferedImage scaledOverlay = scaleOverlay(qrcode, overlay,overlayToQRCodeRatio);

        Integer deltaHeight = qrcode.getHeight() - scaledOverlay.getHeight();
        Integer deltaWidth  = qrcode.getWidth()  - scaledOverlay.getWidth();

        BufferedImage combined = new BufferedImage(qrcode.getWidth(), qrcode.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = (Graphics2D)combined.getGraphics();
        g2.drawImage(qrcode, 0, 0, null);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, overlayTransparency));
        g2.drawImage(scaledOverlay, Math.round(deltaWidth/2), Math.round(deltaHeight/2), null);
        return combined;
    }

    /**
     * 按比例调整LOGO
     *
     * @param qrcode
     * @param overlay
     * @param overlayToQRCodeRatio
     * @return
     */
    private static BufferedImage scaleOverlay(BufferedImage qrcode, BufferedImage overlay, float overlayToQRCodeRatio) {
        Integer scaledWidth = Math.round(qrcode.getWidth() * overlayToQRCodeRatio);
        Integer scaledHeight = Math.round(qrcode.getHeight() * overlayToQRCodeRatio);

        BufferedImage imageBuff = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics g = imageBuff.createGraphics();
        g.drawImage(overlay.getScaledInstance(scaledWidth, scaledHeight, BufferedImage.SCALE_SMOOTH),
                0, 0, new Color(0,0,0), null);
        g.dispose();
        return imageBuff;
    }

    /**
     * BufferedImage 深复制
     * @param bi
     * @return
     */
    private static BufferedImage deepCopy(BufferedImage bi) {
        ColorModel cm = bi.getColorModel();
        boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
        WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster());
        return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
    }
}

因写得仓促,肯定有不少漏洞,希望大家指正,也希望大家不吝扫码、关注、点赞。

扫我

Java生成二维码可以使用第三方库ZXing(Zebra Crossing)。ZXing是一个开源的用于生成解析二维码的库,它支持多种编程语言,包括Java。 首先,我们需要导入ZXing库。你可以从ZXing的官方网站下载最新的jar包,然后将其添加到Java项目的类路径下。 接下来,我们可以使用ZXing提供的编码类来生成二维码。以下是一个简单的示例代码: ``` import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; public class QRCodeGenerator { public static void main(String[] args) { String text = "Hello, World!"; // 需要编码成二维码的文本 int width = 300; // 二维码图片的宽度 int height = 300; // 二维码图片的高度 String format = "png"; // 二维码图片的格式 // 设置二维码的参数 java.util.Map<EncodeHintType, Object> hints = new java.util.HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 使用ZXing生成二维码 BitMatrix bitMatrix; try { bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints); } catch (WriterException e) { e.printStackTrace(); return; } // 将BitMatrix转换为BufferedImage BufferedImage image = new BufferedImage(width, height, 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); } } // 保存生成二维码图片 try { ImageIO.write(image, format, new File("qrcode." + format)); System.out.println("二维码生成"); } catch (IOException e) { e.printStackTrace(); } } } ``` 以上代码演示了如何使用ZXing库生成一个简单的二维码。你可以将需要编码的文本、图片的宽度和高度以及保存的文件格式替换为你所需的值。最终生成二维码将被保存为一个文件。 需要注意的是,运行此代码时必须确保Java项目的类路径中已经包含了ZXing库的jar文件。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Humbunklung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值