文章目录
- 概要
- 实现流程
- 代码如下
- 小结
概要
图片增加水印背景以及水印文字,根据文字内容是否换行,以及文字行高大小自适应计算背景大小
结果图如下:
实现流程
- 定义图片来源,以及读取字体来源(防止中文乱码)
- 计算文字所需高度 + 与背景的边距为背景高度
- 打印文字
代码如下
public static void main(String[] args) {
imageWatermark("流淌着阳关的小溪", "深圳市黄浦江", "2024.02.26", "哆啦A梦");
}
// 读取项目字体路径
static final String FONT_PATH = "C:/Users/didadiandi/Desktop/font/simsun.ttc";
/**
* 图片水印
*
* @date 2024/2/26 15:15
*/
public static void imageWatermark(String name, String location, String date, String userName) {
try {
boolean multiLine = false;
String locationData = String.format("地 点:%s", location);
String dateData = String.format("拍摄时间:%s", date);
String userNameData = String.format("拍摄人员:%s", userName);
File inputFile = new File("C:/Users/didadiandi/Desktop/微信截图_20240303165815.png");
BufferedImage inputImage = ImageIO.read(inputFile);
Graphics2D g2d = (Graphics2D) inputImage.getGraphics();
// 背景距离下一行数据的距离
int bgBottomY = inputImage.getWidth() > inputImage.getHeight() ? inputImage.getHeight() / 30 : inputImage.getWidth() / 30;
// 文字1
int fontSize01 = inputImage.getWidth() / 42;
// 文字2
int fontSize02 = fontSize01 - 2;
// 文字3
int fontSize03 = fontSize01 / 2;
// 时间距离下一行数据的距离
int dataBottomY = bgBottomY - (bgBottomY / 2);
// 背景2左边距离
int bgLeftX02 = bgBottomY + (bgBottomY / 2);
// 背景
// 背景2定义
// 颜色定义
Color color2 = new Color(255, 255, 255, 111);
g2d.setColor(color2);
// 字体文件读取
Font locationFont = loadStyleFont(FONT_PATH, Font.BOLD, fontSize02);
g2d.setFont(locationFont);
// 背景宽度计算
int bgWatermarkWidth = (inputImage.getWidth() / 3) + (inputImage.getWidth() / 20);
// 背景X坐标定义
int bg2X = bgBottomY;
// 水印文字换行处理
// 一个文字所占的高度
int lineHeight02 = g2d.getFontMetrics().getHeight();
// 每一行的宽度
int lineWidthLimit2 = bgWatermarkWidth - (dataBottomY * 2);
// 加上时间以及时间与地点的间隔,总高度累计
int locationHeightSum = lineHeight02;
// 换行后地址的X坐标
int locationXLen = bgLeftX02;
// 计算任务目标名称的文字换行的总高度
StringBuilder sbBg2 = new StringBuilder();
for (char c : locationData.toCharArray()) {
sbBg2.append(c);
int locationLen = g2d.getFontMetrics().stringWidth(sbBg2.toString());
// 一行的宽度 是否大于规定的行宽度
if (locationLen + locationXLen > lineWidthLimit2) {
locationXLen = fontSize02 * 6;
locationHeightSum += lineHeight02;
sbBg2 = new StringBuilder();
}
}
// 时间所需高度
int dateHeight = lineHeight02 + dataBottomY;
// 地址所需高度
locationHeightSum = locationHeightSum + (dataBottomY * 2);
// 图形的大小等于文字的换行总长度 + 间距
int bg2WatermarkHeight = locationHeightSum + dateHeight + (lineHeight02 + dataBottomY);
// 背景X坐标定义
int bg1X = bgBottomY;
// 背景图片的坐标
int bg2Y = inputImage.getHeight() - (bg2WatermarkHeight + bgBottomY);
// 将图形定义到图片上
g2d.fillRect(bg2X, bg2Y, bgWatermarkWidth, bg2WatermarkHeight);
// 背景1
// 背景1颜色定义
Color color1 = new Color(203, 82, 82, 150);
g2d.setColor(color1);
// 背景1上文字的定义
Font nameFont = loadStyleFont(FONT_PATH, Font.BOLD, fontSize01);
g2d.setFont(nameFont);
// 水印文字换行处理
// 一个文字所占的高度
int lineHeight = g2d.getFontMetrics().getHeight();
// 每一行的宽度
int lineWidthLimit1 = bgWatermarkWidth - (dataBottomY * 2) - (bgLeftX02 * 2) + fontSize03;
int nameHeight = lineHeight;
// 计算任务目标名称的文字换行的总高度
StringBuilder sbBg1 = new StringBuilder();
for (char c : name.toCharArray()) {
sbBg1.append(c);
// 一行的宽度 是否大于规定的行宽度
if (g2d.getFontMetrics().stringWidth(sbBg1.toString()) > lineWidthLimit1) {
nameHeight += lineHeight;
sbBg1 = new StringBuilder();
}
}
// 图形的大小等于文字的换行总长度 + 间距
int bg1WatermarkHeight = nameHeight + dataBottomY + (fontSize01 / 2);
// 背景图片的坐标
int bg1Y = bg2Y - (bg1WatermarkHeight);
g2d.fillRect(bg1X, bg1Y, bgWatermarkWidth, bg1WatermarkHeight);
// 文字
// 字体定义
Font font3 = loadStyleFont(FONT_PATH, Font.BOLD, fontSize01);
g2d.setFont(font3);
// 颜色定义
Color color3 = new Color(231, 243, 243, 255);
g2d.setColor(color3);
// 任务目标名称的水印文字换行处理
// 获取名称当前字体行高
int nameLineHeight = g2d.getFontMetrics().getHeight();
// 稳字行高累计
int nameSumLineHeight = nameLineHeight;
// 名字X坐标为 两倍bgLeftX02 边距 + 稳字所需大小
int nameX = (bgLeftX02 * 2) + fontSize03;
// 名字Y坐标为,在背景Y坐标的基础上向下移动 数据的Y坐标边距 + 文字1大小的边距
int nameY = bg1Y + (dataBottomY + fontSize01);
// 名字实际Y坐标(换行所必须参数)
int nameY2 = nameY;
StringBuilder nameSb = new StringBuilder();
for (char c : name.toCharArray()) {
nameSb.append(c);
if (g2d.getFontMetrics().stringWidth(nameSb.toString()) > lineWidthLimit1 - dataBottomY) {
// 将文字写入图片
g2d.drawString(nameSb.toString(), nameX, nameY2);
// 名字下次换行所需Y坐标
nameY2 += nameLineHeight;
// 名字总高度
nameSumLineHeight += nameLineHeight;
nameSb = new StringBuilder();
// 是否换行
multiLine = true;
}
}
g2d.drawString(nameSb.toString(), nameX, nameY2);
// 点坐标定义
Font font5 = loadStyleFont(FONT_PATH, Font.BOLD, fontSize03);
g2d.setFont(font5);
Color color5 = new Color(0, 0, 0, 255);
g2d.setColor(color5);
String point = "●";
int pointX = bgLeftX02;
// 点坐标等于初始文字坐标,如果有多行,则是多行的二分之一坐标
int pointY = multiLine ? nameY + (nameSumLineHeight / 2) : nameY;
g2d.drawString(point, pointX, pointY);
// 时间定义
Font font4 = loadStyleFont(FONT_PATH, Font.BOLD, fontSize02);
g2d.setFont(font4);
Color color4 = new Color(35, 40, 36, 255);
g2d.setColor(color4);
// 水印文字换行处理
int fontSize02LineHeight = g2d.getFontMetrics().getHeight();
int dateX = bgLeftX02;
int dateY = bg2Y + (dataBottomY + fontSize02);
g2d.drawString(dateData, dateX, dateY);
// 人员定义
int userNameX = bgLeftX02;
int userNameY = dateY + (dataBottomY + fontSize02);
g2d.drawString(userNameData, userNameX, userNameY);
// 地址
int locationX = bgLeftX02;
int locationY = userNameY + dataBottomY + fontSize02LineHeight;
StringBuilder locationSb = new StringBuilder();
for (char c : locationData.toCharArray()) {
locationSb.append(c);
int locationLen = g2d.getFontMetrics().stringWidth(locationSb.toString());
if (locationLen + locationX > lineWidthLimit2 - dataBottomY) {
g2d.drawString(locationSb.toString(), locationX, locationY);
locationY += fontSize02LineHeight;
locationX = fontSize02 * 6;
locationSb = new StringBuilder();
}
}
g2d.drawString(locationSb.toString(), locationX, locationY);
// 回收资源
g2d.dispose();
File outputFile = new File("output.png");
ImageIO.write(inputImage, "png", outputFile);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Created by <a href="https://blog.csdn.net/weixin_44951037/article/details/109623821">马男波杰克</a>
*
* @param fontFileName 外部字体名
* @param style 字体样式
* @param fontSize 字体大小
* @return
*/
public static Font loadStyleFont(String fontFileName, int style, float fontSize) throws IOException, FontFormatException {
try (InputStream in = ResourceUtil.getStream(fontFileName);){
Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, in);
return dynamicFont.deriveFont(style, fontSize);
}
}
小结
- 背景的高度是文字高度 + 文字与背景所需边距
- x坐标都是一致的
- 图片第一个文字水印Y坐标定义好之后,其他坐标都可以在这个Y坐标的基础上进行计算,可以保证水印的整体性(即调整前面一个元素的Y坐标,后边元素的Y坐标同时变动)
文字乱码解决方式参考:马男波杰克,读取字体文件解决乱码