一个功能简单的图片工具类

功能说明&演示

1. 文字转换为图片

  • 代码示例(更多用法见重载方法说明)
    /**
     * 文字转换为图片 demo
     */
    public static void main(String[] args) throws Exception {
        BufferedImage bufferedImage = ImgUtil.strConvertToImg(
                Lists.newArrayList(
                        "用户:邓沙利文",
                        "性别:男",
                        "年龄:18岁18天",
                        "爱好:摆烂",
                        "座右铭:我是一只小小小小鸟~嗷!嗷!"
                ), new Font("幼圆", Font.PLAIN, 20),
                new Color(255, 0, 0));
        System.err.println(
                ImageIO.write(bufferedImage, "PNG", new File("E:\\360MoveData\\Users\\邓沙利文\\Desktop\\abc.png"))
        );
    }
    
  • 效果图(图片其实是透明背景的;因为我这里是编辑打开,所以显示的是白色)
    在这里插入图片描述

2. 生成指定大小透明背景的图片,并设置文字

  • 代码示例(更多用法见重载方法说明)
    BufferedImage bufferedImage = ImgUtil.generateImgWithStr(
            Lists.newArrayList(
                    "用户:邓沙利文",
                    "性别:男",
                    "年龄:18岁18天",
                    "爱好:摆烂",
                    "座右铭:我是一只小小小小鸟~嗷!嗷!"
            ),
            // 指定要生成的图片大小
            500, 500,
            // 指定字体及颜色(不指定的话, 默认: 微软雅黑  白色); 当字体大小<=0时,将自动推算字体大小,使其布满图片
            new Font("幼圆", Font.PLAIN, -1), new Color(255, 0, 0));
    System.err.println(
            ImageIO.write(bufferedImage, "PNG", new File("E:\\360MoveData\\Users\\邓沙利文\\Desktop\\abc.png"))
    );
    
  • 效果图(图片其实是透明背景的;因为我这里是编辑打开,所以显示的是白色)
    在这里插入图片描述

3. 添加水印

  • 代码示例(更多用法见重载方法说明)
    // 可设置字体颜色等,详见重载方法说明; 当字体大小<=0时,将自动推算字体大小,使其布满图片
    BufferedImage bufferedImage = ImgUtil.addWatermark(
            ImageIO.read(new File("E:\\360MoveData\\Users\\邓沙利文\\Desktop\\熊猫.jpg"))
            , Lists.newArrayList(
                    "用户:邓沙利文",
                    "性别:男",
                    "年龄:18岁18天",
                    "爱好:摆烂",
                    "座右铭:我是一只小小小小鸟~嗷!嗷!"
            ));
    System.err.println(
            ImageIO.write(bufferedImage, "PNG", new File("E:\\360MoveData\\Users\\邓沙利文\\Desktop\\abc.png"))
    );
    
  • 效果图(图片其实是透明背景的;因为我这里是编辑打开,所以显示的是白色)
    在这里插入图片描述

使用方式

方式一:直接引入common-ds依赖

common-ds:一个工具类开源包
common-spring:一个spring工具类开源包,集成了common-ds

引入以下依赖后,调用ImgUtil的静态方法即可

<dependency>
    <groupId>com.idea-aedi</groupId>
    <artifactId>common-ds</artifactId>
    <version>2100.5.0</version>
</dependency>

方式二:直接将工具类源码复制到项目中

ImgUtil工具类

import org.apache.commons.lang3.tuple.Triple;

import javax.annotation.Nullable;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * 图片工具类
 * <br/>
 * 需要相关依赖:
 * <br/>
 * <dependency>
 *     <groupId>org.apache.commons</groupId>
 *     <artifactId>commons-lang3</artifactId>
 *     <version>3.12.0</version>
 * </dependency>
 * 
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2100.4.6
 */
public class ImgUtil {
    
