目标功能
Java后台生成图片:
原理
代码实现
创建所需参数的实体类
import lombok.Data;
import java.awt.*;
import java.awt.image.BufferedImage;
/**
* 生成图片参数控制
*
* @author h
* @date Created in 2020/5/30 17:25
*/
@Data
public class ImageCreateEntity {
/**
* 宽度
*/
private Integer width = 100;
/**
* 高度
*/
private Integer height = 100;
/*==============图片内容==============*/
/**
* 图片内容
*/
private BufferedImage imgContent;
/**
* 图片宽度
*/
private int imgWidth = 100;
/**
* 图片宽度
*/
private int imgHeight = 100;
/**
* 图片渲染X起点
*/
private int imgX;
/**
* 图片渲染Y轴起点
*/
private int imgY;
/*=====================文本内容===================*/
/**
* 文本内容
*/
private String textContent;
/**
* 字体名称
*/
private String fontName = "";
/**
* 字体文件路径
*/
private String fontFilePath = "";
/**
* 字体尺寸
*/
private float fontSize = 20f;
/**
* 字体风格
*/
private int fontStyle = Font.PLAIN;
/**
* 字体颜色
*/
private Color fontColor = Color.BLACK;
/**
* 字体间距,默认值为零,与当前字体尺寸相关
*/
private Integer fontSpace = 0;
/**
* 行距
*/
private Integer linePadding = 10;
/**
* 文本透明度:值从0-1.0,依次变得不透明
*/
private float textTransparency = 1.0f;
/**
* 文本渲染X起点
*/
private Integer textX = 0;
/**
* 文本渲染Y轴起点
*/
private Integer textY = 0;
/**
* 左边距
*/
private Integer textLeftPadding = 0;
/**
* 右边距
*/
private Integer textRightPadding = 0;
/**
* 每行居中
*/
private boolean isCenterLine;
/*=================背景==============*/
/**
* 背景颜色
*/
private Color backgroundColor = Color.WHITE;
/**
* 背景是否透明
*/
private boolean isTransparentBackground = false;
/**
* 背景图片
*/
private BufferedImage backgroundImg;
}
生成图片基本方法
创建一个 BufferedImage ,再通过Graphics2D 进行绘制,就可以得到一个简单的图片缓存。
Graphics2D class extends the Graphics class to provide more sophisticated control over geometry, coordinate transformations, color management,and text layout. This is the fundamental class for rendering 2-dimensional shapes, text and images on the Java(tm) platform.
Graphics2D类扩展了Graphics类,以提供对几何图形,坐标转换,颜色管理和文本布局的更复杂的控制。 这是在Java(tm)平台上渲染二维形状,文本和图像的基本类。
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bf.createGraphics();
//设置图片背景颜色
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, width, height);
//绘制图片内容
// 中间内容框画到背景图上
BufferedImage imgContent = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//绘制图片
g2d.drawImage(imgContent.getScaledInstance(imgWidth, imgHeight, Image.SCALE_DEFAULT), imgX, imgY, null);
//绘制文本
g2d.drawString(str, x, y);
g2d.dispose();
背景透明设置
使用 Graphics2D 创建一个新的支持透明度的 BufferedImage 即可,新创建的图片默认即是透明的。
bufferedImage = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
Graphics2D g2d = bf.createGraphics();
// 后续操作
字体尺寸、间距等基本设置
通过tracking 属性设置字体间距。
The tracking value is multiplied by the font point size and passed through the font transform to determine an additional amount to add to the advance of each glyph cluster. Positive tracking values will inhibit formation of optional ligatures. Tracking values are typically between -0.1and 0.3; values outside this range are generally not desireable.
跟踪值乘以字体点大小,然后通过字体变换来确定要添加到每个字形簇前移的附加量。 正跟踪值将抑制可选连字的形成。 跟踪值通常在-0.1和0.3之间; 通常不希望超出此范围的值。
//设置字体透明度,字体透明度只在背景不透明时才生效
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f));
//字体基本设置
Font font = new Font(fontName, fontStyle, (int) fontSize);
//字体间距设置
Map<TextAttribute, Object> attributes = new HashMap<TextAttribute, Object>();
attributes.put(TextAttribute.TRACKING, imageCreateEntity.getFontSpace());
font = font.deriveFont(attributes);
g2d.setFont(font);
g2d.setColor(Color.BLACK);
文本换行与居中
实现思路:
拆分行数
/**
* 获取字体分行后list
*
* @param textContent 文本内容
* @param contentWith 文本宽度
* @param fontSpace 字体间距
* @param fontSize 字体尺寸
* @param isChinese 是否生成汉字
* @return java.util.List<java.lang.String>
*/
private static List<String> getLines(String textContent, Integer contentWith, Integer fontSpace, float fontSize, boolean isChinese) {
//每一行的字体个数,
int lineCont = (int) (contentWith / ((1 + fontSpace) * fontSize));
if (isChinese) {
// 英文和汉字的宽度不一样,一般为1:2,
// 计算个数为英文字体的个数,所以需要乘以2
//如果传入的是英文字体,不需要生成汉字,这里就不需要乘以2
lineCont = lineCont * 2;
}
char[] chars = textContent.toCharArray();
List<String> lines = new ArrayList<>();
char[] charArrayTemp = new char[lineCont];
int count = 0;
int indexChar = 0;
for (char c : chars) {
if (count == lineCont) {
//一行数据已满,开始新的一行
lines.add(new String(charArrayTemp));
count = 0;
indexChar = 0;
charArrayTemp = new char[lineCont];
}
charArrayTemp[indexChar] = c;
//汉字的宽度一般是英文数字的2倍,
if (checkCharCN(c)) {
count += 2;
} else {
count++;
}
indexChar++;
}
//最后一行
if (ArrayUtil.isNotEmpty(charArrayTemp)) {
lines.add(new String(charArrayTemp));
}
return lines;
}
每行居中
//逐行渲染
boolean centerLine = imageCreateEntity.isCenterLine();
float textX = imageCreateEntity.getTextX();
Integer textY = imageCreateEntity.getTextY();
float lineWidth;
float paddingAdd = 0;
if (CollectionUtil.isNotEmpty(lines)) {
String contentLine;
boolean b;
int lineLength = 0;
for (int i = 0; i < lines.size(); i++) {
contentLine = lines.get(i).trim();
//计算字符串中汉字和其他字符的宽度,汉字和其他字符的宽度一般为2:1
char[] chars = contentLine.trim().toCharArray();
for (char c : chars) {
b = checkCharCN(c);
if (b) {
lineLength += 2;
} else {
lineLength += 1;
}
}
//设置每行居中
if (centerLine) {
lineWidth = ((1 + fontSpace) * fontSize) * (lineLength / 2f);
paddingAdd = (contentWith - lineWidth) / 2;
textX = paddingAdd + textLeftPadding;
}
g2d.drawString(contentLine, textX, textY + fontSize + (i * (fontSize + linePadding)));
}
}
加载外部字体
//加载字体
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Font font = Font.createFont(Font.TRUETYPE_FONT, new File(imageCreateEntity.getFontFilePath())).deriveFont(fontStyle).deriveFont(fontSize);
ge.registerFont(font);
g2d.setFont(font);
工具类完整代码
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 生成图片工具
* <p>
* 注意,有有文字时,需要服务器有字体存在
*
* @author h
* @date Created in 2020/5/30 17:54
*/
@Log4j2
public class ImageCreateUtil {
/**
* 渲染内容
*
* @param imageCreateEntity imageCreateEntity
* @return java.awt.image.BufferedImage
* @创建人: h
* @创建时间: 2020/6/4 20:42
* @修改人:
* @修改时间:
* @修改备注:
* @version: 1.0
*/
public static BufferedImage createImg(ImageCreateEntity imageCreateEntity) {
Integer width = imageCreateEntity.getWidth();
Integer height = imageCreateEntity.getHeight();
int imgWidth = imageCreateEntity.getImgWidth();
int imgHeight = imageCreateEntity.getImgHeight();
BufferedImage bgImg = imageCreateEntity.getBackgroundImg();
int imgX = imageCreateEntity.getImgX();
int imgY = imageCreateEntity.getImgY();
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d;
g2d = getG2d(bufferedImage);
boolean isFontTransparency = false;
//背景是否透明
if (ObjectUtil.isNotNull(bgImg)) {
g2d.drawImage(bgImg.getScaledInstance(width, height, Image.SCALE_DEFAULT), 0, 0, null);
} else if (imageCreateEntity.isTransparentBackground()) {
bufferedImage = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = getG2d(bufferedImage);
} else {
g2d.setBackground(imageCreateEntity.getBackgroundColor());
g2d.clearRect(0, 0, width, height);
//设置字体透明度,字体透明度只在背景不透明时才生效
isFontTransparency = true;
}
/*=====================渲染图片===================*/
// 中间内容框画到背景图上
BufferedImage imgContent = imageCreateEntity.getImgContent();
g2d.drawImage(imgContent.getScaledInstance(imgWidth, imgHeight, Image.SCALE_DEFAULT), imgX, imgY, null);
/*====================渲染文本==================*/
return renderText(isFontTransparency, bufferedImage, imageCreateEntity);
}
/**
* 渲染文本
*
* @param bufferedImage bufferedImage
* @param imageCreateEntity imageCreateEntity
* @return java.awt.image.BufferedImage
* @创建人: h
* @创建时间: 2020/6/4 20:28
* @修改人:
* @修改时间:
* @修改备注:
* @version: 1.0
*/
private static BufferedImage renderText(boolean isFontTransparency, BufferedImage bufferedImage, ImageCreateEntity imageCreateEntity) {
Graphics2D g2d = initFont(imageCreateEntity, bufferedImage, isFontTransparency);
if (ObjectUtil.isNull(g2d)) {
return null;
}
String textContent = imageCreateEntity.getTextContent();
if (StringUtils.isBlank(textContent)) {
g2d.dispose();
return bufferedImage;
}
//对文本进行分行处理
Integer textLeftPadding = imageCreateEntity.getTextLeftPadding();
int contentWith = imageCreateEntity.getWidth() - textLeftPadding - imageCreateEntity.getTextRightPadding();
float fontSize = imageCreateEntity.getFontSize();
Integer fontSpace = imageCreateEntity.getFontSpace();
Integer linePadding = imageCreateEntity.getLinePadding();
List<String> lines = getLines(textContent, contentWith, fontSpace, fontSize);
//逐行渲染
//当前行文字的长度
boolean centerLine = imageCreateEntity.isCenterLine();
float textX = imageCreateEntity.getTextX();
Integer textY = imageCreateEntity.getTextY();
float lineWidth;
float paddingAdd = 0;
if (CollectionUtil.isNotEmpty(lines)) {
String contentLine;
for (int i = 0; i < lines.size(); i++) {
contentLine = lines.get(i).trim();
//设置每行居中
if (centerLine) {
lineWidth = ((1 + fontSpace) * fontSize) * contentLine.trim().length();
paddingAdd = (contentWith - lineWidth) / 2;
textX = paddingAdd + textLeftPadding;
}
g2d.drawString(contentLine, textX, textY + fontSize + (i * (fontSize + linePadding)));
}
}
g2d.dispose();
return bufferedImage;
}
/**
* 获取字体分行后list
*
* @param textContent 文本内容
* @param contentWith 文本宽度
* @param fontSpace 字体间距
* @param fontSize 字体尺寸
* @return java.util.List<java.lang.String>
* @创建人: h
* @创建时间: 2020/6/4 18:55
* @修改人:
* @修改时间:
* @修改备注:
* @version: 1.0
*/
private static List<String> getLines(String textContent, Integer contentWith, Integer fontSpace, float fontSize) {
//每一行的字体个数
int lineCont = (int) (contentWith / ((1 + fontSpace) * fontSize));
char[] chars = textContent.toCharArray();
List<String> lines = new ArrayList<>();
char[] charArrayTemp = new char[lineCont];
int indexTemp = 0;
for (char c : chars) {
if (indexTemp == lineCont) {
//一行数据已满,开始新的一行
lines.add(new String(charArrayTemp));
indexTemp = 0;
charArrayTemp = new char[lineCont];
}
charArrayTemp[indexTemp] = c;
indexTemp++;
}
//最后一行
if (ArrayUtil.isNotEmpty(charArrayTemp)) {
lines.add(new String(charArrayTemp));
}
return lines;
}
public static Graphics2D getG2d(BufferedImage bf) {
Graphics2D g2d = bf.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
return g2d;
}
/**
* 初始化字体信息
*
* @param imageCreateEntity imageCreateEntity
* @param bufferedImage bufferedImage
* @param isFontTransparency 字体是否透明
* @return java.awt.Graphics2D
* @创建人: h
* @创建时间: 2020/6/5 10:07
* @修改人:
* @修改时间:
* @修改备注:
* @version: 1.0
*/
private static Graphics2D initFont(ImageCreateEntity imageCreateEntity, BufferedImage bufferedImage, boolean isFontTransparency) {
String fontName = imageCreateEntity.getFontName();
float fontSize = imageCreateEntity.getFontSize();
int fontStyle = imageCreateEntity.getFontStyle();
//加载字体
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
boolean contains = ArrayUtil.contains(ge.getAvailableFontFamilyNames(), fontName);
Font font = null;
if (contains) {
font = new Font(fontName, fontStyle, (int) fontSize);
} else {
boolean isOk = true;
try {
font = Font.createFont(Font.TRUETYPE_FONT, new File(imageCreateEntity.getFontFilePath())).deriveFont(fontStyle).deriveFont(fontSize);
ge.registerFont(font);
} catch (FontFormatException e) {
log.error("加载字体失败!", e);
isOk = false;
} catch (IOException e) {
isOk = false;
log.error("加载字体文件失败!", e);
}
if (!isOk) {
return null;
}
}
Graphics2D g2d = ge.createGraphics(bufferedImage);
//设置字体透明度,字体透明度只在背景不透明时才生效
if (isFontTransparency) {
if (imageCreateEntity.getTextTransparency() != 1.0f) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, imageCreateEntity.getTextTransparency()));
}
}
//字体信息
Map<TextAttribute, Object> attributes = new HashMap<TextAttribute, Object>();
attributes.put(TextAttribute.TRACKING, imageCreateEntity.getFontSpace());
font = font.deriveFont(attributes);
g2d.setFont(font);
g2d.setColor(imageCreateEntity.getFontColor());
return g2d;
}
}