Java多种方式动态生成doc文档
本来是要在android端生成doc的(这需求...),最后方法没有好的方法能够在android上做到完美,最后还是只能搬迁到服务器。不浪费,还是记录下各框架不支持android的原因以及他们的特点。java相关的这类框架还是很多的,有几个还不错,可惜要么不支持android,要么要收费还价格不低。
经过亲自测试,android不支持java的awt很多包不能直接在android上用,freemarker挺不错的,能生成复杂漂亮的doc,可惜不支持android。用poi在android上能运行,但是一路因为版本,格式等走了很多坑,用wfs打开还是乱码。jword、aspose.word能完美支持,jword试用期只有30天两者收费都不菲。itext没有测试,不过听说也不支持android。
方法一:freemarker
该方法需要先手动创建一个doc模板(图片记得使用占位符),并保存为xml文件。通过动态替换特定标签${}中的内容生成。example:
先上效果图:
public class docutil { public configuration configure=null; public docutil(){ configure=new configuration(configuration.version_2_3_22); configure.setdefaultencoding("utf-8"); } /** * 根据doc模板生成word文件 * @param datamap 需要填入模板的数据 * @param downloadtype 文件名称 * @param savepath 保存路径 */ public void createdoc(map<string,object> datamap,string downloadtype,string savepath){ try { //加载需要装填的模板 template template=null; //设置模板装置方法和路径,freemarker支持多种模板装载方法。可以重servlet,classpath,数据库装载。 //加载模板文件,放在testdoc下 configure.setclassfortemplateloading(this.getclass(), "/testdoc"); //设置对象包装器 // configure.setobjectwrapper(new defaultobjectwrapper()); //设置异常处理器 configure.settemplateexceptionhandler(templateexceptionhandler.ignore_handler); //定义template对象,注意模板类型名字与downloadtype要一致 template=configure.gettemplate(downloadtype+".xml"); file outfile=new file(savepath); writer out=null; out=new bufferedwriter(new outputstreamwriter(new fileoutputstream(outfile), "utf-8")); template.process(datamap, out); out.close(); } catch (ioexception e) { e.printstacktrace(); } catch (templateexception e) { e.printstacktrace(); } } public string getimagestr(string imgfile){ inputstream in=null; byte[] data=null; try { in=new fileinputstream(imgfile); data=new byte[in.available()]; in.read(data); in.close(); } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } base64encoder encoder=new base64encoder(); return encoder.encode(data); } }
public class testdoc { public static void main(string[] args) { docutil docutil=new docutil(); map<string, object> datamap=new hashmap<string, object>(); datamap.put("name", "joanna"); datamap.put("examnum", "111111111111"); datamap.put("idcard", "222222222222222222"); datamap.put("carmodel", "c1"); datamap.put("drivingschool", "测试驾校"); datamap.put("busytype", "初次申领"); datamap.put("examdate", "2016-03-10"); datamap.put("ordercount", "第1次"); datamap.put("userimg1", docutil.getimagestr("d:\\img\\userimg1.png")); datamap.put("userimg2", docutil.getimagestr("d:\\img\\userimg2.png")); datamap.put("firstexamtime", "12:41:17-12:44:38"); datamap.put("firstexamscores", "0分,不及格"); datamap.put("firstdeductitem", "12:44:15 20102 1号倒车入库,车身出线 扣100分"); datamap.put("firstpic1", docutil.getimagestr("d:\\img\\firstpic1.png")); datamap.put("firstpic2", docutil.getimagestr("d:\\img\\firstpic2.png")); datamap.put("firstpic3", docutil.getimagestr("d:\\img\\firstpic3.png")); datamap.put("secondexamtime", "12:46:50-13:05:37"); datamap.put("secondexamscores", "90分,通过"); datamap.put("seconddeductitem", ""); datamap.put("secondpic1", docutil.getimagestr("d:\\img\\secondpic1.png")); datamap.put("secondpic2", docutil.getimagestr("d:\\img\\secondpic2.png")); datamap.put("secondpic3", docutil.getimagestr("d:\\img\\secondpic3.png")); docutil.createdoc(datamap, "basedoc", "d:\\yanqiong.doc"); } }
xml文件太长,就不贴了...
最后附上android不能使用的原因:http://*.com/questions/25929542/use-freemarker-library-in-android
补充关于动态显示list以及换行的问题
需求明确到:在上面的扣分项中,如果我有几条扣分项,我希望每显示一条换行。
直接在要显示的内容上加换行符,并没有什么效果,起不到换行的作用。
其中在加ftl标签时,如<#list></list>,就会出现一些问题,在xml中并不识别,导致项目不能运行。
解决:
在需要显示多条扣分项的位置加,并加换行符:
<#list firstdeductitem as firstitem>
<w:t>${firstitem}</w:t><w:br/>
</#list>
testdoc.java中改为:
list<string> strs=new arraylist<string>();
strs.add("1111111111111111111");
strs.add("222222222222222");
strs.add("333333333333333");
datamap.put("firstdeductitem", strs);
docutil.java中改为:
//定义template对象,注意模板类型名字与downloadtype要一致
template=configure.gettemplate(downloadtype+".ftl");此时xml文件会报错,当然也不能编译运行项目,需要将.xml文件改为.ftl文件保存。再编译运行,效果图:
方法二:poi
用这个方法遇到了很多版本问题,这里是基于poi3.7+word2007的,测试能够完美运行。
你需要用word2007手动生成文档模板(用其他的生成会报错:无法打开文件),并用${}替换需要动态更新的内容,与上面类似,但是不需要你保存为xml文档格式了。
/** * 自定义xwpfdocument,并重写createpicture()方法 * @author joanna.yan * */ public class customxwpfdocument extends xwpfdocument{ public customxwpfdocument(inputstream in) throws ioexception{ super(in); } public customxwpfdocument(){ super(); } public customxwpfdocument(opcpackage pkg) throws ioexception{ super(pkg); } public void createpicture(int id,int width,int height,xwpfparagraph paragraph){ final int emu=9525; width *=emu; height *=emu; string blipid=((poixmldocumentpart) getallpictures().get(id)).getpackagerelationship().getid(); ctinline inline=paragraph.createrun().getctr().addnewdrawing().addnewinline(); string picxml="" + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" + " <a:graphicdata uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + " <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + " <pic:nvpicpr>" + " <pic:cnvpr id=\"" + id + "\" name=\"generated\"/>" + " <pic:cnvpicpr/>" + " </pic:nvpicpr>" + " <pic:blipfill>" + " <a:blip r:embed=\"" + blipid + "\" xmlns:r=\"http://schemas.openxmlformats.org/officedocument/2006/relationships\"/>" + " <a:stretch>" + " <a:fillrect/>" + " </a:stretch>" + " </pic:blipfill>" + " <pic:sppr>" + " <a:xfrm>" + " <a:off x=\"0\" y=\"0\"/>" + " <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" + " </a:xfrm>" + " <a:prstgeom prst=\"rect\">" + " <a:avlst/>" + " </a:prstgeom>" + " </pic:sppr>" + " </pic:pic>" + " </a:graphicdata>" + "</a:graphic>"; inline.addnewgraphic().addnewgraphicdata(); xmltoken xmltoken=null; try { xmltoken=xmltoken.factory.parse(picxml); } catch (xmlexception e) { e.printstacktrace(); } inline.set(xmltoken); inline.setdistt(0); inline.setdistb(0); inline.setdistl(0); inline.setdistr(0); ctpositivesize2d extent=inline.addnewextent(); extent.setcx(width); extent.setcy(height); ctnonvisualdrawingprops docpr=inline.addnewdocpr(); docpr.setid(id); docpr.setname("图片"+id); docpr.setdescr("测试"); } }
/** * 适用于word 2007 * poi版本 3.7 * @author joanna.yan * */ public class wordutil { public static customxwpfdocument generateword(map<string, object> param,string template){ customxwpfdocument doc=null; try { opcpackage pack=poixmldocument.openpackage(template); doc=new customxwpfdocument(pack); if(param!=null&¶m.size()>0){ //处理段落 list<xwpfparagraph> paragraphlist = doc.getparagraphs(); processparagraphs(paragraphlist, param, doc); //处理表格 iterator<xwpftable> it = doc.gettablesiterator(); while(it.hasnext()){ xwpftable table = it.next(); list<xwpftablerow> rows = table.getrows(); for (xwpftablerow row : rows) { list<xwpftablecell> cells = row.gettablecells(); for (xwpftablecell cell : cells) { list<xwpfparagraph> paragraphlisttable = cell.getparagraphs(); processparagraphs(paragraphlisttable, param, doc); } } } } } catch (ioexception e) { e.printstacktrace(); } return doc; } /** * 处理段落 * @param paragraphlist * @param param * @param doc */ public static void processparagraphs(list<xwpfparagraph> paragraphlist,map<string, object> param,customxwpfdocument doc){ if(paragraphlist!=null&¶graphlist.size()>0){ for (xwpfparagraph paragraph : paragraphlist) { list<xwpfrun> runs=paragraph.getruns(); for (xwpfrun run : runs) { string text=run.gettext(0); if(text!=null){ boolean issettext=false; for (entry<string, object> entry : param.entryset()) { string key=entry.getkey(); if(text.indexof(key)!=-1){ issettext=true; object value=entry.getvalue(); if(value instanceof string){//文本替换 text=text.replace(key, value.tostring()); }else if(value instanceof map){//图片替换 text=text.replace(key, ""); map pic=(map) value; int width=integer.parseint(pic.get("width").tostring()); int height=integer.parseint(pic.get("height").tostring()); int pictype=getpicturetype(pic.get("type").tostring()); byte[] bytearray = (byte[]) pic.get("content"); bytearrayinputstream byteinputstream = new bytearrayinputstream(bytearray); try { int ind = doc.addpicture(byteinputstream,pictype); doc.createpicture(ind, width , height,paragraph); } catch (invalidformatexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } } } } if(issettext){ run.settext(text, 0); } } } } } } /** * 根据图片类型获取对应的图片类型代码 * @param pictype * @return */ public static int getpicturetype(string pictype){ int res = customxwpfdocument.picture_type_pict; if(pictype!=null){ if(pictype.equalsignorecase("png")){ res=customxwpfdocument.picture_type_png; }else if(pictype.equalsignorecase("dib")){ res = customxwpfdocument.picture_type_dib; }else if(pictype.equalsignorecase("emf")){ res = customxwpfdocument.picture_type_emf; }else if(pictype.equalsignorecase("jpg") || pictype.equalsignorecase("jpeg")){ res = customxwpfdocument.picture_type_jpeg; }else if(pictype.equalsignorecase("wmf")){ res = customxwpfdocument.picture_type_wmf; } } return res; } }
public class testpoi { public static void main(string[] args) throws ioexception { map<string, object> param=new hashmap<string, object>(); param.put("${name}", "joanna.yan"); param.put("${examnum}", "000000000001"); param.put("${idcard}", "111111111111111111"); param.put("${carmodel}", "c1"); customxwpfdocument doc=wordutil.generateword(param, "d:\\joanna.docx"); fileoutputstream fopts = new fileoutputstream("d:\\yan.docx"); doc.write(fopts); fopts.close(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 详解java中Reference的实现与相应的执行过程
下一篇: Mongdb常用操作