满填充透明背景二维码生成
前几天项目上线的时候发现一个问题:通过Hutool工具包生成的二维码在内容较少时无法填满(Margin 已设置为 0)给定大小的图片。因此导致前端在显示二维码时样式异常。


从图片中我们可以看到,相同大小的图片,留白内容是不一样的。其中上半部分的图片是一个短字符串,下半部分的图片是一个长的字符串。因此基于Hutool包进行了裁边和缩放。代码如下:
Maven配置
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version>
</dependency>
<dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.3.3</version>
</dependency>
QrCodeConfig.java
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.awt.*;
import java.util.HashMap;
import java.util.Map;/*** 二维吗配置信息*/
@Getter
@Setter
@ToString
public class QrCodeConfig {/*** 塞入二维码的信息*/private String msg;/*** 生成二维码的宽*/private Integer w;/*** 生成二维码的高*/private Integer h;/*** 生成二维码的颜色*/private MatrixToImageConfig matrixToImageConfig;private Map<EncodeHintType, Object> hints;@ToStringpublic static class QrCodeConfigBuilder {/*** The message to put into QrCode*/private String msg;/*** qrcode image width*/private Integer w;/*** qrcode image height*/private Integer h;/*** qrcode message's code, default UTF-8*/private String code;/*** 0 - 4*/private Integer padding;/*** error level, default H*/private ErrorCorrectionLevel errorCorrection;public String getMsg() {return msg;}public QrCodeConfigBuilder setMsg(String msg) {this.msg = msg;return this;}public Integer getW() {return w == null ? (h == null ? 200 : h) : w;}public QrCodeConfigBuilder setW(Integer w) {if (w != null && w < 0) {throw new IllegalArgumentException("???????????0");}this.w = w;return this;}public Integer getH() {if (w != null && w < 0) {throw new IllegalArgumentException("???????????0");}return h == null ? (w == null ? 200 : w) : h;}public QrCodeConfigBuilder setH(Integer h) {this.h = h;return this;}public String getCode() {return code == null ? "UTF-8" : code;}public QrCodeConfigBuilder setCode(String code) {this.code = code;return this;}public Integer getPadding() {if (padding == null) {return 1;}if (padding < 0) {return 0;}if (padding > 4) {return 4;}return padding;}public QrCodeConfigBuilder setPadding(Integer padding) {this.padding = padding;return this;}public ErrorCorrectionLevel getErrorCorrection() {return errorCorrection == null ? ErrorCorrectionLevel.H : errorCorrection;}public QrCodeConfigBuilder setErrorCorrection(ErrorCorrectionLevel errorCorrection) {this.errorCorrection = errorCorrection;return this;}private void validate() {if (msg == null || msg.length() == 0) {throw new IllegalArgumentException("????????????!");}}private QrCodeConfig create() {this.validate();QrCodeConfig qrCodeConfig = new QrCodeConfig();qrCodeConfig.setMsg(getMsg());qrCodeConfig.setH(getH());qrCodeConfig.setW(getW());Map<EncodeHintType, Object> hints = new HashMap<>(3);hints.put(EncodeHintType.ERROR_CORRECTION, this.getErrorCorrection());hints.put(EncodeHintType.CHARACTER_SET, this.getCode());hints.put(EncodeHintType.MARGIN, this.getPadding());qrCodeConfig.setHints(hints);qrCodeConfig.setMatrixToImageConfig(new MatrixToImageConfig(new Color(0, 0, 0, 255).getRGB(),new Color(0, 0, 0, 0).getRGB()));return qrCodeConfig;}/*** create qrcodeConfig** @return 返回构造的 QrCodeConfig 对象*/public QrCodeConfig build() {return create();}}
}
MatrixToImageUtil.java
import com.google.zxing.common.BitMatrix;
import java.awt.*;
import java.awt.image.BufferedImage;public class MatrixToImageUtil {public static BufferedImage toBufferedImage(QrCodeConfig qrCodeConfig, BitMatrix bitMatrix) {int qrCodeWidth = bitMatrix.getWidth();int qrCodeHeight = bitMatrix.getHeight();BufferedImage qrCode = new BufferedImage(qrCodeWidth, qrCodeHeight, BufferedImage.TYPE_INT_ARGB);int onColor = qrCodeConfig.getMatrixToImageConfig().getPixelOnColor();int offColor = qrCodeConfig.getMatrixToImageConfig().getPixelOffColor();for (int x = 0; x < qrCodeWidth; x++) {for (int y = 0; y < qrCodeHeight; y++) {boolean pixelOn = bitMatrix.get(x, y);int pixelColor = pixelOn ? onColor : offColor;// 设置透明度int alpha = pixelOn ? 255 : 0;Color color = new Color(pixelColor, true);Color colorWithAlpha = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);qrCode.setRGB(x, y, colorWithAlpha.getRGB());}}// 缩放二维码图片int realQrCodeWidth = qrCodeConfig.getW();int realQrCodeHeight = qrCodeConfig.getH();if (qrCodeWidth != realQrCodeWidth || qrCodeHeight != realQrCodeHeight) {BufferedImage tmp = new BufferedImage(realQrCodeWidth, realQrCodeHeight, BufferedImage.TYPE_INT_ARGB);tmp.getGraphics().drawImage(qrCode.getScaledInstance(realQrCodeWidth, realQrCodeHeight, Image.SCALE_SMOOTH),0, 0, null);qrCode = tmp;}return qrCode;}
}
QrCodeGenWrapper.java
import cn.hutool.core.img.ImgUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;/*** 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题,原参考 <a href="https://my.oschina.net/u/566591/blog/872770">...</a>*/
@Slf4j
public class QrCodeGenWrapper {private static final Logger logger = LoggerFactory.getLogger(QrCodeGenWrapper.class);private static final int QUIET_ZONE_SIZE = 4;/*** 构造 二维吗配置信息* @return QrCodeConfig*/public static QrCodeConfig.QrCodeConfigBuilder createQrCodeConfig() {return new QrCodeConfig.QrCodeConfigBuilder();}/*** 生成base64格式二维吗* @param content 二维吗内容* @param width 宽度 默认 300* @param height 高度 默认 300* @param imageType 图片类型默认 png* @return 返回base64格式二维码信息*/public static String generateAsBase64(String content, Integer width, Integer height, String imageType){QrCodeConfig qrConfig = QrCodeGenWrapper.createQrCodeConfig().setMsg(content).setH(width == null? 300 : width).setW(height == null? 300 : height).setPadding(0).setErrorCorrection(ErrorCorrectionLevel.L).build();try {return ImgUtil.toBase64DataUri(asBufferedImage(qrConfig), StringUtils.isBlank(imageType)? "png" : imageType);} catch (Exception e) {log.error("QrCodeGenWrapper.generateAsBase64 error", e);throw new RuntimeException("QrCodeGenWrapper.generateAsBase64 生成二维码异常");}}public static BufferedImage asBufferedImage(QrCodeConfig qrCodeConfig) throws WriterException, IOException {BitMatrix bitMatrix = encode(qrCodeConfig);return MatrixToImageUtil.toBufferedImage(qrCodeConfig, bitMatrix);}/*** 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题* <p/>* 源码参考 {@link com.google.zxing.qrcode.QRCodeWriter#encode(String, BarcodeFormat, int, int, Map)}*/private static BitMatrix encode(QrCodeConfig qrCodeConfig) throws WriterException {ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;int quietZone = 1;if (qrCodeConfig.getHints() != null) {if (qrCodeConfig.getHints().containsKey(EncodeHintType.ERROR_CORRECTION)) {errorCorrectionLevel = ErrorCorrectionLevel.valueOf(qrCodeConfig.getHints().get(EncodeHintType.ERROR_CORRECTION).toString());}if (qrCodeConfig.getHints().containsKey(EncodeHintType.MARGIN)) {quietZone = Integer.parseInt(qrCodeConfig.getHints().get(EncodeHintType.MARGIN).toString());}if (quietZone > QUIET_ZONE_SIZE) {quietZone = QUIET_ZONE_SIZE;} else if (quietZone < 0) {quietZone = 0;}}QRCode code = Encoder.encode(qrCodeConfig.getMsg(), errorCorrectionLevel, qrCodeConfig.getHints());return renderResult(code, qrCodeConfig.getW(), qrCodeConfig.getH(), quietZone);}/*** 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题* <p/>* 源码参考** @param code {@link QRCode}* @param width 高* @param height 宽* @param quietZone 取值 [0, 4]* @return {@link BitMatrix}*/private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {ByteMatrix input = code.getMatrix();if (input == null) {throw new IllegalStateException();}// xxx 二维码宽高相等, 即 qrWidth == qrHeightint inputWidth = input.getWidth();int inputHeight = input.getHeight();int qrWidth = inputWidth + (quietZone * 2);int qrHeight = inputHeight + (quietZone * 2);// 白边过多时, 缩放int minSize = Math.min(width, height);int scale = calculateScale(qrWidth, minSize);if (scale > 0) {if (logger.isDebugEnabled()) {logger.debug("qrCode scale enable! scale: {}, qrSize:{}, expectSize:{}x{}", scale, qrWidth, width, height);}int padding, tmpValue;// 计算边框留白padding = (minSize - qrWidth * scale) / QUIET_ZONE_SIZE * quietZone;tmpValue = qrWidth * scale + padding;if (width == height) {width = tmpValue;height = tmpValue;} else if (width > height) {width = width * tmpValue / height;height = tmpValue;} else {height = height * tmpValue / width;width = tmpValue;}}int outputWidth = Math.max(width, qrWidth);int outputHeight = Math.max(height, qrHeight);int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;int topPadding = (outputHeight - (inputHeight * multiple)) / 2;BitMatrix output = new BitMatrix(outputWidth, outputHeight);for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {// Write the contents of this row of the barcodefor (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {if (input.get(inputX, inputY) == 1) {output.setRegion(outputX, outputY, multiple, multiple);}}}return output;}/*** 如果留白超过15% , 则需要缩放* (15% 可以根据实际需要进行修改)** @param qrCodeSize 二维码大小* @param expectSize 期望输出大小* @return 返回缩放比例, <= 0 则表示不缩放, 否则指定缩放参数*/private static int calculateScale(int qrCodeSize, int expectSize) {if (qrCodeSize >= expectSize) {return 0;}int scale = expectSize / qrCodeSize;int abs = expectSize - scale * qrCodeSize;// 在这里配置超过多少留白,则进行缩放(这里已经把 0.15 改成 0 了)if (abs < 0) {return 0;}return scale;}
}
最终效果:


---------------------------------- 只能活一次的人生当然要比谁都炽热,浑浑噩噩谁也可以。 ---------------------------------
相关文章:
满填充透明背景二维码生成
前几天项目上线的时候发现一个问题:通过Hutool工具包生成的二维码在内容较少时无法填满(Margin 已设置为 0)给定大小的图片。因此导致前端在显示二维码时样式异常。 从图片中我们可以看到,相同大小的图片,留白内容是不一样的。其中上半部分…...
Python | Leetcode Python题解之第452题用最少数量的箭引爆气球
题目: 题解: class Solution:def findMinArrowShots(self, points: List[List[int]]) -> int:if not points:return 0points.sort(keylambda balloon: balloon[1])pos points[0][1]ans 1for balloon in points:if balloon[0] > pos:pos balloo…...
代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作删除二叉搜索树中的节点修剪二叉搜索树
代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作&&删除二叉搜索树中的节点&&修剪二叉搜索树 主要学习内容: 二叉搜索树的插入删除操作 701.二叉搜索树中的插入操作 701. 二叉搜索树中的插入操作 - 力扣(LeetCode&…...
使用Apifox创建接口文档,部署第一个简单的基于Vue+Axios的前端项目
前言 在当今软件开发的过程中,接口文档的创建至关重要,它不仅能够帮助开发人员更好地理解系统架构,还能确保前后端开发的有效协同。Apifox作为一款集API文档管理、接口调试、Mock数据模拟为一体的工具,能够大幅度提高开发效率。在…...
TCP的第三次握手没有回复,会出现哪些问题现象
从三次握手的一开始来讲,刚开始客户端和服务器都处于close状态 这里不能是2次握手的原因就在于,服务器端即女孩子,无法确认客户端即男孩子,是否已经收到了,我也愿意建立连接即我也爱你,这一条最终确认的信息…...
【工具】arxiv_latex_cleaner 去除latex注释
https://github.com/google-research/arxiv-latex-cleaner/issues/24 文章目录 1.修改编码2.如何安装2.1.打包2.2.安装 3.测试功能 注意:需要创建python3.9的环境 1.修改编码 官方提供的arxiv_latex_cleaner的编码格式是有问题的,见这里。这个有位朋友说…...
macOS开发环境配置与应用开发
一、macOS开发环境配置 1. 安装Xcode Xcode 是Apple官方开发环境工具,用于macOS、iOS、watchOS和tvOS应用开发。它集成了代码编辑、编译、调试、性能分析、界面设计等功能。 下载与安装: 打开 App Store,搜索“Xcode”。 点击安装ÿ…...
15分钟学 Python :编程工具 Idea 和 vscode 中配置 Python ( 补充 )
编程工具配置 Python 在 IDE 和 VSCode 中 在编程学习的过程中,选择合适的开发工具至关重要。本文将详细介绍在两种流行的IDE(IntelliJ IDEA 和 Visual Studio Code)中如何配置Python环境,帮助你更高效地进行Python开发。 一、编…...
MyBatis 如何实现延迟加载?深度探讨 MyBatis 的延迟加载:如何优化数据访问效率
在当今的应用程序开发中,尤其是与数据库交互时,性能成为了重中之重。频繁的数据库访问会导致响应时间变慢,甚至影响用户体验。为了优化数据访问,MyBatis 提供了延迟加载(Lazy Loading)的强大功能。本文将详…...
springboot系列--web相关知识探索三
一、前言 web相关知识探索二中研究了请求是如何映射到具体接口(方法)中的,本次文章主要研究请求中所带的参数是如何映射到接口参数中的,也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、…...
AI冲击下的编程职业未来:你缺的不是技术,而是跨学科思维!
随着AIGC技术(如ChatGPT、MidJourney、Claude等大语言模型)的不断进化,AI辅助编程工具迅速普及,程序员的工作方式正在经历前所未有的转型。代码自动补全、智能化代码生成等功能大幅提升了工作效率,但与此同时ÿ…...
是否是 2 的幂次方
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 如果存在一个整数 x 使得 n 2x ,则认为 n 是 2 的幂次方。 示例 1: 输入:n 1 输出:tr…...
音视频入门
一个视频,一秒内普遍大于等于25帧。 入门知识: 1.帧,一张画面就是一帧。一个视频就是由许许多多帧组成的。 帧率,单位时间内帧的数量。单位:帧/秒 或 fps。 分类:I帧,P帧,B帧 I…...
C++随心记 续一
C中的模板 在其它语言中如Java或者C#中可能叫做泛型,在C中为模板,泛型的限制通常比模板多。模板可以解决多次的代码重复问题,如以下场景 #include <iostream> #include <string>void print(int value) {std::cout << val…...
消息中间件:RabbitMQ
消息中间件:RabbitMQ 前言安装Window安装Linux安装 管理页面什么是RabbitMQ?入门基本概念简单队列工作队列(Work Queues)发布/订阅(Publish/Subscribe)临时队列 路由(Routing)主题&a…...
sql-labs:42~65
less42(单引号闭合、报错回显) login_useradmin login_password123 and if(11,sleep(2),1) # # 单引号闭合 login_useradmin login_password123and updatexml(1,concat(0x7e,database(),0x7e),1)# # 报错回显…...
KaTeX.js渲染数学公式
什么是KaTeX.js ? KaTeX 是一个集成速度快且功能丰富的数学公式渲染库,专为 Web 设计。它由 Khan Academy 开发,提供接近印刷品质的数学公式展示,同时保持与浏览器的高效互动性。KaTeX 特点包括快速渲染速度、高质量的输出、独立运行、跨平…...
算法训练营打卡Day19
目录 1.二叉搜索树的最近公共祖先 2.二叉树中的插入操作 3.删除二叉搜索树中的节点 题目1、二叉搜索树的最近公共祖先 力扣题目链接(opens new window) 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有…...
H.264编解码工具 - FFmpeg
一、简介 FFmpeg是一款用于处理多媒体数据的开源软件,可以完成音频、视频和多媒体流的编解码、转码、解码、录制、流媒体播放等功能。它提供了丰富的命令行工具和库函数,适用于各种平台和操作系统。 FFmpeg支持多种常见的音视频格式,包括MP3、WAV、FLAC、MP4、AVI、MKV等。它…...
60 序列到序列学习(seq2seq)_by《李沐:动手学深度学习v2》pytorch版
系列文章目录 文章目录 系列文章目录一、理论知识比喻机器翻译Seq2seq编码器-解码器细节训练衡量生成序列的好坏的BLEU(值越大越好)总结 二、代码编码器解码器损失函数训练预测预测序列的评估小结练习 一、理论知识 比喻 seq2seq就像RNN的转录工作一样,非常形象的比…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