    /**
     * 中文&中文标点正则
     * <br />
     * [\u4e00-\u9fa5]是中文,其余的是中文标点符号
     */
    private static final Pattern CHINESE_PATTERN = Pattern.compile("[\u4e00-\u9fa5]|[\uFF01]|[\uFF0C-\uFF0E]|[\uFF1A-\uFF1B"
            + "]|[\uFF1F]|[\uFF08-\uFF09]|[\u3001-\u3002]|[\u3010-\u3011]|[\u201C-\u201D]|[\u2013-\u2014]|[\u2018"
            + "-\u2019]|[\u2026]|[\u3008-\u300F]|[\u3014-\u3015]");
    
    /** 行间距(px) */
    public static final int DEFAULT_LINE_SPACING = 5;
    
    /** 左下角 */
    public static final int POSITION_BOTTOM_LEFT = 1;
    
    /** 右下角 */
    public static final int POSITION_BOTTOM_RIGHT = 2;
    
    /** 左上角 */
    public static final int POSITION_TOP_LEFT = 3;
    
    /** 右上角 */
    public static final int POSITION_TOP_RIGHT = 4;
    
    /**
     * 生成带有文字的透明背景的图片 <br/> 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小
     *
     * @return 图片
     * @see ImgUtil#generateImgWithStr(List, int, int, Font, Color, int, int)
     */
    public static BufferedImage generateImgWithStr(List<String> contentLines, int imgWidth, int imgHeight) {
        return generateImgWithStr(contentLines, imgWidth, imgHeight, new Font("微软雅黑", Font.PLAIN, 0),
                new Color(255, 255, 255), DEFAULT_LINE_SPACING, POSITION_BOTTOM_LEFT);
    }
    
    /**
     * 生成带有文字的透明背景的图片 <br/> 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小
     *
     * @return 图片
     * @see ImgUtil#generateImgWithStr(List, int, int, Font, Color, int, int)
     */
    public static BufferedImage generateImgWithStr(List<String> contentLines, int imgWidth, int imgHeight,
                                                   Font font, Color fontColor) {
        return generateImgWithStr(contentLines, imgWidth, imgHeight, font, fontColor, DEFAULT_LINE_SPACING,
                POSITION_BOTTOM_LEFT);
    }
    
    /**
     * 生成带有文字的透明背景的图片 <br/> 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小
     *
     * @param contentLines
     *         文本
     * @param imgWidth
     *         图片宽
     * @param imgHeight
     *         图片高
     * @param font
     *         字
     * @param fontColor
     *         字体颜色
     * @param lineSpacing
     *         行间距(单位: 像素)
     * @param contentPosition
     *         文字位置 <br />
     *         {@link ImgUtil#POSITION_BOTTOM_LEFT}
     *         {@link ImgUtil#POSITION_BOTTOM_RIGHT}
     *         {@link ImgUtil#POSITION_TOP_LEFT}
     *         {@link ImgUtil#POSITION_TOP_RIGHT}
     *
     * @return 图片
     */
    public static BufferedImage generateImgWithStr(List<String> contentLines, int imgWidth, int imgHeight,
                                                   Font font, Color fontColor, int lineSpacing, int contentPosition) {
        if (imgWidth <= 0 || imgHeight <= 0) {
            throw new IllegalArgumentException("imgWidth or imgHeight cannot less than 0 or equal 0.");
        }
        
        // step1. 计算文本的行数以及最大长度
        Triple<Integer, Integer, Integer> triple = computeLineCountAndMaxLength(null, contentLines, font);
        int lineCount = triple.getLeft();
        int maxLength;
        
        // step2. 计算字体大小(当字体大小<=0时,将根据图片大小自动计算字体的大小)
        int fontSize = font.getSize();
        if (fontSizeIsIllegal(fontSize)) {
            //noinspection PointlessArithmeticExpression
            double computeHeight = imgHeight * 1 - (lineCount - 1) * lineSpacing;
            //noinspection PointlessArithmeticExpression
            double computeWidth = imgWidth * 1;
            if (computeHeight <= 0 || computeWidth <= 0) {
                throw new IllegalArgumentException("computeHeight or computeWidth is illegal.");
            }
            Integer maxLengthLineCharCount = triple.getRight();
            Objects.requireNonNull(maxLengthLineCharCount, "maxLengthLineCharCount should not be null while fontSize "
                    + "<= 0.");
            fontSize = (int) Math.min(computeHeight / lineCount, computeWidth / maxLengthLineCharCount);
            // 文本的最大长度(像素)
            maxLength = fontSize * maxLengthLineCharCount;
        } else {
            Integer middle = triple.getMiddle();
            Objects.requireNonNull(middle, "middle should not be null while fontSize > 0.");
            // 文本的最大长度(像素)
            maxLength = middle;
        }
        
        // step3. 创建 图形1(图层1) 作为 底色
        BufferedImage bufferedImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = bufferedImage.createGraphics();
        graphics.clearRect(0, 0, imgWidth, imgHeight);
        bufferedImage = graphics.getDeviceConfiguration().createCompatibleImage(imgWidth, imgHeight,
                Transparency.TRANSLUCENT);
        graphics.dispose();
        
        // step4. 创建 图形2(图层2) 勾画文本
        graphics = bufferedImage.createGraphics();
        graphics.setColor(fontColor);
        graphics.setFont(new Font(font.getName(), font.getStyle(), fontSize));
        // x向右为正, y向下为正
        int startX;
        //  这里发现:Y坐标, 还需要调整(fontSize / 6)后,文字才能正好显示
        int startY;
        switch (contentPosition) {
            case POSITION_BOTTOM_LEFT:
                startX = 0;
                startY = imgHeight - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
                break;
            case POSITION_BOTTOM_RIGHT:
                startX = imgWidth - maxLength;
                startY = imgHeight - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
                break;
            case POSITION_TOP_LEFT:
                startX = 0;
                startY = fontSize - (fontSize / 6);
                break;
            case POSITION_TOP_RIGHT:
                startX = imgWidth - maxLength;
                startY = fontSize - (fontSize / 6);
                break;
            default:
                // 默认位于左下角
                startX = 0;
                startY = imgHeight - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
        }
        for (int i = 0; i < contentLines.size(); i++) {
            graphics.drawString(contentLines.get(i), startX, startY + i * (fontSize + lineSpacing));
        }
        graphics.dispose();
        
        return bufferedImage;
    }
    
