1. 概述

在某些场景下,我们需要给一张或多张图片添加文字水印、标签或说明。手动操作当然可以用图像编辑软件轻松完成,但如果你面对的是成百上千张图,且需要统一格式加相同内容,那就必须上程序化处理了。

本文将带你用 Java 实现在图片上添加文字的功能。核心目标是:自动化、可复用、位置可控、字体自适应。

✅ 适用场景:生成带用户名的邀请卡、批量加水印、自动化报告封面等
⚠️ 踩坑提示:中文乱码、字体不生效、文字超出边界——这些我们都会覆盖到


2. 在图片上添加文字

Java 提供了多种方式读取图像并绘制文字。下面介绍两种主流方案,一种依赖第三方库,另一种纯原生实现。

2.1 使用 ImageJ 的 ImagePlus 和 ImageProcessor

ImageJ 是一个老牌图像处理库,适合科研和批量处理任务。它提供了 ImagePlusImageProcessor 类,封装了图像操作的细节。

首先引入 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 进图片。

步骤拆解:

  1. 用基础字体测量文字预期宽高
  2. 判断是否超出图片边界
  3. 若超出,则按比例缩小字体

实现代码:

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() 等方法是关键
  • ✅ 自动缩放字体需结合 GlyphVectorShape 精确测量文本轮廓
  • ✅ 记得调用 Graphics.dispose(),避免资源泄露

💡 进阶建议:

  • 中文字体问题:建议显式指定 "微软雅黑""SimHei",避免默认字体不支持中文
  • 抗锯齿:开启 RenderingHints.KEY_TEXT_ANTIALIASING 提升文字清晰度
  • 背景遮挡:可在文字下方加半透明矩形提升可读性

完整示例代码已托管至 GitHub:https://github.com/baeldung/java-image-text


原始标题:Adding Text to an Image in Java | Baeldung