使用doc4j生成word文档
来源:互联网 发布:万德数据库电话 编辑:程序博客网 时间:2024/06/05 05:13
docx4j 因为方法实现过于底层,相关文档说明特别少,而很少被人熟知
当需要使用 docx4j 创建office文档时,往往要自己实现一些常用的基本功能,这带来了一定的开发难度和不必要的精力开销
在经历一轮 docx4j 的学习和开发工作后,总结了一些基本方法,大家可以拿去参考
这其中难免有些错误和纰漏,烦请批评和指正
先看下导出文件的效果吧:
------------- page1
------------- page2
------------- page3
------------- page4
------------- 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("我是结论的详细信息"); } }
说明:本处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版本和自定义模板文件本身,这里有一段说不清道不明的故事
- 使用doc4j生成word文档
- Maven项目中doc4j生成word文档
- 使用java生成word文档
- 使用FreeMarker生成word文档
- 使用FreeMarker生成Word文档
- POI 使用word模板生成word文档
- 使用ASP生成/导出WORD文档代码
- 使用VSTO自动生成word文档
- 使用java快速生成word文档
- 使用itext-rtl 生成word文档
- SpringMVC中使用FreeMarker生成Word文档
- 使用freemaker模板生成word文档
- SpringMVC中使用FreeMarker生成Word文档
- 使用FreeMarker生成Word文档中的表格
- asp生成WORD文档
- 控制生成word文档
- JSP生成WORD文档
- JSP生成WORD文档
- CSDN如何转载别人的文章
- 设计模式 装饰者模式 带你重回传奇世界
- Python的zip函数
- Mybatis中的resultType和resultMap
- 基础数据机构之WeakHashMap源码分析
- 使用doc4j生成word文档
- 【Spring+SpringMVC+MyBatis深入学习及搭建】11.SpringMVC架构
- MySQL数据类型和常用字段属性总结
- java实现敏感词过滤
- 【WPF】UI虚拟化之------自定义VirtualizingWrapPanel
- discuz论坛用户--设置--修改头像不显示
- 微信开发遇到的问题及解决
- java 文件上传
- 前段node项目构建常见问题