Java docx4j 操作word 1.0

一、简介

      本工具类复制即可使用,内附测试代码,包含以下操作:

      -- word 中 属性值替换

      -- word 中 列表动态插入数据

      -- word 转 pdf

二、环境

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.2</version>
</dependency>
<dependency>
    <groupId>freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.8</version>
</dependency>
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j</artifactId>
    <version>6.1.2</version>
</dependency>
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-export-fo</artifactId>
    <version>8.1.7</version> //版本号不能高于8.1.7
</dependency>
  <!-- 条形码 -->
<dependency>
    <groupId>net.sf.barcode4j</groupId>
    <artifactId>barcode4j-light</artifactId>
    <version>2.0</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.5</version>
</dependency>

三、工具类

import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import org.docx4j.Docx4J;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.ClassFinder;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.jaxb.Context;
import org.docx4j.model.datastorage.migration.VariablePrepare;
import org.docx4j.model.table.TblFactory;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.springframework.core.io.ClassPathResource;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import java.io.*;
import java.util.*;

/**
 * Time:   2021/12/20 13:22
 * Author: Dily
 * Remark: 
 */
public class Docx4jUtils {

    /**
     * 创建一个空白 Docx 文档
     *
     * @param filePath 文件路径
     */
    public void createDocx(String filePath) throws Docx4JException {
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
        wordMLPackage.save(new File(filePath));
    }

    /**
     * 加载 Docx 文件
     *
     * @param filePath 文件地址
     * @return WordProcessingMLPackage操作包
     */
    public WordprocessingMLPackage loadDocx(String filePath) throws Docx4JException {
        return WordprocessingMLPackage.load(new File(filePath));
    }

    /**
     * 创建一个空白 Docx 文档
     *
     * @param outputStream 流
     */
    public void createDocx(OutputStream outputStream) throws Docx4JException {
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
        wordMLPackage.save(outputStream);

    }

    /**
     * 添加带样式的文本/段落
     *
     * @param wordMLPackage docx 操作包
     * @param styled        样式
     * @param conText       文本
     * @return WordProcessingMLPackage 操作包
     */
    public WordprocessingMLPackage addContext(WordprocessingMLPackage wordMLPackage, String styled, String conText) {
        wordMLPackage.getMainDocumentPart().addStyledParagraphOfText(styled, conText);
        return wordMLPackage;
    }

    /**
     * 添加文本/段落
     *
     * @param wordMLPackage docx 操作包
     * @param conText       正文
     * @return WordProcessingMLPackage 操作包
     */
    public WordprocessingMLPackage addContext(WordprocessingMLPackage wordMLPackage, String conText) {
        wordMLPackage.getMainDocumentPart().addParagraphOfText(conText);
        return wordMLPackage;
    }

