【Java处理word文档】

Java处理word文档

  • 前言
  • 一、word是什么?
  • 二、Java处理word
    • 2.1、依赖包
    • 2.2、加载word样式
    • 2.3、读入文件
    • 2.4、单一样式段落
    • 2.5、复合样式段落
    • 2.6、将段落写入word
    • 2.7、word表格
    • 2.8、超链接
    • 2.9、写入样式及文件保存
    • 总结
    • 示例源码
      • 样式xml
      • WordConstant
      • WordStylesUtil
      • WordUtil
      • Main

        前言

        这篇文章记录Java处理word的相关内容。

        看完以下内容,word中的大多数甚至所有操作你都可以通过java代码实现。

        注意:此篇文章处理的word格式为Microsoft Office Word 2007及之后的版本,也就是后缀为docx的word文件。


        一、word是什么?

        word其实是xml文件(格式为Microsoft Office Word 2007及之后的版本)。手动创建一个word文档,将后缀名改为zip,然后使用解压缩工具解压。在解压结果的word目录下有一个document.xml文件。这个文件就是word文档的主要内容。还有一个styles.xml文件,此文件定义word中的样式。

        二、Java处理word

        首先给一个通过java生成的word文档的示例。

        将word文档后缀修改为zip,然后解压得到如下图中的文件:

        打开word目录后,如下,其中的document.xml就是word文档的主要内容。

        下来我们针对这个例子进行说明。

        2.1、依赖包

        以下依赖包中包含hutool相关的内容,此依赖包和处理word没有任何关系,只是其中的各种工具类用起来比较方便。

          org.docx4j docx4j-JAXB-Internal 8.2.9   cn.hutool hutool-all 5.3.10 

        2.2、加载word样式

        word的样式是存放在一个名为styles.xml的文件中,我们在处理word样式的时候为了方便,可以将想要的样式存放在代码的资源文件中,然后通过docx4j提供的api加载即可。以下为加载样式xml文件的代码。(样式xml文件后续在示例代码中给出源码)

        InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("styles.xml");
        Styles styles = (Styles) XmlUtils.unmarshal(resourceAsStream);
        

        2.3、读入文件

        通过docx4j依赖包提供的api加载文件(此文件如果不存在则会自动创建),返回WordprocessingMLPackage对象,后续处理都是在其基础上。

        File outputFile = new File("C:\\Users\\admin\\Desktop\\test.docx");
        // Create the package 建包
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
        // 另存为新的文件 保存
        // 如果原文件已存在的话,里面的内容会被清空
        wordMLPackage.save(docxFile);
        WordprocessingMLPackage wPackage = WordprocessingMLPackage.load(outputFile);
        

        2.4、单一样式段落

        首先我们看一下word文档解压后的document.xml文件中的内容。

        先看示例中的“标题1”。

        标题1

        通过docx4j提供的ObjectFactory factory = Context.getWmlObjectFactory();对象工厂。ObjectFactory可以生成段落、表格、超链接等等与word相关的对象。

        以下java代码生成一个单一样式的段落。

        通过xml文件我们可以看到,首先我们需要创建一个段落P对象,同时设置段落的ppr。然后还需要创建一个R对象,将R对象通过P.getContent().add(R);与段落进行关联。

         /**
             * 根据样式ID获取段落对象P
             *
             * @param paragraphText
             * @param styleId
             * @return org.docx4j.wml.P
             */
            public static P getPByStyleId(String paragraphText, String styleId) { Style style = WordStylesUtil.getStyleById(styleId);
                ObjectFactory factory = Context.getWmlObjectFactory();
                P paragraph = factory.createP();
                R run = factory.createR();
                run.setRPr(style.getRPr());
                String[] split = paragraphText == null ? new String[0] : paragraphText.split("\n");
                if (split.length > 1) { List list = new ArrayList<>();
                    for (String s : split) { Text text = factory.createText();
                        text.setValue(s);
                        // 保留前后空格
                        text.setSpace(SchemaSymbols.ATTVAL_PRESERVE);
                        // 将换行符进行处理为
        Br br = factory.createBr(); list.add(text); list.add(br); } run.getContent().addAll(list); } else { Text text = factory.createText(); text.setValue(paragraphText); run.getContent().add(text); } paragraph.getContent().add(run); paragraph.setPPr(style.getPPr()); return paragraph; }

        2.5、复合样式段落

        示例中的“title:测试title”,其中“title:”进行了加粗处理。

        先看word文档解压后的document.xml文件中的内容。

        title测试title

        可以看到段落P中存在多个R,每个R都有其自己特定的rPr。

        以下java代码实现。

         /**
             * 给word中写入复合样式的段落信息
             *
             * @param wPackage
             * @param context   二维数组,二维中的数据只能有两个元素,第一个为段落内容,第二个为样式ID。例如:[["内容1","样式1"],["内容2","样式2"]]
             */
            public static void complexPWrite(WordprocessingMLPackage wPackage, String[][] context) { if (context.length <= 0) { return;
                }
                P complexP = WordUtil.getComplexP();
                for (int i = 0; i < context.length; i++) { String[] temp = context[i];
                    if (temp.length != 2) { continue;
                    }
                    String paragraph = temp[0];
                    String styleId = temp[1];
                    Style style = WordStylesUtil.getStyleById(styleId);
                    if (StringUtils.isBlank(paragraph) || style == null) { continue;
                    }
                    addRun(complexP, paragraph, style);
                }
                // 写入
                insertOneParagraph(wPackage, complexP);
            }
            /**
             * 复合段落某一段数据处理
             *
             * @param paragraph         整个段落
             * @param paragraphText     当前处理的某段数据
             * @param style             样式
             * @return void
             */
            public static void addRun(P paragraph, String paragraphText, Style style) { ObjectFactory factory = Context.getWmlObjectFactory();
                Text text = factory.createText();
                text.setValue(paragraphText);
                // 保留前后空格
                text.setSpace(SchemaSymbols.ATTVAL_PRESERVE);
                R run = factory.createR();
                run.getContent().add(text);
                run.setRPr(style.getRPr());
                paragraph.getContent().add(run);
            }
        

        2.6、将段落写入word

         /**
             * 添加段落,可以是复合样式的段落
             *
             * @param wPackage
             * @param paragraph 用户自定义好的段落(包括样式与内容)
             * @return void
             */
            public static void insertOneParagraph(WordprocessingMLPackage wPackage, P paragraph) { wPackage.getMainDocumentPart().getContent().add(paragraph);
            }
        

        2.7、word表格

        先看word文档解压后的document.xml文件中的表格内容。

        以下内容看着很长,其实总结下来就是表格tbl下嵌套行tr,行tr下嵌套单元格tc,单元格tc中示例中给的都是段落P。其他就是表格的样式处理。

        单元格1单元格2单元格3单元格4单元格1单元格2单元格4单元格1单元格2单元格4单元格1单元格2单元格4

        然后看java的实现。

        通过ObjectFactory生成Tbl、Tr、Tc等对象,然后将这些对象进行关联,最后将表格Tbl对象写入到word中。

         public static void wordTable(WordprocessingMLPackage wPackage) { // 生成参数表格
                ObjectFactory objectFactory = Context.getWmlObjectFactory();
                Tbl table = objectFactory.createTbl();
                // 标题行 处理
                Tr tr = objectFactory.createTr();
                wordTc(tr, WordUtil.getPByStyleId("单元格1", WHQ_3), A41);
                wordTc(tr, WordUtil.getPByStyleId("单元格2", WHQ_3), A42);
                wordTc(tr, WordUtil.getPByStyleId("单元格3", WHQ_3), A43);
                wordTc(tr, WordUtil.getPByStyleId("单元格4", WHQ_3), A44);
                table.getContent().add(tr);
                // 数据行 处理,测试添加3条数据
                for (int i = 0; i < 3; i++) { Tr trTemp = objectFactory.createTr();
                    wordTc(trTemp, WordUtil.getPByStyleId("单元格1", WHQ_4), A51);
                    wordTc(trTemp, WordUtil.getPByStyleId("单元格2", WHQ_4), A52);
                    wordTc(trTemp, WordUtil.getPByStyleId("是", WHQ_5), A53);
                    wordTc(trTemp, WordUtil.getPByStyleId("单元格4", WHQ_4), A54);
                    table.getContent().add(trTemp);
                }
                // 表格整体样式
                Style tabStyle3 = WordStylesUtil.getStyleById(A3);
                table.setTblPr(new TblPr());
                // 边框线处理
                table.getTblPr().setTblBorders(tabStyle3.getTblPr().getTblBorders());
                // 表格列长度固定不可变
                table.getTblPr().setTblLayout(tabStyle3.getTblPr().getTblLayout());
                // 表格写入word文件
                wPackage.getMainDocumentPart().getContent().add(table);
            }
            public static void wordTc(Tr tr, P p, String tcStyleId) { ObjectFactory objectFactory = Context.getWmlObjectFactory();
                Tc tc = objectFactory.createTc();
                tc.setTcPr(WordStylesUtil.getStyleById(tcStyleId).getTcPr());
                tc.getContent().add(p);
                tr.getContent().add(tc);
            }
        

        2.8、超链接

        示例给出的超链接为word文档内部的超链接。而且是将整个段落作为超链接进行处理。

        还是先看word文档解压后的document.xml文件中的超链接内容。

        为了在word文档中看到超链接跳转的效果。此处加了一个分页。

        从以下xml代码中可以看到。段落的超链接是嵌套在段落中的w:hyperlink标签控制的,w:hyperlink标签中的w:anchor属性为“锚点”。与跳转目标要匹配一致才可以成功跳转,可以看到锚点目标使用w:bookmarkStart标签,其中的w:name属性与w:hyperlink标签中的w:anchor属性一致。

        w:hyperlink标签中的w:history属性也很重要,当我们跳转到目标地方后,如果想通过快捷键alt + ⬅返回上一次的位置,则此属性必须设置为true。

                     超链接1                      超链接2    

        java代码实现。

        超链接起始位置。

         /**
             * 生成超链接段落(调用锚点)。整个段落都是超链接。
             *
             * @param paragraphText
             * @param anchor        超链接跳转锚点标识
             * @param styleId
             * @return org.docx4j.wml.P
             */
            public static P createHyperlinkConsume(String paragraphText, String anchor, String styleId) { ObjectFactory factory = Context.getWmlObjectFactory();
                P p = factory.createP();
                P.Hyperlink pHyperlink = factory.createPHyperlink();
                pHyperlink.setAnchor("_" + getAnchor(anchor));
                // ctrl跳转后,按alt + ⬅ 可以跳转回来。以下参数必须为true,否则光标返回位置不对。
                pHyperlink.setHistory(true);
                Style style = WordStylesUtil.getStyleById(styleId);
                addRun(pHyperlink, paragraphText, style);
                p.getContent().add(pHyperlink);
                p.setPPr(style.getPPr());
                return p;
            }
        

        超链接目标位置。

         /**
             * 生成超链接段落(锚点)
             *
             * @param paragraphText
             * @param anchor
             * @param styleId
             * @return org.docx4j.wml.P
             */
            public static P createHyperlinkProduce(String paragraphText, String anchor, String styleId) { P p = getPByStyleId(paragraphText, styleId);
                ObjectFactory objectFactory = Context.getWmlObjectFactory();
                // 创建一个超链接对象
                BigInteger hli = BigInteger.valueOf(getHyperlinkId());
                CTBookmark ctBookmark = new CTBookmark();
                ctBookmark.setId(hli);
                ctBookmark.setName("_" + getAnchor(anchor));
                JAXBElement pHyperlinkBookmarkStart = objectFactory.createPHyperlinkBookmarkStart(ctBookmark);
                p.getContent().add(pHyperlinkBookmarkStart);
                CTMarkupRange ctMarkupRange = new CTMarkupRange();
                ctMarkupRange.setId(hli);
                objectFactory.createPBookmarkEnd(ctMarkupRange);
                p.getContent().add(ctMarkupRange);
                return p;
            }
        

        2.9、写入样式及文件保存

        最后一定要将样式写入word文档,否则样式都不生效。

         // 写入word文档样式信息
                wPackage.getMainDocumentPart().getStyleDefinitionsPart().getJaxbElement().getStyle().addAll(WordStylesUtil.STYLES.values());
                wPackage.save(outputFile);
        

        总结

        此篇文章只是从word的使用中挑选了部分功能进行说明。如果以上功能没有你想要的结果。可以自己摸索实现。首先你要新建一个word文档,然后将你要的功能写进去,之后将word文档后缀重命名为zip并解压缩,最后你就得到了xml文件,打开xml文件查看其中的xml实现(word目录下的document.xml),或者你想要某种样式,可以查看word目录下的styles.xml。

        最后给出示例中的所有java源码。

        示例源码

        样式xml

                                                                                                                                                                                                                                                                                                                                                                                                                

        WordConstant

        package com.demo.word;
        public class WordConstant { public final static String NORMAL = "Normal";
            // 一级标题
            public final static String HEADING_1 = "Heading1";
            // 二级标题
            public final static String HEADING_2 = "Heading2";
            // 正文段落 加粗 居左
            public final static String WHQ_1 = "whq1";
            // 正文段落 不加粗 居左
            public final static String WHQ_2 = "whq2";
            // 正文段落 加粗 居中,表格单元格,表头使用
            public final static String WHQ_3 = "whq3";
            // 正文段落 不加粗 居左 增加段前间距,表格单元格使用
            public final static String WHQ_4 = "whq4";
            // 正文段落 不加粗 居左 增加段前间距,表格单元格使用,添加超链接样式
            public final static String WHQ_41 = "whq41";
            // 正文段落 不加粗 居中 增加段前间距,表格单元格使用
            public final static String WHQ_5 = "whq5";
            // 表格:边框线、列长度固定不可变
            public final static String A3 = "a3";
            // 表格:表头样式,添加背景色,宽度25
            public final static String A41 = "a41";
            // 表格:表头样式,添加背景色,宽度15
            public final static String A42 = "a42";
            // 表格:表头样式,添加背景色,宽度15
            public final static String A43 = "a43";
            // 表格:表头样式,添加背景色,宽度45
            public final static String A44 = "a44";
            // 表格:其他单元格,默认样式,宽度25
            public final static String A51 = "a51";
            // 表格:其他单元格,默认样式,宽度15
            public final static String A52 = "a52";
            // 表格:其他单元格,默认样式,宽度15
            public final static String A53 = "a53";
            // 表格:其他单元格,默认样式,宽度45
            public final static String A54 = "a54";
        }
        

        WordStylesUtil

        package com.demo.word;
        import cn.hutool.core.collection.CollectionUtil;
        import org.docx4j.XmlUtils;
        import org.docx4j.wml.Style;
        import org.docx4j.wml.Styles;
        import javax.xml.bind.JAXBException;
        import java.io.InputStream;
        import java.util.Map;
        import java.util.function.Function;
        import java.util.stream.Collectors;
        public class WordStylesUtil { // 正文样式ID
            public static final String NORMAL_STYLE = "Normal";
            private WordStylesUtil() { try { InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("styles.xml");
                    Styles styles = (Styles) XmlUtils.unmarshal(resourceAsStream);
                    if (styles != null && !CollectionUtil.isEmpty(styles.getStyle())) { STYLES = styles.getStyle().stream().collect(Collectors.toMap(Style::getStyleId, Function.identity(), (oldVal, newVal) -> newVal));
                    }
                } catch (JAXBException e) { System.err.println("加载styles.xml异常!");
                    System.err.println(e);
                }
            }
            private static WordStylesUtil wordStylesUtil = new WordStylesUtil();
            public static Map STYLES;
            private static WordStylesUtil getInstance() { if (wordStylesUtil == null) { wordStylesUtil = new WordStylesUtil();
                }
                return wordStylesUtil;
            }
            /**
             * 通过样式ID获取Style
             *
             * @param styleId
             * @return org.docx4j.wml.Style
             */
            public static Style getStyleById(String styleId) { getInstance();
                return STYLES.get(styleId);
            }
        }
        

        WordUtil

        package com.demo.word;
        import cn.hutool.crypto.digest.MD5;
        import com.sun.org.apache.xerces.internal.impl.xs.SchemaSymbols;
        import org.apache.commons.lang3.StringUtils;
        import org.docx4j.jaxb.Context;
        import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
        import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
        import org.docx4j.wml.*;
        import org.jvnet.jaxb2_commons.ppp.Child;
        import javax.xml.bind.JAXBElement;
        import java.io.File;
        import java.math.BigInteger;
        import java.nio.charset.StandardCharsets;
        import java.util.ArrayList;
        import java.util.List;
        public class WordUtil { // word 超链接ID
            private static Integer hyperlinkId = 0;
            /**
             * 获取超链接ID,每次自增1
             *
             * @param
             * @return java.lang.Integer
             */
            public static Integer getHyperlinkId() { return ++hyperlinkId;
            }
            /**
             * 创建docx文档
             * 原文件如果已经存在的情况下,原文件中的内容会被清空
             * 在windows中,只要是相同后缀名且前缀也相同就是同一文件(不区分大小写),当是同一后缀名且大小写不一致时,已存在的文件后缀名大小写不会改动
             * 如果原文件已存在,则该word文件不能处于打开状态
             *
             * @param docxFile
             * @return void
             */
            public static void createDocx(File docxFile) throws Exception { // Create the package 建包
                WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
                // 另存为新的文件 保存
                // 如果原文件已存在的话,里面的内容会被清空
                wordMLPackage.save(docxFile);
            }
            /**
             * 添加段落,整个段落使用同一种样式
             *
             * @param wPackage
             * @param paragraphText
             * @param styleId
             * @return void
             */
            public static void insertOneParagraph(WordprocessingMLPackage wPackage, String paragraphText, String styleId) { if (StringUtils.isBlank(paragraphText)) { return;
                }
                MainDocumentPart mdt = wPackage.getMainDocumentPart();
                Style style = WordStylesUtil.getStyleById(styleId);
                if (style == null) { // 未查询到样式,直接添加默认段落
                    mdt.addParagraphOfText(paragraphText);
                } else { mdt.getContent().add(getPByStyleId(paragraphText, styleId));
                }
            }
            /**
             * 根据样式ID获取段落对象P
             *
             * @param paragraphText
             * @param styleId
             * @return org.docx4j.wml.P
             */
            public static P getPByStyleId(String paragraphText, String styleId) { Style style = WordStylesUtil.getStyleById(styleId);
                ObjectFactory factory = Context.getWmlObjectFactory();
                P paragraph = factory.createP();
                R run = factory.createR();
                run.setRPr(style.getRPr());
                String[] split = paragraphText == null ? new String[0] : paragraphText.split("\n");
                if (split.length > 1) { List list = new ArrayList<>();
                    for (String s : split) { Text text = factory.createText();
                        text.setValue(s);
                        // 保留前后空格
                        text.setSpace(SchemaSymbols.ATTVAL_PRESERVE);
                        // 将换行符进行处理为
        Br br = factory.createBr(); list.add(text); list.add(br); } run.getContent().addAll(list); } else { Text text = factory.createText(); text.setValue(paragraphText); run.getContent().add(text); } paragraph.getContent().add(run); paragraph.setPPr(style.getPPr()); return paragraph; } /** * 添加段落,可以是复合样式的段落 * * @param wPackage * @param paragraph 用户自定义好的段落(包括样式与内容) * @return void */ public static void insertOneParagraph(WordprocessingMLPackage wPackage, P paragraph) { wPackage.getMainDocumentPart().getContent().add(paragraph); } /** * 给word中写入复合样式的段落信息 * * @param wPackage * @param context 二维数组,二维中的数据只能有两个元素,第一个为段落内容,第二个为样式ID。例如:[["内容1","样式1"],["内容2","样式2"]] */ public static void complexPWrite(WordprocessingMLPackage wPackage, String[][] context) { if (context.length <= 0) { return; } P complexP = WordUtil.getComplexP(); for (int i = 0; i < context.length; i++) { String[] temp = context[i]; if (temp.length != 2) { continue; } String paragraph = temp[0]; String styleId = temp[1]; Style style = WordStylesUtil.getStyleById(styleId); if (StringUtils.isBlank(paragraph) || style == null) { continue; } addRun(complexP, paragraph, style); } // 写入 insertOneParagraph(wPackage, complexP); } /** * 给word中写入复合样式的段落信息 * * @param wPackage * @param prefix 前缀 * @param prefixStyleId 正文段落 加粗 居左 样式ID * @param text 内容 * @param textStyleId 正文段落 不加粗 居左 样式ID */ public static void complexPWrite(WordprocessingMLPackage wPackage, String prefix, String prefixStyleId, String text, String textStyleId) { String[][] context = new String[2][2]; context[0][0] = prefix; context[0][1] = prefixStyleId; context[1][0] = text; context[1][1] = textStyleId; complexPWrite(wPackage, context); } /** * 复合段落某一段数据处理 * * @param paragraph 整个段落 * @param paragraphText 当前处理的某段数据 * @param style 样式 * @return void */ public static void addRun(P paragraph, String paragraphText, Style style) { ObjectFactory factory = Context.getWmlObjectFactory(); Text text = factory.createText(); text.setValue(paragraphText); // 保留前后空格 text.setSpace(SchemaSymbols.ATTVAL_PRESERVE); R run = factory.createR(); run.getContent().add(text); run.setRPr(style.getRPr()); paragraph.getContent().add(run); } /** * 某一段数据处理-添加超链接 * * @param pHyperlink * @param paragraphText * @param style * @return void */ public static void addRun(P.Hyperlink pHyperlink, String paragraphText, Style style) { ObjectFactory factory = Context.getWmlObjectFactory(); Text text = factory.createText(); text.setValue(paragraphText); // 保留前后空格 text.setSpace(SchemaSymbols.ATTVAL_PRESERVE); R run = factory.createR(); run.getContent().add(text); run.setRPr(style.getRPr()); pHyperlink.getContent().add(run); } /** * 复合段落使用正文pPr(Previous Paragraph Properties) * * @param * @return org.docx4j.wml.P */ public static P getComplexP() { ObjectFactory factory = Context.getWmlObjectFactory(); P paragraph = factory.createP(); Style style = WordStylesUtil.getStyleById(WordStylesUtil.NORMAL_STYLE); paragraph.setPPr(style.getPPr()); return paragraph; } /** * 生成超链接段落(锚点) * * @param paragraphText * @param anchor * @param styleId * @return org.docx4j.wml.P */ public static P createHyperlinkProduce(String paragraphText, String anchor, String styleId) { P p = getPByStyleId(paragraphText, styleId); ObjectFactory objectFactory = Context.getWmlObjectFactory(); // 创建一个超链接对象 BigInteger hli = BigInteger.valueOf(getHyperlinkId()); CTBookmark ctBookmark = new CTBookmark(); ctBookmark.setId(hli); ctBookmark.setName("_" + getAnchor(anchor)); JAXBElement pHyperlinkBookmarkStart = objectFactory.createPHyperlinkBookmarkStart(ctBookmark); p.getContent().add(pHyperlinkBookmarkStart); CTMarkupRange ctMarkupRange = new CTMarkupRange(); ctMarkupRange.setId(hli); objectFactory.createPBookmarkEnd(ctMarkupRange); p.getContent().add(ctMarkupRange); return p; } /** * 生成超链接段落(锚点)并写入word。整个段落都是超链接。 * 超链接锚点anchor自定义 * * @param wPackage * @param paragraphText 超链接段落 * @param anchor 超链接锚点 * @param styleId * @return void */ public static void createHyperlinkProduce(WordprocessingMLPackage wPackage, String paragraphText, String anchor, String styleId) { insertOneParagraph(wPackage, createHyperlinkProduce(paragraphText, anchor, styleId)); } /** * 生成超链接段落(锚点)并写入word。整个段落都是超链接。 * 超链接锚点anchor与段落内容相同。 * * @param wPackage * @param paragraphText * @param styleId * @return void */ public static void createHyperlinkProduce(WordprocessingMLPackage wPackage, String paragraphText, String styleId) { insertOneParagraph(wPackage, createHyperlinkProduce(paragraphText, paragraphText, styleId)); } /** * 生成超链接段落(调用锚点)。整个段落都是超链接。 * * @param paragraphText * @param anchor 超链接跳转锚点标识 * @param styleId * @return org.docx4j.wml.P */ public static P createHyperlinkConsume(String paragraphText, String anchor, String styleId) { ObjectFactory factory = Context.getWmlObjectFactory(); P p = factory.createP(); P.Hyperlink pHyperlink = factory.createPHyperlink(); pHyperlink.setAnchor("_" + getAnchor(anchor)); // ctrl跳转后,按alt + ⬅ 可以跳转回来。以下参数必须为true,否则光标返回位置不对。 pHyperlink.setHistory(true); Style style = WordStylesUtil.getStyleById(styleId); addRun(pHyperlink, paragraphText, style); p.getContent().add(pHyperlink); p.setPPr(style.getPPr()); return p; } /** * 生成超链接段落(调用锚点)。整个段落都是超链接。 * 超链接锚点anchor自定义 * * @param wPackage * @param paragraphText * @param anchor * @param styleId * @return void */ public static void createHyperlinkConsume(WordprocessingMLPackage wPackage, String paragraphText, String anchor, String styleId) { insertOneParagraph(wPackage, createHyperlinkConsume(paragraphText, anchor, styleId)); } /** * 生成超链接段落(调用锚点)。整个段落都是超链接。 * 超链接锚点anchor与段落内容相同。 * * @param wPackage * @param paragraphText * @param styleId * @return void */ public static void createHyperlinkConsume(WordprocessingMLPackage wPackage, String paragraphText, String styleId) { insertOneParagraph(wPackage, createHyperlinkConsume(paragraphText, paragraphText, styleId)); } /** * 超链接锚点处理。 * 锚点不能含有某些特殊字符,此处使用MD5进行转换。 * * @param anchor * @return java.lang.String */ public static String getAnchor(String anchor) { return MD5.create().digestHex(anchor, StandardCharsets.UTF_8); } /** * 分页 * * @param wPackage * @return void */ public static void setPage(WordprocessingMLPackage wPackage) { ObjectFactory factory = Context.getWmlObjectFactory(); P paragraph = factory.createP(); Style style = WordStylesUtil.getStyleById(WordStylesUtil.NORMAL_STYLE); paragraph.setPPr(style.getPPr()); // 创建换行,且类型设置为分页 Br br = factory.createBr(); br.setType(STBrType.PAGE); R run = factory.createR(); run.setRPr(style.getRPr()); run.getContent().add(br); paragraph.getContent().add(run); insertOneParagraph(wPackage, paragraph); } }

        Main

        package com.demo.word;
        import org.docx4j.jaxb.Context;
        import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
        import org.docx4j.wml.*;
        import java.io.File;
        import static com.demo.word.WordConstant.*;
        public class Main { public static void main(String[] args) throws Exception { File outputFile = new File("C:\\Users\\admin\\Desktop\\test.docx");
                WordUtil.createDocx(outputFile);
                WordprocessingMLPackage wPackage = WordprocessingMLPackage.load(outputFile);
                // 写入一级标题
                WordUtil.insertOneParagraph(wPackage,"标题1", HEADING_1);
                // 写入单一样式段落
                WordUtil.insertOneParagraph(wPackage, "这是一个正文段落", NORMAL);
                // 写入复合样式段落
                WordUtil.complexPWrite(wPackage, "title:", WHQ_1, "测试title", WHQ_2);
                // 写入一个表格
                wordTable(wPackage);
                // 写入一个超链接(文档内部的超链接) 跳转起始处
                WordUtil.createHyperlinkConsume(wPackage,"超链接1", "这个必须唯一且与跳转的地方一致", WHQ_41);
                // 分页
                WordUtil.setPage(wPackage);
                // 写入一个超链接(文档内部的超链接) 跳转目标处
                WordUtil.createHyperlinkProduce(wPackage, "超链接2", "这个必须唯一且与跳转的地方一致", WHQ_1);
                // 写入word文档样式信息
                wPackage.getMainDocumentPart().getStyleDefinitionsPart().getJaxbElement().getStyle().addAll(WordStylesUtil.STYLES.values());
                wPackage.save(outputFile);
            }
            public static void wordTable(WordprocessingMLPackage wPackage) { // 生成参数表格
                ObjectFactory objectFactory = Context.getWmlObjectFactory();
                Tbl table = objectFactory.createTbl();
                // 标题行 处理
                Tr tr = objectFactory.createTr();
                wordTc(tr, WordUtil.getPByStyleId("单元格1", WHQ_3), A41);
                wordTc(tr, WordUtil.getPByStyleId("单元格2", WHQ_3), A42);
                wordTc(tr, WordUtil.getPByStyleId("单元格3", WHQ_3), A43);
                wordTc(tr, WordUtil.getPByStyleId("单元格4", WHQ_3), A44);
                table.getContent().add(tr);
                // 数据行 处理,测试添加3条数据
                for (int i = 0; i < 3; i++) { Tr trTemp = objectFactory.createTr();
                    wordTc(trTemp, WordUtil.getPByStyleId("单元格1", WHQ_4), A51);
                    wordTc(trTemp, WordUtil.getPByStyleId("单元格2", WHQ_4), A52);
                    wordTc(trTemp, WordUtil.getPByStyleId("是", WHQ_5), A53);
                    wordTc(trTemp, WordUtil.getPByStyleId("单元格4", WHQ_4), A54);
                    table.getContent().add(trTemp);
                }
                // 表格整体样式
                Style tabStyle3 = WordStylesUtil.getStyleById(A3);
                table.setTblPr(new TblPr());
                // 边框线处理
                table.getTblPr().setTblBorders(tabStyle3.getTblPr().getTblBorders());
                // 表格列长度固定不可变
                table.getTblPr().setTblLayout(tabStyle3.getTblPr().getTblLayout());
                // 表格写入word文件
                wPackage.getMainDocumentPart().getContent().add(table);
            }
            public static void wordTc(Tr tr, P p, String tcStyleId) { ObjectFactory objectFactory = Context.getWmlObjectFactory();
                Tc tc = objectFactory.createTc();
                tc.setTcPr(WordStylesUtil.getStyleById(tcStyleId).getTcPr());
                tc.getContent().add(p);
                tr.getContent().add(tc);
            }
        }