0

0

如何在 Apache POI 中精准定位并插入图片到 Word 文档指定文本后

聖光之護

聖光之護

发布时间:2026-01-01 18:32:22

|

542人浏览过

|

来源于php中文网

原创

如何在 Apache POI 中精准定位并插入图片到 Word 文档指定文本后

本文详解使用 apache poi(xwpf)在 ms word 文档中,于每个匹配关键词(如 "word")之后自动插入图片的完整实现方案,重点解决图片不可见、并发修改异常及文本分段导致匹配失败三大核心问题。

在基于 Apache POI 操作 Word(.docx)文档时,许多开发者尝试通过遍历 XWPFRun 并调用 insertNewRun() 插入图片,却发现图片“未显示”或程序抛出 ConcurrentModificationException。根本原因在于:Word 的底层文本结构高度碎片化、图片尺寸单位易被误解、以及动态插入操作破坏迭代器稳定性。以下为专业级解决方案。

✅ 核心问题与修复要点

  1. 图片尺寸单位错误(最常见隐形 Bug)
    XWPFRun.addPicture(..., width, height) 的 width/height 参数单位是 EMU(English Metric Units),而非像素(px)或点(pt)。直接传入 200 表示 200 EMU ≈ 0.000556 cm —— 远小于 1 像素,肉眼完全不可见。
    ✅ 正确做法:使用 Units.pixelToEMU(200)(若原始尺寸为像素)或 Units.toEMU(200)(若为点):

    import org.apache.poi.util.Units;
    // ...
    imageRun.addPicture(
        new FileInputStream(imgFile),
        XWPFDocument.PICTURE_TYPE_PNG,
        imgFile,
        Units.pixelToEMU(200),    // ✅ 转换为 EMU
        Units.pixelToEMU(150)     // ✅ 高度同理
    );
  2. 避免 ConcurrentModificationException
    使用增强 for 循环 for (XWPFRun run : runs) 时,若在循环体内调用 paragraph.insertNewRun(),会向 runs 列表插入新元素,导致底层 Iterator 失效。
    ✅ 安全替代:改用索引遍历,并注意 insertNewRun() 后需重新获取 runs 列表(因内部结构已变更):

    List runs = paragraph.getRuns();
    for (int r = 0; r < runs.size(); r++) {
        XWPFRun run = runs.get(r);
        String text = run.getText(0);
        if (text != null && text.contains("word")) {
            // ✅ 安全插入:使用当前索引 + 1
            XWPFRun imageRun = paragraph.insertNewRun(r + 1);
            // ⚠️ 注意:此时 runs 已过期,后续需重新 getRuns()
            runs = paragraph.getRuns(); // 重载最新 runs
            // ... 插入图片逻辑
        }
    }
  3. 精准匹配被拆分的关键词(Word 文本分段陷阱)
    Word 可能将一个单词 "word" 拆分为多个 XWPFRun(如 word),导致 run.getText(0).contains("word") 永远为 false。
    ✅ 推荐方案:使用 XWPFParagraph.searchText() 配合 TextSegment,它能跨 Run 级别进行语义搜索:

    PositionInParagraph startPos = new PositionInParagraph(0, 0, 0);
    TextSegment segment;
    while ((segment = paragraph.searchText("word", startPos)) != null) {
        int endRunIndex = segment.getEndRun();
        List runs = paragraph.getRuns();
        XWPFRun targetRun = runs.get(endRunIndex);
    
        // 在目标 Run 后插入新 Run(即关键词后)
        XWPFRun imageRun = paragraph.insertNewRun(endRunIndex + 1);
        // ... 添加图片
    
        // 更新搜索起始位置:跳过刚插入的图片 Run
        startPos = new PositionInParagraph(
            runs.indexOf(imageRun),  // 新 Run 的索引
            0, 0                      // 从该 Run 开头继续
        );
    }

✅ 完整可运行示例(推荐使用)

import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.util.Units;
import java.util.List;

public class ImageWord {
    public static void main(String[] args) throws Exception {
        // ✅ 使用 .docx(非 .doc)格式;路径建议用相对路径或 Path.of()
        try (XWPFDocument doc = new XWPFDocument(new FileInputStream("./image.docx"))) {
            List paragraphs = doc.getParagraphs();
            File[] images = new File("./images/").listFiles((dir, name) -> 
                name.toLowerCase().endsWith(".png") || 
                name.toLowerCase().endsWith(".jpg"));

            if (images == null || images.length == 0) {
                throw new RuntimeException("No images found in ./images/");
            }

            int imageCounter = 0;
            String keyword = "word";

            for (XWPFParagraph paragraph : paragraphs) {
                PositionInParagraph pos = new PositionInParagraph(0, 0, 0);
                TextSegment segment;

                while ((segment = paragraph.searchText(keyword, pos)) != null) {
                    List runs = paragraph.getRuns();
                    int insertIndex = segment.getEndRun() + 1;

                    // 创建新 Run 并插入图片
                    XWPFRun imageRun = paragraph.insertNewRun(insertIndex);
                    File imgFile = images[imageCounter % images.length];

                    try (FileInputStream fis = new FileInputStream(imgFile)) {
                        imageRun.addPicture(
                            fis,
                            XWPFDocument.PICTURE_TYPE_PNG,
                            imgFile.getName(),
                            Units.pixelToEMU(200),
                            Units.pixelToEMU(150)
                        );
                    }
                    imageRun.addBreak(); // 可选:图片后换行

                    // 更新搜索位置,避免重复匹配或越界
                    runs = paragraph.getRuns();
                    int newIndex = runs.indexOf(imageRun);
                    pos = new PositionInParagraph(newIndex, 0, 0);
                    imageCounter++;
                }
            }

            // ✅ 使用 try-with-resources 或显式 close
            try (FileOutputStream out = new FileOutputStream("./images_output.docx")) {
                doc.write(out);
            }
        }
    }
}