    /**
     * 将文字转换为透明背景的图片
     *
     * @param contentLines
     *         文本
     * @param fontSize
     *         字体大小(像素)
     *
     * @return 图片
     * @see ImgUtil#strConvertToImg(List, Font, Color, int)
     */
    public static BufferedImage strConvertToImg(List<String> contentLines, int fontSize) {
        return strConvertToImg(contentLines, new Font("微软雅黑", Font.PLAIN, fontSize),
                new Color(255, 255, 255), DEFAULT_LINE_SPACING);
    }
    
    /**
     * 将文字转换为透明背景的图片
     *
     * @return 图片
     * @see ImgUtil#strConvertToImg(List, Font, Color, int)
     */
    public static BufferedImage strConvertToImg(List<String> contentLines, Font font, Color fontColor) {
        return strConvertToImg(contentLines, font, fontColor, DEFAULT_LINE_SPACING);
    }
    
    /**
     * 将文字转换为透明背景的图片
     *
     * @param contentLines
     *         文本
     * @param font
     *         字
     * @param fontColor
     *         字体颜色
     * @param lineSpacing
     *         行间距(单位: 像素)
     *
     * @return 图片
     */
    public static BufferedImage strConvertToImg(List<String> contentLines, Font font, Color fontColor,
                                                int lineSpacing) {
        int fontSize = font.getSize();
        if (fontSizeIsIllegal(fontSize)) {
            throw new IllegalArgumentException("fontSize cannot less than 0 or equal 0.");
        }
        
        // step1. 计算文本的行数以及最大长度
        Triple<Integer, Integer, Integer> triple = computeLineCountAndMaxLength(null, contentLines, font);
        int lineCount = triple.getLeft();
        Integer middle = triple.getMiddle();
        Objects.requireNonNull(middle, "middle should not be null while fontSize > 0.");
        int width = middle;
        
        // step2. 根据字体大小,计算高度
        int height = fontSize * lineCount + (lineCount - 1) * lineSpacing;
        
        // step3.  创建 图形1(图层1) 作为 底色
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = bufferedImage.createGraphics();
        graphics.clearRect(0, 0, width, height);
        bufferedImage = graphics.getDeviceConfiguration().createCompatibleImage(width, height,
                Transparency.TRANSLUCENT);
        graphics.dispose();
        
        // step4. 创建 图形2(图层2) 勾画文本
        graphics = bufferedImage.createGraphics();
        graphics.setColor(fontColor);
        graphics.setFont(font);
        // x向右为正, y向下为正
        int startX = 0;
        //  这里发现:还需要 调整(fontSize / 6)后,文字才能正好显示
        int startY = height - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
        for (int i = 0; i < contentLines.size(); i++) {
            graphics.drawString(contentLines.get(i), startX, startY + i * (fontSize + lineSpacing));
        }
        graphics.dispose();
        return bufferedImage;
    }
    
