spire.doc for java 循环复制文档中的某一个段落

652 阅读2分钟

这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

前言

最近我的新任务是以word的形式导出当前仪表板展示的 全部 数据。我之前写过只导出一个仪表板展示的数据,导出全部数据怎么写?每个菜单下的仪表板展示的数据长度(个数)不一样,应该怎么办?菜单显示的内容只需要一次而仪表板展示的数据要根据实际的数据长度来看,这时我想到了先制作一个word文档的模板,然后在复制使用spire.doc来循环赋值出特定的部分。如下图:第一张图是我制作的固定的模板,我的业务需求是要赋值出来多个${name}仪表板下面的多个段落的内容,最后复制的结果如第二张图所示。

369d2ec0fe274c8c75a2504528ec321.png

image.png

>>正文

读文档

spire.doc这个类库的加载文档和保存文档分别使用的是:loadFromFile()方法saveToFile()方法

  • loadFromFile()方法
    • loadFromFile(String fileName):加载文件的名称;
    • loadFromFile(String fileName, FileFormat fileformat):加载的文件名称和文件类型;
  • saveToFile()方法
    • saveToFile(String fileName):保存文件的名称,自动默认文档输入的格式保存文档;
    • saveToFile(String fileName, FileFormat fileformat):保存文件的名称和保存的参数格式;
/**导出数据的具体处理细节
 * @param projectEntity: 项目信息实体类
 * @param interfaceList: 项目下的接口信息
 * @param response: 浏览器的应答
 * */
public void exportWord(){
    // 获取要导入更新的模板--获取项目的绝对路径
    String path = new File("src\templates\tempAll.docx").getAbsolutePath(); 
    //创建文档对象
    Document sourceDocument = new Document();
    // 加载文档
    sourceDocument.loadFromFile(path);
    //处理文档:复制,删除,添加文字背景色等处理
   
     //遍历文档1中的所有子对象
    for (int i = 0; i < sourceDocument.getSections().getCount(); i++) {
        Section section = sourceDocument.getSections().get(i);
        for( int j = 0;j< section.getBody().getChildObjects().getCount();j++){
            Object object = section.getBody().getChildObjects().get(j);
            //复制文档中的同一个段落
            sourceDocument.getSections().get(0).getBody().getChildObjects().add(((DocumentObject) object).deepClone());
        }
    }
   
    //处理完文档之后保存文档
    sourceDocument.saveToFile("CopyDoc.docx", FileFormat.Docx_2013);
        
}

循环复制段落个表格

解释
Document表示文档模型
Section表示文档模型中的节
Paragraph表示文档模型中的段落

核心思想:

  1. 使用findAllString()方法在文档中找匹配对应的字符串,将找到的字符串依次添加到TextSelection[]数组中。
  2. 判断文章的结尾的时候开始的节==结尾的节,如果不等的话则不同的节进行复制;反之,开始复制整个文档。
/** 根据interfaceList的长度来生成模板的表格和段落
 * @param interfaceList: 项目下的接口信息,根据interfaceList.size的长度来决定要复制的次数。
 * */
private  void addParagraphAndTable(List<InterfaceEntity> interfaceList){
    //获取要导入更新的模板--获取项目的绝对路径
    String path = new File("src\templates\tempAll.docx").getAbsolutePath(); 
    Document sourceDocument = new Document();
    sourceDocument.loadFromFile(path);
    // 开始节
    int startSec = 0;
    // 结束节
    int endSec = 0;
    // 开始段落
    int startPara = 0;
    // 结束段落
    int endPara=0;
    //需要复制的次数:模板中本来就有一个,所以需要减1
    int copyNum = interfaceList.size()-1;
    // int copyNum = 4;
    //通过查找定位要复制的开始段落位置
    TextSelection[] selection = sourceDocument.findAllString("${name}仪表板", false, true);
    //textIndex 文章开始的复制段落的标志
    for(int textIndex = 0; textIndex < selection.length; textIndex++ ){
        TextSelection textSelection = selection[textIndex];
        TextRange range = textSelection.getAsOneRange();
        Paragraph paragraph = range.getOwnerParagraph();
        Section sec = (Section) paragraph.getOwner().getOwner();
        // 文档开始段落的位置
        // 查找文章的开始节
        startSec = sourceDocument.getSections().indexOf(sec);
        // 查找文章的开始段
        startPara = sec.getBody().getParagraphs().indexOf(paragraph);
    }
    //文档结尾段落位置
    endSec = sourceDocument.getSections().indexOf(sourceDocument.getLastSection());
    endPara= sourceDocument.getLastSection().getBody().getChildObjects().getCount() - 1;
    if (startSec == endSec) {
        copyBetweenParagraphsInSameSection(sourceDocument, startPara, endPara,startSec, copyNum);
    } else {
        copyBetweenParagraphsInDifferentSection(sourceDocument, startPara, endPara, startSec, endSec, copyNum);
    }
    // 将结果保存到流中
    sourceDocument.saveToFile("D:\Output.docx", FileFormat.Docx);
   
}

