在工作中遇到了要把两张图片合并成一张图片的情况,具体场景是用户头像➕二维码➕背景图
本着不重复造轮子的想法,搜索了hutool的utils包,还真让我找到了一个cn.hutool.core.img.Img,以下的方法都基于此类
<!-- 二维码工具 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
<!--hutool核心工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.5</version>
</dependency>
import cn.hutool.core.img.Img;
import cn.hutool.core.io.IoUtil;
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;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Base64;
import java.util.Hashtable;
/**
* @author miao
*/
@Slf4j
public class ImageUtilDemo {
//定义的字体样式
private final static String IMAGE_FONT = "宋体";
private final static String CHARSET = "utf-8";
/**
* 生成正方形背景图➕头像➕二维码信息
*
* @param background 背景图
* @param avatar 头像
* @param content 二维码内容信息
* @param text1 文字水印1
* @param text2 文字水印2
* @return InputStream
* @throws IOException 异常
*/
public static InputStream getInputStreamById(String background, String avatar,
String content, String text1, String text2) throws IOException {
//背景图和头像的,可以from多种格式,比如InputStream、File、Path等等
Img backgroundImg = Img.from(new URL(background));
Img avatarImg = Img.from(new URL(avatar));
//背景图缩放大小,因为我们要正方形展示背景图,所以选择最小的边长缩放
int backgroundScale =
Math.min(backgroundImg.getImg().getHeight(null), backgroundImg.getImg().getWidth(null));
backgroundImg.scale(backgroundScale, backgroundScale);
//二维码
BufferedImage contentImage = createImage(content);
Img qr = Img.from(contentImage);
//二维码缩放大小
int qrScale = backgroundScale * 3 / 4;
qr.scale(qrScale, qrScale);
//用户头像缩放大小,方便嵌套二维码中
int avatarScale = qrScale / 4;
avatarImg.scale(avatarScale, avatarScale);
//文字大小
Font f = new Font(IMAGE_FONT, Font.PLAIN, 20);
//文字上下偏移量
int y = (backgroundScale + qrScale) / 4;
//背景+二维码+头像+文字
backgroundImg
.pressImage(qr.getImg(), 0, 0, 1)
.pressImage(avatarImg.getImg(), 0, 0, 1)
//因为文字是底部偏移的,所以我微调了一下y轴
.pressText(text1, Color.white, f, 0, -y + 20, 1)
.pressText(text2, Color.white, f, 0, y + 20, 1);
//创建一个ByteArrayOutputStream
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write((RenderedImage) backgroundImg.getImg(), "PNG", os);
//ByteArrayOutputStream转成InputStream
return new ByteArrayInputStream(os.toByteArray());
}
/**
* 内容生成生成二维码
*
* @param content 扫描成功的内容
* @return 二维码图片
*/
private static BufferedImage createImage(String content) {
//等同于hashmap,hashtable是线程安全的
Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
//高容错率二维码
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix;
try {
bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, 250, 250, hints);
} catch (WriterException e) {
log.info("生成二维码信息异常,请稍后重试");
throw new RuntimeException("The generated QR code information is abnormal, please try again later");
}
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
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);
}
}
//直接返回生成的二维码
return image;
}
public static void main(String[] args) throws Exception {
InputStream in = getInputStreamById(
"https://cdn.nlark.com/yuque/0/2023/jpeg/437634/1678363983015-a1704c08-722b-4d5a-856f-95e80de27c32.jpeg",
"https://cdn.nlark.com/yuque/0/2023/png/437634/1678364047500-2e92eed3-0e12-42b4-8ae6-cdd65cd8e211.png?x-oss-process=image%2Fresize%2Cw_350%2Climit_0",
"两张图片合并且附带二维码和文字水印测试",
"我真有起床气", "不定时更新文章");
String uriPrefix = "data:image/jpeg;base64,";
String str = uriPrefix + imageToBase64(in);
System.out.println(str);
}
/**
* base64展示,可以直接在线显示合并内容
* 网址:https://tool.chinaz.com/tools/imgtobase
*
* @param in InputStream
* @return String
*/
public static String imageToBase64(InputStream in) {
try {
byte[] data = IoUtil.readBytes(in);
return Base64.getEncoder().encodeToString(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
关于具体缩放比例和偏移量,同学们可以通过自己的业务来制定,还有生成Inputstream无论是保存数据库还是上传到服务器都很方便!!
那我们就下次文章再见吧