1. 概述
在某些场景下,我们需要给一张或多张图片添加文字水印、标签或说明。手动操作当然可以用图像编辑软件轻松完成,但如果你面对的是成百上千张图,且需要统一格式加相同内容,那就必须上程序化处理了。
本文将带你用 Java 实现在图片上添加文字的功能。核心目标是:自动化、可复用、位置可控、字体自适应。
✅ 适用场景:生成带用户名的邀请卡、批量加水印、自动化报告封面等
⚠️ 踩坑提示:中文乱码、字体不生效、文字超出边界——这些我们都会覆盖到
2. 在图片上添加文字
Java 提供了多种方式读取图像并绘制文字。下面介绍两种主流方案,一种依赖第三方库,另一种纯原生实现。
2.1 使用 ImageJ 的 ImagePlus 和 ImageProcessor
ImageJ 是一个老牌图像处理库,适合科研和批量处理任务。它提供了 ImagePlus
和 ImageProcessor
类,封装了图像操作的细节。
首先引入 Maven 依赖:
<dependency>
<groupId>net.imagej</groupId>
<artifactId>ij</artifactId>
<version>1.51h</version>
</dependency>
加载图片并绘制文字:
ImagePlus image = IJ.openImage("/path/to/your/image.jpg");
Font font = new Font("Arial", Font.BOLD, 18);
ImageProcessor ip = image.getProcessor();
ip.setColor(Color.GREEN);
ip.setFont(font);
ip.drawString("Hello, World!", 0, 20);
📌 解释:
drawString(text, x, y)
:x 是距左边距离,y 是基线(baseline)距顶部距离- 颜色和字体通过
setColor()
和setFont()
设置 - 文字位置偏移需手动计算,否则容易被裁掉
最后别忘了保存:
IJ.save(image, "/path/to/output.jpg");
2.2 使用原生 BufferedImage 和 Graphics
如果你不想引入额外依赖,Java 自带的 BufferedImage
+ Graphics
组合完全够用,且更轻量。
读取图片:
BufferedImage image = ImageIO.read(new File("/path/to/your/image.jpg"));
绘制文字:
Graphics g = image.getGraphics();
Font font = new Font("Arial", Font.BOLD, 18);
g.setFont(font);
g.setColor(Color.GREEN);
g.drawString("Hello, World!", 0, 20);
g.dispose(); // ✅ 重要:释放资源,避免内存泄漏
📌 对比小结:
方案 | 是否需要依赖 | 灵活性 | 推荐场景 |
---|---|---|---|
ImagePlus | ✅ 需要 | 高 | 批量处理、科研图像分析 |
BufferedImage | ❌ 不需要 | 中 | 通用业务场景(如水印) |
💡
Graphics
是底层绘图上下文,所有绘制操作都通过它完成。记得调用dispose()
释放系统资源。
2.3 基于 AttributedCharacterIterator 的富文本绘制
有时候你想让一段文字里包含不同样式(比如部分加粗、变色),虽然完整富文本支持有限,但可以通过 AttributedString
实现基础效果。
AttributedString attributedText = new AttributedString("Powered by Baeldung");
attributedText.addAttribute(TextAttribute.FONT, new Font("Arial", Font.BOLD, 18));
attributedText.addAttribute(TextAttribute.FOREGROUND, Color.BLUE);
Graphics g = image.getGraphics();
g.drawString(attributedText.getIterator(), 0, 20);
g.dispose();
✅ 优势:
- 样式与文本绑定,逻辑更清晰
- 多段不同样式的文本更容易管理
⚠️ 局限:
- 不支持段落排版、换行等复杂布局
- 中文换字体时要注意是否包含中文字形(否则显示方框)
3. 文字对齐方式
默认从左上角开始画文字太死板,实际项目中我们更关心如何居中、右对齐、底部对齐等。
关键工具:FontMetrics
—— 它能告诉你当前字体下文字的宽度、高度、基线位置等。
Graphics g = image.getGraphics();
g.setFont(font);
FontMetrics metrics = g.getFontMetrics();
3.1 居中文本
要让文字在图片中心,必须动态计算坐标:
int positionX = (image.getWidth() - metrics.stringWidth(text)) / 2;
int positionY = (image.getHeight() - metrics.getHeight()) / 2 + metrics.getAscent();
📌 解释:
stringWidth(text)
:获取实际像素宽度getHeight()
:整行高度(含上下间距)getAscent()
:基线到顶部的距离,Y 坐标必须加上这个值才能正确显示
最终绘制:
g.drawString(text, positionX, positionY);
3.2 右下角对齐
常用于加水印或版权信息:
int positionX = image.getWidth() - metrics.stringWidth(text);
int positionY = image.getHeight() - metrics.getDescent(); // 或 + metrics.getAscent()
📌 提示:getDescent()
是基线到底部的距离,常用于底部对齐参考
3.3 左上角对齐
最简单的情况,但注意 Y 不是 0:
int positionX = 0;
int positionY = metrics.getAscent(); // ✅ 必须使用 Ascent,否则文字顶部会被裁剪
❌ 错误写法:drawString(text, 0, 0)
→ 文字可能看不见!
4. 根据图片大小自适应文字尺寸
一个常见问题是:固定字号的文字在小图上会溢出,在大图上又太小。理想情况是自动缩放字体,使其刚好 fit 进图片。
步骤拆解:
- 用基础字体测量文字预期宽高
- 判断是否超出图片边界
- 若超出,则按比例缩小字体
实现代码:
Graphics2D g2d = image.createGraphics();
g2d.setFont(baseFont);
FontMetrics fm = g2d.getFontMetrics();
GlyphVector gv = baseFont.createGlyphVector(fm.getFontRenderContext(), text);
Shape outline = gv.getOutline(0, 0);
double expectedWidth = outline.getBounds().getWidth();
double expectedHeight = outline.getBounds().getHeight();
boolean textFits = image.getWidth() >= expectedWidth && image.getHeight() >= expectedHeight;
if (!textFits) {
double widthRatio = (double) image.getWidth() / expectedWidth;
double heightRatio = (double) image.getHeight() / expectedHeight;
double scaleFactor = Math.min(widthRatio, heightRatio); // 取最小比例,确保完全容纳
float newSize = (float) (baseFont.getSize2D() * scaleFactor);
Font newFont = baseFont.deriveFont(baseFont.getStyle(), newSize);
g2d.setFont(newFont);
}
📌 关键点:
deriveFont()
:创建新字体实例,不影响原字体- 缩放因子取宽高比的最小值,防止任一方向溢出
- 使用
getSize2D()
而非getSize()
,支持浮点精度
✅ 效果:无论图片多大,文字都能“智能”适配
5. 总结
本文覆盖了 Java 中在图片上添加文字的核心技术路径:
- ✅ 推荐优先使用原生
BufferedImage
+Graphics
,无需依赖,够用且稳定 - ✅ 对齐控制靠
FontMetrics
,掌握getAscent()
、stringWidth()
等方法是关键 - ✅ 自动缩放字体需结合
GlyphVector
和Shape
精确测量文本轮廓 - ✅ 记得调用
Graphics.dispose()
,避免资源泄露
💡 进阶建议:
- 中文字体问题:建议显式指定
"微软雅黑"
或"SimHei"
,避免默认字体不支持中文- 抗锯齿:开启
RenderingHints.KEY_TEXT_ANTIALIASING
提升文字清晰度- 背景遮挡:可在文字下方加半透明矩形提升可读性
完整示例代码已托管至 GitHub:https://github.com/baeldung/java-image-text