    /**
     * 给图片添加水印
     * <br/>
     * 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小
     *
     * @return 图片(与入参bufferedImage是同一个对象)
     * @see ImgUtil#addWatermark(BufferedImage, List, Font, Color, int, int)
     */
    public static BufferedImage addWatermark(BufferedImage bufferedImage, List<String> contentLines) {
        return addWatermark(bufferedImage, contentLines, new Font("微软雅黑", Font.PLAIN, 0), new Color(255, 255, 255), DEFAULT_LINE_SPACING, POSITION_BOTTOM_LEFT);
    }
    
    /**
     * 给图片添加水印
     * <br/>
     * 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小
     *
     * @return 图片(与入参bufferedImage是同一个对象)
     * @see ImgUtil#addWatermark(BufferedImage, List, Font, Color, int, int)
     */
    public static BufferedImage addWatermark(BufferedImage bufferedImage, List<String> contentLines, Font font, Color fontColor) {
        return addWatermark(bufferedImage, contentLines, font, fontColor, DEFAULT_LINE_SPACING, POSITION_BOTTOM_LEFT);
    }
    
    /**
     * 给图片添加水印
     * <br/>
     * 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小
     *
     * @param bufferedImage
     *         图片
     * @param contentLines
     *         文本
     * @param font
     *         字
     * @param fontColor
     *         字体颜色
     * @param lineSpacing
     *         行间距(单位: 像素)
     * @param contentPosition
     *         文字位置 <br />
     *         {@link ImgUtil#POSITION_BOTTOM_LEFT}
     *         {@link ImgUtil#POSITION_BOTTOM_RIGHT}
     *         {@link ImgUtil#POSITION_TOP_LEFT}
     *         {@link ImgUtil#POSITION_TOP_RIGHT}
     *
     * @return 图片(与入参bufferedImage是同一个对象)
     */
    public static BufferedImage addWatermark(BufferedImage bufferedImage, List<String> contentLines,
                                             Font font, Color fontColor, int lineSpacing, int contentPosition) {
        if (bufferedImage == null) {
            throw new IllegalArgumentException("bufferedImage cannot be null.");
        }
        // step1. 计算文本的行数以及最大长度
        Triple<Integer, Integer, Integer> triple = computeLineCountAndMaxLength(bufferedImage, contentLines, font);
        int lineCount = triple.getLeft();
        int maxLength;
    
    
        int imgHeight = bufferedImage.getHeight();
        int imgWidth = bufferedImage.getWidth();
        // step2. 计算字体大小(当字体大小<=0时,将根据图片大小自动计算字体的大小)
        int fontSize = font.getSize();
        if (fontSizeIsIllegal(fontSize)) {
            //noinspection PointlessArithmeticExpression
            double computeHeight = imgHeight * 1 - (lineCount - 1) * lineSpacing;
            //noinspection PointlessArithmeticExpression
            double computeWidth = imgWidth * 1;
            if (computeHeight <= 0 || computeWidth <= 0) {
                throw new IllegalArgumentException("computeHeight or computeWidth is illegal.");
            }
            Integer maxLengthLineCharCount = triple.getRight();
            Objects.requireNonNull(maxLengthLineCharCount, "maxLengthLineCharCount should not be null while fontSize "
                    + "<= 0.");
            fontSize = (int) Math.min(computeHeight / lineCount, computeWidth / maxLengthLineCharCount);
            // 文本的最大长度(像素)
            maxLength = fontSize * maxLengthLineCharCount;
        } else {
            Integer middle = triple.getMiddle();
            Objects.requireNonNull(middle, "middle should not be null while fontSize > 0.");
            // 文本的最大长度(像素)
            maxLength = middle;
        }
        // step3. 在原图的基础上创建图层,用来勾画文本
        Graphics2D graphics = bufferedImage.createGraphics();
        graphics.setColor(fontColor);
        graphics.setFont(new Font(font.getName(), font.getStyle(), fontSize));
        // x向右为正, y向下为正
        int startX;
        //  这里发现:Y坐标, 还需要调整(fontSize / 6)后,文字才能正好显示
        int startY;
        switch (contentPosition) {
            case POSITION_BOTTOM_LEFT:
                startX = 0;
                startY = imgHeight - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
                break;
            case POSITION_BOTTOM_RIGHT:
                startX = imgWidth - maxLength;
                startY = imgHeight - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
                break;
            case POSITION_TOP_LEFT:
                startX = 0;
                startY = fontSize - (fontSize / 6);
                break;
            case POSITION_TOP_RIGHT:
                startX = imgWidth - maxLength;
                startY = fontSize - (fontSize / 6);
                break;
            default:
                // 默认位于左下角
                startX = 0;
                startY = imgHeight - fontSize * lineCount - lineSpacing * (lineCount - 1) + fontSize - (fontSize / 6);
        }
        for (int i = 0; i < contentLines.size(); i++) {
            graphics.drawString(contentLines.get(i), startX, startY + i * (fontSize + lineSpacing));
        }
        graphics.dispose();
        return bufferedImage;
    }
    
