在文档协作与审阅场景中,Word 的"修订"功能是一个常用工具。启用该功能后,文档中的所有编辑操作------包括插入、删除、替换、格式更改------都会被记录为修订,并显示修改者、修改时间等信息。本文将介绍如何使用 Java 语言,借助 Spire.Doc for Java 库,从 Word 文档中提取所有的修订记录。
一、环境准备
在开始编码之前,需要在 Java 项目中引入 Spire.Doc for Java 库。
如果项目使用 Maven 管理依赖,可以在 pom.xml 中配置以下内容:
xml
<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>14.4.9</version>
</dependency>
</dependencies>
对于非 Maven 项目,可以手动下载 JAR 文件并将其添加到项目的 classpath 中。
二、修订获取的基本思路
Word 文档启用修订功能后,文档中的修改会以特定格式存储。获取修订信息的基本流程如下:
- 加载包含修订记录的 Word 文档
- 遍历文档中的所有节(Section)
- 遍历每个节中的文档对象,识别段落级别的修订
- 进一步遍历段落内部的文本区域,获取更细粒度的修订
- 提取修订的类型、作者等信息,并输出或存储
修订主要分为插入修订和删除修订两种类型,需要分别进行获取。格式更改类修订的获取方式略有不同。
三、段落与文本区域级别的修订获取
以下代码演示了如何从 Word 文档中获取段落级别和文本区域级别的插入修订与删除修订。
java
import com.spire.doc.Document;
import com.spire.doc.DocumentObject;
import com.spire.doc.Section;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.fields.TextRange;
import com.spire.doc.formatting.revisions.EditRevision;
import java.io.FileWriter;
public class GetInsertAndDeleteRevisions {
public static void main(String[] args) throws Exception {
// 加载包含修订记录的 Word 文档
Document document = new Document();
document.loadFromFile("RevisedDocument.docx");
// 存储插入修订信息的 StringBuilder
StringBuilder insertRevision = new StringBuilder();
insertRevision.append("插入修订记录:\n");
int insertIndex = 0;
// 存储删除修订信息的 StringBuilder
StringBuilder deleteRevision = new StringBuilder();
deleteRevision.append("删除修订记录:\n");
int deleteIndex = 0;
// 遍历所有节
for (Section section : (Iterable<Section>) document.getSections()) {
// 遍历节中 Body 下的所有子对象
for (DocumentObject docItem : (Iterable<DocumentObject>) section.getBody().getChildObjects()) {
if (docItem instanceof Paragraph) {
Paragraph para = (Paragraph) docItem;
// 判断段落是否为插入修订
if (para.isInsertRevision()) {
insertIndex++;
insertRevision.append("索引: ").append(insertIndex).append("\n");
EditRevision insertRev = para.getInsertRevision();
insertRevision.append("类型: ").append(insertRev.getType()).append("\n");
insertRevision.append("作者: ").append(insertRev.getAuthor()).append("\n");
insertRevision.append("内容: ").append(para.getText()).append("\n\n");
}
// 判断段落是否为删除修订(整个段落被删除的情况)
if (para.isDeleteRevision()) {
deleteIndex++;
deleteRevision.append("索引: ").append(deleteIndex).append("\n");
EditRevision deleteRev = para.getDeleteRevision();
deleteRevision.append("类型: ").append(deleteRev.getType()).append("\n");
deleteRevision.append("作者: ").append(deleteRev.getAuthor()).append("\n\n");
}
// 遍历段落内部的子对象(TextRange 级别)
for (DocumentObject obj : (Iterable<DocumentObject>) para.getChildObjects()) {
if (obj instanceof TextRange) {
TextRange textRange = (TextRange) obj;
// 判断文本区域是否为插入修订
if (textRange.isInsertRevision()) {
insertIndex++;
insertRevision.append("索引: ").append(insertIndex).append("\n");
EditRevision insertRev = textRange.getInsertRevision();
insertRevision.append("类型: ").append(insertRev.getType()).append("\n");
insertRevision.append("作者: ").append(insertRev.getAuthor()).append("\n");
insertRevision.append("内容: ").append(textRange.getText()).append("\n\n");
}
// 判断文本区域是否为删除修订
if (textRange.isDeleteRevision()) {
deleteIndex++;
deleteRevision.append("索引: ").append(deleteIndex).append("\n");
EditRevision deleteRev = textRange.getDeleteRevision();
deleteRevision.append("类型: ").append(deleteRev.getType()).append("\n");
deleteRevision.append("作者: ").append(deleteRev.getAuthor()).append("\n");
deleteRevision.append("删除内容: ").append(textRange.getText()).append("\n\n");
}
}
}
}
}
}
// 将结果保存到文本文件
try (FileWriter writer1 = new FileWriter("InsertRevisions.txt")) {
writer1.write(insertRevision.toString());
}
try (FileWriter writer2 = new FileWriter("DeleteRevisions.txt")) {
writer2.write(deleteRevision.toString());
}
System.out.println("修订记录已导出完成");
System.out.println("插入修订数量: " + insertIndex);
System.out.println("删除修订数量: " + deleteIndex);
document.close();
}
}
四、代码逻辑说明
上述代码的核心逻辑分为三个层次:
段落级别检测 :通过 Paragraph.isInsertRevision() 和 Paragraph.isDeleteRevision() 方法判断整个段落是否为修订内容。如果是,则使用 getInsertRevision() 或 getDeleteRevision() 获取修订对象,进而得到修订类型(getType())和作者(getAuthor())。
文本区域检测 :段落内部的文本区域也需要单独遍历,因为修订可能发生在段落内部的局部区域(而非整段)。通过 TextRange.isInsertRevision() 和 TextRange.isDeleteRevision() 方法进行判断。
修订类型区分 :插入修订类型的枚举值为 Insertion,删除修订类型的枚举值为 Deletion。
五、获取格式更改修订
对于格式更改类的修订(如字体、字号、颜色的修改),上述基础代码无法直接获取。较新版本的 API 提供了 RevisionInfoCollection 来获取更全面的修订信息:
java
import com.spire.doc.Document;
import com.spire.doc.revision.RevisionInfo;
import com.spire.doc.revision.RevisionType;
public class GetAllRevisionsWithFormat {
public static void main(String[] args) {
Document document = new Document();
document.loadFromFile("RevisedDocument.docx");
com.spire.doc.revision.RevisionInfoCollection revisions = document.getRevisionInfos();
for (RevisionInfo revision : (Iterable<RevisionInfo>) revisions) {
System.out.println("修订类型: " + revision.getRevisionType());
System.out.println("作者: " + revision.getAuthor());
System.out.println("时间: " + revision.getDateTime());
if (revision.getRevisionType() == RevisionType.Format_Change) {
System.out.println("该条修订为格式更改类型");
}
}
document.close();
}
}
六、注意事项
-
修订与批注的区别:修订记录的是对文档内容的实际修改(插入、删除、格式更改),而批注是附加的注释信息。两者是不同的概念,获取方式也有所不同。
-
版本兼容性:获取格式更改修订的功能在较新版本中才得到支持。如果使用的是较早版本,格式更改可能无法被正确识别。建议查阅所使用的版本对应的 API 文档。
-
性能考虑:对于包含大量修订的长文档,遍历全部修订可能需要一定时间。建议在需要完整修订记录时使用完整遍历,若仅需特定类型的修订,可以在遍历过程中增加筛选条件。
-
文档格式 :上述方法主要适用于
.docx格式的文档。对于旧版.doc格式,修订信息的读取可能存在兼容性问题,建议提前转换格式。
总结
通过上述示例可以看出,使用 Java 获取 Word 文档中的修订记录,主要涉及文档加载、遍历段落和文本区域、判断修订类型并提取信息等步骤。根据需求的不同,可以选择获取段落级别的修订,也可以进一步获取文本区域级别的细粒度修订。对于格式更改类的修订,则需要使用较新版本的 API 通过 RevisionInfoCollection 进行获取。这些方法可以应用于文档审阅、版本对比、修改追踪等业务场景。在实际项目开发中,建议根据文档的复杂程度和修订类型需求,选择合适的遍历粒度和 API 版本,以确保获取到的修订信息完整且准确。此外,对于需要批量处理多个文档或生成修订报告的情况,可以在现有代码基础上进一步封装,形成可复用的工具方法。