当前位置:网站首页>背景图和二维码合成
背景图和二维码合成
2022-07-24 22:12:00 【努力奋斗GO】
需求: 类似于做一个码牌, UI提供背景图模板, 后端将二维码合成到背景图模板中
背景图模板 最终效果图

开撸
package cc.sunni;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import org.springframework.core.io.ClassPathResource;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* 码牌: 将二维码合成到背景图片中
*
* @author 江黎
* @since 2022-07-21
*/
public class QrCodeCard {
/**
* 二维码的宽度
*/
private static final int QR_CODE_WIDTH = 640;
/**
* 二维码的高度
*/
private static final int QR_CODE_HEIGHT = 640;
/**
* title字体
*/
private static final Font TITLE_FONT = new Font("宋体", Font.BOLD, 30);
/**
* desc字体
*/
private static final Font DESC_FONT = new Font("宋体", Font.PLAIN, 25);
/**
* bottom字体
*/
private static final Font BOTTOM_FONT = new Font("宋体", Font.ITALIC, 24);
private static final String FORMAT_NAME = "PNG";
/**
* @param title 标题
* @param desc 描述
* @param content 二维码内容
* @param bottom 底部文字
* @param imgLogo 二维码中间图片地址
* @param backgroundImage 背景图片地址
* @param imageY 二维码在背景图片的Y轴位置, X轴是居中的
* @return 返回BufferedImage方便后续处理是生成图片还是生成base64字符串
*/
public static BufferedImage createQrCode(String title, String desc, String content, String bottom, String imgLogo, String backgroundImage, int imageY) throws IOException {
// 创建主模板图片
int maxWidth = getMaxWidth(title, desc, bottom);
int maxHeight = getMaxHeight();
BufferedImage image = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
if (StrUtil.isNotBlank(backgroundImage)) {
// 设置图片的背景色为透明, 需要合成到背景图片就设置为透明
image = graphics.getDeviceConfiguration().createCompatibleImage(maxWidth, maxHeight, Transparency.TRANSLUCENT);
} else {
// 设置图片的背景色为白色
graphics.setColor(Color.white);
}
graphics.fillRect(0, 0, maxWidth, maxHeight);
// 动态高度
int height = 0;
// *********************** title ***********************
if (StrUtil.isNotBlank(title)) {
graphics = image.createGraphics();
// 设置字体颜色 black黑 white白
graphics.setColor(Color.black);
// 设置字体
graphics.setFont(TITLE_FONT);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
// 居中 x开始的位置:(图片宽度-字体大小*字的个数)/2
int x = (maxWidth - (TITLE_FONT.getSize() * title.length())) / 2;
int strHeight = getStringHeight(graphics);
graphics.drawString(title, x, strHeight);
height += strHeight;
}
// ********************** desc **********************
if (StrUtil.isNotBlank(desc)) {
graphics = image.createGraphics();
// 设置字体颜色,先设置颜色,再填充内容
graphics.setColor(Color.black);
// 设置字体
graphics.setFont(DESC_FONT);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
// 获取字符高度
int strHeight = getStringHeight(graphics);
// 按行居中显示
String[] info = desc.split("\\$"); // 多行文字以 $ 分隔
for (int i = 0; i < info.length; i++) {
String s = info[i];
// x开始的位置:(图片宽度-字体大小*字的个数)/2
int strWidth = graphics.getFontMetrics().stringWidth(s);
// 总长度减去文字长度的一半 (居中显示)
int startX = (maxWidth - strWidth) / 2;
height += strHeight * (i + 1);
graphics.drawString(s, startX, height);
}
}
// ********************** 插入二维码图片 **********************
Graphics codePic = image.getGraphics();
BufferedImage codeImg;
QrConfig config = new QrConfig();
config.setMargin(2);
config.setWidth(QR_CODE_WIDTH);
config.setHeight(QR_CODE_HEIGHT);
if (StrUtil.isNotBlank(imgLogo)) {
config.setImg(imgLogo);
}
codeImg = QrCodeUtil.generate(content, config);
// 绘制二维码
codePic.drawImage(codeImg, (maxWidth - QR_CODE_WIDTH) / 2, height, QR_CODE_WIDTH, QR_CODE_HEIGHT, null);
codePic.dispose();
// ********************** bottom **********************
if (StrUtil.isNotBlank(bottom)) {
graphics = image.createGraphics();
// 设置字体颜色
graphics.setColor(Color.black);
// 设置字体
graphics.setFont(BOTTOM_FONT);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
// x开始的位置:(图片宽度-字体大小*字的个数)/2
int startX = (maxWidth - (BOTTOM_FONT.getSize() * bottom.length())) / 2;
// 获取字符高度
int footerStrHeight = getStringHeight(graphics);
height += QR_CODE_HEIGHT + footerStrHeight;
graphics.drawString(bottom, startX, height);
}
if (StrUtil.isNotBlank(backgroundImage)) {
// 加载背景模板
ClassPathResource resource = new ClassPathResource(backgroundImage);
InputStream inputStream = resource.getInputStream();
BufferedImage templateImage = ImageIO.read(inputStream);
Graphics graphicsTemplate = templateImage.getGraphics();
// 根据模板宽度 - 二维码宽度 进行居中算出X坐标
int widthX = (templateImage.getWidth() - image.getWidth()) / 2;
// 添加二维码
graphicsTemplate.drawImage(image, widthX, imageY, null);
graphicsTemplate.dispose();
return templateImage;
} else {
return image;
}
}
// 生成图片文件
public static void createImage(BufferedImage image, String fileLocation) {
if (image != null) {
try {
ImageIO.write(image, "png", new File(fileLocation));
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 获取图片base64数据
public static String base64ImageString(BufferedImage image) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();//io流
ImageIO.write(image, FORMAT_NAME, bos);//写入流中
byte[] bytes = bos.toByteArray();//转换成字节
BASE64Encoder encoder = new BASE64Encoder();
String jpgBase64 = encoder.encodeBuffer(bytes).trim();//转换成base64串
jpgBase64 = jpgBase64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n
return "data:image/jpg;base64," + jpgBase64;
}
// 字符串总宽度
private static int getStringLength(Graphics g, String str) {
char[] chars = str.toCharArray();
return g.getFontMetrics().charsWidth(chars, 0, str.length());
}
// 字符高度
private static int getStringHeight(Graphics g) {
return g.getFontMetrics().getHeight();
}
// 每一行字符的个数
private static int getRowStrNum(int strNum, int rowWidth, int strWidth) {
return (rowWidth * strNum) / strWidth;
}
// 字符行数
private static int getRows(int strWidth, int rowWidth) {
int rows;
if (strWidth % rowWidth > 0) {
rows = strWidth / rowWidth + 1;
} else {
rows = strWidth / rowWidth;
}
return rows;
}
// 计算二维码图片最大宽度
private static int getMaxWidth(String title, String desc, String bottom) {
int width = 0;
BufferedImage image = new BufferedImage(QR_CODE_WIDTH, QR_CODE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
if (StrUtil.isNotBlank(title)) {
width = Math.max(getStringLength(graphics, title), width);
}
if (StrUtil.isNotBlank(desc)) {
width = Math.max(getStringLength(graphics, desc), width);
}
if (StrUtil.isNotBlank(bottom)) {
width = Math.max(getStringLength(graphics, bottom), width);
}
return Math.max(QR_CODE_WIDTH, width);
}
// 计算二维码图片最大高度
private static int getMaxHeight() {
int height = QR_CODE_HEIGHT;
BufferedImage image = new BufferedImage(QR_CODE_WIDTH, QR_CODE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
graphics.setFont(TITLE_FONT);
height += getStringHeight(graphics);
graphics.setFont(DESC_FONT);
height += getStringHeight(graphics);
graphics.setFont(BOTTOM_FONT);
height += getStringHeight(graphics);
return height;
}
public static void main(String[] args) throws IOException {
String title = "码牌";
String desc = "知名互联网公司CEO";
String content = "https://www.baidu.com";
String logo = "templates/[email protected]";
String bottom = "国家认证";
String backgroundImage = "templates/announcer.png";
// BufferedImage bufferedImage = createQrCode(null, null, content, logo, null, backgroundImage, 290);
BufferedImage bufferedImage = createQrCode(title, desc, content, logo, bottom, backgroundImage, 290);
createImage(bufferedImage, "D:/test.png");
}
}
不带背景图 带背景图

附上下载的方法
@RestController
public class Controller {
@PostMapping("/downloadQrCode")
public void downloadQrCode(@RequestParam String content, @RequestParam(required = false) String title,
@RequestParam(required = false) String desc, @RequestParam(required = false) String bottom,
HttpServletResponse response) throws IOException {
BufferedImage targetBufferedImage = QrCodeCard.createQrCode(title, desc, content,
bottom, "templates/[email protected]", "templates/announcer.png", 290);
OutputStream os = response.getOutputStream();
response.setCharacterEncoding(Charsets.UTF_8.name());
response.setContentType("multipart/form-data");
response.setHeader("content-type", "image/png");
ImageIO.write(targetBufferedImage, "png", os);
}
}边栏推荐
- Local data enhancement method of icml2022 | graph neural network
- Random sampling and thinning of PCL point cloud processing randomsample (60)
- Visual studio input! No prompt
- Integrated swagger learning
- Database - Metadata databasemetadata beginner
- Gradle learning - getting started with gradle
- Get the solution to the slow running speed of Mengxin Xiaobai computer! ٩ ( ‘ ω‘ )و get! ٩ ( ‘ ω‘ )و
- 力扣 1184. 公交站间的距离
- 【数据库学习】Redis 解析器&&单线程&&模型
- [icml2022] climate change and machine learning: opportunities, challenges and considerations, 121 ppt
猜你喜欢

Get the solution to the slow running speed of Mengxin Xiaobai computer! ٩ ( ‘ ω‘ )و get! ٩ ( ‘ ω‘ )و

IndexTree

Database - Metadata databasemetadata beginner

物联网平台返回数据解析时遇到org.json.JSONException: No value for Value怎么办

Helm —— 强大的 Kubernetes 应用的包管理工具

一种兼容、更小、易用的WEB字体API

Multi task face attribute analysis based on deep learning (based on paddlepaddle)

C # use SQLite

Uniform sampling and thinning of PCL point cloud processing (61)

How to adjust the default output of vscode to the debugging console to the terminal and the problem of garbled code in both
随机推荐
单调栈结构
Dialogue with celebrities: where are the opportunities and challenges in the second half when brands gather at the shuzang track?
Is it safe to log in the securities account on the flush
Process / thread synchronization mechanism
Plane regularization of PCL point cloud processing (55)
PCL点云处理之直线点集投影规则化(五十六)
【ICML2022】气候变化与机器学习:机遇、挑战与考虑,121页ppt
Gradle learning - getting started with gradle
一文读懂Elephant Swap的LaaS方案的优势之处
阿里云SSL证书
PHP get thumbnails
头脑风暴之——利用reduce方法重构concat函数
[icml2022] climate change and machine learning: opportunities, challenges and considerations, 121 ppt
Baidu online disk +chrome plug-in
基于深度学习的多任务人脸属性分析(基于飞桨PaddlePaddle)
Database - Metadata databasemetadata beginner
一种兼容、更小、易用的WEB字体API
When texturebrush is created, it prompts that there is insufficient memory
Boundary extraction of PCL point cloud processing (58)
Website resources