使用doc4j生成word文档

来源:互联网 发布:万德数据库电话 编辑:程序博客网 时间:2024/06/05 05:13

docx4j 因为方法实现过于底层,相关文档说明特别少,而很少被人熟知

当需要使用 docx4j 创建office文档时,往往要自己实现一些常用的基本功能,这带来了一定的开发难度和不必要的精力开销

在经历一轮 docx4j 的学习和开发工作后,总结了一些基本方法,大家可以拿去参考


这其中难免有些错误和纰漏,烦请批评和指正


先看下导出文件的效果吧:

------------- page1

page1

------------- page2

page2

------------- page3

page3

------------- page4

page4

------------- page5

page5


对于页眉页脚要求很高、格式花俏的 word文档,完全没有必要全靠代码实现

可以先制作一个模板文件,并放到(Web)项目 resource 路径(当然你也可以指定本地路径)

代码实现上直接加载该模板,然后替换、添加。。


本例中使用到的 模板文件



好了,下面是粘贴代码了。。


1、很多情况默认样式的标题编号并不能满足需要,为了“自动”生成“任意”格式编号的标题

自定义 HeadingStyle、HeadingFormat 和 HeadingTool 三个类:

(1)HeadingStyle 类方便实现标题文本样式、编号生成规则以及标题等级

(2)HeadingFormat 可用于生成标题的编号,以用户指定的规则

(3)HeadingTool 是标题工具类,只要实现了标题编号的自动叠加

 

HeadingStyle 

/** * word 文档中标题的样式,含编号样式和文字样式 */public class HeadingStyle {    /**     * 标题的等级,≥1     */private int grade;    /**     * 标题所在段落的整体样式,如 1、heading 2等     */private String pStyle;/** * 标题编码格式的正则表达式 * 目前仅支持 #.#,即数字之间以 . 分割 */private String hStyle;public int getGrade() {return grade;}public void setGrade(int grade) {this.grade = grade;}public String getPStyle() {return pStyle;}public void setPStyle(String pStyle) {this.pStyle = pStyle;}public String getHStyle() {return hStyle;}public void setHStyle(String hStyle) {this.hStyle = hStyle;}public HeadingStyle(int grade, String pStyle) {super();this.grade = grade;this.pStyle = pStyle;this.hStyle = "#.#";}public HeadingStyle(int grade, String pStyle, String hStyle) {super();this.grade = grade;this.pStyle = pStyle;this.hStyle = hStyle;}}

 

HeadingFormat 

/** * word 文档中标题的编码格式化工具类 */public class HeadingFormat {/** * 编码格式的正则表达式 * 目前仅支持 #.#,即数字之间以 . 分割 */private String pattern;public HeadingFormat(String pattern) {super();this.pattern = pattern;}/** * 给定Map生成等级编码,起始等级为默认1,正则表达式为默认 #.# * @param gradeMaxVals 等级与该等级最大值Map,其中key是等级,value是该等级当前最大值 * @param endGrade 显示输出的结束等级 * @return */public String format(Map<Integer, Integer> gradeMaxVals, int endGrade){return format(gradeMaxVals, 1 ,endGrade, this.pattern);}/** * 给定Map生成等级编码,起始等级为默认1 * @param gradeMaxVals 等级与该等级最大值Map,其中key是等级,value是该等级当前最大值 * @param endGrade 显示输出的结束等级 * @param pattern 编码格式的正则表达式 * @return */public String format(Map<Integer, Integer> gradeMaxVals, int endGrade, String pattern){return format(gradeMaxVals, 1 ,endGrade, pattern);}/** * 给定Map生成等级编码 * @param gradeMaxVals 等级与该等级最大值Map,其中key是等级,value是该等级当前最大值 * @param startGrade 显示输出的起始等级 * @param endGrade 显示输出的结束等级 * @param pattern 编码格式的正则表达式 * @return */public String format(Map<Integer, Integer> gradeMaxVals, int startGrade , int endGrade, String pattern){if(gradeMaxVals ==null || gradeMaxVals.size() <1){return "";}if(startGrade <1){throw new ArrayIndexOutOfBoundsException("起始等级应当 ≥1!");}if(startGrade > endGrade){throw new ArrayIndexOutOfBoundsException("起始等级应当小于等于结束等级!");}if(pattern != "#.#"){throw new ArrayIndexOutOfBoundsException("非标准格式的暂未支持!");}int minGrade = CollectionUtils.getMinKey(gradeMaxVals);int maxGrade = CollectionUtils.getMaxKey(gradeMaxVals);if(startGrade < minGrade){startGrade = minGrade;}if(endGrade > maxGrade){endGrade = maxGrade;}StringBuilder headNum = new StringBuilder("");for(int i= startGrade; i<= maxGrade; i++){headNum.append(".");headNum.append(gradeMaxVals.get(i));}String headNums = headNum.toString();if(headNums.startsWith(".")){headNums = headNums.replaceFirst(".", "");}return headNums;}/** * 根据现有 编码 反推 gradeMaxVals,编码字符串的首个数字默认为等级1,正则表达式为默认 #.# * @param headNum 符合 pattern 正则表达式的 编码字符串 * @return */public Map<Integer, Integer> parse(String headNum){return parse(headNum, 1, this.pattern);}/** * 根据现有 编码 反推 gradeMaxVals * @param headNum 符合 pattern 正则表达式的 编码字符串 * @param startGrade 编码字符串中首个数字所代表的编码等级 * @param pattern 编码格式的正则表达式 * @return */public Map<Integer, Integer> parse(String headNum, int startGrade, String pattern){if(headNum ==null || "".equals(headNum.trim())){return null;}if(startGrade <1){throw new ArrayIndexOutOfBoundsException("起始等级应当 ≥1!");}String[] numStrArray = headNum.split(".");if(numStrArray.length <1){throw new ArrayIndexOutOfBoundsException("编码字符无效!");}if(pattern != "#.#"){throw new ArrayIndexOutOfBoundsException("非标准格式的暂未支持!");}int level = startGrade + numStrArray.length - 1;Map<Integer, Integer> gradeMaxVals = new HashMap<>();for(int i=0; i< level; i++){if(i< startGrade){gradeMaxVals.put(i+1, 0);}else{gradeMaxVals.put(i+1, new Integer(numStrArray[i-level+1]));}}return gradeMaxVals;}}
 

