用 Java 批量处理 Word 文档时,给指定文字上色是个挺常见的需求——标红合同里改动的条款、给审核意见加个颜色区分、或者让批量生成的报告更容易一眼看出重点。这个需求看着简单,但实际做的时候会发现"改一段"和"改一个词"用的方法不一样,选错了方法经常出现"改了但没完全生效"的情况。这篇整理一下三种常见做法。
环境准备
代码用的是 Spire.Doc for Java 这个库,Maven 依赖配置如下:
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.doc</artifactId>
<version>13.5.3</version>
</dependency>
</dependencies>
不用 Maven 的话官网也能直接下 jar 手动导入。免费版会在生成的文件里加提示文字,正式环境记得提前换成授权版本。
一、用段落样式整段上色
如果要把一整个段落的文字都改成同一种颜色,最直接的做法是新建一个 ParagraphStyle,把颜色设置在样式里,再把样式应用到段落上。
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import com.spire.doc.Section;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.documents.ParagraphStyle;
import java.awt.*;
public class ColorWholeParagraph {
public static void main(String[] args) {
Document document = new Document();
document.loadFromFile("report.docx");
Section section = document.getSections().get(0);
Paragraph paragraph = section.getParagraphs().get(0);
ParagraphStyle style = new ParagraphStyle(document);
style.setName("HighlightRed");
style.getCharacterFormat().setTextColor(new Color(178, 34, 34));
document.getStyles().add(style);
paragraph.applyStyle(style.getName());
document.saveToFile("output/colored.docx", FileFormat.Docx);
}
}
这个方法适合段落本身格式比较"干净"、没有被手动调过格式的场景。这里有个注意事项:给段落应用样式,并不会覆盖段落里已经存在的、直接设置在文字上的格式。 Word 的格式优先级是"直接格式高于样式格式",如果段落里有些文字之前已经单独设过颜色(比如被人手动改过,或者是从别处粘贴过来的时候带了自己的格式),那应用新样式之后,这部分文字的颜色是不会变的,最终效果就是整段颜色改了一半。想保证段落里的每个字都变色,得用下面第二种方法。
二、直接遍历文字块上色
一个段落的内容其实是由一串子对象组成的,真正的文字存放在 TextRange 对象里,中间可能还穿插着换行符、图片这些其他类型的子对象。遍历这些子对象,找到 TextRange 类型的逐个设置颜色,就绕开了上面样式优先级的问题,因为这是直接改的是文字本身的格式。
import com.spire.doc.Document;
import com.spire.doc.Section;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.fields.TextRange;
import com.spire.doc.FileFormat;
import java.awt.*;
public class ColorParagraphRuns {
public static void main(String[] args) {
Document document = new Document();
document.loadFromFile("report.docx");
Section section = document.getSections().get(0);
Paragraph paragraph = section.getParagraphs().get(1);
for (int i = 0; i < paragraph.getChildObjects().getCount(); i++) {
if (paragraph.getChildObjects().get(i) instanceof TextRange) {
TextRange run = (TextRange) paragraph.getChildObjects().get(i);
run.getCharacterFormat().setTextColor(Color.blue);
}
}
document.saveToFile("output/coloredRuns.docx", FileFormat.Docx);
}
}
代码比第一种方法啰嗦一点,但实测更稳,尤其是处理那些已经被人工编辑过、格式不太干净的文档时,基本上都得用这种方式才能保证颜色真的全改到位。
三、查找指定文字并上色
有时候要改颜色的不是某一整段,而是散落在文档各处的某个词或短语。用 findAllString() 找出所有匹配项,每个匹配结果都是一个 TextSelection,可以转成可格式化的文字范围。
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import com.spire.doc.documents.TextSelection;
import com.spire.doc.fields.TextRange;
import java.awt.*;
public class ColorMatchedText {
public static void main(String[] args) {
Document document = new Document();
document.loadFromFile("report.docx");
TextSelection[] matches = document.findAllString("机密", false, true);
for (TextSelection match : matches) {
TextRange range = match.getAsOneRange();
range.getCharacterFormat().setTextColor(Color.red);
}
document.saveToFile("output/coloredMatches.docx", FileFormat.Docx);
}
}
这里 getAsOneRange() 这一步不能省。文档里看上去是一个完整的词或句子,内部存储时不一定是单个 TextRange——拼写检查状态、修订痕迹、或者原文档编辑时留下的格式碎片,都可能导致同一个匹配结果被拆成好几个相邻的文字块。getAsOneRange() 会把命中的这几个块合并成一个整体再返回,这样格式化的时候才是整个匹配结果一起变色,不然很容易出现只改了匹配词前半截颜色的情况。
补充
颜色用的就是普通的 java.awt.Color,Color.red 这种命名常量,或者 new Color(r, g, b) 自己传 RGB 值都可以。另外 findAllString() 后面跟着两个布尔参数,分别控制大小写敏感和全词匹配,实际用之前最好核对一下这两个参数跟自己的搜索词搭不搭——比如开了全词匹配去搜"PDF",是匹配不到"PDFs"或者"PDF-2"这种词里带着"PDF"的情况的。
三种方法怎么选:如果能确认文档格式没被手动改过,样式着色写起来最快;如果文档来源不可控(比如是用户上传的、之前被编辑过的),直接遍历文字块上色更保险;要按关键词批量改颜色,就用查找+上色这套组合。选错了方法,最常见的表现就是"运行没报错,但颜色只改了一半",排查起来还挺费劲的,提前想清楚场景能省不少事。