    /**
     * 添加图片
     *
     * @param wordMLPackage 操作包
     * @param imagePath     图片地址
     * @return WordProcessingMLPackage 操作包
     */
    public WordprocessingMLPackage addImage(WordprocessingMLPackage wordMLPackage, String imagePath) throws Exception {
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, new File(imagePath));
        Inline inline = imagePart.createImageInline("Filename hint", "Alternative text", 1, 2, false);
        ObjectFactory factory = new ObjectFactory();
        P paragraph = factory.createP();
        R run = factory.createR();
        paragraph.getContent().add(run);
        Drawing drawing = factory.createDrawing();
        run.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);
        wordMLPackage.getMainDocumentPart().addObject(paragraph);
        return wordMLPackage;
    }

    /**
     * 添加空表格
     *
     * @param wordMLPackage 操作包
     * @param row           行数
     * @param col           列数
     * @return WordProcessingMLPackage 操作包
     */
    public WordprocessingMLPackage addTable(WordprocessingMLPackage wordMLPackage, int row, int col) {
        Tbl table = TblFactory.createTable(row, col, 20000 / col);
        wordMLPackage.getMainDocumentPart().addObject(table);
        return wordMLPackage;
    }

    /**
     * 添加带数据表格, 数据必须是整齐的
     * 表头为数据的 key, 表头在第一行
     *
     * @param wordMLPackage 操作包
     * @param list          数据
     * @return WordProcessingMLPackage 操作包
     */
    public WordprocessingMLPackage addTableWithDataAndTopHeader(WordprocessingMLPackage wordMLPackage, List<Map<String, String>> list) {
        Set<String> keySet = new HashSet<>(list.get(0).keySet());
        ObjectFactory factory = Context.getWmlObjectFactory();
        Tbl table = TblFactory.createTable(0, 0, 20000 / list.get(0).size());

        // 表头
        Tr tableHeader = factory.createTr();
        keySet.forEach(e -> {
            Tc tableCell = factory.createTc();
            tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(e));
            tableHeader.getContent().add(tableCell);
        });
        table.getContent().add(tableHeader);

        // 数据
        list.forEach(e -> {
            Tr tableRow = factory.createTr();
            keySet.forEach(item -> {
                Tc tableCell = factory.createTc();
                tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(e.get(item)));
                tableRow.getContent().add(tableCell);
            });
            table.getContent().add(tableRow);
        });
        wordMLPackage.getMainDocumentPart().addObject(table);
        return wordMLPackage;
    }

    /**
     * 添加带数据表格, 数据必须是整齐的
     * 表头为数据的 key, 表头在第一列
     *
     * @param wordMLPackage 操作包
     * @param list          数据
     * @return WordProcessingMLPackage 操作包
     */
    public WordprocessingMLPackage addTableWithDataAndLeftHeader(WordprocessingMLPackage wordMLPackage, List<Map<String, String>> list) {
        Set<String> keySet = new HashSet<>(list.get(0).keySet());
        ObjectFactory factory = Context.getWmlObjectFactory();
        Tbl table = TblFactory.createTable(0, 0, 20000 / list.get(0).size());
        keySet.forEach(e -> {
            Tr tableRow = factory.createTr();
            Tc tableHeader = factory.createTc();
            tableHeader.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(e));
            tableRow.getContent().add(tableHeader);
            list.forEach(item -> {
                Tc tableCell = factory.createTc();
                tableCell.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(item.get(e)));
                tableRow.getContent().add(tableCell);
            });
            table.getContent().add(tableRow);
        });
        wordMLPackage.getMainDocumentPart().addObject(table);
        return wordMLPackage;
    }

    /**
     * 保存到流
     *
     * @param wordMLPackage 操作包
     * @param outputStream  流
     * @throws Docx4JException 异常
     */
    public void save(WordprocessingMLPackage wordMLPackage, OutputStream outputStream) throws Docx4JException {
        wordMLPackage.save(outputStream);
    }

    /**
     * 保存到文件,文件必须为Docx
     *
     * @param wordMLPackage 操作包
     * @param docxPath      文件地址
     * @throws Docx4JException 异常
     */
    public void save(WordprocessingMLPackage wordMLPackage, String docxPath) throws Docx4JException {
        wordMLPackage.save(new File(docxPath));
    }

    /**
     * 获取文件中所有内容
     *
     * @param obj      JAXBElement子类
     * @param toSearch 搜索的类型
     * @return 符合搜索类型的所有对象
     */
    public List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
        List<Object> result = new ArrayList<>();
        if (obj instanceof JAXBElement)
            obj = ((JAXBElement<?>) obj).getValue();
        if (obj.getClass().equals(toSearch))
            result.add(obj);
        else if (obj instanceof ContentAccessor) {
            List<?> children = ((ContentAccessor) obj).getContent();
            for (Object child : children) {
                result.addAll(getAllElementFromObject(child, toSearch));
            }
        }
        return result;
    }

    /**
     * 加载模板并替换数据
     *
     * @param filePath 模板文件路径
     * @param data     数据属性map
     * @return 输出文件路径
     * @throws Exception 异常
     */
    public String replaceData(String filePath, Map<String, String> data, String out) throws Exception {
        // 生成文件
        String target = UUID.randomUUID().toString();
        String outPath = out + target + ".docx";

        //加载模板文件并创建Word processingMLPackage对象
        InputStream templateInputStream = new FileInputStream(filePath);
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
        VariablePrepare.prepare(wordMLPackage);

        // 替换属性
        documentPart.variableReplace(data);

        OutputStream os = new FileOutputStream(outPath);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        wordMLPackage.save(outputStream);
        outputStream.writeTo(os);
        os.close();
        outputStream.close();
        templateInputStream.close();
        return outPath;
    }

    /**
     * 替换模板Docx中数据和表格数据动态添加
     *
     * @param filePath      文件路径
     * @param data          全局替换属性Map
     * @param tableDataList 列表属性
     * @return 输出文件路径
     * @throws Exception 异常
     */
    public String replaceData(String filePath, Map<String, String> data, List<Map<String, Object>> tableDataList, String out) throws Exception {
        String target = UUID.randomUUID().toString();
        // 输出文件名
        String outPath = out + target + ".docx";

        // 加载模板文件并创建Word processingMLPackage对象
        InputStream templateInputStream = new FileInputStream(filePath);
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);

        // 构造循环列表的数据
        ClassFinder find = new ClassFinder(Tbl.class);

        new TraversalUtil(wordMLPackage.getMainDocumentPart().getContent(), find);

        // 获取第二个表格属性
        if (find.results.size() > 0) {
            Tbl table = (Tbl) find.results.get(0);

            // 第二行约定为模板
            Tr dynamicTr = (Tr) table.getContent().get(1);

            // 获取模板行的xml数据
            String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);

            // 循环填充模板表格行数据
            for (Map<String, Object> dataMap : tableDataList) {

                Tr newTr = (Tr) XmlUtils.unmarshallFromTemplate(dynamicTrXml, dataMap);

                table.getContent().add(newTr);

            }

            // 删除模板行的占位行
            table.getContent().remove(1);
        }

        // 设置全局的变量替换
        wordMLPackage.getMainDocumentPart().variableReplace(data);

        Docx4J.save(wordMLPackage, new File(outPath));

        return outPath;
    }

    /**
     * 替换模板Docx中数据和表格数据动态添加
     *
     * @param filePath      文件路径
     * @param data          全局替换属性Map
     * @param tableDataList 列表属性
     */
    public void replaceData(String filePath, Map<String, String> data, List<Map<String, Object>> tableDataList, OutputStream out) throws Exception {
        // 加载模板文件并创建Word processingMLPackage对象
        InputStream templateInputStream = new FileInputStream(filePath);
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);

        // 构造循环列表的数据
        ClassFinder find = new ClassFinder(Tbl.class);

        new TraversalUtil(wordMLPackage.getMainDocumentPart().getContent(), find);

        // 获取第一个表格属性
        if (find.results.size() > 0) {
            Tbl table = (Tbl) find.results.get(0);

            // 第二行约定为模板
            Tr dynamicTr = (Tr) table.getContent().get(1);

            // 获取模板行的xml数据
            String dynamicTrXml = XmlUtils.marshaltoString(dynamicTr);

            // 循环填充模板表格行数据
            tableDataList.forEach(e -> {
                try {
                    table.getContent().add((Tr) XmlUtils.unmarshallFromTemplate(dynamicTrXml, e));
                } catch (JAXBException jaxbException) {
                    jaxbException.printStackTrace();
                }
            });

            // 删除模板行的占位行
            table.getContent().remove(1);
        }

        // 设置全局的变量替换
        wordMLPackage.getMainDocumentPart().variableReplace(data);
        Docx4J.save(wordMLPackage, out);
        templateInputStream.close();
    }

    /**
     * 加载模板并替换数据
     *
     * @param filePath 模板文件路径
     * @param data     数据属性map
     */
    public void replaceData(String filePath, Map<String, String> data, OutputStream out) throws Exception {
        //加载模板文件并创建Word processingMLPackage对象
        InputStream templateInputStream = new FileInputStream(filePath);
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
        VariablePrepare.prepare(wordMLPackage);

        // 替换属性
        documentPart.variableReplace(data);
        wordMLPackage.save(out);
        templateInputStream.close();
    }

    /**
     * word 转 pdf
     *
     * @param wordPath word文档地址
     * @return pdf地址
     */
    public String Docx2Pdf(String wordPath) {
        OutputStream os = null;
        InputStream is = null;
        //输出pdf文件路径和名称
        String pdfNoMarkPath = wordPath.substring(0, wordPath.indexOf('.')) + ".pdf";
        try {
            is = new FileInputStream(wordPath);
            WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is);
            Mapper fontMapper = new IdentityPlusMapper();
            fontMapper.put("等线", PhysicalFonts.get("SimSun"));
            fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
            fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
            fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
            fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
            fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
            fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
            fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
            fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
            fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
            fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
            fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
            fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
            fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
            fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
            fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
            fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
            fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
            fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
            fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
            fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
            fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
            fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
            fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));
            PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));            //解决宋体(正文)和宋体(标题)的乱码问题
            PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
            fontMapper.put("SimSun", PhysicalFonts.get("SimSun"));             //宋体&新宋体
            mlPackage.setFontMapper(fontMapper);

            os = new FileOutputStream(pdfNoMarkPath);

            //docx4j  docx转pdf
            FOSettings foSettings = Docx4J.createFOSettings();
            foSettings.setWmlPackage(mlPackage);
            Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);

            is.close();//关闭输入流
            os.close();//关闭输出流

            return pdfNoMarkPath;
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } finally {
            File file = new File(wordPath);
            if (file.isFile() && file.exists()) {
                file.delete();
            }
        }
        return "";
    }

    /**
     * 添加水印图片
     *
     * @param inPdfPath 无水印pdf路径
     * @param markPath  水印图片地址
     * @return 生成的带水印的pdf路径
     */
    public String addTextMark(String inPdfPath, String markPath) {
        PdfStamper stamp = null;
        PdfReader reader = null;
        try {
            //输出pdf带水印文件路径和名称
            String outPdfMarkPath = inPdfPath.substring(0, inPdfPath.indexOf('.')) + "水印.pdf";

            //添加水印
            reader = new PdfReader(inPdfPath, "PDF".getBytes());
            stamp = new PdfStamper(reader, new FileOutputStream(outPdfMarkPath));
            PdfContentByte under;
            int pageSize = reader.getNumberOfPages();// 原pdf文件的总页数
            //水印图片
            Image image;
            if (markPath.contains(":"))
                image = Image.getInstance(markPath);
            else {
                ClassPathResource resource = new ClassPathResource(markPath);
                image = Image.getInstance(resource.getFile().getPath());
            }
            for (int i = 1; i <= pageSize; i++) {
                under = stamp.getOverContent(i);// 水印在之前文本下
                for (int j = 0; j < 4; j++) {
                    image.setAbsolutePosition(0, j * 250 + 100);//水印位置
                    under.addImage(image);
                    image.setAbsolutePosition(200, j * 250 + 100);//水印位置
                    under.addImage(image);
                    image.setAbsolutePosition(400, j * 250 + 100);//水印位置
                    under.addImage(image);
                }
            }
            stamp.close();// 关闭
            reader.close();//关闭

            return outPdfMarkPath;
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (stamp != null) {
                    stamp.close();
                }
                if (reader != null) {
                    reader.close();//关闭
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } finally {
            //删除生成的无水印pdf
            File file = new File(inPdfPath);
            if (file.exists() && file.isFile()) {
                file.delete();
            }
        }
        return "";
    }
}

四、水印工具类

Java 生成水印https://mp.csdn.net/mp_blog/creation/editor/121976565https://mp.csdn.net/mp_blog/creation/editor/121976565

五、docx模板及替换结果

Word模板文档
替换后的PDF文档(未加水印)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值