    /**
     * 计算文本的行数 & 最大长度(像素)  & 预估最长行的中文字符个数
     *
     * @param bufferedImage
     *         图片上下文
     * @param contentLines
     *         (多行)文本
     * @param font
     *         字体
     *
     * @return <ul>
     * <li>左 - 行数
     * <li>中 - 最长行长度(像素)</li>
     * <li>右 - 预估的最长行的中文字符个数(其它字符按中文字符长度进行了个数换算)</li>
     * </ul>
     */
    public static Triple<Integer, Integer, Integer> computeLineCountAndMaxLength(@Nullable BufferedImage bufferedImage,
                                                                                 List<String> contentLines, Font font) {
        if (contentLines == null || contentLines.size() == 0) {
            throw new IllegalArgumentException("contentLines cannot be null.");
        }
        int fontSize = font.getSize();
        Integer maxLength = null;
        Integer maxLengthLineCharCount = null;
        if (fontSize > 0) {
            if (bufferedImage == null) {
                bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
            }
            Graphics2D graphics = bufferedImage.createGraphics();
            FontMetrics fontMetrics = graphics.getFontMetrics(font);
            // 最长行的长度(像素)
            maxLength = contentLines.stream()
                    .map(fontMetrics::stringWidth)
                    .max(Double::compare).orElse(0);
            if (maxLength == 0) {
                throw new IllegalArgumentException("contentLines is illegal. maxLength less than 0. contentLines -> " + contentLines);
            }
        } else {
            // 最长行的长度(计算式,按:中文字符标点的长度为1, 其余的长度为0.5进行计算)
            double maxLengthDouble = contentLines.stream()
                    .map(str -> {
                        double length = 0;
                        String[] charStr = str.split("");
                        for (String c : charStr) {
                            if (CHINESE_PATTERN.matcher(c).matches()) {
                                // 中文文字、中文符号,算2/2个长度
                                length = length + 2;
                            } else {
                                // 其余的算1.1/2个长度
                                length = length + 1.1;
                            }
                        }
                        return length;
                    })
                    .max(Double::compare).orElse(0d);
            maxLengthLineCharCount = (int) ((maxLengthDouble + 1) / 2);
        }
        return Triple.of(contentLines.size(), maxLength, maxLengthLineCharCount);
    }
    
    /**
     * 文字大小是否非法
     *
     * @param fontSize
     *         大小
     *
     * @return true-非法;false-合法
     */
    private static boolean fontSizeIsIllegal(int fontSize) {
        return fontSize <= 0;
    }
    
}

相关资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值