All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ofdrw.tool.merge.OFDMerger Maven / Gradle / Ivy

Go to download

OFD Reader and Writer 文档操作工具,提供如文档合并等操作。

There is a newer version: 2.3.4
Show newest version
package org.ofdrw.tool.merge;


import org.bouncycastle.jcajce.provider.digest.SM3;
import org.bouncycastle.util.encoders.Hex;
import org.dom4j.*;
import org.ofdrw.core.OFDElement;
import org.ofdrw.core.basicStructure.doc.CT_PageArea;
import org.ofdrw.core.basicStructure.pageObj.CT_TemplatePage;
import org.ofdrw.core.basicStructure.pageObj.Template;
import org.ofdrw.core.basicStructure.pageTree.Page;
import org.ofdrw.core.basicStructure.pageTree.Pages;
import org.ofdrw.core.basicStructure.res.CT_MultiMedia;
import org.ofdrw.core.basicType.ST_ID;
import org.ofdrw.core.basicType.ST_Loc;
import org.ofdrw.core.basicType.ST_RefID;
import org.ofdrw.core.compositeObj.CT_VectorG;
import org.ofdrw.core.pageDescription.color.colorSpace.CT_ColorSpace;
import org.ofdrw.core.pageDescription.drawParam.CT_DrawParam;
import org.ofdrw.core.text.font.CT_Font;
import org.ofdrw.pkg.container.PageDir;
import org.ofdrw.pkg.container.PagesDir;
import org.ofdrw.pkg.container.ResDir;
import org.ofdrw.reader.ResourceLocator;
import org.ofdrw.reader.model.TemplatePageEntity;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 文档合并工具
 *
 * @author 权观宇
 * @since 2021-11-08 20:49:36
 */
public class OFDMerger implements Closeable {

    /**
     * 新页面列表
     * 

* 每一个元素代表新文档中的一页 */ public final ArrayList pageArr; /** * 文档上下文映射 */ private final Map docCtxMap; /** * 合并后生成文档位置 */ private final Path dest; /** * 合并的目标文档,仅在合并时设置 * 在合并完成后将会被打包存储 */ private BareOFDDoc ofdDoc; /** * 资源文件哈希表 *

* Key: 文件SM3 Hash Hex * Value: 文件在新文档中的文件名 */ private final Map resFileHashTable; /** * 模板页面映射表 *

* Key: 模板页对象ID * Value: 模板页面对象 */ private final Map tplPageMap; private final AtomicInteger resFileCounter; /** * - Layer 的 DrawParam * - 每个图像对象都可能含有 DrawParam 引用 * - Color 中 Pattern CellContent Thumbnail 引用 * - Color 中 ColorSpace 引用 * - Image 中 ResourceID、Substitution、ImageMask * - Text 中 Font * - Composite 复合对象 中 ResourceID * Res资源中的 CompositeGraphUnit CT_VectorG:Thumbnail、Substitution */ private static final Map AttrQueries = new HashMap() {{ this.put("Font", DocumentHelper.createXPath("//*[@Font]")); this.put("ResourceID", DocumentHelper.createXPath("//*[@ResourceID]")); this.put("Substitution", DocumentHelper.createXPath("//*[@Substitution]")); this.put("ImageMask", DocumentHelper.createXPath("//*[@ImageMask]")); this.put("Thumbnail", DocumentHelper.createXPath("//*[@Thumbnail]")); this.put("DrawParam", DocumentHelper.createXPath("//*[@DrawParam]")); this.put("ColorSpace", DocumentHelper.createXPath("//*[@ColorSpace]")); }}; public OFDMerger(Path dest) { if (dest == null) { throw new IllegalArgumentException("合并结果路径(dest)为空"); } pageArr = new ArrayList<>(10); docCtxMap = new HashMap<>(); this.dest = dest; final Path parent = dest.getParent(); if (parent == null || !Files.exists(parent)) { throw new IllegalArgumentException("OFD文件存储路径(dest)上级目录 [" + parent + "] 不存在"); } resFileHashTable = new HashMap<>(3); tplPageMap = new HashMap<>(2); resFileCounter = new AtomicInteger(0); } /** * 向合并文件中添加页面 * * @param filepath 待合并的OFD文件路径 * @param pageIndexes 页面序序列,如果为空表示所有页面(页码从1开始) * @return this * @throws IOException 页面读写异常 */ public OFDMerger add(Path filepath, int... pageIndexes) throws IOException { String key = filepath.toAbsolutePath().getFileName().toString(); DocContext ctx = docCtxMap.get(key); // 缓存中没有该文件映射 if (ctx == null) { // 加载文件上下文 ctx = new DocContext(filepath); docCtxMap.put(key, ctx); } // 没有传递页码时认为需要追加所有页面 if (pageIndexes == null || pageIndexes.length == 0) { int numberOfPages = ctx.reader.getNumberOfPages(); pageIndexes = new int[numberOfPages]; for (int i = 0; i < pageIndexes.length; i++) { pageIndexes[i] = i + 1; } } // 追加内容到页面列表中 for (int pageIndex : pageIndexes) { pageArr.add(new PageEntry(pageIndex, ctx)); } return this; } /** * 向合并文件中添加页面 *

* 通过该方法可以详细设置页面迁移时的属性参数 * * @param pages 页面对象 * @return this */ public OFDMerger add(PageEntry... pages) { if (pages == null) { return this; } for (PageEntry page : pages) { if (page.docCtx == null || page.docCtx.filepath == null) { continue; } String key = page.docCtx.filepath.toAbsolutePath().getFileName().toString(); // 缓存中没有该文件映射 if (!docCtxMap.containsKey(key)) { docCtxMap.put(key, page.docCtx); } pageArr.add(page); } return this; } /** * 执行合并 */ private void doMerge() throws IOException, DocumentException { // 删除原来存在的问题 if (Files.exists(dest)) { Files.delete(dest); } // 创建新文档 try (final BareOFDDoc ofdDoc = new BareOFDDoc(dest)) { this.ofdDoc = ofdDoc; final Pages pages = ofdDoc.document.getPages(); // 如果存在Pages那么获取,不存在那么创建 final PagesDir pagesDir = ofdDoc.docDir.obtainPages(); for (final PageEntry pageEntry : pageArr) { // 取0文档对象 final CT_PageArea docDefaultArea = new CT_PageArea((Element) pageEntry.docCtx.getDefaultArea(0).clone()); org.ofdrw.core.basicStructure.pageObj.Page page = null; // 解析原OFD页面的Content.xml 为Page对象 try { Element copy = (Element) pageEntry.docCtx.reader.getPage(pageEntry.pageIndex).clone(); final Document document = DocumentHelper.createDocument(); document.add(copy); page = new org.ofdrw.core.basicStructure.pageObj.Page(copy); } catch (NumberFormatException e) { // 忽略页码非法的页面复制 continue; } // 若当前页面的页面区域的大小和位置为空,则使用文档默认的尺寸 if (page.getArea() == null) { page.setArea(docDefaultArea); } // 创建页面容器 final PageDir pageDir = newPage(pages, pagesDir); if (pageEntry.copyTemplate) { // 页面模板的迁移的替换 final List