html 试题试卷(包含latex)下载成word - - java
· pkg:part:/word/_rels/document.xml.rels :用于定义header(页眉)试卷格式模板,footer(页脚),styles(样式),image(图片)等pkg:part的引用
说明:页眉,页脚可以定义多个,需要在word中展现的照片模式跟引用在此定义
·pkg:part:/word/document.xml :正文部分,word中呈现的除页眉页脚以外的主要内容都在此部分(最重要的个别,后面介绍里面的格式)
·pkg:part:/word/footer.xml:页脚部分,设置试卷第几页,共几页
·pkg:part:/word/header.xml:页眉部分,设置试卷密封线
·pkg:part:/word/styles.xml:样式,设置默认图标等
·pkg:part:/word/media/image.png:图片内容(base64),docx中将图片放在/word/media文件夹下
docx 格式的样板,用解压工具解压开(不要惊讶,没漏掉就是解压开,docx 是一种压缩文件,它是将原来的doc的xml中的每个构架块拆开成独立文件压缩而成的)[例图:样例文件结构如下]
word文件夹下文件
docx的结构大概如将doc中每个pkg:part单独拆成文件,放在一定结构的文件夹下,最后压缩而成的,每块的功能和doc的相应pkg:part一样
docx有的[Content_Types].xml文件:定义图片类型,和其它pkg:part
正文部分:/word/document.xml 用到的标签简单介绍:
1>段落: :段落;:段落样式;:内容部分;:内容样式;:文字内容
<w:p w:rsidR="004D42A0" w:rsidRDefault="005F1054"> <w:pPr> <w:jc w:val="center"/> <w:rPr> <w:rFonts w:eastAsia="黑体"/> <w:b/> <w:sz w:val="30"/> <w:szCs w:val="30"/> w:rPr> w:pPr> <w:r w:rsidRPr="00771D19"> <w:rPr> <w:rFonts w:eastAsia="黑体" w:hint="eastAsia"/> <w:b/> <w:sz w:val="30"/> <w:szCs w:val="30"/> w:rPr> <w:t>学年w:t> w:r> w:p>
2>图片::图片标签;:图片样式;:引入图片(根据/word/_rels/document.xml.rels定义的截图id)
<w:r> <w:pict> <v:shape id="_x0000768de9d0ea6111e98c2a28843b052b2f" type="_x0000_t75" style="width:85pt;height:43pt"> <v:imagedata r:id="rId768de9d0ea6111e98c2a28843b052b2f" o:title="2"/> v:shape> w:pict> w:r>
3>表格:
<w:tbl> <w:tblPr> <w:tblW w:w="0" w:type="auto"/> <w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/> w:tblPr> <w:tblGrid> <w:gridCol w:w="222"/> <w:gridCol w:w="1376"/> w:tblGrid> <w:tr w:rsidR="00A93926" w:rsidTr="00682485"> <w:tc> <w:tcPr> <w:tcW w:w="0" w:type="auto"/> w:tcPr> <w:p w:rsidR="00A93926" w:rsidRDefault="00286E3F" w:rsidP="00A93926"/> w:tc> <w:tc> <w:tcPr> <w:tcW w:w="0" w:type="auto"/> <w:vAlign w:val="center"/> w:tcPr> <w:p w:rsidR="00A93926" w:rsidRPr="00A93926" w:rsidRDefault="00BF47F0" w:rsidP="00A93926"> <w:pPr> <w:rPr> <w:b/> w:rPr> w:pPr> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> <w:b/> w:rPr> <w:t>一、w:t> w:r> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> <w:b/> w:rPr> <w:t xml:space="preserve"> w:t> w:r> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> <w:b/> w:rPr> <w:t>解答题w:t> w:r> w:p> w:tc> w:tr> w:tbl>
4>latex转换成的word公式:omml:
<m:oMath> <m:f> <m:fPr> <m:ctrlPr> <w:rPr> <w:sz w:val="30"/> w:rPr> m:ctrlPr> m:fPr> <m:num> <m:r> <w:rPr> <w:sz w:val="30"/> w:rPr> <m:t>am:t> m:r> m:num> <m:den> <m:r> <w:rPr> <w:sz w:val="30"/> w:rPr> <m:t>sinAm:t> m:r> m:den> m:f> m:oMath>
5> 纸张大小,单双栏,页眉,页脚显示设置:
: 页眉设置 w:type:可以修改单双页显示
: 页脚设置 w:type:可以修改单双页显示
: 纸张宽高设定,此处可以调整A3,A4,B4,B5等样式
: 页边距等修改
: 单双栏设置
: 分栏线设置
<w:p w:rsidR="005320E8" w:rsidRDefault="00286E3F" w:rsidP="00B33EF9"> <w:pPr> <w:sectPr w:rsidR="005320E8" w:rsidSect="007A55E5"> <w:headerReference w:type="even" r:id="rId9"/> <w:headerReference w:type="default" r:id="rId10"/> <w:footerReference w:type="even" r:id="rId11"/> <w:footerReference w:type="default" r:id="rId12"/> <w:pgSz w:w="23814" w:h="16840" w:orient="landscape" w:code="9"/> <w:pgMar w:top="1134" w:right="1000" w:bottom="1134" w:left="1000" w:header="851" w:footer="692" w:gutter="0"/> <w:cols w:num="2" w:sep="1" w:space="425"/> <w:docGrid w:type="lines" w:linePitch="312"/> w:sectPr> w:pPr> w:p>
以上就是word试卷样板跟word结构和标签的简洁介绍,这写对于生成word很重要,涉及的内容也非常多,本人知道的也不多,是一个个试出来的,如有其它需求大家可以查文档或借助控制变量的方法,打开word编辑出自己想要的风格转成xml和原版xml对比找出不同的个别锁定样式设定的地方
2. 将xml样板转换为freemark的ftl模板
1).doc的xml文件直接将前缀改为.ftl,并将模板中的过多信息删除(每个后期填充的照片,段落样式保持一个),做到更精简的模板,放入项目中
2).docx的xml文件将 /word/document.xml 正文部分取出,如果页眉页脚样式也会有灵活变化也可以取下来变成模板,放入项目中
3).编写数据填充逻辑(得有一定基础的freemark语法基础)
doc模板的数据填充逻辑编写描述:
1>:图片引用编写:在/word/_rels/document.xml.rels 部分,动态引入图片链接,同时图片的base64也需要引到pkg:part 里
<pkg:part pkg:name="/word/_rels/document.xml.rels" pkg:contentType="application/vnd.openxmlformats-package.relationships+xml" pkg:padding="256"> <pkg:xmlData> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header2.xml"/> .......... <#list relations.relationshipStr as rs> ${rs} #list> Relationships> pkg:xmlData> pkg:part>
2>:题文内容填充:在/word/document.xml部分
<w:p w:rsidR="00C93DDE" w:rsidRPr="00771D19" w:rsidRDefault="00BF47F0" w:rsidP="00C93DDE"> <w:pPr> <w:jc w:val="center"/> <w:rPr> <w:rFonts w:eastAsia="黑体"/> <w:b/> <w:sz w:val="30"/> <w:szCs w:val="30"/> w:rPr> w:pPr> <w:r w:rsidRPr="00771D19"> <w:rPr> <w:rFonts w:eastAsia="黑体" w:hint="eastAsia"/> <w:b/> <w:sz w:val="30"/> <w:szCs w:val="30"/> w:rPr> <w:t>${(mainTitle.mainTitleName)!'XXXX学年度XX学校XX考试'}w:t> w:r> w:p> <#list questionsList as qMap> <w:p w:rsidR="00A81065" w:rsidRDefault="00BF47F0" w:rsidP="00744A41"> <w:pPr> <w:pStyle w:val="a7"/> <w:ind w:firstLineChars="0"/> <w:textAlignment w:val="center"/> <w:spacing w:line="360" w:lineRule="auto"/> w:pPr> ${(qMap.questionContent)!""} w:p> #list>
基本就是些if,else,循环list的操作就可以满足基本的意愿,不再列举,只要找到恰当样式跟风格位置就可以完成
根据freemark模板下载word程序编写:
1. 引入freemarker jar包
<dependency> <groupId>org.freemarkergroupId> <artifactId>freemarkerartifactId> <version>2.3.26-incubatingversion> dependency>
2. 模板读取
public class PaperDownloadSeting { private static Configuration configuration = null; private static MapallTemplate = null; private static Logger logger= LoggerFactory.getLogger(PaperDownloadSeting.class.getName()); static{ configuration = new Configuration(Configuration.VERSION_2_3_0); configuration.setDefaultEncoding("UTF-8"); configuration.setClassForTemplateLoading(PaperDownloadSeting.class, "/freemarkmould/papermould/"); allTemplate = new HashMap (); try{ allTemplate.put("docmould", configuration.getTemplate("docmould.ftl")); allTemplate.put("docxDocumentMould", configuration.getTemplate("docxmould/document.ftl")); allTemplate.put("docxHeader1Mould", configuration.getTemplate("docxmould/header1.ftl")); allTemplate.put("docxHeader2Mould", configuration.getTemplate("docxmould/header2.ftl")); allTemplate.put("docxStylesMould", configuration.getTemplate("docxmould/styles.ftl")); }catch(IOException e){ e.printStackTrace(); throw new RuntimeException(e); } }
3. 数据填充,word流生成
/** *@Desc: 获取word文件流,paperMap:处理成word样式的试卷信息,out:word输出流 */ public static void getwordFileOutPutStream(MappaperMap,String docType, OutputStream out){ try{ Writer w = new OutputStreamWriter(out,"utf-8"); if(docType.equals("doc")) { Template t = allTemplate.get("docmould"); t.process(paperMap,w);//freemark向模板填充数据 w.close(); deleteTmpImages(paperMap.get("tempMediaPath").toString()); } if(docType.equals("docx")) { ZipOutputStream zipout = new ZipOutputStream(out); zipdocx(paperMap,zipout); } }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(e); }finally { try { out.close(); deleteTmpImages(paperMap.get("tempMediaPath").toString()); } catch (Exception e2) { e2.printStackTrace(); } } } /** *@Desc: docx的试卷下载,解压静态样板,用已经填充的模板替换正文,页眉,页脚,样式等样板文件,在压缩 */ @SuppressWarnings("unchecked") public static void zipdocx(Map paperMap,ZipOutputStream zipout) throws IOException, TemplateException, URISyntaxException { Map relationsMap= JsonUtil.fromJson(JsonUtil.toJson(paperMap.get("relations")), Map.class); List relationshipStr=JsonUtil.fromJson(JsonUtil.toJson(relationsMap.get("relationshipStr")), List.class); Template doct = allTemplate.get("docxDocumentMould");//正文模板 Template hea1t = allTemplate.get("docxHeader1Mould");//页眉样式 Template hea2t = allTemplate.get("docxHeader2Mould");//另一个页眉样式 Template stylt = allTemplate.get("docxStylesMould");//全局样式模板 Writer w = new OutputStreamWriter(zipout,"utf-8"); File file=makeDirs(); ZipFile zipFile = new ZipFile(file); Enumeration extends ZipEntry> zipEntrys = zipFile.entries(); int len = -1; byte[] buffer = new byte[1024]; while (zipEntrys.hasMoreElements()) { ZipEntry next = zipEntrys.nextElement(); zipout.putNextEntry(new ZipEntry(next.toString())); if ("word/document.xml".equals(next.toString())) { doct.process(paperMap,w);//向模板填充试卷正文内容 } else if ("word/header1.xml".equals(next.toString())) { hea1t.process(paperMap,w);//向模板填充页眉样式 } else if ("word/header2.xml".equals(next.toString())) { hea2t.process(paperMap,w);//向模板填充页眉样式 } else if ("word/styles.xml".equals(next.toString())) { stylt.process(paperMap,w);//向模板填充全局样式 } else { //其他文件复制进去 InputStream is = zipFile.getInputStream(next); while ((len = is.read(buffer)) != -1) { zipout.write(buffer, 0, len); } //向Relationships写入图片链接 if ("word/_rels/document.xml.rels".equals(next.toString())) { for(String str:relationshipStr) { zipout.write(str.getBytes()); } String endStr=""; zipout.write(endStr.getBytes()); } is.close(); } } //往word/media/下添加图片 String tempMediaPath=paperMap.get("tempMediaPath").toString(); File imagesFile=new File(tempMediaPath); if(tempMediaPath!=null&&tempMediaPath!=""&&imagesFile.exists()) { File[] imagesList = imagesFile.listFiles(); if(imagesList.length>0) { for(File tmpFile:imagesList) { if (tmpFile.isFile()) { zipout.putNextEntry(new ZipEntry("word/media/"+tmpFile.getName())); InputStream input=new FileInputStream(tmpFile); while ((len = input.read(buffer)) != -1) { zipout.write(buffer, 0, len); } input.close(); } tmpFile.delete(); } } } imagesFile.delete(); w.close(); zipout.close(); zipFile.close(); } /** *@Desc: 创建word图片临时文件夹,并且返回docx静态模板文件,将静态模板文件放在临时目录下(静态模板较大可以单独放在其他地方,本项目为了方便就放在resources下) */ public static File makeDirs() { String filePath=PaperDownloadSeting.class.getClassLoader().getResource("").getPath().replace("classes", "tempfile"); String fileName="docxmould.docx"; File file=new File(filePath, fileName); if(file.exists()) { if(file.length()>0) { return file; } file.delete(); } file.getParentFile().mkdirs(); try { file.createNewFile(); OutputStream out=new FileOutputStream(file); File modulFile = ResourceUtils.getFile("classpath:freemarkmould/staticmould/docxmould.docx"); InputStream in= new FileInputStream(modulFile); byte[] buffer = new byte[1024]; int bytesToRead = -1; while ((bytesToRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesToRead); } in.close(); out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } return file; } /** *@Desc: 删除临时图片 */ public static void deleteTmpImages(String floderPath) { File imagesFloder=new File(floderPath); if(floderPath==null||floderPath.equals("")||!imagesFloder.exists()) {return;} File[] imagesList = imagesFloder.listFiles(); if(imagesList.length<1) {imagesFloder.delete();return;} for(File tmpFile:imagesList) { if (tmpFile.isFile()) {tmpFile.delete();} } imagesFloder.delete(); }
我们大家一起来支持一直支持你