java使用Aspose.word保存word更新目录页码报错以及样式错乱解决

保存文件之前,使用aspose.word中的这个方法:

Document.updateFields()

 更新域时会更新目录,但是页码可能会有偏差,原因是无法保证域的更新顺序,目录可能不是最后一个更新的,而在更新其他域时导致页码再次发生变化。而且这个更新方法不止会更新页码,还会导致样式和更新前发生改变。

    /**
     * 按格式保存文件
     * @param outputPath    输出路径
     * @param saveFormat    文件格式
     */
    public void saveFile(String outputPath,int saveFormat) throws Exception {
        //更新域的方法
        document.updateFields();
        File newFile = new File(outputPath);
        FileOutputStream fos = null;
        try{
            fos = new FileOutputStream(newFile);
            this.save(fos,saveFormat);
            fos.flush();
        }finally {
            StreamUtil.closeQuietly(fos);
        }
    }

于是我摸索了一番,得到下面这种解决方式,我们可以自己去写更新域的逻辑:

    
    /**
     * 拿到所有书签
     */
    public Set getAllBookmark(){
        Set r = new HashSet<>();
        BookmarkCollection collection = document.getRange().getBookmarks();
        for (Bookmark bookmark : collection) {
            r.add(bookmark.getName());
        }
        return r;
    }

    /**
     * 更新文档域以及目录页码
     */
    @SneakyThrows
    public void update(){
        FieldCollection fields = document.getRange().getFields();
        //FieldType.FIELD_PAGE_REF => 目录中的每一个标题,这些标题如果设置成目录,则标题必须添加_Toc开头的隐藏标签(设置目录会自动添加标签),才能成功更新目录
        //否则会报错“bookmark not defined”
        //预处理所有目录标题,添加_Toc标签
        try{
            Set abs = this.getAllBookmark();
            for (int i = 0; i < fields.getCount(); i++) {
                Field field = fields.get(i);
                //判断域对象是否是目录的引用
                if(field.getType() == FieldType.FIELD_PAGE_REF){
                    FieldPageRef pageRef = (FieldPageRef) field;
                    String name = pageRef.getBookmarkName();
                    //判断目录的引用是否设置了_Toc前缀的标签,没有则手动添加一个,自动校正
                    if (name.startsWith("_Toc")) {
                        if(!abs.contains(name)){
                            try{
                                builder.moveTo(pageRef.getStart());
                                builder.startBookmark(name);
                                builder.moveTo(pageRef.getEnd());
                                builder.endBookmark(name);
                            }catch (Exception e){
                                log.warn("给【{}】添加标签失败:{}",name,e.getMessage());
                            }
                            abs.add(name);
                        }

                    }
                }
            }
            List tocList = new ArrayList<>();
            //先更新文档全局
            for (int i = 0; i < fields.getCount(); i++) {
                Field field = fields.get(i);
                if(field.getType() == FieldType.FIELD_TOC){
                    FieldToc fieldToc = (FieldToc) field;
                    tocList.add(fieldToc);
                }else{
                    field.update();
                }
            }
            //最后更新页数
            for (FieldToc fieldToc : tocList) {
                fieldToc.updatePageNumbers();
            }
            //处理bookmark not defined的错误,但目录样式会改变
            String text = this.getRange().getText().toLowerCase();
            if(text.contains("error! bookmark not defined")){
                log.warn("目录更新页码时出错:目录中的标题的标签丢失,尝试更新整个文档。请重新生成模板文件的目录保证此错误不会出现");
                document.getRange().updateFields();
            }
        }catch (Exception e){
            log.error("自动修复目录时发生错误:",e);
            document.getRange().updateFields();
        }
    }

注意:目录里面的每个标题,在文档中都会有一个引用, 这些引用都必须加上一个_Toc开头的隐藏书签,否则目录页码无法正常更新,手动更新目录时会出现标签未定义的错误。

你可能感兴趣的:(java,word,java,开发语言)