HeadingTool 

/** * word 文档中标题样式、编码生成等相关的工具类 */public class HeadingTool {/** * 等级与该等级最大值Map,其中key是等级,value是该等级当前最大值 */Map<Integer, Integer> gradeMaxVals;/** * 等级与该等级的统一样式Map,其中key是等级,value是该等级的样式类 */Map<Integer, HeadingStyle> gradeHeadingStyles;public Map<Integer, Integer> getGradeMaxVals(){return gradeMaxVals;}public void setGradeMaxVals(Map<Integer, Integer> gradeMaxVals) {this.gradeMaxVals = gradeMaxVals;}public Map<Integer, HeadingStyle> getGradeHeadingStyles() {return gradeHeadingStyles;}public void setGradeHeadingStyles(Map<Integer, HeadingStyle> gradeHeadingStyles) {this.gradeHeadingStyles = gradeHeadingStyles;}public HeadingTool() {super();this.gradeMaxVals = initGradeMaxVals();this.gradeHeadingStyles = initGradeHeadingStyles();}public HeadingTool(Map<Integer, Integer> gradeMaxVals,Map<Integer, HeadingStyle> gradeHeadingStyles) {super();this.gradeMaxVals = gradeMaxVals;this.gradeHeadingStyles = gradeHeadingStyles;}/** * 初始化 gradeMaxVals * @return */public Map<Integer, Integer> initGradeMaxVals(){Map<Integer, Integer> _gradeMaxVals = new HashMap<>();_gradeMaxVals.put(1, 0);return _gradeMaxVals;}/** * 初始化 gradeHeadingStyles * @return */public Map<Integer, HeadingStyle> initGradeHeadingStyles(){Map<Integer, HeadingStyle> _gradeHeadingStyles = new HashMap<>();HeadingStyle headingStyle1 = new HeadingStyle(1, "1");//1级标题应当声明1,其他等级方可声明Heading*_gradeHeadingStyles.put(1, headingStyle1);HeadingStyle headingStyle2 = new HeadingStyle(2, "Heading2");_gradeHeadingStyles.put(2, headingStyle2);HeadingStyle headingStyle3 = new HeadingStyle(3, "Heading3");_gradeHeadingStyles.put(3, headingStyle3);HeadingStyle headingStyle4 = new HeadingStyle(4, "Heading4");_gradeHeadingStyles.put(4, headingStyle4);HeadingStyle headingStyle5 = new HeadingStyle(5, "Heading5");_gradeHeadingStyles.put(5, headingStyle5);HeadingStyle headingStyle6 = new HeadingStyle(6, "Heading6");_gradeHeadingStyles.put(6, headingStyle6);HeadingStyle headingStyle7 = new HeadingStyle(7, "Heading7");_gradeHeadingStyles.put(7, headingStyle7);HeadingStyle headingStyle8 = new HeadingStyle(8, "Heading8");_gradeHeadingStyles.put(8, headingStyle8);HeadingStyle headingStyle9 = new HeadingStyle(9, "Heading9");_gradeHeadingStyles.put(9, headingStyle9);return _gradeHeadingStyles;}/** * 以指定样式生成当前标题组下的特定自增等级的标题编码 * @param increGrade 特定自增等级 * @param headingFormat 指定的标题编码样式 * @return */public String getAutoHeadingNum(int increGrade, HeadingFormat headingFormat){return getAutoHeadingNum( increGrade, headingFormat, this.gradeMaxVals);}/** * 以指定样式生成指定标题组下的特定自增等级的标题编码 * @param increGrade 特定自增等级 * @param headingFormat 指定的标题编码样式 * @param _gradeMaxVals 指定标题组 * @return */public static String getAutoHeadingNum(int increGrade, HeadingFormat headingFormat,Map<Integer, Integer> _gradeMaxVals){selfIncreGradeMaxVals(_gradeMaxVals, increGrade);return headingFormat.format(_gradeMaxVals, increGrade);}/** * 从指定等级上自增,并更新 gradeMaxVals * @param increGrade 指定的等级 */public void selfIncreGradeMaxVals(int increGrade){selfIncreGradeMaxVals(this.gradeMaxVals, increGrade);}/** * 从指定等级上自增,并更新 gradeMaxVals * @param _gradeMaxVals 原始 等级与该等级最大值Map,其中key是等级,value是该等级当前最大值 * @param increGrade 指定的等级 */public static void selfIncreGradeMaxVals(Map<Integer, Integer> _gradeMaxVals, int increGrade) {int minGrade = CollectionUtils.getMinKey(_gradeMaxVals);int maxGrade = CollectionUtils.getMaxKey(_gradeMaxVals);if(increGrade <minGrade){throw new ArrayIndexOutOfBoundsException("自增等级应当 ≥该Map最小key!");}if(maxGrade < increGrade){//增加一个子级标题_gradeMaxVals.put(increGrade, 1);}else{//新加父级标题for(int i = maxGrade; i>increGrade ; i--){_gradeMaxVals.remove(i);}Integer currentValue = _gradeMaxVals.get(increGrade);if(currentValue ==null){currentValue =0;}_gradeMaxVals.put(increGrade, currentValue + 1);}}}
 

2、自定义实现的工具类,代码核心部分

WmlTool

/** * word 文档的创建工具 */public class WmlTool {/** * 以下备注仅为方便认识 docx4j 相关类 *///wml:无线置标语言,用以处理word文档创建//P:段落 paragraph //R:行 row//Br:换行//fldChar:Field Charend//TOC:内容标题 Title of Content//Tbl:表格Table//Tc:Table Cell表单元格//Tr:Table Row表格的行//*Pr:property,属性、样式设置/** * wml 的文档包,文档操作的主要对象 */    private WordprocessingMLPackage wmlPackage;    /** * wml 的文档工厂,是用以创建相关 element 的工具类 */    private ObjectFactory wmlFactory;    /** * wml 的文档主体部分 */    private MainDocumentPart wmlMainPart;    /** * wml 文档内的所有内容 */    private List<Object> wmlAllContents;        /**     * 标题相关工具包     */    private HeadingTool headingTool;    public WordprocessingMLPackage getWmlPackage() {return wmlPackage;}public void setWmlPackage(WordprocessingMLPackage wmlPackage) {this.wmlPackage = wmlPackage;}public ObjectFactory getWmlFactory() {return wmlFactory;}public void setWmlFactory(ObjectFactory wmlFactory) {this.wmlFactory = wmlFactory;}public MainDocumentPart getWmlMainPart() {return wmlMainPart;}public void setWmlMainPart(MainDocumentPart wmlMainPart) {this.wmlMainPart = wmlMainPart;}public List<Object> getWmlAllContents() {return wmlAllContents;}public void setWmlAllContents(List<Object> wmlAllContents) {this.wmlAllContents = wmlAllContents;}public HeadingTool getHeadingTool() {return headingTool;}public void setHeadingTool(HeadingTool headingTool) {this.headingTool = headingTool;}/** * 文档工厂、文档包等变量的初始化 * @param fileResource 放在resource路径的模板文件名称,含文件后缀 */public WmlTool(String fileResource) throws FileNotFoundException, Docx4JException {URL url = this.getClass().getClassLoader().getResource(fileResource);this.wmlPackage = WordprocessingMLPackage.load(new FileInputStream(new File(url.getPath())));        this.wmlMainPart = this.wmlPackage.getMainDocumentPart();        this.wmlAllContents = this.wmlMainPart.getJaxbElement().getBody().getContent();        this.wmlFactory = Context.getWmlObjectFactory();        this.headingTool = new HeadingTool();}/** * 获取当前文档下所有表格 */public List<Tbl> getAllTables(){List<Object> elements = getClazzChildren4Element(this.wmlMainPart, Tbl.class);List<Tbl> tables = new ArrayList<Tbl>(elements.size());for(Object element:elements){if(element ==null){continue;}tables.add((Tbl)element);}return tables;}/** * 获取当前表格下所有文本域 * @param table 表格 */public static List<Text> getAllTexts4Table(Tbl table){List<Object> elements = getClazzChildren4Element(table, Text.class);List<Text> texts = new ArrayList<Text>(elements.size());for(Object element:elements){if(element ==null){continue;}texts.add((Text)element);}return texts;}    /** * 获取元素下的所有clazz类型子元素,可包含其本身 * 例(1)元素 MainDocumentPart 下查找 Tbl.class * (2)元素 Tbl 下查找 Text.class * @param element * @param clazz * @return */    public static List<Object> getClazzChildren4Element(Object element, Class<?> clazz) {        List<Object> elements = new ArrayList<Object>();        if (element instanceof JAXBElement)            element = ((JAXBElement<?>) element).getValue();        if(element.getClass().equals(clazz)){        elements.add(element);        }else if (element instanceof ContentAccessor) {            List<?> children = ((ContentAccessor) element).getContent();            for (Object child : children) {                elements.addAll(getClazzChildren4Element(child, clazz));            }        }        return elements;    }        /**     * 增加分页,从当前行直接跳转到下页     */    public void addPageBreak() {    addPageBreak(this.wmlMainPart, this.wmlAllContents, this.wmlFactory);    }        /**     * 增加分页,从当前行直接跳转到下页     * @param mainPart 文档主体     * @param contents 文档内容     */    public static void addPageBreak(MainDocumentPart mainPart, List<Object> contents, ObjectFactory factory) {        Br br = new Br();//换行        br.setType(STBrType.PAGE);//换页方式                P paragraph = factory.createP();//段落        paragraph.getContent().add(br);                contents.add(paragraph);    }        /**     * 复杂字符域上边界     * @param paragraph     */    public void addFieldBegin(P paragraph) {    addFieldBegin(paragraph, this.wmlFactory);    }        /**     * 复杂字符域上边界     * @param paragraph     * @param factory     */    public static void addFieldBegin(P paragraph, ObjectFactory factory) {        FldChar fldChar = factory.createFldChar();//字符域        fldChar.setFldCharType(STFldCharType.BEGIN);        fldChar.setDirty(true);                R row = factory.createR();//行        row.getContent().add(getWrappedFldChar(fldChar));        paragraph.getContent().add(row);    }        /**     * 复杂字符域下边界     * @param paragraph     */    public void addFieldEnd(P paragraph) {    addFieldEnd(paragraph, this.wmlFactory);    }        /**     * 复杂字符域下边界     * @param paragraph     * @param factory     */    public static void addFieldEnd(P paragraph, ObjectFactory factory) {        FldChar fldChar = factory.createFldChar();//字符域        fldChar.setFldCharType(STFldCharType.END);                R row = factory.createR();//行        row.getContent().add(getWrappedFldChar(fldChar));        paragraph.getContent().add(row);    }        /**     * 包装复杂字符域     * @param fldChar 字符域     * @return     */    public static JAXBElement getWrappedFldChar(FldChar fldChar) {    QName qName = new QName(Namespaces.NS_WORD12, "fldChar");    JAXBElement element = new JAXBElement(qName, FldChar.class, fldChar);        return element;    }        /**     * 创建目录信息     * @param showMaxGrade 目录显示的最大等级     */    public void createCatalog(int showMaxGrade) throws JAXBException{    createCatalog(this.wmlPackage , this.wmlFactory, showMaxGrade);    }        /**     * 创建目录信息     * @param _package 文档包     * @param factory 文档工具类     * @param showMaxGrade 目录显示的最大等级     * @throws JAXBException     */    public static void createCatalog(WordprocessingMLPackage _package , ObjectFactory factory, int showMaxGrade) throws JAXBException {    MainDocumentPart mainPart = _package.getMainDocumentPart();    List<Object> allContents = mainPart.getJaxbElement().getBody().getContent();        addPageBreak(mainPart, allContents, factory);        StringBuilder xml = new StringBuilder();    xml.append("<w:p w:rsidR='00C54076' w:rsidRDefault='00C54076' ");    xml.append("xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'> ");    xml.append("    <w:pPr> ");    xml.append("        <w:pStyle w:val='TOC'/> ");    xml.append("    </w:pPr> ");    xml.append("    <w:r> ");    xml.append("        <w:t>目录</w:t> ");    xml.append("    </w:r> ");    xml.append("</w:p> ");        P toc = (P) XmlUtils.unmarshalString(xml.toString());//目录标题Title of catalog        _package.getMainDocumentPart().addObject(toc);        P catalog = factory.createP();        addFieldBegin(catalog, factory);        addTable4Catalog(catalog, showMaxGrade, factory);        addFieldEnd(catalog, factory);                allContents.add(catalog);    }        /**     * 添加目录详情,word内alt + f9可见     * @param catalog 目录段落     * @param grade 目录显示的最大等级     * @param factory 文档工具类     */    public static void addTable4Catalog(P catalog, int grade, ObjectFactory factory) {        Text txt = new Text();        txt.setSpace("preserve");//目录中保留 heading 前后的空格        txt.setValue("TOC \\o \"1-"+grade+"\" \\h \\z \\u");                R row = factory.createR();        row.getContent().add(factory.createRInstrText(txt));                catalog.getContent().add(row);    }        /**     * 以指定文本样式创建段落标题 Title of Content,并以默认方式生成编码     * 注意:一级标题应当声明为1,其他标题方可声明Heading*     * @param grade 标题等级     * @param headingText 标题的文本信息(不含编号)     * @param headingStyle 标题的文本信息(不含编号)     * @throws JAXBException     */    public void addHeading(String headingText, HeadingStyle headingStyle) throws JAXBException{    addHeading(headingText, headingStyle, new HeadingFormat("#.#")) ;    }        /**     * 创建段落标题 Title of Content,当前文档自增编号     * 注意:一级标题应当声明为1,其他标题方可声明Heading*     * @param headingFormat 标题编号格式化     * @param headingText 标题的文本信息(不含编号)     * @param headingStyle 标题的样式     * @throws JAXBException     */    public void addHeading(String headingText, HeadingStyle headingStyle, HeadingFormat headingFormat ) throws JAXBException{    int grade = headingStyle.getGrade();    String headingNum = headingTool.getAutoHeadingNum(grade, headingFormat);    addHeading(headingNum, headingText, headingStyle);        //通过直接调用方法 addHeading(String headingNum, String headingText, HeadingStyle headingStyle) [以下简称方法1]创建标题,不会自动更新 标题组Map    //本方法 [以下简称方法2]因为使用 标题组Map 生成编码,故调用一次更新一次 标题组Map    //对同一 wmlTool 的操作,如果时而调用方法1,时而调用方法2,这会导致标题等级混乱    //TODO 区分开 生成编码方式,再在调用方法1 时更新或创建新 标题组Map,这要使用到 HeadingFormat.parse方法    }        /**     * 创建段落标题 Title of Content     * @param headingNum 标题的编号     * @param headingText 标题的文本信息(不含编号)     * @param headingStyle 标题的样式     */    private void addHeading(String headingNum, String headingText, HeadingStyle headingStyle) throws JAXBException {    String heading = headingNum + " " +headingText;    heading = heading.trim();        String pStyle = headingStyle.getPStyle();    int blank = headingStyle.getGrade();        while (blank > 1) {// 按级缩进            heading = " " + heading;            blank--;        }                StringBuilder xml = new StringBuilder();        xml.append("<w:p xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' ");        xml.append("    w:rsidRDefault='00C54076' w:rsidP='00C54076' w:rsidR='00C54076'> ");        xml.append("    <w:pPr> ");        xml.append("        <w:pStyle w:val='" + pStyle + "'/> ");        xml.append("        <w:numPr> ");        xml.append("            <w:ilvl w:val='0'/> ");        xml.append("            <w:numId w:val='0'/> ");//如果pStyle是Heading类,不采用自带编号        xml.append("        </w:numPr> ");        xml.append("        <w:rPr> ");        xml.append("            <w:rFonts w:eastAsia='黑体' w:ascii='黑体'/> ");        xml.append("            <w:b w:val='false'/> ");        xml.append("            <w:sz w:val='21'/> ");        xml.append("            <w:szCs w:val='21'/> ");        xml.append("        </w:rPr> ");        xml.append("    </w:pPr> ");        xml.append("    <w:bookmarkStart w:name='_Toc343672792' w:id='0'/> ");        xml.append("    <w:r> ");        xml.append("        <w:rPr> ");        xml.append("            <w:rFonts w:eastAsia='黑体' w:ascii='黑体' w:hint='eastAsia'/> ");        xml.append("            <w:color w:val='000000'/> ");        xml.append("            <w:b w:val='false'/> ");        xml.append("            <w:sz w:val='21'/> ");        xml.append("            <w:szCs w:val='21'/> ");        xml.append("        </w:rPr> ");        xml.append("        <w:t xml:space='preserve'>" + heading + "</w:t> ");//属性preserve 保留空格        xml.append("    </w:r> ");        xml.append("    <w:bookmarkEnd w:id='0'/> ");        xml.append("</w:p> ");                P paragraph = (P) XmlUtils.unmarshalString(xml.toString());                this.wmlMainPart.addObject(paragraph);    }        /**     * 创建段落标题 Title of Content     * 建议使用方法 addHeading(String headingNum, String headingText, HeadingStyle headingStyle);     */    @Deprecated    private void createHeading(String headingNum, String headingText, HeadingStyle headingStyle){        String rowTextVal = headingNum + " " +headingText;    rowTextVal = rowTextVal.trim();        int grade = headingStyle.getGrade();    int blank = grade;        while (blank > 1) {// 按级缩进            rowTextVal = " " + rowTextVal;            blank--;        }            RPr rowPr = setFontPr4Row(this.wmlFactory, "微软雅黑", 21, false, "000000");//黑色        R row = this.wmlFactory.createR();//行row.setRPr(rowPr);Text rowText = this.wmlFactory.createText();//行的文本内容rowText.setValue(rowTextVal);rowText.setSpace("preserve");//保留文本内容的中的空格row.getContent().add(rowText);PStyle pStyle = this.wmlFactory.createPPrBasePStyle();//段落样式pStyle.setVal("Heading" + grade);//目录分级Jc jc = this.wmlFactory.createJc();jc.setVal(JcEnumeration.LEFT);//标题居左PPr pPr = this.wmlFactory.createPPr();//段落属性pPr.setPStyle(pStyle);pPr.setJc(jc);//不用自带编码:pPr.setNumPr(PPrBase.NumPr.NumId=0);P paragraph = this.wmlFactory.createP();//段落paragraph.getContent().add(row);paragraph.setPPr(pPr);this.wmlMainPart.addObject(paragraph);    }        /**     * 添加字符段落     * @param text 文本信息     */    public void addTextParagraph(String text){    addTextParagraph(text, this.wmlMainPart);    }        /**     * 添加字符段落     * @param text 文本信息     * @param mainPart 文档主体     */    public static void addTextParagraph(String text, MainDocumentPart mainPart){    mainPart.addParagraphOfText(text);    }        /**     * 在文档主体添加元素     * @param element     */    public void addElement(Object element){    this.wmlMainPart.addObject(element);    }        /**     * 设置表格边框颜色及文字对齐方式     */    public static void addTblBorders(Tbl table) {        CTBorder border = new CTBorder();        border.setColor("auto");        border.setSz(new BigInteger("4"));        border.setSpace(new BigInteger("0"));        border.setVal(STBorder.SINGLE);        TblBorders borders = new TblBorders();        borders.setTop(border);//上边框        borders.setBottom(border);//下边框        borders.setLeft(border);//左边框        borders.setRight(border);//右边框                borders.setInsideH(border);//纵向内边框        borders.setInsideV(border);//横向内边框                TblPr tblPr = new TblPr();//表格属性        tblPr.setTblBorders(borders);        table.setTblPr(tblPr);        Jc jc = new Jc();//文字对象方式        jc.setVal(JcEnumeration.CENTER);                table.getTblPr().setJc(jc);    }        /**     * 添加表单元格     * @param cellText 单元格文本     * @param width 单元格宽度     * @return     */    public Tc createTblCell(String cellText, Integer width){    return createTblCell( cellText, width, this.wmlFactory);    }        /**     * 添加表单元格     * @param cellText 单元格文本     * @param width 单元格宽度     * @param factory 文档工具类     * @return     */    public static Tc createTblCell(String cellText, Integer width, ObjectFactory factory) {        Text text = factory.createText();        text.setValue(cellText);                R row = factory.createR();        row.getContent().add(text);                P paragraph = factory.createP();        paragraph.getContent().add(row);            Tc tblCell = factory.createTc();        tblCell.getContent().add(paragraph);        if (width != null) {            TblWidth cellWidth = factory.createTblWidth();            cellWidth.setType("dxa");//横向宽度            cellWidth.setW(BigInteger.valueOf(width));                        TcPr tcPr = factory.createTcPr();//Table cell property            tcPr.setTcW(cellWidth);                        tblCell.setTcPr(tcPr);        }        return tblCell;    }        /**     * 设置表格宽度     * @param table 表格     * @param width 宽度     */    public static void setTblWidth(Tbl table, int width) {        TblPr tblPr = table.getTblPr();        if (tblPr == null) {            tblPr = new TblPr();            table.setTblPr(tblPr);        }        TblWidth tblW = tblPr.getTblW();        if (tblW == null) {            tblW = new TblWidth();            tblPr.setTblW(tblW);        }        tblW.setType("dxa");//横向宽度        tblW.setW(new BigInteger(width+""));    }        /**     * 设置单元格背景色     * @param tblCell 单元格     * @param colorStr 颜色,如:FFFF00     */    public void setTblCellColor(Tc tblCell, String colorStr){    setTblCellColor(tblCell, colorStr, this.wmlFactory);    }        /**     * 设置单元格背景色     * @param tblCell 单元格     * @param colorStr 颜色,如:FFFF00     * @param factory 文档工具类     */    public static void setTblCellColor(Tc tblCell, String colorStr, ObjectFactory factory) {    CTShd shd = factory.createCTShd();    shd.setFill(colorStr);            TcPr tblCellPro = factory.createTcPr();        tblCellPro.setShd(shd);        tblCell.setTcPr(tblCellPro);    }        /**     * 获取文档的可用宽度     */    public int getWritableWidth() throws NullPointerException{    return getWritableWidth(this.wmlPackage);    }        /**     * 获取文档的可用宽度     * @param _package 文档包     */    public static int getWritableWidth(WordprocessingMLPackage _package) throws NullPointerException {    DocumentModel docModel = _package.getDocumentModel();//模板文件    List<SectionWrapper> sectionWrappers = docModel.getSections();//包装器    if(sectionWrappers !=null && sectionWrappers.size()>0){    PageDimensions pageDim = sectionWrappers.get(0).getPageDimensions();//页面尺寸    return pageDim.getWritableWidthTwips();    }            throw new NullPointerException();    }        /**     * 添加书签     * @param id 书签id     * @param name 书签名称     * @param paragraph 段落     * @param row 行     * @param factory 文档工具类     */    public void addBookMark(String name,P paragraph, R row){    addBookMark( 0, name, paragraph, row, this.wmlFactory);    }        /**     * 添加书签     * @param id 书签id     * @param name 书签名称     * @param paragraph 段落     * @param row 行     * @param factory 文档工具类     */    public static void addBookMark( int id, String name,P paragraph, R row, ObjectFactory factory) throws ArrayIndexOutOfBoundsException{        int index = paragraph.getContent().indexOf(row);        if (index < 0) {            throw new ArrayIndexOutOfBoundsException("The current Row does not exist!");        }        BigInteger _id = BigInteger.valueOf(id);        CTMarkupRange mr = factory.createCTMarkupRange();        mr.setId(_id);        JAXBElement<CTMarkupRange> bmEnd = factory.createBodyBookmarkEnd(mr);        paragraph.getContent().add(index + 1, bmEnd);        CTBookmark bm = factory.createCTBookmark();        bm.setId(_id);        bm.setName(name);        JAXBElement<CTBookmark> bmStart = factory.createBodyBookmarkStart(bm);        paragraph.getContent().add(index, bmStart);    }        /**     * 创建含有内联图片的段落     * @param picBytes 图片文件流     * @param width 图片宽度     * @param height 图片高度     */    public P createPWithPicture(byte[] picBytes, long width, long height) throws Exception{    return createPWithPicture(picBytes, width, height, this.wmlPackage, this.wmlFactory);    }        /**     * 创建含有内联图片的段落     * @param picBytes 图片文件流     * @param width 图片宽度     * @param height 图片高度     * @param _package 文档包     * @param factory 文档工具类     */    public static P createPWithPicture(byte[] picBytes, long width, long height,    WordprocessingMLPackage _package, ObjectFactory factory) throws Exception{        BinaryPartAbstractImage picPart = BinaryPartAbstractImage.createImagePart(_package, picBytes);        Inline inline = picPart.createImageInline("", "", 1, 2, UnitsOfMeasurement.twipToEMU(width),                UnitsOfMeasurement.twipToEMU(height), false);        Drawing drawing = factory.createDrawing();        drawing.getAnchorOrInline().add(inline);        R row = factory.createR();        row.getContent().add(drawing);        P paragraph = factory.createP();        paragraph.getContent().add(row);        return paragraph;    }        /**     * 设置行文字字体、大小、加粗、颜色     * @param factory wml 的文档工厂,是用以创建相关 element 的工具类     * @param fontVal 字体名称,如黑体、微软雅黑、宋体     * @param fontSize 字体大小,镑,如12     * @param isBlod 是否加粗     * @param colorVal 文字颜色,如 FFFF00     * @return     */    private static RPr setFontPr4Row(ObjectFactory factory, String fontVal ,  int fontSize , boolean isBlod , String colorVal){          RPr rowPr = factory.createRPr();                    RFonts rowFont = factory.createRFonts();        rowFont.setHint(STHint.EAST_ASIA);        rowFont.setAscii(fontVal);        rowFont.setHAnsi(fontVal);        rowPr.setRFonts(rowFont);                HpsMeasure _fontSize = factory.createHpsMeasure();        _fontSize.setVal(BigInteger.valueOf(fontSize));        rowPr.setSz(_fontSize);        rowPr.setSzCs(_fontSize);                  BooleanDefaultTrue fontBold = factory.createBooleanDefaultTrue();          rowPr.setBCs(fontBold);          if(isBlod){          rowPr.setB(fontBold);          }                Color color = factory.createColor();        color.setVal(colorVal);        rowPr.setColor(color);                return rowPr;      }     }

3、测试用例

(1)业务相关po类

FileParam 

/** * 与文件的业务相关的参数类 */public class FileParam {private List<byte[]> pictures;public List<byte[]> getPictures() {return pictures;}public void setPictures(List<byte[]> pictures) {this.pictures = pictures;}}

(2)业务相关Service类

FileService 

/** * 文件相关的Service类 */public class FileService {    /**     * 第2步:更新封面     * @param wmlTool     * @param fileParam     */    public void updateCover(WmlTool wmlTool, FileParam fileParam) {        List<Tbl> docTbls = wmlTool.getAllTables();        Tbl table1 = docTbls.get(0);                List<Text> tbl1Txts = WmlTool.getAllTexts4Table(table1);        Text tbl1Txt1 = tbl1Txts.get(0);                String tbl1Txt1Key = tbl1Txt1.getValue();        if (tbl1Txt1Key.equals("key0")) {        tbl1Txt1.setValue("文档标题(表1行1)");        }        Tbl table2 = docTbls.get(1);        List<Text> tbl2Txts = WmlTool.getAllTexts4Table(table2);        for(Text tbl2Txt:tbl2Txts){        String tbl2TxtVal = tbl2Txt.getValue();        if(tbl2TxtVal.indexOf("key") >=0){        tbl2Txt.setValue(tbl2TxtVal.replace("key", "value"));        }        }    }    /**     * 第4步:写入摘要信息     * @param wmlTool     * @param fileParam     */    public void createAbstract(WmlTool wmlTool, FileParam fileParam) throws JAXBException{    wmlTool.addPageBreak();            wmlTool.addHeading("摘要", new HeadingStyle(1, "1"));        wmlTool.addTextParagraph("文本内容第1行");        wmlTool.addTextParagraph("文本内容第2行");        int tblNIndex =wmlTool.getAllTables().size() - 1;//size - 2 +1,2个封面tbl,1个当前tbl        String tblNTitle = "表" + tblNIndex + " 我是表标题";        wmlTool.addTextParagraph(tblNTitle);        ObjectFactory factory = wmlTool.getWmlFactory();                Tbl tblN = factory.createTbl();        WmlTool.addTblBorders(tblN);        wmlTool.addElement(tblN);        Tr tblRow1 = factory.createTr();        tblRow1.getContent().add(wmlTool.createTblCell("标题1", 5300));        tblRow1.getContent().add(wmlTool.createTblCell("标题2", 5300));        tblN.getContent().add(tblRow1);        Tr tblRowN = null;        int rowNum = 0;//行号        int colNum = 0;//列号        for (int i=1; i<=7; i++) {        colNum = (i%2 == 1 ? 1 : 2);            if (colNum == 1) {                tblRowN = factory.createTr();                rowNum ++;            }            tblRowN.getContent().add(wmlTool.createTblCell("我是行" + rowNum + "列" + colNum + "的内容", 5300));                        if (colNum ==2 || i ==7) {                tblN.getContent().add(tblRowN);            }        }                wmlTool.addTextParagraph("这是一行");        wmlTool.addTextParagraph("这又是一行");        wmlTool.addTextParagraph("可以再来一行");    }        /**     * 第5步:创建图片表格和多级标题     * @param wmlTool     * @param fileParam     * @throws Exception     */    public void createPictures(WmlTool wmlTool, FileParam fileParam) throws Exception{    wmlTool.addPageBreak();            wmlTool.addHeading("我是1级标题", new HeadingStyle(1, "1"));        wmlTool.addHeading("我是2级标题", new HeadingStyle(2, "Heading2"));                ObjectFactory factory = wmlTool.getWmlFactory();        Tbl picTbl = factory.createTbl();        WmlTool.addTblBorders(picTbl);        WmlTool.setTblWidth(picTbl, 9000);                List<byte[]> pictures = fileParam.getPictures();//至少2张图片        for (int i=0; i<2; i++) {            Jc jc = factory.createJc();            jc.setVal(JcEnumeration.CENTER);                        PPr paragraphProperty = factory.createPPr();            paragraphProperty.setJc(jc);                        P paragraph = wmlTool.createPWithPicture(pictures.get(i), 3200, 1800);            paragraph.setPPr(paragraphProperty);            Tc tblCell = factory.createTc();            tblCell.getContent().add(paragraph);            Tr tblRow = factory.createTr();            tblRow.getContent().add(tblCell);            picTbl.getContent().add(tblRow);        }        wmlTool.addElement(picTbl);                wmlTool.addHeading("我是2级标题", new HeadingStyle(2, "Heading2"));    }    /**     * 第6步:写入结论部分     * @param wmlTool     * @param fileParam     * @throws JAXBException     */    public void createConclusion(WmlTool wmlTool, FileParam fileParam) throws JAXBException{    wmlTool.addPageBreak();        wmlTool.addHeading("结论", new HeadingStyle(1, "1"));        wmlTool.addTextParagraph("我是结论的详细信息");    }    }


(3)业务相关Controller类

说明:本处main方法模拟实现的是保存到本地路径

public class Main {public static FileService fileService= new FileService();public static void main(String[] args){try {List<byte[]> pictures = new ArrayList<>();InputStream is1 = new FileInputStream("d:/picture1.jpg");byte[] picture1  = new byte[is1.available()];is1.read(picture1); is1.close(); pictures.add(picture1); InputStream is2 = new FileInputStream("d:/picture2.jpg");byte[] picture2  = new byte[is2.available()];is2.read(picture2); is2.close(); pictures.add(picture2);  FileParam fileParam = new FileParam(); fileParam.setPictures(pictures);  WordprocessingMLPackage  wmlPackage = createWmlPackage(fileParam); wmlPackage.save(new File("d:/生成文件.docx"));} catch (Exception e) {e.printStackTrace();}}public static WordprocessingMLPackage createWmlPackage(FileParam fileParam) throws Exception{//第1步:加载模板文件WmlTool wmlTool = new WmlTool("fileModel.docx");//第2步:更新封面信息fileService.updateCover(wmlTool, fileParam);//第3步:生成目录信息wmlTool.createCatalog(2);//第4步:写入摘要信息fileService.createAbstract(wmlTool, fileParam);//第5步:创建图片表格和多级标题fileService.createPictures(wmlTool, fileParam);//第6步:写入结论部分fileService.createConclusion(wmlTool, fileParam);return wmlTool.getWmlPackage();}}


4、文件流输出到浏览器(Web项目的下载功能)

将3(3)的main方法中的wmlPackage.save()方法修改为如下即可

response.reset();response.setHeader("Content-disposition", "attachment;filename=" + new String(fileName.getBytes("gb2312"),"ISO8859-1"));response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");SaveToZipFile saver = new SaveToZipFile(wmlPackage);saver.save(response.getOutputStream());  


注意:如果你遇到奇葩现象,请检查office版本和自定义模板文件本身,这里有一段说不清道不明的故事






原创粉丝点击