签入版本

This commit is contained in:
2026-09-10 21:41:50 +08:00
commit 717116a605
2693 changed files with 355025 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>com.xiaomayi</groupId>
<artifactId>xiaomayi-common</artifactId>
<version>${revision}</version>
</parent>
<artifactId>xiaomayi-qrcode</artifactId>
<!-- 依赖声明 -->
<dependencies>
<!-- 核心依赖模块 -->
<dependency>
<groupId>com.xiaomayi</groupId>
<artifactId>xiaomayi-core</artifactId>
</dependency>
<!-- 二维码生成依赖 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,128 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | Copyright (c) 2020-2026 小蚂蚁云团队 版权所有
// +----------------------------------------------------------------------
// | 官方平台: https://www.xiaomayicloud.com
// +======================================================================
// | 【知识产权声明】
// | 1. 权属声明:本软件是受《中华人民共和国著作权法》
// | 保护的专有财产,完整版权归小蚂蚁云团队所有
// | 2. 权利范围:所有知识产权(商标、专利、著作权、
// | 商业秘密等)均归小蚂蚁云团队所有,受法律保护
// | 3. 授权性质:用户仅可获得有限的、非独占的使用许可
// | 4. 禁止行为:未经书面授权,禁止对本软件本身
// | 申请任何知识产权
// | 5. 权利保留:未在本协议中明示授予的一切权利
// | 均由小蚂蚁云团队保留
// +======================================================================
// | 【授权状态说明】
// | 1. 未授权状态:用户未购买有效的商业许可证
// | 2. 商业授权:已购买正式商业许可证的用户
// +======================================================================
// | 【绝对禁止条款】
// | 无论授权状态如何,任何用户均严格禁止以下行为:
// | 1. 逆向工程:对软件进行反编译、反汇编、技术破解或任何形式的逆向分析
// | 2. 版权破坏:移除、修改、遮挡软件中的版权标识、许可控制或技术保护措施
// | 3. 非法传播:复制、分发、出租、出借、转售、再许可本软件或其许可权利
// | 4. 竞争性使用:利用本软件的代码、架构、设计开发直接竞争的产品或服务
// | 5. 侵权使用:将软件用于侵犯第三方知识产权或其他合法权益的用途
// +======================================================================
// | 【商业授权许可范围】
// | 购买有效商业许可证的用户,在遵守上述禁止条款的前提下,获得:
// | 1. 使用权利:在许可证约定的项目数量、开发者数量范围内使用本软件
// | 2. 技术支持:商业授权用户可以获取本地开发部署的技术支持服务
// | 3. 版本更新:在许可证有效期内获取软件更新、安全补丁和功能增强
// | 4. 商业部署:将基于本软件开发的应用程序用于商业运营和生产环境
// +======================================================================
// | 【许可证获取】
// | 1. 获取途径:仅通过小蚂蚁云官方渠道购买(官网、授权代理商),购买记录凭证
// | 2. 转让限制:许可证与购买主体绑定,未经书面同意不得转让
// | 3. 终止情形:许可证到期、违反协议、破产清算等情况下自动终止
// +======================================================================
// | 【合法使用要求】
// | 1. 仅限用于合法合规的软件研发
// | 2. 严禁用于任何违法、侵权或恶意行为
// | 3. 严禁违反我国法律法规和政策
// +======================================================================
// | 【免责声明】
// | 因使用本框架导致的以下情况,我们概不负责:
// | 1. 项目研发中的意外、疏忽、违约等风险
// | 2. 侵犯第三方知识产权或其他合法权益
// | 3. 任何直接、间接、附带或衍生一切损失
// | 4. 用户违规违法行为的一切法律后果
// +======================================================================
// | 【协议重要提示】
// | 1. 本声明是《小蚂蚁云最终用户许可协议》的摘要版本
// | 2. 下载、安装或使用本软件即表示您接受全部协议条款
// | 3. 我们保留单方面修改本声明的权利,修改后的版本在官网公布即生效
// | 4. 用户有义务定期查看最新版本,继续使用视为接受修改
// +======================================================================
// | 【侵权举报与合规】
// | 1. 知识产权侵权举报:请联系官方客服
// | 2. 许可证合规咨询:xiaomayicloud@163.com
// | 3. 违规使用将终止授权并追究责任
// | 4. 无条件配合司法机关调查取证
// | 5. 保留一切法律追诉权利
// | 6. 对有效举报和合规建议,我们将给予感谢和奖励
// +======================================================================
package com.xiaomayi.qrcode.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* 谷歌二维码DTO
* </p>
*
* @author 小蚂蚁云团队
* @since 2024-05-31
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QrCodeParam {
/**
* 二维码的内容(非空)
*/
private String qrCodeContent;
/**
* 二维码的宽度(非空)
*/
private Integer qrCodeWidth;
/**
* 二维码的高度(非空)
*/
private Integer qrCodeHeight;
/**
* 二维码内嵌图片的文件路径(为空则表示:二维码中间不嵌套图片)
*/
private String embeddedImgFilePath;
/**
* 文字的大小(即:正方形文字的长度、宽度)(非空)
*/
private Integer wordSize;
/**
* 文字的内容(非空)
*/
private String wordContent;
/**
* 二维码文件的输出路径(非空)
*/
private String qrCodeFileOutputPath;
}
@@ -0,0 +1,831 @@
// +======================================================================
// | 小蚂蚁云·企业数字化开发平台 [ 加速数字化转型,构建智能未来 ]
// +----------------------------------------------------------------------
// | 核心价值:赋能开发者 · 服务企业 · 共建生态
// +----------------------------------------------------------------------
// | Copyright (c) 2020-2026 小蚂蚁云团队 版权所有
// +----------------------------------------------------------------------
// | 官方平台: https://www.xiaomayicloud.com
// +======================================================================
// | 【知识产权声明】
// | 1. 权属声明:本软件是受《中华人民共和国著作权法》
// | 保护的专有财产,完整版权归小蚂蚁云团队所有
// | 2. 权利范围:所有知识产权(商标、专利、著作权、
// | 商业秘密等)均归小蚂蚁云团队所有,受法律保护
// | 3. 授权性质:用户仅可获得有限的、非独占的使用许可
// | 4. 禁止行为:未经书面授权,禁止对本软件本身
// | 申请任何知识产权
// | 5. 权利保留:未在本协议中明示授予的一切权利
// | 均由小蚂蚁云团队保留
// +======================================================================
// | 【授权状态说明】
// | 1. 未授权状态:用户未购买有效的商业许可证
// | 2. 商业授权:已购买正式商业许可证的用户
// +======================================================================
// | 【绝对禁止条款】
// | 无论授权状态如何,任何用户均严格禁止以下行为:
// | 1. 逆向工程:对软件进行反编译、反汇编、技术破解或任何形式的逆向分析
// | 2. 版权破坏:移除、修改、遮挡软件中的版权标识、许可控制或技术保护措施
// | 3. 非法传播:复制、分发、出租、出借、转售、再许可本软件或其许可权利
// | 4. 竞争性使用:利用本软件的代码、架构、设计开发直接竞争的产品或服务
// | 5. 侵权使用:将软件用于侵犯第三方知识产权或其他合法权益的用途
// +======================================================================
// | 【商业授权许可范围】
// | 购买有效商业许可证的用户,在遵守上述禁止条款的前提下,获得:
// | 1. 使用权利:在许可证约定的项目数量、开发者数量范围内使用本软件
// | 2. 技术支持:商业授权用户可以获取本地开发部署的技术支持服务
// | 3. 版本更新:在许可证有效期内获取软件更新、安全补丁和功能增强
// | 4. 商业部署:将基于本软件开发的应用程序用于商业运营和生产环境
// +======================================================================
// | 【许可证获取】
// | 1. 获取途径:仅通过小蚂蚁云官方渠道购买(官网、授权代理商),购买记录凭证
// | 2. 转让限制:许可证与购买主体绑定,未经书面同意不得转让
// | 3. 终止情形:许可证到期、违反协议、破产清算等情况下自动终止
// +======================================================================
// | 【合法使用要求】
// | 1. 仅限用于合法合规的软件研发
// | 2. 严禁用于任何违法、侵权或恶意行为
// | 3. 严禁违反我国法律法规和政策
// +======================================================================
// | 【免责声明】
// | 因使用本框架导致的以下情况,我们概不负责:
// | 1. 项目研发中的意外、疏忽、违约等风险
// | 2. 侵犯第三方知识产权或其他合法权益
// | 3. 任何直接、间接、附带或衍生一切损失
// | 4. 用户违规违法行为的一切法律后果
// +======================================================================
// | 【协议重要提示】
// | 1. 本声明是《小蚂蚁云最终用户许可协议》的摘要版本
// | 2. 下载、安装或使用本软件即表示您接受全部协议条款
// | 3. 我们保留单方面修改本声明的权利,修改后的版本在官网公布即生效
// | 4. 用户有义务定期查看最新版本,继续使用视为接受修改
// +======================================================================
// | 【侵权举报与合规】
// | 1. 知识产权侵权举报:请联系官方客服
// | 2. 许可证合规咨询:xiaomayicloud@163.com
// | 3. 违规使用将终止授权并追究责任
// | 4. 无条件配合司法机关调查取证
// | 5. 保留一切法律追诉权利
// | 6. 对有效举报和合规建议,我们将给予感谢和奖励
// +======================================================================
package com.xiaomayi.qrcode.utils;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.xiaomayi.core.utils.StringUtils;
import com.xiaomayi.qrcode.dto.QrCodeParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 谷歌二维码工具类
* </p>
* 说明: 二维码是一种编码形式, 将内容encode,得到二维码;, 将内容decode,得到数据内容。
*
* @author 小蚂蚁云团队
* @since 2024-05-31
*/
@Slf4j
@SuppressWarnings("unused")
public class QRCodeUtil {
/**
* 嵌入的图片(被程序处理后)的高度(px)
* <p>
* 注:内嵌图片不应占据遮挡二维码太多,否者可能导致生成的二维码无法识别。 建议内嵌图片的尺寸设置为二维码尺寸的1/5 或 1/4
*/
private static int embeddedImgDefaultWidth;
/**
* 嵌入的图片(被程序处理后)的高度(px)
*/
private static int embeddedImgDefaultHeight;
/**
* 嵌入的图片与二维码图片之间的框的宽度(px)
*/
private static int frameWidth;
/**
* 嵌入的图片与二维码图片之间的框的颜色.
*/
private static int frameWidthColor;
/**
* 二维码 码的颜色
*/
private static int qrCodeColor;
/**
* 二维码 背景的颜色
*/
private static int qrCodeBackgroundColor;
/**
* 图片缩小后,对那些"空"下的区域补色
*/
private static int fillColor;
/**
* 二维码写码器
*/
private static final MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
/*
* 初始化基本参数
*/
static {
QRCodeUtil.embeddedImgDefaultWidth = 80;
QRCodeUtil.embeddedImgDefaultHeight = 80;
QRCodeUtil.frameWidth = 0;
QRCodeUtil.frameWidthColor = Color.BLUE.getRGB();
QRCodeUtil.qrCodeColor = Color.BLACK.getRGB();
QRCodeUtil.qrCodeBackgroundColor = Color.WHITE.getRGB();
QRCodeUtil.fillColor = Color.RED.getRGB();
}
/**
* 设置修改基本参数的参数值
*
* @param embeddedImgDefaultWidth 嵌入的图片(被程序处理后)的高度(px)
* @param embeddedImgDefaultHeight 嵌入的图片(被程序处理后)的高度(px)
* @param frameWidth 嵌入的图片与二维码图片之间的框的宽度(px)
* @param frameWidthColor 嵌入的图片与二维码图片之间的框的颜色
* @param qrCodeColor 二维码 码的颜色
* @param qrCodeBackgroundColor 二维码 背景的颜色
* @param fillColor 图片缩小后,对那些"空"下的区域补色
*/
public static void modifyBasicParamsValues(Integer embeddedImgDefaultWidth, Integer embeddedImgDefaultHeight,
Integer frameWidth, Color frameWidthColor, Color qrCodeColor, Color qrCodeBackgroundColor,
Color fillColor) {
if (embeddedImgDefaultWidth != null) {
QRCodeUtil.embeddedImgDefaultWidth = embeddedImgDefaultWidth;
}
if (embeddedImgDefaultHeight != null) {
QRCodeUtil.embeddedImgDefaultHeight = embeddedImgDefaultHeight;
}
if (frameWidth != null) {
QRCodeUtil.frameWidth = frameWidth;
}
if (frameWidthColor != null) {
QRCodeUtil.frameWidthColor = frameWidthColor.getRGB();
}
if (qrCodeColor != null) {
QRCodeUtil.qrCodeColor = qrCodeColor.getRGB();
}
if (qrCodeBackgroundColor != null) {
QRCodeUtil.qrCodeBackgroundColor = qrCodeBackgroundColor.getRGB();
}
if (fillColor != null) {
QRCodeUtil.fillColor = fillColor.getRGB();
}
}
/**
* 生成二维码(到destImagePath指向的File) --- 方式一
*
* @param content 二维码的内容
* @param width 二维码的宽度(px)
* @param height 二维码的高度(px)
* @param destImagePath 生成二维码图片的地址
* @return 生成的二维码文件path
* @throws IOException IOException
* @throws WriterException WriterException
*/
public static String encodeQrCode(String content, int width, int height, String destImagePath)
throws IOException, WriterException {
File dest = getFile(destImagePath);
// 图像类型
String format = "jpg";
Map<EncodeHintType, Object> hints = new HashMap<>(4);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
try (FileOutputStream output = new FileOutputStream(dest)) {
// 生成矩阵
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
// 输出图像
MatrixToImageWriter.writeToStream(bitMatrix, format, output);
}
return destImagePath;
}
/**
* 生成二维码(到destImagePath指向的File) --- 方式二
*
* @param content 二维码的内容
* @param width 二维码的宽度(px)
* @param height 二维码的高度(px)
* @param destImagePath 生成二维码图片的地址
* @return 生成的二维码文件path
* @throws IOException IOException
* @throws WriterException WriterException
*/
public static String encodeQrCodeAnotherWay(String content, int width, int height, String destImagePath)
throws IOException, WriterException {
boolean result = ImageIO.write(genBufferedImage(content, width, height), "jpg", getFile(destImagePath));
log.info(" generate Qr code file {} {}", destImagePath, result ? "success" : "fail");
return destImagePath;
}
/**
* 生成带图片的二维码(到destImagePath指向的File)
*
* @param content 二维码的内容
* @param width 二维码的宽度(px)
* @param height 二维码的高度(px)
* @param embeddedImgPath 被镶嵌的图片的地址
* @param destImagePath 生成二维码图片的地址
* @return 生成的二维码文件path
* @throws IOException IOException
* @throws WriterException WriterException
*/
public static String encodeQrCodeWithEmbeddedImg(String content, int width, int height, String embeddedImgPath,
String destImagePath) throws IOException, WriterException {
boolean result = ImageIO.write(genBufferedImageWithEmbeddedImg(content, width, height, embeddedImgPath), "jpg",
getFile(destImagePath));
log.info(" generate Qr code file {} {}", destImagePath, result ? "success" : "fail");
return destImagePath;
}
/**
* 生成带图片的二维码(到outputStream流)
*
* @param content 二维码的内容
* @param width 二维码的宽度(px)
* @param height 二维码的高度(px)
* @param embeddedImgPath 被镶嵌的图片的地址 注:被镶嵌的图片,如果尺寸
* @throws IOException IOException
* @throws WriterException WriterException
*/
public static void encodeQrCodeWithEmbeddedImg(String content, int width, int height, String embeddedImgPath,
OutputStream outputStream) throws IOException, WriterException {
boolean result = ImageIO.write(genBufferedImageWithEmbeddedImg(content, width, height, embeddedImgPath), "jpg",
outputStream);
log.info(" generate Qr code file to OutputStream {}", result ? "success" : "fail");
}
/**
* 生成上方带内嵌图片、带文字的二维码
* <p>
* 注:若 嵌入的图片的参数值为null 或者为 空的字符,那么 只生成带文字的二维码
*
* @param codeParam 参数模型
* @return 生成的二维码文件path
* @throws IOException IOException
* @throws WriterException WriterException
*/
public static String encodeQrCodeWithEmbeddedImgAndFonts(QrCodeParam codeParam) throws IOException, WriterException {
// 首先生成二维码图片
BufferedImage qrImage;
String embeddedImgFilePath = codeParam.getEmbeddedImgFilePath();
String qrCodeContent = codeParam.getQrCodeContent();
Integer qrCodeWidth = codeParam.getQrCodeWidth();
Integer qrCodeHeight = codeParam.getQrCodeHeight();
if (StringUtils.isEmpty(embeddedImgFilePath)) {
qrImage = genBufferedImage(qrCodeContent, qrCodeWidth, qrCodeHeight);
} else {
qrImage = genBufferedImageWithEmbeddedImg(qrCodeContent, qrCodeWidth, qrCodeHeight, embeddedImgFilePath);
}
int qrCodeImageWidth = qrImage.getWidth();
int qrCodeImageHeight = qrImage.getHeight();
String[] splitStrLines;
splitStrLines = splitStrLines(codeParam.getWordContent(), qrCodeImageWidth / codeParam.getWordSize());
// 文字图片的高度 = 文字行数 * 每行高度(文字高度) + 10px;
// 为了防止 文字图片 下面部分显示不全, 特意在这里额外加10像素的高度。
int fontsImageHeight = splitStrLines.length * codeParam.getWordSize() + 10;
// 创建顶部文字的图片
BufferedImage imageWithFontsBufferedImage = new BufferedImage(qrCodeImageWidth, fontsImageHeight,
BufferedImage.TYPE_4BYTE_ABGR);
Graphics fontsImageGraphics = imageWithFontsBufferedImage.getGraphics();
fontsImageGraphics.fillRect(0, 0, qrCodeImageWidth, fontsImageHeight);
fontsImageGraphics.setColor(Color.black);
fontsImageGraphics.setFont(new Font("微软雅黑", Font.PLAIN, codeParam.getWordSize()));
// 文字长度大于一行的长度,进行分行
// 每行限制的文字个数
if (codeParam.getWordContent().length() > qrCodeImageWidth / codeParam.getWordSize()) {
for (int i = 0; i < splitStrLines.length; i++) {
fontsImageGraphics.drawString(splitStrLines[i], 0, codeParam.getWordSize() * (i + 1));
}
} else {
fontsImageGraphics.drawString(codeParam.getWordContent(),
// 总长度减去字长度除以2为向右偏移长度
((qrCodeImageWidth / codeParam.getWordSize() - codeParam.getWordContent().length()) / 2)
* codeParam.getWordSize(),
codeParam.getWordSize());
}
// 从图片中读取RGB
int[] imageArrayFonts = new int[qrCodeImageWidth * fontsImageHeight];
imageArrayFonts = imageWithFontsBufferedImage.getRGB(0, 0, qrCodeImageWidth, fontsImageHeight, imageArrayFonts,
0, qrCodeImageWidth);
int[] imageArrayQr = new int[qrCodeImageWidth * qrCodeImageHeight];
imageArrayQr = qrImage.getRGB(0, 0, qrCodeImageWidth, qrCodeImageHeight, imageArrayQr, 0, qrCodeImageWidth);
// 生成新图片(在setsetRGB时,通过控制图像的startX与startY, 可达到不同的拼接效果)
BufferedImage newBufferedImage = new BufferedImage(qrCodeImageWidth, qrCodeImageHeight + fontsImageHeight,
BufferedImage.TYPE_INT_RGB);
// 图片在上, 文字在下
// 设置上半部分的RGB
newBufferedImage.setRGB(0, 0, qrCodeImageWidth, qrCodeImageHeight, imageArrayQr, 0, qrCodeImageWidth);
// 设置下半部分的RGB
newBufferedImage.setRGB(0, qrCodeImageHeight, qrCodeImageWidth, fontsImageHeight, imageArrayFonts, 0,
qrCodeImageWidth);
// 文字在上,图片在下
// // 设置上半部分的RGB
/// newBufferedImage.setRGB(0, 0, qrCodeImageWidth, fontsImageHeight,
// imageArrayFonts, 0, qrCodeImageWidth);
// // 设置下半部分的RGB
/// newBufferedImage.setRGB(0, fontsImageHeight, qrCodeImageWidth,
// qrCodeImageHeight, imageArrayQr, 0, qrCodeImageWidth);
String qrCodeFileOutputPath = codeParam.getQrCodeFileOutputPath();
File outFile = getFile(qrCodeFileOutputPath);
boolean result = ImageIO.write(newBufferedImage, "jpg", outFile);
log.info(" generate Qr code file {} {}", qrCodeFileOutputPath, result ? "success" : "fail");
return qrCodeFileOutputPath;
}
/**
* 识别二维码内容信息
*
* @param file 二维码图片文件
* @return 二维码内容
* @throws NotFoundException NotFoundException
* @throws IOException IOException
*/
public static String decodeQrCode(File file) throws NotFoundException, IOException {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
String data = decodeQrCode(image);
log.info(" Qr code from [{}] data is -> {}", file.getAbsolutePath(), data);
return data;
}
/**
* 识别二维码内容信息
*
* @param is 二维码图片文件流
* @return 二维码内容
* @throws NotFoundException NotFoundException
* @throws IOException IOException
*/
public static String decodeQrCode(InputStream is) throws NotFoundException, IOException {
BufferedImage image;
image = ImageIO.read(is);
if (image == null) {
return null;
}
String data = decodeQrCode(image);
log.info(" Qr code from InputStream data is -> {}", data);
return data;
}
/// --------------------------------------------------------以下为辅助方法、辅助类
/**
* 获取文件(顺带创建文件夹,如果需要的话)
*
* @param filePath 文件path
* @return 文件对象
*/
private static File getFile(String filePath) {
File file = new File(filePath);
if (!file.getParentFile().exists()) {
boolean result = file.getParentFile().mkdirs();
log.info(" create directory {} {}", file.getParent(), result);
}
return file;
}
/**
* 识别二维码内容信息
*
* @param image 二维码图片信息BufferedImage
* @return 二维码内容
* @throws NotFoundException NotFoundException
*/
private static String decodeQrCode(BufferedImage image) throws NotFoundException {
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
HashMap<DecodeHintType, Object> hints = new HashMap<>(4);
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
/**
* 生成二维码图片信息BufferedImage
* <p>
* 注:BufferedImage即为二维码图片的数据容器,可以利用其进一步生成二维码图片
*
* @param content 二维码的数据内容
* @param width 二维码的宽(px)
* @param height 二维码的高(px)
* @return 二维码图片信息BufferedImage
* @throws WriterException WriterException
*/
private static BufferedImage genBufferedImage(String content, int width, int height) throws WriterException {
Map<EncodeHintType, Object> hints = getHints();
// 生成一个矩阵(即:生成一个二维数组)
BitMatrix matrix;
matrix = multiFormatWriter.encode(content, BarcodeFormat.DATA_MATRIX, width, height, hints);
int[] pixels = new int[width * height];
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
// 控制二维码颜色, 前面那个为 二维码的颜色, 后面那个为底色
pixels[y * width + x] = matrix.get(x, y) ? qrCodeColor : qrCodeBackgroundColor;
}
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 生成(带内嵌图片的)二维码图片信息BufferedImage
* <p>
* 注:BufferedImage即为二维码图片的数据容器,可以利用其进一步生成二维码图片
*
* @param content 二维码的数据内容
* @param width 二维码的宽(px)
* @param height 二维码的高(px)
* @param embeddedImagePath 二维码中要嵌套的图片path
* @return 二维码图片信息BufferedImage
* @throws IOException IOException
* @throws WriterException WriterException
*/
private static BufferedImage genBufferedImageWithEmbeddedImg(String content, int width, int height,
String embeddedImagePath) throws WriterException, IOException {
// 读取要嵌套的图像,并将其压缩到指定的宽高
BufferedImage scaleImage = scale(embeddedImagePath, embeddedImgDefaultWidth, embeddedImgDefaultHeight, false);
int embeddedImgFinalWidth = scaleImage.getWidth();
int embeddedImgFinalHalfWidth = embeddedImgFinalWidth / 2;
int embeddedImgFinalHeight = scaleImage.getHeight();
int embeddedImgFinalHalfHeight = embeddedImgFinalHeight / 2;
int[][] srcPixels = new int[embeddedImgFinalWidth][embeddedImgFinalHeight];
for (int i = 0; i < embeddedImgFinalWidth; i++) {
for (int j = 0; j < embeddedImgFinalHeight; j++) {
srcPixels[i][j] = scaleImage.getRGB(i, j);
}
}
Map<EncodeHintType, Object> hint = getHints();
// 生成一个矩阵(即:生成一个二维数组)( 注:下面会在这个矩阵上绘图)
BitMatrix matrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint);
// 二维码矩阵转为一维像素数组
int qrCodeHalfWidth = matrix.getWidth() / 2;
int qrCodeHalfHeight = matrix.getHeight() / 2;
int[] pixels = new int[width * height];
// 是否处于【嵌入图片 与 二维码图片 之间的框】里
boolean atEmbeddedImgFrameArea;
// 是否处于【嵌入图片的位置】里
boolean atEmbeddedImgArea;
// 内嵌的图片(左下角) 在 二维码图片 中的坐标X
int embeddedImgCoordinatesX = qrCodeHalfWidth - embeddedImgFinalHalfWidth;
// 内嵌的图片(左下角) 在 二维码图片 中的坐标Y
int embeddedImgCoordinatesY = qrCodeHalfHeight - embeddedImgFinalHalfHeight;
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
atEmbeddedImgArea = x > qrCodeHalfWidth - embeddedImgFinalHalfWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight;
atEmbeddedImgFrameArea = (x > qrCodeHalfWidth - embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth - embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight + frameWidth)
||
(x > qrCodeHalfWidth + embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight + frameWidth)
||
(x > qrCodeHalfWidth - embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight - embeddedImgFinalHalfHeight + frameWidth)
||
(x > qrCodeHalfWidth - embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight + embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight + frameWidth);
// 在二维码矩阵数像素组里 嵌入 嵌入图片的像素数组
if (atEmbeddedImgArea) {
pixels[y * width + x] = srcPixels[x - embeddedImgCoordinatesX][y - embeddedImgCoordinatesY];
}
// 嵌入图片 与 二维码图片 之间的框(设置其颜色)
else if (atEmbeddedImgFrameArea) {
pixels[y * width + x] = frameWidthColor;
// 二维码图片区域
} else {
// 控制二维码颜色, 前面那个为 二维码的颜色, 后面那个为底色
pixels[y * width + x] = matrix.get(x, y) ? qrCodeColor : qrCodeBackgroundColor;
}
}
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 获取hint信息
*
* @return hint
*/
private static Map<EncodeHintType, Object> getHints() {
Map<EncodeHintType, Object> hint = new HashMap<>(8);
hint.put(EncodeHintType.CHARACTER_SET, "utf-8");
hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 二维码整体白框
hint.put(EncodeHintType.MARGIN, 1);
return hint;
}
/**
* 把传入的原始图像按高度(或宽度)进行缩小,生成符合要求的图标的BufferedImage信息
* <p>
* 注:若图片原来的大小比给定的宽高小,那么不会进行放大,而是仍然保持原来的大小。
*
* @param srcImageFile 源文件地址
* @param width (缩小后的)宽度
* @param height (缩小后的)高度
* @param autoFill 是否对按比例缩小后的图片进行填充,使其完全达到给定的宽高。
* <p>
* 注:图片按照最长边与对应的宽(或高)的比例缩小后,最长边缩小后的尺寸与给定的宽(或高)一致了,
* 但是其高(或宽),与缩小后的最短边不一定一样。此时,我们可以考虑使用给定的颜色对 其进行填充。如:
* 想要嵌入进去的图片的实际宽是80, 高是100; 但是这里指定缩小后的尺寸为 宽为10,高为10。
* 由下面的逻辑可知:缩小比例为0.1, 缩小后,图片的宽就变成了8,高就变
* 成了10,此时高是满足我们的要求的,但是宽差了2个像素。此时就可以将其进行填充了。
* <p>
* 注:建议为false。
* @return 图片的BufferedImage信息
* @throws IOException IOException
*/
private static BufferedImage scale(String srcImageFile, int width, int height, boolean autoFill) throws IOException {
// 缩小比例
double ratio;
File file = getFile(srcImageFile);
BufferedImage srcImage = ImageIO.read(file);
Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
// 如果原图片的高或者宽 大于 指定的高或宽时,(以最长的一边来计算缩放比例,并)进行缩放
if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) {
if (srcImage.getHeight() > srcImage.getWidth()) {
ratio = Integer.valueOf(height).doubleValue() / srcImage.getHeight();
} else {
ratio = Integer.valueOf(width).doubleValue() / srcImage.getWidth();
}
AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
destImage = op.filter(srcImage, null);
}
if (autoFill) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = image.createGraphics();
graphic.setColor(Color.red);
graphic.fillRect(0, 0, width, height);
if (width == destImage.getWidth(null)) {
graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, destImage.getWidth(null),
destImage.getHeight(null), Color.white, null);
} else {
graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, destImage.getWidth(null),
destImage.getHeight(null), Color.white, null);
}
graphic.dispose();
destImage = image;
}
return (BufferedImage) destImage;
}
/**
* 一行字符串拆分成多行
*/
private static String[] splitStrLines(String str, int len) {
int blocks = str.length() / len + 1;
String[] strArray = new String[blocks];
for (int i = 0; i < blocks; i++) {
if ((i + 1) * len > str.length()) {
strArray[i] = str.substring(i * len);
} else {
strArray[i] = str.substring(i * len, (i + 1) * len);
}
}
return strArray;
}
/**
* Writes a {@link BitMatrix} to {@link BufferedImage}, file or stream. Provided
* here instead of core since it depends on Java SE libraries.
*/
private static class MatrixToImageWriter {
private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();
private MatrixToImageWriter() {
}
/**
* Renders a {@link BitMatrix} as an image, where "false" bits are rendered as
* white, and "true" bits are rendered as black.
*/
private static BufferedImage toBufferedImage(BitMatrix matrix) {
return toBufferedImage(matrix, DEFAULT_CONFIG);
}
/**
* As {@link #toBufferedImage(BitMatrix)}, but allows customization of the
* output.
*/
private static BufferedImage toBufferedImage(BitMatrix matrix, MatrixToImageConfig config) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, config.getBufferedImageColorModel());
int onColor = config.getPixelOnColor();
int offColor = config.getPixelOffColor();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? onColor : offColor);
}
}
return image;
}
/**
* @deprecated use {@link #writeToPath(BitMatrix, String, Path)}
*/
@Deprecated
private static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
writeToPath(matrix, format, file.toPath());
}
/**
* Writes a {@link BitMatrix} to a file.
*
* @see #toBufferedImage(BitMatrix)
*/
private static void writeToPath(BitMatrix matrix, String format, Path file) throws IOException {
writeToPath(matrix, format, file, DEFAULT_CONFIG);
}
/**
* @deprecated use
* {@link #writeToPath(BitMatrix, String, Path, MatrixToImageConfig)}
*/
@Deprecated
private static void writeToFile(BitMatrix matrix, String format, File file, MatrixToImageConfig config)
throws IOException {
writeToPath(matrix, format, file.toPath(), config);
}
/**
* As {@link #writeToFile(BitMatrix, String, File)}, but allows customization of
* the output.
*/
@SuppressWarnings("all")
private static void writeToPath(BitMatrix matrix, String format, Path file, MatrixToImageConfig config)
throws IOException {
BufferedImage image = toBufferedImage(matrix, config);
if (!ImageIO.write(image, format, file.toFile())) {
throw new IOException("Could not write an image of format " + format + " to " + file);
}
}
/**
* Writes a {@link BitMatrix} to a stream.
*
* @see #toBufferedImage(BitMatrix)
*/
private static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException {
writeToStream(matrix, format, stream, DEFAULT_CONFIG);
}
/**
* As {@link #writeToStream(BitMatrix, String, OutputStream)}, but allows
* customization of the output.
*/
private static void writeToStream(BitMatrix matrix, String format, OutputStream stream,
MatrixToImageConfig config) throws IOException {
BufferedImage image = toBufferedImage(matrix, config);
if (!ImageIO.write(image, format, stream)) {
throw new IOException("Could not write an image of format 【" + format + "】");
}
}
/**
* 配置类封装
* <p>
* Encapsulates custom configuration used in methods of
* {@link MatrixToImageWriter}.
*/
private static class MatrixToImageConfig {
private static final int BLACK = Color.BLACK.getRGB();
private static final int WHITE = Color.WHITE.getRGB();
private final int onColor;
private final int offColor;
/**
* Creates a default config with on color {@link #BLACK} and off color
* {@link #WHITE}, generating normal black-on-white barcodes.
*/
private MatrixToImageConfig() {
this(BLACK, WHITE);
}
/**
* @param onColor pixel on color, specified as an ARGB value as an int
* @param offColor pixel off color, specified as an ARGB value as an int
*/
private MatrixToImageConfig(int onColor, int offColor) {
this.onColor = onColor;
this.offColor = offColor;
}
private int getPixelOnColor() {
return onColor;
}
private int getPixelOffColor() {
return offColor;
}
private int getBufferedImageColorModel() {
// Use faster BINARY if colors match default
return onColor == BLACK && offColor == WHITE ? BufferedImage.TYPE_BYTE_BINARY
: BufferedImage.TYPE_INT_RGB;
}
}
}
public static void main(String[] args) {
// 演示案例一
try {
// QRCodeUtil.encodeQrCode("小蚂蚁云", 500, 500, "E:\\qrcode\\二维码(通过方式一生成).jpg");
// QRCodeUtil.encodeQrCodeAnotherWay("小蚂蚁云", 500, 500, "E:\\qrcode\\二维码(通过方式二生成).jpg");
// 生成带LOGO的二维码
ClassPathResource classPathResource = new ClassPathResource("微信 -0210.png");
String filePath = classPathResource.getFile().getPath();
QRCodeUtil.encodeQrCodeWithEmbeddedImg("小蚂蚁云", 500, 500, filePath,
"E:\\qrcode\\带图片的二维码.jpg");
// 生成带LOGO、文字的二维码
QrCodeParam para = QrCodeParam.builder()
.qrCodeFileOutputPath("E:\\qrcode\\带文字带图片的二维码.jpg")
.qrCodeContent("https://blog.csdn.net/weixin_45730091")
.qrCodeWidth(500)
.qrCodeHeight(500)
.embeddedImgFilePath(filePath)
.wordContent("小蚂蚁云")
.wordSize(20)
.build();
QRCodeUtil.encodeQrCodeWithEmbeddedImgAndFonts(para);
//
// // 生成带文字不带图片二维码
// QrCodeParam para2 = QrCodeParam.builder().qrCodeFileOutputPath("E:\\qrcode\\带文字不带图片的二维码.jpg")
// .qrCodeContent("二维码").qrCodeWidth(500).qrCodeHeight(500).wordContent("小蚂蚁云").wordSize(20).build();
// QRCodeUtil.encodeQrCodeWithEmbeddedImgAndFonts(para2);
//
// // 识别二维码
// String result = QRCodeUtil.decodeQrCode(new File("E:\\qrcode\\二维码(通过方式一生成).jpg"));
// System.out.println(result);
//
// String result2 = QRCodeUtil.decodeQrCode(new FileInputStream("E:\\qrcode\\带文字带图片的二维码.jpg"));
// System.out.println(result2);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB