Itext操作word文档动态生成数学试卷及Latex转mathML公式处理方式
Itext操作word文档动态生成数学试卷及Latex转mathML公式处理方式
转发请注明出处,谢谢!
介绍: 我下这篇博客的时候所在的公司是一个关于中小学教育的公司,之所以写这篇博客,是因为公司有这样一个需求.刚开始做的时候,近乎看完了网上所有关于poi,Itext,Java2word等操作word文档的博客,但是网上关于这种类似的资料很少,更不要说数学latex,mathml公式生成word文档了,所以只能自己研究,用了两周时间,终于做出来了,所以决定认真的写好这篇博客,希望以后对有些人提供一些帮助.
需求: 学生在网上做题,可以自选考题然后生成考试试卷
需求实现过程: 熟悉了poi,Itext,Java2word等技术操作word后,最后决定用poi和Itext操作word文档的实现方式,itext操作word文档功能很齐全.相对poi操作cexel比较强大,可以实现简单的word操作.
博客介绍: 这篇博客主要讲怎么讲latex,mathml写入到word文档及数学试卷里面题目的一些格式,像word文档的页眉页脚,水印啊什么的都是简单的功能,网上也有很好的博客,我就不讲了.重要的是IText的实现方式,遇到的坑及解决办法等.
POI操作word文档生成数学试卷 --->
http://blog.****.net/qq_35834998/article/details/79235304
好,说了这么多,希望大家不要介意.现在切入正题: 先看下面的效果图
Itext生成的数学试卷文档: doc不可编辑公式,数据库存储的事latex公式,及公式图片
例:latex公式: \[(\cfrac{x-y}{x+y}-\cfrac{x+y}{x-y})\div \cfrac{2x}{{{x}^{2}}y-{{y}^{3}}}\]
图片: http://xxx/xxx.png
POI生成数学试卷文档: docx可编辑公式
基础开发环境: IDEA,java1.8,JPA,Itext,maven
第一步 : maven 依赖 Itext依赖
<!-- https://mvnrepository.com/artifact/com.lowagie/itext --> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.7</version> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.lowagie/itext-rtf --> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext-rtf</artifactId> <version>2.1.7</version> </dependency>Latex转mathml公式依赖
<dependency> <groupId>fmath</groupId> <artifactId>fmath-mathml-java</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>org.scilab.forge</groupId> <artifactId>jlatexmath</artifactId> <version>1.0.6</version> </dependency>第二步:
补充说明 : Itext生成试卷文档,文档中所有的公式都是以图片的形式写入===实现方式,将latex公司转换成图片后在写入相关位置
直接上代码 :
@RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class WordControllerTest{
@Autowired private QuesService quesService; //查询数据库的service根据自己的改 private Pattern pattern = Pattern.compile("\\\\\\\\\\[(.*?)\\\\\\\\\\]");// private Pattern compile = Pattern.compile("\\p{Lower}");//正则
@Test public void produceItextWord() throws Exception {
//调用wordStart方法,文档名称,生成路径 Document document = wordStart("初中数学试卷","E:\\题库文档\\test06\\word.doc"); //题干title段落 Paragraph paragraph = new Paragraph(); paragraph.setSpacingAfter(8); //题目编号 int number = 0;
List<String> stringList = Arrays.asList( //考题的id
"01162767-2f7d-458d-8945-877a81fd3361",
"0040110f-2c26-4865-b999-c8754f5803e3");
for (String id : stringList ){ //去数据库查询,获取题目信息 Ques ques = quesService.getQuesFromId(id); //调用titleGroup方法,题目类容段落处理 String[] titleSplit = titleGroup(++number, ques); //按照顺序写入文档 for (int i = 0 ; i < titleSplit.length ; i++ ) { Matcher mather = compile.matcher(titleSplit[i]);//匹配是否是公式 if (!mather.find()){ //paragraph.add(titleSplit[i]); paragraph.add(new Phrase(titleSplit[i]));//不是,直接写入 }else{//是,接着写入图片 //调用latexImage方法将公式转换成图片,wordLocalImage方法处理图片,图片生成 Image img = wordLocalImage(latexImage(titleSplit[i],"E:\\题库文档\\test06\\"+ i +".png")); paragraph.add(new Phrase(new Chunk(img, 0, 0, true))); } } document.add(paragraph);//题干段落结束 //选项段落 paragraph = new Paragraph(); paragraph.setSpacingAfter(8); //段落距离 //四个选项 例:\[-\cfrac{1}{2}\]|||\[\cfrac{1}{4}\]|||1|||\[\cfrac{25}{4}\] 以|||截取出四个选项 String[] split = ques.getOptions().split("\\|\\|\\|"); for (int i = 0 ; i < split.length ; i++ ) { if (split[i].contains("http")){//判断选项是否是图片 //调用wordHttpImage方法,图片读取 Image img = wordHttpImage(chooseUrl(i,split)); //调用chooseTag方法,选择题标签文本添加 paragraph.add(chooseTag(i)); //文本带图片写入 paragraph.add(new Phrase(new Chunk(img, 0, 0, true))); }else{//不是 //调用latexIamge方法,图片生成 Image img = wordLocalImage(latexImage(split[i],"E:\\题库文档\\test06\\"+ i +".png")); //选择题标签文本添加 paragraph.add(chooseTag(i)); //文本带图片 paragraph.add(new Phrase(new Chunk(img,0,0,true))); } } //添加选择题段落 document.add(paragraph); //详解类容 paragraph = new Paragraph(); paragraph.setSpacingAfter(8); //调用analysisSplit方法,题目类容段落 String[] analysisSplit = analysisGroup(ques); //按照顺序写入文档 for (int i = 0 ; i < analysisSplit.length ; i++ ) { Matcher mather = compile.matcher(analysisSplit[i]); if (mather.find()){ //图片生成 Image img = wordLocalImage(latexImage(analysisSplit[i],"E:\\题库文档\\test06\\"+ i +".png")); paragraph.add(new Phrase(new Chunk(img,0, 0, true))); }else{ paragraph.add(new Phrase(analysisSplit[i])); } } document.add(paragraph); paragraph = new Paragraph(); } // 关闭document document.close(); }
}
/** * 文档生成开始配置 * */ private Document wordStart(String wordTitle,String wordUrl) throws Exception { // 创建word文档,并设置纸张的大小 Document document = new Document(PageSize.A4); RtfWriter2.getInstance(document, new FileOutputStream(wordUrl)); // 打开document document.open(); // 设置字体,字号,加粗,颜色 Font font = new Font(Font.NORMAL, 20, Font.BOLD, new Color(0, 0, 0)); // 设置新的段落,使其字体为font Paragraph paragraph = new Paragraph(wordTitle, font); // 设置段落居中,其中1为居中对齐,2为右对齐,3为左对齐 paragraph.setAlignment(1); // 文档中加入该段落 document.add(paragraph); //调用系统的“楷体”字体,设置该段落时楷体 //BaseFont bf = BaseFont.createFont("C:\\Windows\\Fonts\\simli.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //font = new Font(bf, 16, Font.NORMAL, new Color(0, 0, 0)); return document; }
/** * 题干类容段落处理 * */ private String[] titleGroup(Integer number,Ques ques){ String title = number + "." + ques.getTitle(); //公式处理\[(\cfrac{x-y}{x+y}-\cfrac{x+y}{x-y})\div \cfrac{2x}{{{x}^{2}}y-{{y}^{3}}}\] 将\cfrac转换成\\,否者后面\cfrac转换成mathml其实转换的事cfrac,报错 title = title.replaceAll("\\\\","\\\\\\\\"); //\[ \] latex其实是没有的,为了转换公式不出错需要去掉,把整个公式替换成@@@,再把去掉\[ \]的然后以@@@截取后,就是 文字-公式-文字-公式的数组形式,最后依次写入文档 String titleNot = title.replaceAll("\\\\\\\\\\[(.*?)\\\\\\\\\\]","@@@"); Matcher mather = pattern.matcher(title); while (mather.find()){ String formula = mather.group(1); titleNot = titleNot.replaceFirst("@@@", "@@"+formula+"@@"); } String[] titleSplit = titleNot.split("@@"); return titleSplit; }
/** * latex公式转图片 * */ private String latexImage(String formulaStr,String path) throws IOException, BadElementException { TeXFormula tf = new TeXFormula(formulaStr); TeXIcon ti = tf.createTeXIcon(TeXConstants.STYLE_DISPLAY, 40); BufferedImage bimg = new BufferedImage(ti.getIconWidth(), ti.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR); Graphics2D g2d = bimg.createGraphics(); g2d.setColor(Color.white); g2d.fillRect(0,0,ti.getIconWidth(),ti.getIconHeight()); JLabel jl = new JLabel(); jl.setForeground(new Color(0, 0, 0)); ti.paintIcon(jl, g2d, 0, 0); File out = new File(path); ImageIO.write(bimg, "png", out); return path; }
/** * 文档线下图片获取 * */ private Image wordLocalImage(String imageUrl) throws Exception{ //声明图片 Image img = Image.getInstance(imageUrl); //绝对大小设置 //img.scaleAbsolute(img.getWidth()/3,img.getHeight()/3); //比例大小 img.scalePercent(30,30); //图片位置坐标 img.setAbsolutePosition(0, 0); //图片位置靠左并且文字绕图形显示| Image.TEXTWRAP ,图片背景Image.UNDERLYING img.setAlignment(Image.LEFT | Image.TEXTWRAP); return img; }
/** * 文档线上图片获取 * */ private Image wordHttpImage(String imageUrl) throws Exception{ //声明图片 Image img = Image.getInstance(new URL(imageUrl)); //img.scaleAbsolute(img.getWidth()/3, img.getHeight()/3); img.scalePercent(50,50); img.setAbsolutePosition(0, 0); img.setAlignment(Image.BOTTOM | Image.TEXTWRAP); return img; }
/** * 获取选择题选项标签 * */ private Phrase chooseTag(Integer i){ Phrase phrase = null; if (i == 0){ phrase = new Phrase("A:"); }else if(i == 1){ phrase = new Phrase(" B:"); }else if(i == 2){ phrase = new Phrase(" C:"); }else{ phrase = new Phrase(" D:"); } return phrase; }
/** * 详解类容段落处理 * */ private String[] analysisGroup(Ques ques){ String analysis = ques.getAnalysis(); //公式处理 analysis = analysis.replaceAll("\\\\","\\\\\\\\"); String analysisNot = analysis.replaceAll("\\\\\\\\\\[(.*?)\\\\\\\\\\]","@@@"); Matcher mather = pattern.matcher(analysis); while (mather.find()){ String formula = mather.group(1); analysisNot = analysisNot.replaceFirst("@@@", "@@"+formula+"@@"); } String[] analysisSplit = analysisNot.split("@@"); return analysisSplit; }
}
到此 : Itext生成的数学试卷文档结束
补充说明 : 如果是poi生成可编辑公式的数学试卷,将latex生成mathml公式写入文档的时候,博主用了很多latex转mathml的方法,但是没有找到一个是完善的,比如 如果是复杂的有关圆的公式都是转换不了的,解决办法 : 把latex转mathml失败的公式都把latex转图片写入到文档,这样呈现出可编辑公式和图片公式共存的文档.
如果在操作中遇到什么问题,可留言,博主会及时回答,给你意见及解决的办法,博客写得不好的地方,希望谅解!
如果需要POI生成数学试卷的方式,请关注博主,后续会更新.
如果觉得此博客对你有所帮助请打赏博主一元,微信扫码,无需加友,谢谢!