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