复制处理

/**在同一个节中复制段落
 * @param sourceDocument:复制的源文件
 * @param startPara:复制的开始段落
 * @param endPara:复制的结束段落
 * @param sec:文章的开始节
 * @param copyNum:复制的次数
 * */
private static void copyBetweenParagraphsInSameSection(Document sourceDocument, int startPara, int endPara, int sec, int copyNum) {
    for (int j = 0; j < copyNum; j++) {
        for (int i = startPara ; i < endPara; i++) {
            DocumentObject docObj = sourceDocument.getSections().get(sec).getBody().getChildObjects().get(i).deepClone();
            //复制之后,在文档的节后面加一个分节符
            sourceDocument.getSections().get(sec).setBreakCode(SectionBreakType.No_Break);
            //文档结尾添加:将文档中获取到的内容追加到当前文档的结尾处开
            sourceDocument.getLastSection().getBody().getChildObjects().add(docObj);
        }
    }
}
/**在不同的节中复制段落
 * @param sourceDocument:复制的源文件
 * @param startPara:复制的开始段落
 * @param endPara:复制的结束段落
 * @param startSec:文章的开始节
 * @param endSec :文章的结束节
 * @param copyNum:复制的次数
 * */
private static void copyBetweenParagraphsInDifferentSection(Document sourceDocument, int startPara, int endPara,int startSec, int endSec, int copyNum) {
    // int m:文档是否已经被复制过的一个标志
    int m = 0;
    for (int j = 0; j < copyNum; j++) {
        for (int sec = startSec; sec <= endSec;) {
            //在同一个章节下的段落开始复制
            if (m == 0) {
                for (int i = startPara; i < (sourceDocument.getSections().get(sec).getBody().getChildObjects().getCount() - 1); i++) {
                    DocumentObject documentObj = sourceDocument.getSections().get(sec).getBody().getChildObjects().get(i).deepClone();
                    //文档结尾添加
                    sourceDocument.getLastSection().getBody().getChildObjects().add(documentObj);
                   //添加分节符
                   sourceDocument.getLastSection().setBreakCode(SectionBreakType.No_Break);
                }
                sec++;
                m++;
            }
            if (m != 0 && (sec != endSec)) {
                for (int i = 0; i < (sourceDocument.getSections().get(sec).getBody().getChildObjects().getCount() - 1); i++) {
                    DocumentObject documentObj = sourceDocument.getSections().get(sec).getBody().getChildObjects().get(i).deepClone();
                    //文档结尾添加
                    sourceDocument.getLastSection().getBody().getChildObjects().add(documentObj);
                    sourceDocument.getLastSection().setBreakCode(SectionBreakType.No_Break);
                }
                m++;
                sec++;
            }
            if (m != 0 && (sec== endSec)) {
                for (int i = 0; i <= endPara; i++) {
                    DocumentObject documentObj = sourceDocument.getSections().get(sec).getBody().getChildObjects().get(i).deepClone();
                    //文档结尾添加
                    sourceDocument.getLastSection().getBody().getChildObjects().add(documentObj);
                }
                m++;
                sec++;
            }
        }
    }
}

>>补充

Spire.Doc for Java 是由E-iceblue开发的一款Java系列的office文档操作类库,用于操作MS word、xls、PDF,MS Power Point、条形码和二维码,可以实现文档的创建、编辑、转换、打印等功能,但是如果你同时在你的项目中分别使用了docxls的依赖,就会报错。

//分为商业版本和免费版本,我这里使用的是免费版本
//xls的依赖
<dependency>
    <groupId>com.szht</groupId>
    <artifactId>xls</artifactId>
    <version>3.9.1</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/jar/spire.xls.free-3.9.1.jar</systemPath>
</dependency>

// doc的依赖
<dependency>
    <groupId>com.szht</groupId>
    <artifactId>doc</artifactId>
    <version>3.9.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/jar/spire.doc.free-3.9.0.jar</systemPath>
</dependency>

我先在项目中导入了xls的依赖,后导入doc的依赖,当我开发完了导出word的功能,发现导出excel 的功能又报如下图的错误,当时在想我没有修改导出excel的代码,怎么会导出excel的功能不能用了?疑惑了几天之后,偶然在官网发现:当你在项目中同时使用spire下的多个依赖库的时候,就会报初始化错误(could not initialize class com.spire.xls.......)等之类的错误,这是我们导入一个spire.office的依赖来替换前俩个导入的依赖。

11.png

22.png

//导入这个依赖就可以代替:doc、xls、PDF等
<dependency>
    <groupId>com.szht</groupId>
    <artifactId>office</artifactId>
    <version>3.9.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/jar/spire.office.free-3.9.0.jar</systemPath>
</dependency>

>> 最后

我正在成长,如果有什么问题欢迎大家留言,我们一起讨论。。。

如果对您有用,希望您留下评论/👍/收藏。

您的三连,是对我创作的最大的鼓励🙇‍♀️🙇‍♀️🙇‍♀️