利用POI将word转换成html实现在线阅读

转载2015年11月03日 10:22:04

利用POI将word转换成html实现在线阅读

一、分析

通过网上找资料,发现用java实现word在线阅读有以下的实现方式:

1

Word=>PDF(OpenOffice+JodConverter)=>SWF(pdf2swf)=>FlexPaper浏览

2

Word=>PDF(MSOffice+JACOB)=>SWF(pdf2swf)=>FlexPaper浏览

3

Word =>SWF (FlashPaper)=> FlexPaper浏览

4

Word=>SWF(print2flash)=> FlexPaper浏览

5

用第三方收费组件:PageOffice

6

1)利用POIWord2003转换成html

2)利用OpenOffice+JodConverterword2003转换成html

前4种方式,目标都是一致的,就是都将word文档转换成flash文件,只是中间的实现不大一样。前两种方式比较麻烦,都是先转成PDF,再转成SWF,最后用FlexPaper浏览。两种比较快捷,可直接将源文件转为SWF,用FlexPaper浏览。第二种方式用到的jacob是微软的组件,在linux平台下基本是无望的了,第一个淘汰。由于FlashPaper不是开源工具,加之Win8系统不兼容(我现在用的系统),所以就没采用第三种实现方式。Print2flash是开源工具,即使公司产品中用到也不会出现版权纠纷,遗憾的是没找到如何用程序控制该工具转换文件的命令。所以第3,4种方式也淘汰了。通过下载,预使用,发现第5种方式用PageOffice是最省时省力的,也能将word文档完美的展现,但是,要钱!!好吧,一提到钱,此种实现只能暂作废。

后面一开始是想用OpenOffice+JodConverter实现转swf的,后面在逛百度文库的时候,发现一个让我很好奇的东西。就是,百度文库里的文档基本上都用html进行展示了,也就是说,我们上传的word文档,百度对其做了html转换的处理,与页面的嵌合也相当的好。这让我想到,我们的项目中是否也可以用此方式实现word的在线预览呢。

基于这个想法,我到谷歌找相关的资料,发现将word转html的开源工具没几个。其中,介绍得比较多的就是用POI进行转换,但是,由于POI对word的处理功能相当的弱,因此,开启了使用POI将wordàhtml的艰苦历程(后面发现网上有介绍用OpenOffice+JodConverter将word2003转换成html的方式,但是,我没有深究,有兴趣的同学可以去观望一下http://www.cnblogs.com/codeplus/archive/2011/10/22/2220952.html):

二、实现

1.      POI介绍:

Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“可怜的模糊实现”。

Apache POI 是创建和维护操作各种符合Office Open XML(OOXML)标准和微软的OLE 2复合文档格式(OLE2)的Java API。用它可以使用Java读取和创建,修改MS Excel文件.而且,还可以使用Java读取和创建MS Word和MSPowerPoint文件。Apache POI 提供Java操作Excel解决方案(适用于Excel97-2008)。

基本结构:

HSSF -提供读写Microsoft Excel XLS格式档案的功能。

XSSF -提供读写Microsoft Excel OOXML XLSX格式档案的功能。

HWPF -提供读写Microsoft Word DOC格式档案的功能。

HSLF -提供读写Microsoft PowerPoint格式档案的功能。

HDGF -提供读Microsoft Visio格式档案的功能。

HPBF -提供读Microsoft Publisher格式档案的功能。

HSMF -提供读Microsoft Outlook格式档案的功能。

其实,POI比较拿手的是处理Excel表格,即上面的HSSF及XSSF,我们的很多项目,只要涉及报表的,基本上都有用到它吧。用对于HWPF即处理DOC的包,功能就没有那么健全了,且API也不完善。

2.      poi相关包及依赖包配置。

3.      处理流程图:

1)   主体流程:

2)   进行word文档解释转换子流程

3)   处理表格子流程(略)

4)   处理图片子流程(略)

4.      代码实现

[java] view plaincopy

  1. packagecom;
  2. importjava.awt.image.BufferedImage;
  3. importjava.io.BufferedWriter;
  4. importjava.io.File;
  5. importjava.io.FileInputStream;
  6. importjava.io.FileNotFoundException;
  7. importjava.io.FileOutputStream;
  8. importjava.io.IOException;
  9. importjava.io.OutputStream;
  10. importjava.io.OutputStreamWriter;
  11. importjavax.imageio.ImageIO;
  12. importorg.apache.poi.hwpf.HWPFDocument;
  13. importorg.apache.poi.hwpf.model.PicturesTable;
  14. importorg.apache.poi.hwpf.usermodel.CharacterRun;
  15. importorg.apache.poi.hwpf.usermodel.Paragraph;
  16. importorg.apache.poi.hwpf.usermodel.Picture;
  17. importorg.apache.poi.hwpf.usermodel.Range;
  18. importorg.apache.poi.hwpf.usermodel.Table;
  19. importorg.apache.poi.hwpf.usermodel.TableCell;
  20. importorg.apache.poi.hwpf.usermodel.TableIterator;
  21. importorg.apache.poi.hwpf.usermodel.TableRow;
  22. importorg.apache.xmlbeans.impl.piccolo.io.FileFormatException;
  23. /**
  24. * @Description: 利用poi将word简单的转换成html文件
  25. * @author 柯颖波
  26. * @date 2013-12-20 上午09:32:44
  27. * @version v1.0
  28. */
  29. publicclassWord2Html {
  30. /**
  31. * 回车符ASCII码
  32. */
  33. privatestaticfinalshortENTER_ASCII =13;
  34. /**
  35. * 空格符ASCII码
  36. */
  37. privatestaticfinalshortSPACE_ASCII =32;
  38. /**
  39. * 水平制表符ASCII码
  40. */
  41. privatestaticfinalshortTABULATION_ASCII =9;
  42. privatestaticString htmlText ="";
  43. privatestaticString htmlTextTbl ="";
  44. privatestaticintcounter =0;
  45. privatestaticintbeginPosi =0;
  46. privatestaticintendPosi =0;
  47. privatestaticintbeginArray[];
  48. privatestaticintendArray[];
  49. privatestaticString htmlTextArray[];
  50. privatestaticbooleantblExist =false;
  51. /**
  52. * 项目路径
  53. */
  54. privatestaticString projectRealPath ="";
  55. /**
  56. * 临时文件路径
  57. */
  58. privatestaticString tempPath ="/upfile/"+ File.separator +"transferFile"+ File.separator;
  59. /**
  60. * word文档名称
  61. */
  62. privatestaticString wordName ="";
  63. publicstaticvoidmain(String argv[]) {
  64. try{
  65. wordToHtml("F:\\SVN\\BobUtil\\web\\", "2012年高考广东数学(文)试卷解析(精析word版)(学生版).doc");
  66. catch(Exception e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. /**
  71. * 读取每个文字样式
  72. *
  73. * @param fileName
  74. * @throws Exception
  75. */
  76. privatestaticvoidgetWordAndStyle(String fileName)throwsException {
  77. FileInputStream in = newFileInputStream(newFile(fileName));
  78. HWPFDocument doc = newHWPFDocument(in);
  79. Range rangetbl = doc.getRange();// 得到文档的读取范围
  80. TableIterator it = newTableIterator(rangetbl);
  81. intnum =100;
  82. beginArray = newint[num];
  83. endArray = newint[num];
  84. htmlTextArray = newString[num];
  85. tblExist = false;
  86. // 取得文档中字符的总数
  87. intlength = doc.characterLength();
  88. // 创建图片容器
  89. PicturesTable pTable = doc.getPicturesTable();
  90. // 创建段落容器
  91. htmlText = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title>"
  92. + doc.getSummaryInformation().getTitle()
  93. "</title></head><body><div style='margin:60px;text-align:center;'><div style='width:620px;text-align:left;line-height:24px;'>";
  94. // 创建临时字符串,好加以判断一串字符是否存在相同格式
  95. if(it.hasNext()) {
  96. readTable(it, rangetbl);
  97. }
  98. intcur =0;
  99. String tempString = "";
  100. for(inti =0; i < length -1; i++) {
  101. // 整篇<span class="wp_keywordlink"><a target=_blank href="http://www.it-crazy.com/" title="文章" target="_blank">文章</a></span>的字符通过一个个字符的来判断,range为得到文档的范围
  102. Range range = newRange(i, i +1, doc);
  103. CharacterRun cr = range.getCharacterRun(0);
  104. // beginArray=new int[num];
  105. // endArray=new int[num];
  106. // htmlTextArray=new String[num];
  107. if(tblExist) {
  108. if(i == beginArray[cur]) {
  109. htmlText += tempString + htmlTextArray[cur];
  110. tempString = "";
  111. i = endArray[cur] - 1;
  112. cur++;
  113. continue;
  114. }
  115. }
  116. if(pTable.hasPicture(cr)) {
  117. htmlText += tempString;
  118. // 读写图片
  119. try{
  120. readPicture(pTable, cr);
  121. catch(Exception e) {
  122. e.printStackTrace();
  123. }
  124. tempString = "";
  125. else{
  126. Range range2 = newRange(i +1, i +2, doc);
  127. // 第二个字符
  128. CharacterRun cr2 = range2.getCharacterRun(0);
  129. charc = cr.text().charAt(0);
  130. // System.out.println(c);
  131. // /System.out.println(i+"::"+range.getEndOffset()+"::"+range.getStartOffset()+"::"+c);
  132. // 判断是否为回车符
  133. if(c == ENTER_ASCII) {
  134. tempString += "<br/>";
  135. }
  136. // 判断是否为空格符
  137. elseif(c == SPACE_ASCII)
  138. tempString += " ";
  139. // 判断是否为水平制表符
  140. elseif(c == TABULATION_ASCII)
  141. tempString += "    ";
  142. // 比较前后2个字符是否具有相同的格式
  143. booleanflag = compareCharStyle(cr, cr2);
  144. if(flag)
  145. tempString += cr.text();
  146. else{
  147. String fontStyle = "<span style=\"font-family:"+ cr.getFontName() +";font-size:"
  148. + cr.getFontSize() / 2+"pt;";
  149. if(cr.isBold())
  150. fontStyle += "font-weight:bold;";
  151. if(cr.isItalic())
  152. fontStyle += "font-style:italic;";
  153. if(cr.isStrikeThrough())
  154. fontStyle += "text-decoration:line-through;";
  155. intfontcolor = cr.getIco24();
  156. int[] rgb =newint[3];
  157. if(fontcolor != -1) {
  158. rgb[0] = (fontcolor >>0) &0xff;// red;
  159. rgb[1] = (fontcolor >>8) &0xff;// green
  160. rgb[2] = (fontcolor >>16) &0xff;// blue
  161. }
  162. fontStyle += "color: rgb("+ rgb[0] +","+ rgb[1] +","+ rgb[2] +");";
  163. htmlText += fontStyle + "\">"+ tempString + cr.text() +"</span>";
  164. tempString = "";
  165. }
  166. }
  167. }
  168. htmlText += tempString + "</div></div></body></html>";
  169. // System.out.println(htmlText);
  170. }
  171. /**
  172. * 读写文档中的表格
  173. *
  174. * @param pTable
  175. * @param cr
  176. * @throws Exception
  177. */
  178. privatestaticvoidreadTable(TableIterator it, Range rangetbl)throwsException {
  179. htmlTextTbl = "";
  180. // 迭代文档中的表格
  181. counter = -1;
  182. while(it.hasNext()) {
  183. tblExist = true;
  184. htmlTextTbl = "";
  185. Table tb = (Table) it.next();
  186. beginPosi = tb.getStartOffset();
  187. endPosi = tb.getEndOffset();
  188. // System.out.println("............"+beginPosi+"...."+endPosi);
  189. counter = counter + 1;
  190. // 迭代行,默认从0开始
  191. beginArray[counter] = beginPosi;
  192. endArray[counter] = endPosi;
  193. htmlTextTbl += "<table border='1' cellpadding='0' cellspacing='0' >";
  194. for(inti =0; i < tb.numRows(); i++) {
  195. TableRow tr = tb.getRow(i);
  196. htmlTextTbl += "<tr align='center'>";
  197. // 迭代列,默认从0开始
  198. for(intj =0; j < tr.numCells(); j++) {
  199. TableCell td = tr.getCell(j);// 取得单元格
  200. intcellWidth = td.getWidth();
  201. // 取得单元格的内容
  202. for(intk =0; k < td.numParagraphs(); k++) {
  203. Paragraph para = td.getParagraph(k);
  204. CharacterRun crTemp = para.getCharacterRun(0);
  205. String fontStyle = "<span style=\"font-family:"+ crTemp.getFontName() +";font-size:"
  206. + crTemp.getFontSize() / 2+"pt;color:"+ crTemp.getColor() +";";
  207. if(crTemp.isBold())
  208. fontStyle += "font-weight:bold;";
  209. if(crTemp.isItalic())
  210. fontStyle += "font-style:italic;";
  211. String s = fontStyle + "\">"+ para.text().toString().trim() +"</span>";
  212. if(s =="") {
  213. s = " ";
  214. }
  215. // System.out.println(s);
  216. htmlTextTbl += "<td width="+ cellWidth +">"+ s +"</td>";
  217. // System.out.println(i + ":" + j + ":" + cellWidth + ":" + s);
  218. // end for
  219. // end for
  220. // end for
  221. htmlTextTbl += "</table>";
  222. htmlTextArray[counter] = htmlTextTbl;
  223. // end while
  224. }
  225. /**
  226. * 读写文档中的图片
  227. *
  228. * @param pTable
  229. * @param cr
  230. * @throws Exception
  231. */
  232. privatestaticvoidreadPicture(PicturesTable pTable, CharacterRun cr)throwsException {
  233. // 提取图片
  234. Picture pic = pTable.extractPicture(cr, false);
  235. BufferedImage image = null;// 图片对象
  236. // 获取图片样式
  237. intpicHeight = pic.getHeight() * pic.getAspectRatioY() /100;
  238. intpicWidth = pic.getAspectRatioX() * pic.getWidth() /100;
  239. if(picWidth >500) {
  240. picHeight = 500* picHeight / picWidth;
  241. picWidth = 500;
  242. }
  243. String style = " style='height:"+ picHeight +"px;width:"+ picWidth +"px'";
  244. // 返回POI建议的图片文件名
  245. String afileName = pic.suggestFullFileName();
  246. //单元测试路径
  247. String directory = "images/"+ wordName +"/";
  248. //项目路径
  249. //String directory = tempPath + "images/" + wordName + "/";
  250. makeDir(projectRealPath, directory);// 创建文件夹
  251. intpicSize = cr.getFontSize();
  252. intmyHeight =0;
  253. if(afileName.indexOf(".wmf") >0) {
  254. OutputStream out = newFileOutputStream(newFile(projectRealPath + directory + afileName));
  255. out.write(pic.getContent());
  256. out.close();
  257. afileName = Wmf2Png.convert(projectRealPath + directory + afileName);
  258. File file = newFile(projectRealPath + directory + afileName);
  259. try{
  260. image = ImageIO.read(file);
  261. catch(Exception e) {
  262. e.printStackTrace();
  263. }
  264. intpheight = image.getHeight();
  265. intpwidth = image.getWidth();
  266. if(pwidth >500) {
  267. htmlText += "<img style='width:"+ pwidth +"px;height:"+ myHeight +"px'"+" src=\""+ directory
  268. + afileName + "\"/>";
  269. else{
  270. myHeight = (int) (pheight / (pwidth / (picSize *1.0)) *1.5);
  271. htmlText += "<img style='vertical-align:middle;width:"+ picSize *1.5+"px;height:"+ myHeight
  272. "px'"+" src=\""+ directory + afileName +"\"/>";
  273. }
  274. else{
  275. OutputStream out = newFileOutputStream(newFile(projectRealPath + directory + afileName));
  276. // pic.writeImageContent(out);
  277. out.write(pic.getContent());
  278. out.close();
  279. // 处理jpg或其他(即除png外)
  280. if(afileName.indexOf(".png") == -1) {
  281. try{
  282. File file = newFile(projectRealPath + directory + afileName);
  283. image = ImageIO.read(file);
  284. picHeight = image.getHeight();
  285. picWidth = image.getWidth();
  286. if(picWidth >500) {
  287. picHeight = 500* picHeight / picWidth;
  288. picWidth = 500;
  289. }
  290. style = " style='height:"+ picHeight +"px;width:"+ picWidth +"px'";
  291. catch(Exception e) {
  292. // e.printStackTrace();
  293. }
  294. }
  295. htmlText += "<img "+ style +" src=\""+ directory + afileName +"\"/>";
  296. }
  297. if(pic.getWidth() >450) {
  298. htmlText += "<br/>";
  299. }
  300. }
  301. privatestaticbooleancompareCharStyle(CharacterRun cr1, CharacterRun cr2) {
  302. booleanflag =false;
  303. if(cr1.isBold() == cr2.isBold() && cr1.isItalic() == cr2.isItalic()
  304. && cr1.getFontName().equals(cr2.getFontName()) && cr1.getFontSize() == cr2.getFontSize()) {
  305. flag = true;
  306. }
  307. returnflag;
  308. }
  309. /**
  310. * 写文件(成功返回true,失败则返回false)
  311. *
  312. * @param s
  313. *            要写入的内容
  314. * @param filePath
  315. *            文件
  316. */
  317. privatestaticbooleanwriteFile(String s, String filePath) {
  318. FileOutputStream fos = null;
  319. BufferedWriter bw = null;
  320. s = s.replaceAll("EMBED","").replaceAll("Equation.DSMT4","");
  321. try{
  322. makeDir(projectRealPath, tempPath);// 创建文件夹
  323. File file = newFile(filePath);
  324. if(file.exists()) {
  325. returnfalse;
  326. }
  327. fos = newFileOutputStream(file);
  328. bw = newBufferedWriter(newOutputStreamWriter(fos,"utf-8"));
  329. bw.write(s);
  330. // System.out.println(filePath + "文件写入成功!");
  331. catch(FileNotFoundException fnfe) {
  332. fnfe.printStackTrace();
  333. catch(IOException ioe) {
  334. ioe.printStackTrace();
  335. finally{
  336. try{
  337. if(bw !=null)
  338. bw.close();
  339. if(fos !=null)
  340. fos.close();
  341. catch(IOException ie) {
  342. ie.printStackTrace();
  343. }
  344. }
  345. returntrue;
  346. }
  347. /**
  348. * 根据路径名生成多级路径
  349. *
  350. * @param url
  351. *            参数要以"\classes\cn\qtone\"或者"/classes/cn/qtone/"
  352. */
  353. privatestaticString makeDir(String root, String url) {
  354. String[] sub;
  355. url = url.replaceAll("\\/","\\\\");
  356. if(url.indexOf("\\") > -1) {
  357. sub = url.split("\\\\");
  358. else{
  359. return"-1";
  360. }
  361. File dir = null;
  362. try{
  363. dir = newFile(root);
  364. for(inti =0; i < sub.length; i++) {
  365. if(!dir.exists() && !sub[i].equals("")) {
  366. dir.mkdir();
  367. }
  368. File dir2 = newFile(dir + File.separator + sub[i]);
  369. if(!dir2.exists()) {
  370. dir2.mkdir();
  371. }
  372. dir = dir2;
  373. }
  374. catch(Exception e) {
  375. e.printStackTrace();
  376. return"-1";
  377. }
  378. returndir.toString();
  379. }
  380. /**
  381. * 将word文档转化,返回转化后的文件路径
  382. *
  383. * @param projectPath
  384. *            项目路径
  385. * @param relativeFilePath
  386. *            文件相对路径
  387. * @return 返回生成的htm路径(如果出错,则返回null)
  388. */
  389. publicstaticString wordToHtml(String projectPath, String relativeFilePath) {
  390. String resultPath = null;
  391. projectRealPath = projectPath;// 项目路径
  392. String filePath = "";
  393. // System.out.println(projectRealPath + tempPath);
  394. // System.out.println(makeDir(projectRealPath, tempPath));
  395. try{
  396. File file = newFile(projectPath + relativeFilePath);
  397. if(file.exists()) {
  398. if(file.getName().indexOf(".doc") == -1|| file.getName().indexOf(".docx") >0) {
  399. thrownewFileFormatException("请确认文件格式为doc!");
  400. else{
  401. wordName = file.getName();
  402. wordName = wordName.substring(0, wordName.indexOf("."));
  403. filePath = projectRealPath + tempPath + wordName + ".htm";
  404. synchronized(relativeFilePath) {// 处理线程同步问题
  405. File ff = newFile(filePath);
  406. if(!ff.exists()) {// 如果不存在则进行转换
  407. getWordAndStyle(projectPath + relativeFilePath);
  408. writeFile(htmlText, filePath);
  409. }
  410. }
  411. resultPath = tempPath + wordName + ".htm";
  412. }
  413. else{
  414. thrownewFileNotFoundException("没找到相关文件!");
  415. }
  416. catch(NullPointerException e) {
  417. e.printStackTrace();
  418. catch(FileNotFoundException e) {
  419. e.printStackTrace();
  420. catch(Exception e) {
  421. e.printStackTrace();
  422. }
  423. returnresultPath;
  424. }
  425. }
[java] view plaincopy

  1. packagecom;
  2. importjava.io.ByteArrayInputStream;
  3. importjava.io.ByteArrayOutputStream;
  4. importjava.io.File;
  5. importjava.io.FileInputStream;
  6. importjava.io.FileOutputStream;
  7. importjava.io.InputStream;
  8. importjava.io.OutputStream;
  9. importjava.util.Scanner;
  10. importjava.util.zip.GZIPOutputStream;
  11. importjavax.xml.parsers.DocumentBuilder;
  12. importjavax.xml.parsers.DocumentBuilderFactory;
  13. importjavax.xml.transform.OutputKeys;
  14. importjavax.xml.transform.Transformer;
  15. importjavax.xml.transform.TransformerFactory;
  16. importjavax.xml.transform.dom.DOMSource;
  17. importjavax.xml.transform.stream.StreamResult;
  18. importnet.arnx.wmf2svg.gdi.svg.SvgGdi;
  19. importnet.arnx.wmf2svg.gdi.wmf.WmfParser;
  20. importorg.apache.batik.transcoder.TranscoderInput;
  21. importorg.apache.batik.transcoder.TranscoderOutput;
  22. importorg.apache.batik.transcoder.TranscodingHints;
  23. importorg.apache.batik.transcoder.image.PNGTranscoder;
  24. importorg.apache.batik.transcoder.wmf.tosvg.WMFTranscoder;
  25. importorg.apache.commons.lang.StringUtils;
  26. importorg.w3c.dom.Document;
  27. importorg.w3c.dom.Element;
  28. publicclassWmf2Png {
  29. publicstaticvoidmain(String[] args)throwsException {
  30. // convert("F:\\SVN\\BobUtil\\web\\25177.wmf");
  31. // System.out.println((20 / (21 * 1.0)));
  32. // svgToPng("F:\\SVN\\BobUtil\\web\\25177.svg", "F:\\SVN\\BobUtil\\web\\25177.png");
  33. }
  34. /**
  35. * @Description: 进行转换
  36. * @param filePath
  37. *            文件路径
  38. * @return 设定文件
  39. */
  40. publicstaticString convert(String filePath) {
  41. String pngFile = "";
  42. File wmfFile = newFile(filePath);
  43. try{
  44. if(!wmfFile.getName().contains(".wmf")) {
  45. thrownewException("请确认输入的文件类型是wmf");
  46. }
  47. // wmf -> svg
  48. String svgFile = filePath.replace("wmf","svg");
  49. wmfToSvg(filePath, svgFile);
  50. // 对svg做预出理
  51. PreprocessSvgFile(svgFile);
  52. // svg -> png
  53. pngFile = filePath.replace("wmf","png");
  54. svgToPng(svgFile, pngFile);
  55. // 删除 svg
  56. File file = newFile(svgFile);
  57. if(file.exists()) {
  58. file.delete();
  59. }
  60. // 删除 wmf
  61. if(wmfFile.exists()) {
  62. wmfFile.delete();
  63. }
  64. catch(Exception e) {
  65. try{
  66. e.printStackTrace();
  67. wmfToJpg(filePath);
  68. catch(Exception e1) {
  69. e1.printStackTrace();
  70. }
  71. }
  72. returnwmfFile.getName().replace("wmf","png");
  73. }
  74. /**
  75. * 将wmf转换为svg
  76. *
  77. * @param src
  78. * @param dest
  79. */
  80. publicstaticvoidwmfToSvg(String src, String dest)throwsException {
  81. booleancompatible =false;
  82. try{
  83. InputStream in = newFileInputStream(src);
  84. WmfParser parser = newWmfParser();
  85. finalSvgGdi gdi =newSvgGdi(compatible);
  86. parser.parse(in, gdi);
  87. Document doc = gdi.getDocument();
  88. OutputStream out = newFileOutputStream(dest);
  89. if(dest.endsWith(".svgz")) {
  90. out = newGZIPOutputStream(out);
  91. }
  92. output(doc, out);
  93. catch(Exception e) {
  94. throwe;
  95. }
  96. }
  97. /**
  98. * @Description: 输出svg文件
  99. * @param doc
  100. * @param out
  101. * @throws Exception
  102. *             设定文件
  103. */
  104. privatestaticvoidoutput(Document doc, OutputStream out)throwsException {
  105. TransformerFactory factory = TransformerFactory.newInstance();
  106. Transformer transformer = factory.newTransformer();
  107. transformer.setOutputProperty(OutputKeys.METHOD, "xml");
  108. transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
  109. transformer.setOutputProperty(OutputKeys.INDENT, "yes");
  110. transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD SVG 1.0//EN");
  111. transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
  112. "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
  113. transformer.transform(newDOMSource(doc),newStreamResult(out));
  114. out.flush();
  115. out.close();
  116. out = null;
  117. }
  118. /**
  119. * @Description:对svg文件做预处理(这里主要是调整大小,先缩小10倍,如果还大于默认值,则按比例缩小)
  120. * @param svgFile
  121. * @throws Exception
  122. *             设定文件
  123. */
  124. privatestaticvoidPreprocessSvgFile(String svgFile)throwsException {
  125. intdefaultWeight =500;// 默认宽度
  126. FileInputStream inputs = newFileInputStream(svgFile);
  127. Scanner sc = newScanner(inputs,"UTF-8");
  128. ByteArrayOutputStream os = newByteArrayOutputStream();
  129. while(sc.hasNextLine()) {
  130. String ln = sc.nextLine();
  131. if(!ln.startsWith("<!DOCTYPE")) {
  132. os.write((ln + "\r\n").getBytes());
  133. }
  134. }
  135. os.flush();
  136. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  137. DocumentBuilder builder;
  138. builder = factory.newDocumentBuilder();
  139. Document doc = null;
  140. try{
  141. doc = builder.parse(newByteArrayInputStream(os.toByteArray()));
  142. catch(Exception e) {
  143. inputs = newFileInputStream(svgFile);
  144. os = newByteArrayOutputStream();
  145. intnoOfByteRead =0;
  146. while((noOfByteRead = inputs.read()) != -1) {
  147. os.write(noOfByteRead);
  148. }
  149. os.flush();
  150. doc = builder.parse(newByteArrayInputStream(os.toByteArray()));
  151. finally{
  152. os.close();
  153. inputs.close();
  154. }
  155. intheight = Integer.parseInt(((Element) doc.getElementsByTagName("svg").item(0)).getAttribute("height"));
  156. intwidth = Integer.parseInt(((Element) doc.getElementsByTagName("svg").item(0)).getAttribute("width"));
  157. intnewHeight =0;// 新高
  158. intnewWidth =0;// 新宽
  159. newHeight = height / 10;// 高缩小10倍
  160. newWidth = width / 10;// 宽缩小10倍
  161. // 如果缩小10倍后宽度还比defaultHeight大,则进行调整
  162. if(newWidth > defaultWeight) {
  163. newWidth = defaultWeight;
  164. newHeight = defaultWeight * height / width;
  165. }
  166. ((Element) doc.getElementsByTagName("svg").item(0)).setAttribute("width", String.valueOf(newWidth));
  167. ((Element) doc.getElementsByTagName("svg").item(0)).setAttribute("height", String.valueOf(newHeight));
  168. OutputStream out = newFileOutputStream(svgFile);
  169. output(doc, out);
  170. }
  171. /**
  172. * 将svg图片转成png图片
  173. *
  174. * @param filePath
  175. * @throws Exception
  176. */
  177. publicstaticvoidsvgToPng(String svgPath, String pngFile)throwsException {
  178. File svg = newFile(svgPath);
  179. FileInputStream wmfStream = newFileInputStream(svg);
  180. ByteArrayOutputStream imageOut = newByteArrayOutputStream();
  181. intnoOfByteRead =0;
  182. while((noOfByteRead = wmfStream.read()) != -1) {
  183. imageOut.write(noOfByteRead);
  184. }
  185. imageOut.flush();
  186. imageOut.close();
  187. wmfStream.close();
  188. ByteArrayOutputStream jpg = newByteArrayOutputStream();
  189. FileOutputStream jpgOut = newFileOutputStream(pngFile);
  190. byte[] bytes = imageOut.toByteArray();
  191. PNGTranscoder t = newPNGTranscoder();
  192. TranscoderInput in = newTranscoderInput(newByteArrayInputStream(bytes));
  193. TranscoderOutput out = newTranscoderOutput(jpg);
  194. t.transcode(in, out);
  195. jpgOut.write(jpg.toByteArray());
  196. jpgOut.flush();
  197. jpgOut.close();
  198. imageOut = null;
  199. jpgOut = null;
  200. }
  201. /**
  202. * 将wmf图片转成png图片(备用方法,即当上面的转换失败时用这个)
  203. *
  204. * @param filePath
  205. * @throws Exception
  206. */
  207. publicstaticString wmfToJpg(String wmfPath)throwsException {
  208. //先wmf-->svg
  209. File wmf = newFile(wmfPath);
  210. FileInputStream wmfStream = newFileInputStream(wmf);
  211. ByteArrayOutputStream imageOut = newByteArrayOutputStream();
  212. intnoOfByteRead =0;
  213. while((noOfByteRead = wmfStream.read()) != -1) {
  214. imageOut.write(noOfByteRead);
  215. }
  216. imageOut.flush();
  217. imageOut.close();
  218. wmfStream.close();
  219. // WMFHeaderProperties prop = new WMFHeaderProperties(wmf);
  220. WMFTranscoder transcoder = newWMFTranscoder();
  221. TranscodingHints hints = newTranscodingHints();
  222. transcoder.setTranscodingHints(hints);
  223. TranscoderInput input = newTranscoderInput(newByteArrayInputStream(imageOut.toByteArray()));
  224. ByteArrayOutputStream svg = newByteArrayOutputStream();
  225. TranscoderOutput output = newTranscoderOutput(svg);
  226. transcoder.transcode(input, output);
  227. //再svg-->png
  228. ByteArrayOutputStream jpg = newByteArrayOutputStream();
  229. String jpgFile = StringUtils.replace(wmfPath, "wmf","png");
  230. FileOutputStream jpgOut = newFileOutputStream(jpgFile);
  231. byte[] bytes = svg.toByteArray();
  232. PNGTranscoder t = newPNGTranscoder();
  233. TranscoderInput in = newTranscoderInput(newByteArrayInputStream(bytes));
  234. TranscoderOutput out = newTranscoderOutput(jpg);
  235. t.transcode(in, out);
  236. jpgOut.write(jpg.toByteArray());
  237. jpgOut.flush();
  238. jpgOut.close();
  239. returnjpgFile;
  240. }
  241. }

重点难点解释探讨:

1)  读取表格部分:

a)        找出表格的开始与结束标记;

b)        遍历整个表格内容,逐个单元格的内容取出并追加到变量中。

2)  读取图片部分

a)        图片文件的格式问题。

如果图片格式为png或者jpg,则可以直接进行处理并加入标签中,前台的html展示没有问题,但是,如果图片格式为wmf(详细看附录1),则html无法对基解释,那么我们只能对其进行转换格式:

百度后,网上很多说法都建议用batik工具包进行格式转换,其实思路就是:wmfàsvgàpng。查阅相关资料(如附录2),发现其处理svg文件的能力相当的强,即从svg—>png这一步是比较完美的。但是,在处理wmf—>svg这一步却导致部分图像丢失,即失真的情况,且很严重。查看相关的api看是否参数设置问题,但是无论怎么设置,结果还是不尽人意。一度想放弃,找别的包。

后来,无意中,在csdn中有网友建议先用wmf2svg工具类将wmf转换为svg,再用batiksvg转换为png。Very
good!!有了这个思路,感觉已经看到署光了。

类写出来后,进行类型转换测试,确实效果很好,完全没有失真。于是将其嵌入word—>html这个工具类中。再用各种包含了wmf图片的文档进行测试。生成的html文件,基本没有问题,当时那个开心啊!!(我去,程序员也就这德行)

好景不长,放到正式项目进行测试过程中,发现有个别文档一进行转换,服务器就跨了,直接报内存溢出。通过排查检测,原来就是进行图片转换过程中,将内存给挤爆了。奇怪了,虽然知道图片处理是比较耗内存,但也没想到1G的内存,一下子就被挤爆(刚跑起来占去300M左右,一跑word转换功能,不过一会就报OutOfMemorry)。

一度怀疑,是不是batik这个工具包是不是有bug,处理不了大的svg。还将问题放上了bakit的官网。后来,查看相关资料后,发现是wmf2svg工具生成的svg的高与宽都太大了,举个例子:15040* 13088,宽高都达到上万级别,结果得到的象素是上亿的,不爆内存才怪。

用dom工具,将每一个生成的svg文件再进行预处理,即将其高与宽都先缩小一倍,如果宽度依然比500要大,则将其设成500,并将高也按比例缩小。经过此步骤生成的svg再用batik进行转换就没有任何问题了。

到这里,差不多已经解决图片转换的问题了,但是,在使用过程中,发现wmf2svg这个工具也不是很稳定,偶尔会报异常,并且,我测试发现,报异常的这个wmf用之前batik直接进行wmf—>svgàpng的方案可以成功生成没有失真的png,于是,在wmf2svg的产生异常进行捕捉,并调用了wmfToJpg(String wmfPath)的备用方法。到此,大部分的wmf转换问题已经解决。

b)        生成html文本的<img />标签的width与height问题。

如果图片格式原本为png的话,直接用

[java] view plaincopy

  1. // 获取图片样式
  2. intpicHeight = pic.getHeight() * pic.getAspectRatioY() / 100;
[java] view plaincopy

  1. intpicWidth = pic.getAspectRatioX() * pic.getWidth() /100;

即可以将图片的宽与高设置与word文档一致;但是,发果wmf格式,要分两种情况分析:

Ø  如果转换生成的png宽度不小于500,则将期作为一般图片处理:

[java] view plaincopy

  1. BufferedImage  image = ImageIO.read(file);
  2. intpheight = image.getHeight();
  3. intpwidth = image.getWidth();

Ø  如果转换生成的png宽度小于500,则认为是一般的公式,则应该与它旁边的字体宽度相近,这里设成字体的1.5倍宽度,高度为:

[java] view plaincopy

  1. myHeight= (int) (pheight / (pwidth / (picSize *1.0)) *1.5);

如果图片即非wmf与非png(如jpg)的情况下,上面获取高与宽的方法不起作用,不知道是不是POI的bug。只能按以下方式处理:

[java] view plaincopy

  1. BufferedImage  image = ImageIO.read(file);
  2. intpheight = image.getHeight();
  3. intpwidth = image.getWidth();

即跟上面处理wmf的第一种方式一致。

三、结束语

讲到这,将word转换成html的处理也大体上讲完了。这几天的边学边用,特别是真正能解决问题的时候,非常有成就感。其实,上面的处理还存在以下的问题待解决的:

1)读取表格部分:

a)        表格中如果再含有表格,POI无法进行很好的区分,比如,有一个两行两列的表格中,第一行第一列中又包含了一个两行两列的表格,那POI会将此表格解释成:第一行为2+2*2 = 6个单元格;第二行为2个单元格,这样解释出来的表格就很怪异了。

b)        表格中有果有合并单格的情况,程序暂未做此处理(后续看不能优化),表格也很怪异。

c)        表格中如果有图像,程序没有做相应的处理。

2)读取图片部分:

a) 有部分wmf->png的方式有个别图片还是没有转换成功,会报异常,但没有影响整体的功能;

b) word有部分公式生成的图片无法识别模式,不知道是不是POI无法将其解释,还是其他原因,就是有文档,生成没有后缀的图片文件,且这部分文件无法读取,用图片工具也打不开,暂时未找到很好的解决方案。

3)读取word的目录:

在读取目录会出现将格式化符号也解释出来。

4)其他未知的一些问题,反正,就觉得用POI来解释word是件很坚苦的事情,如果全是文本还好,如果里面包含图片,表格,公式等这些对象的时候,POI就显得太弱了。

附:

1.       wmf文件:

MicrosoftOffice 的剪贴画使用的就是这个格式。

Wmf是WindowsMetafile 的缩写,简称图元文件,它是微软公司定义的一种Windows平台下的图形文件格式。

wmf格式文件的特点如下:

1)                 wmf格式文件是MicrosoftWindows操作平台所支持的一种图形格式文件,目前,其它操作系统尚不支持这种格式,如Unix、Linux等。

2)                 与bmp格式不同,wmf格式文件是和设备无关的,即它的输出特性不依赖于具体的输出设备。

3)                 其图象完全由Win32 API所拥有的GDI函数来完成。

4)                 wmf格式文件所占的磁盘空间比其它任何格式的图形文件都要小得多。

5)                 在建立图元文件时,不能实现即画即得,而是将GDI调用记录在图元文件中,之后,在GDI环境中重新执行,才可显示图象。

6)                 显示图元文件的速度要比显示其它格式的图象文件慢,但是它形成图元文件的速度要远大于其它格式。

2.      Batik介绍

Batik是使用svg格式图片来实现各种功能的应用程序以及Applet提供的一个基于java的工具包。

通过Batik,你可以在JAVA可以使用的地方操作SVG文档,您还可以在你的应用程序使用Batik模块来生成, 处理和转码SVG图像。Batik很容易让基于Java的应用程序或小程序来处理SVG内容。 例如,使用Batik的SVG的发生器模块 ,Java应用程序或小程序可以很轻松地导出SVG格式的图形到。用Batik的SVG的查看组件,应用程序或小程序可以很容易地集成SVG的浏览和交互功能。另一种可能性是使用Batik的模块转换成各种格式SVG的通过,如光栅图像(JPEG,PNG或TIFF格式)或其它矢量格式(EPS或PDF格式,后两者由于转码器由Apache
FOP提供)。 Batik工程创建的目的是为开发者提供一系列可以结合或单独使用来支持特殊的svg解决方案的核心模块。模块主要有SVGParser,SVGGernerator,SVGDOM。Batik工程的其他目的是使它具有高度的扩展性。

(SVG的规范:可缩放矢量图形(SVG),是一个W3C的推荐标准。 它定义了丰富的2D图形的XML语法,其中包括诸如透明度功能,几何形状,滤镜效果(阴影,灯光效果等),脚本和动画)

利用poi实现word转换html相关推荐

  1. poi处理word内容的公式_利用poi操作word文档

    关键字:POI JAVA 批注 总页数 总字符数 一:认识POI Apache POI是一个开源的利用Java读写Excel.WORD等微软OLE2组件文档的项目.最新的3.5版本有很多改进,加入了对 ...

  2. 利用poi操作word文档(针对docx格式)

    一:认识POI  Apache POI是一个开源的利用Java读写Excel.WORD等微软OLE2组件文档的项目.最新的3.5版本有很多改进,加入了对采用OOXML格式的Office 2007支持, ...

  3. 利用poi读取word模板文件生成新的word文档

    利用poi读取word模板文件生成新的word文档 利用poi读取word模板文件,并回填逻辑数据,生成并导出需要的word文档源码.解决模板读取异常问题,提供wordUtils工具类(各种功能实现) ...

  4. java word模板poi生成文件_利用poi读取word模板文件生成新的word文档

    利用poi读取word模板文件生成新的word文档 利用poi读取word模板文件,并回填逻辑数据,生成并导出需要的word文档源码.解决模板读取异常问题,提供wordUtils工具类(各种功能实现) ...

  5. java利用POI替换word文档中的标签

    java利用POI替换word文档中的标签 <dependency><groupId>org.apache.poi</groupId><artifactId& ...

  6. Java利用poi生成word(包含插入图片,动态表格,行合并)

    Java利用poi生成word(包含插入图片,动态表格,行合并) 测试模板样式: 图表 1 Word生成结果: 图表 2 需要的jar包:(具体jar可自行去maven下载) Test测试类: imp ...

  7. java利用poi导出word文档

    项目中,有时候需要使用poi实现将固定数据导入word中 效果图: 代码: package poiword;import java.io.File; import java.io.FileOutput ...

  8. 利用poi向word模板填充数据

    java基于POI实现向word模板填充数据 在做项目的时候遇到需要将多张表单导出为word,就想到了这个方法. 注意:XWPFDocument不支持doc类型文档,做模板的时候要另存为docx. 示 ...

  9. poi 顺序解析word_利用POI读取word、Excel文件的最佳实践教程

    前言 POI是 Apache 旗下一款读写微软家文档声名显赫的类库.应该很多人在做报表的导出,或者创建 word 文档以及读取之类的都是用过 POI.POI 也的确对于这些操作带来很大的便利性.我最近 ...

  10. java利用POI在word中绘制折线图

    1.poi的简介 Apache POI 简介是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel.WORD ...

最新文章

  1. ADO.NET 2.0中的SqlCommand.ExecutePageReader
  2. 第十一届山东省大学生程序设计竞赛 L. Construction of 5G Base Stations(概率期望,递推前缀和优化)
  3. 【设计模式】原型模式 ( 概念简介 | 使用场景 | 优缺点 | 基本用法 )
  4. ubuntu部署git
  5. android 获取屏幕的宽高
  6. FPGA图像处理的开发流程
  7. 【ArcGIS Pro风暴】Data Interoperability Tools快速将CASS等高线dwg转为shp案例教程
  8. pandas groupby count_数据分析14-利用pandas进行数据分组
  9. 通用的类-可直接存储的Dictionary,可以被JSON或NSUserDefaults
  10. android 来电自动接听和自动挂断
  11. 如何短期通过2022年3月PMP考试?
  12. springboot activiti 7 和activiti 6 配置详解
  13. 一篇文章带你认识什么是数学建模
  14. 计算机网络共享自动关,启用网络发现,重新打开“高级共享设置”对话框,显示仍是关闭状态...
  15. Python open()函数详解:打开指定文件
  16. 自动驾驶上的三种感知传感器(激光、毫米波雷达和摄像头)优缺点比较
  17. 北京某公司IBM X3650M3存储崩溃的解决过程
  18. 金融级湖仓一体架构——SequoiaDB巨杉数据库初探
  19. scrapy 爬取链家二手房数据
  20. malloc()函数与free()函数的使用

热门文章

  1. Windows安全设置-当前的安全设置不允许从该位置下载文件
  2. 什么是软件危机?它有哪些典型表现?为什么会出现软件危机?
  3. Github删除历史提交记录的方法
  4. 高德网络定位之“移动WiFi识别”
  5. BugkuCTF:宽带泄露;隐写2
  6. 论文阅读:SPR:Supervised Personalized Ranking Based on Prior Knowledge for Recommendation
  7. 【013】故宫博物院数字文物库-让文物随时可赏
  8. K-mer频率分布图代码实现
  9. 【数字电路抢救】2 逻辑函数的代数化简 3逻辑表达式的化简
  10. Ubuntu配置静态IP以及interfaces配置不生效问题解决