All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ideaaedi.commonds.img.ImgUtil Maven / Gradle / Ivy

The newest version!
package com.ideaaedi.commonds.img;


import com.google.common.collect.Lists;
import com.ideaaedi.commonds.constants.PatternConstant;
import org.apache.commons.lang3.tuple.Triple;

import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.List;
import java.util.Objects;

/**
 * 图片工具类
 *
 * @author JustryDeng 
 * @since 2100.4.6
 */
public class ImgUtil {
    
    /** 行间距(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;
    
    /**
     * 生成带有文字的透明背景的图片 
注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小 * * @return 图片 * @see ImgUtil#generateImgWithStr(List, int, int, Font, Color, int, int) */ public static BufferedImage generateImgWithStr(List 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); } /** * 生成带有文字的透明背景的图片
注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小 * * @return 图片 * @see ImgUtil#generateImgWithStr(List, int, int, Font, Color, int, int) */ public static BufferedImage generateImgWithStr(List contentLines, int imgWidth, int imgHeight, Font font, Color fontColor) { return generateImgWithStr(contentLines, imgWidth, imgHeight, font, fontColor, DEFAULT_LINE_SPACING, POSITION_BOTTOM_LEFT); } /** * 生成带有文字的透明背景的图片
注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小 * * @param contentLines * 文本 * @param imgWidth * 图片宽 * @param imgHeight * 图片高 * @param font * 字 * @param fontColor * 字体颜色 * @param lineSpacing * 行间距(单位: 像素) * @param contentPosition * 文字位置
* {@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 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 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 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 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 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 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; } /** * 给图片添加水印 *
* 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小 * * @return 图片(与入参bufferedImage是同一个对象) * @see ImgUtil#addWatermark(BufferedImage, List, Font, Color, int, int) */ public static BufferedImage addWatermark(BufferedImage bufferedImage, List contentLines) { return addWatermark(bufferedImage, contentLines, new Font("微软雅黑", Font.PLAIN, 0), new Color(255, 255, 255), DEFAULT_LINE_SPACING, POSITION_BOTTOM_LEFT); } /** * 给图片添加水印 *
* 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小 * * @return 图片(与入参bufferedImage是同一个对象) * @see ImgUtil#addWatermark(BufferedImage, List, Font, Color, int, int) */ public static BufferedImage addWatermark(BufferedImage bufferedImage, List contentLines, Font font, Color fontColor) { return addWatermark(bufferedImage, contentLines, font, fontColor, DEFAULT_LINE_SPACING, POSITION_BOTTOM_LEFT); } /** * 给图片添加水印 *
* 注:若参数font中设置的字体大小<=0,则会自动根据图片设置字体大小 * * @param bufferedImage * 图片 * @param contentLines * 文本 * @param font * 字 * @param fontColor * 字体颜色 * @param lineSpacing * 行间距(单位: 像素) * @param contentPosition * 文字位置
* {@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 contentLines, Font font, Color fontColor, int lineSpacing, int contentPosition) { if (bufferedImage == null) { throw new IllegalArgumentException("bufferedImage cannot be null."); } // step1. 计算文本的行数以及最大长度 Triple 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
    *
  • 左 - 行数 *
  • 中 - 最长行长度(像素)
  • *
  • 右 - 预估的最长行的中文字符个数(其它字符按中文字符长度进行了个数换算)
  • *
*/ public static Triple computeLineCountAndMaxLength(@Nullable BufferedImage bufferedImage, List 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 (PatternConstant.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; } /** * demo */ public static void main(String[] args) throws Exception { BufferedImage bufferedImage = strConvertToImg( Lists.newArrayList( "服务对象:叶桃花", "服务内容:理疗", "服务时长:1小时20分钟", "服务人员:张三", "提交时间:2022-08-08 12:13:14.123456789101112131415", "详细地址:成都市双流区新怡花园" ), new Font("幼圆", Font.PLAIN, 20), new Color(255, 0, 0)); System.err.println( ImageIO.write(bufferedImage, "PNG", new File("E:\\360MoveData\\Users\\邓沙利文\\Desktop\\abc.jpg")) ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy