Java,图片添加二维码和文字水印,合并为一张图

182 阅读2分钟

在工作中遇到了要把两张图片合并成一张图片的情况,具体场景是用户头像➕二维码➕背景图 本着不重复造轮子的想法,搜索了hutool的utils包,还真让我找到了一个cn.hutool.core.img.Img,以下的方法都基于此类

1. 引入依赖

        <!-- 二维码工具 -->
        <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>

2. 创建自己的ImageUtil



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;
    }
}

3. 结果

在这里插入图片描述 在这里插入图片描述

4. 写在最后的话

关于具体缩放比例和偏移量,同学们可以通过自己的业务来制定,还有生成Inputstream无论是保存数据库还是上传到服务器都很方便!! 那我们就下次文章再见吧👋