⚠️ 注意事项与最佳实践

  • 文件格式:确保输入为 .docx(OOXML 格式),Apache POI 对旧版 .doc 支持有限且不稳定。
  • 依赖版本:使用 Apache POI ≥ 5.2.4,以获得完整的 TextSegment 和 searchText 支持。
  • 图片路径:addPicture 的第三个参数(pictureName)建议传入文件名(如 imgFile.getName()),而非绝对路径,利于文档可移植性。
  • 异常处理:务必包裹 FileInputStream 在 try-with-resources 中,防止资源泄漏;对 images 数组判空,避免 NullPointerException。
  • 性能提示:若文档极大或图片极多,考虑批量写入或启用 XWPFDocument 的 setCompressPictures(true)。

通过以上三重加固(EMU 单位转换、安全索引遍历、TextSegment 语义搜索),即可稳定、精准地在任意 Word 文档关键词后插入图片,真正实现自动化文档生成场景下的生产级需求。

Kacha
Kacha

KaCha是一款革命性的AI写真工具,用AI技术将照片变成杰作!

下载

相关专题

更多
apache是什么意思
apache是什么意思

Apache是Apache HTTP Server的简称,是一个开源的Web服务器软件。是目前全球使用最广泛的Web服务器软件之一,由Apache软件基金会开发和维护,Apache具有稳定、安全和高性能的特点,得益于其成熟的开发和广泛的应用实践,被广泛用于托管网站、搭建Web应用程序、构建Web服务和代理等场景。本专题为大家提供了Apache相关的各种文章、以及下载和课程,希望对各位有所帮助。

403

2023.08.23

apache启动失败
apache启动失败

Apache启动失败可能有多种原因。需要检查日志文件、检查配置文件等等。想了解更多apache启动的相关内容,可以阅读本专题下面的文章。

924

2024.01.16

PHP 命令行脚本与自动化任务开发
PHP 命令行脚本与自动化任务开发

本专题系统讲解 PHP 在命令行环境(CLI)下的开发与应用,内容涵盖 PHP CLI 基础、参数解析、文件与目录操作、日志输出、异常处理,以及与 Linux 定时任务(Cron)的结合使用。通过实战示例,帮助开发者掌握使用 PHP 构建 自动化脚本、批处理工具与后台任务程序 的能力。

21

2025.12.13

word背景色怎么改成白色
word背景色怎么改成白色

Word是微软公司的一个文字处理器软件。word为用户提供了专业而优雅的文档工具,帮助用户节省时间并得到优雅美观的结果。word提供了许多易于使用的文档创建工具,同时也提供了丰富的功能供创建复杂的文档使用。怎么word背景色怎么该呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

3680

2023.07.21

word最后一页空白页怎么删除
word最后一页空白页怎么删除

word最后一页空白页删除方法有:通过删除回车符、调整页边距、删除分节符或调整分页符位置,您可以轻松去除最后一页的空白页。根据您实际的文档情况,选择适合您的方法进行操作,使您的文档更加美观和整洁。本专题为大家提供word最后一页空白页怎么删除不了相关的各种文章、以及下载和课程。

309

2023.07.24

word最后一页空白页怎么删除不了
word最后一页空白页怎么删除不了

word删除最后一页空白页,可以尝试使用Backspace键删除空白页,如果无效,查找和删除分页符,或者调整页面边距和行距。还可以尝试将文档保存为其他格式并重新打开和保存。本专题为大家提供word最后一页空白页为啥删除不了的相关的文章、下载、课程内容,供大家免费下载体验。

365

2023.07.25

word单页改变纸张方向
word单页改变纸张方向

word单页改变纸张方向:1、在界面上选择文档纸张方向;2、自定义页面设置;3、分节功能。本专题为大家提供word单页改变纸张方向的相关的文章、下载、课程内容,供大家免费下载体验。

615

2023.07.27

word转ppt
word转ppt

Word是一款常用的文字处理软件,而PowerPoint则是一款专门用于制作演示文稿的软件。在某些情况下,我们可能需要将Word文档转换为PowerPoint演示文稿,以便更好地展示我们的内容。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

369

2023.08.03

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.2万人学习

C# 教程
C# 教程

共94课时 | 5.7万人学习

Java 教程
Java 教程

共578课时 | 40.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号