You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为同一段落设置不同样式?Apache POI HTML转Word表格样式问题

嘿,这个问题我熟!你现在的核心问题是只用了一个XWPFRun来处理所有文本,但Word里每个Run只能对应一套样式——所以嵌套的HTML标签(比如<i>里套<b>)肯定没法正确渲染。咱们得换个思路:把HTML拆成一个个带样式的文本片段,每个片段对应一个独立的XWPFRun,然后给每个Run设置对应的属性。

核心思路

HTML是嵌套结构,手动用indexOf处理嵌套标签会非常麻烦,我推荐用轻量的HTML解析库Jsoup来帮我们遍历节点,这样能轻松处理嵌套、转义字符这些问题。核心逻辑是:解析HTML的每个文本节点,根据它所在的标签层级,为它创建对应的XWPFRun并设置样式。

步骤1:引入Jsoup依赖(Maven为例)
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.17.2</version> <!-- 用最新稳定版即可 -->
</dependency>
步骤2:编写递归处理HTML节点的方法

这个方法会遍历Jsoup解析后的HTML节点,为每个文本片段创建XWPFRun,并根据父标签的样式设置Run的属性:

private static void processElement(Element element, XWPFParagraph paragraph, int baseFontSize) {
    // 遍历当前节点的所有子节点
    for (Node node : element.childNodes()) {
        if (node instanceof TextNode) {
            // 如果是文本节点,创建Run并设置当前样式
            TextNode textNode = (TextNode) node;
            String text = textNode.text().trim();
            if (text.isEmpty()) continue; // 跳过空文本

            XWPFRun run = paragraph.createRun();
            run.setFontSize(baseFontSize);

            // 从当前节点往上遍历,收集所有生效的样式
            Element current = element;
            while (current != null) {
                switch (current.tagName()) {
                    case "b":
                    case "strong":
                        run.setBold(true);
                        break;
                    case "i":
                    case "em":
                        run.setItalic(true);
                        break;
                    case "font":
                        // 处理font标签的size和color属性
                        String fontSizeStr = current.attr("size");
                        if (!fontSizeStr.isEmpty()) {
                            try {
                                run.setFontSize(Integer.parseInt(fontSizeStr));
                            } catch (NumberFormatException e) {
                                // 无效的字体大小,用默认值
                                run.setFontSize(baseFontSize);
                            }
                        }
                        String color = current.attr("color");
                        if (!color.isEmpty()) {
                            run.setColor(color);
                        }
                        break;
                    // 可以继续扩展其他标签,比如u(下划线)、strike(删除线)等
                }
                current = current.parent();
            }

            run.setText(text);
        } else if (node instanceof Element) {
            // 如果是元素节点,递归处理
            Element childElement = (Element) node;
            if ("br".equals(childElement.tagName())) {
                // 处理换行标签,添加换行符
                XWPFRun run = paragraph.createRun();
                run.addBreak();
            } else {
                processElement(childElement, paragraph, baseFontSize);
            }
        }
    }
}
步骤3:修改你的getTableParagraph方法

现在用Jsoup解析HTML文本,然后调用上面的处理方法:

private static XWPFParagraph getTableParagraph(XWPFTableCell cell, String htmlText) {
    int baseFontSize = 11;
    XWPFParagraph paragraph = cell.addParagraph();
    cell.removeParagraph(0); // 移除默认创建的空段落
    paragraph.setSpacingAfterLines(0);
    paragraph.setSpacingAfter(0);

    if (htmlText == null || htmlText.isEmpty()) {
        // 处理空文本,创建一个空Run
        XWPFRun emptyRun = paragraph.createRun();
        emptyRun.setFontSize(baseFontSize);
        return paragraph;
    }

    // 把转义的&lt;和&gt;转成真实的HTML标签
    String unescapedHtml = org.jsoup.parser.Parser.unescapeEntities(htmlText, true);
    Document doc = Jsoup.parseBodyFragment(unescapedHtml);
    Element body = doc.body();

    // 递归处理所有节点
    processElement(body, paragraph, baseFontSize);

    return paragraph;
}
手动解析的思路(如果不想用Jsoup)

如果不想引入第三方库,你需要用栈结构来跟踪当前的样式状态:

  • 遍历HTML文本,遇到开始标签(比如<b>)时,把对应的样式(加粗)压入栈。
  • 遇到结束标签(比如</b>)时,把对应的样式从栈中弹出。
  • 遇到文本片段时,创建一个XWPFRun,应用栈中所有当前有效的样式(比如同时有斜体和加粗),然后设置文本。
  • 处理<br>时添加换行。

不过手动处理需要考虑很多边界情况:比如标签的嵌套顺序、转义字符、不闭合的标签等等,所以还是推荐用Jsoup更省心。

测试你的示例文本

对于你给出的例子:

This is my text <i> which now is in italic<b> but also in bold</b> depending on its importance</i>

用上面的代码处理后,会生成4个Run:

  • "This is my text ":默认样式(11号字,无加粗斜体)
  • " which now is in italic":斜体
  • " but also in bold":斜体+加粗
  • " depending on its importance":斜体

完全符合你的需求!

内容的提问来源于stack exchange,提问作者Gerard Martinelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:01:55