執法文書打印的實現(二) html
基於freemaker技術生成可打印的word文檔: java
基於FreeMarker生成word.doc文檔是一項比較成熟的技術。前承上篇博客(),這個方案只能在windows下部署,不支持linux。這方面的示例網上已經不少了,我也簡單上下代碼好了(深刻的話,我也不瞭解(汗)。node
略過樣例測試,首先須要肯定word模板。好比: 現場檢查筆錄.doc ,打開文件,把須要替換的內容加上標記 格式:${插值},並設置好頁眉等樣式: linux
其次:將word模板另存爲xml 2003,並用xml編輯器打開,這裏推薦使用XMLSPY ,這個工具打開xml的顯示易於查找和編輯。打開xml,編輯word另存爲後插值分開的部分,從新合併(這個在上篇博客已經詳細描述,這裏就不贅述了)。 sql
最後把xcjc.xml修改成xcjc.ftl,模板文件也就準備好了,下面就須要添加具體的java代碼。 數據庫
在項目中導入對應的jar包,書寫生成word.docx文檔的方法: vim
public String createWord(Map dataMap,String templateName){ String fileName=UUID.randomUUID().toString()+".docx"; try { //建立配置實例 Configuration configuration = new Configuration(Configuration.VERSION_2_3_21); //設置編碼 configuration.setDefaultEncoding("UTF-8"); //ftl模板文件統一放至com.sinosoft.zhifa.common.commonPrint.ftlFile.包下面 //configuration.setClassForTemplateLoading(PrintUtil.class,"/com/sinosoft/zhifa/common/commonPrint/ftlFile/"); //這種方法取不到已修改的模板 //設置從文件系統中加載模板 ftlFilePath文件在系統的路徑 configuration.setDirectoryForTemplateLoading(new File(ftlFilePath)); //獲取模板 Template template = configuration.getTemplate(templateName); //輸出文件 File.separator分隔符unix爲/ window爲\\ File outFile = new File(wordFilePath+File.separator+fileName); //若是輸出目標文件夾不存在,則建立 if (!outFile.getParentFile().exists()){ outFile.getParentFile().mkdirs(); } //將模板和數據模型合併生成文件 Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8")); //生成文件 template.process(dataMap, out); //關閉流 out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } return fileName; }
其中,dataMap是要替換的數據map,這個map的key是模板的插值,value是具體的數據,並且value不能爲null,下面是示例: windows
Map dataMap=new HashMap(); dataMap.put("被檢查單位",(String)scenMap.get("ENT_NAME")==null?" ":(String)scenMap.get("ENT_NAME"));//ENT_NAME 北京恩彼凱化肥廠 dataMap.put("營業執照",(String)scenMap.get("BUSINESS_LICENSE")==null?" ":(String)scenMap.get("BUSINESS_LICENSE"));//BUSINESS_LICENSE 這是咱們的營業執照BUSINESS_LICENSE 這是咱們的營業執照 dataMap.put("編號",(String)scenMap.get("BUSINESS_LICENSE_ID")==null?" ":(String)scenMap.get("BUSINESS_LICENSE_ID"));//BUSINESS_LICENSE_ID xxxxxxx dataMap.put("組織機構代碼",(String)scenMap.get("ORGANIZATION_CODE")==null?" ":(String)scenMap.get("ORGANIZATION_CODE"));//ORGANIZATION_CODE 123123412384組織機構代碼 dataMap.put("地址",(String)scenMap.get("ILLEGALACT_ADDRESS")==null?" ":(String)scenMap.get("ILLEGALACT_ADDRESS"));//ILLEGALACT_ADDRESS 北京市朝陽區霄雲路 dataMap.put("電話",(String)scenMap.get("ILLEGALACT_PHONE")==null?" ":(String)scenMap.get("ILLEGALACT_PHONE"));//ILLEGALACT_PHONE 13366009185 dataMap.put("法定表明人",(String)scenMap.get("LEGAL_REPRESENTATIVE")==null?" ":(String)scenMap.get("LEGAL_REPRESENTATIVE"));//LEGAL_REPRESENTATIVE 法定負責人 dataMap.put("性別",(String)scenMap.get("GENDER")==null?" ":(String)scenMap.get("GENDER"));//GENDER 男 dataMap.put("職務",(String)scenMap.get("DUTY")==null?" ":(String)scenMap.get("DUTY"));//DUTY 總經理
因爲插值{現場檢查}是一個富文本,須要圖文混排,我在這裏是解析數據庫的html的標籤,根據須要向xcjc.ftl添加插值,並向dataMap中寫入對應的key、value: app
解析html中的圖片並調用修改ftl的方法: dom
//用圖片分割後的富文本
String [] imgs=inspection.split("<img\\s+[^>]*/>|<img\\s+[^>]*>[^<]*</img>");
//若是有圖片,則從新生成ftl文件
XmlUtils xmlutils=new XmlUtils(getRequest());
Map newMap=xmlutils.xmlHandle("xcjc_copy.xml",imgs,pictList);
修改ftl的具體實現:
public Map xmlHandle(String xmlfilepath, String[] imgs, List<String> pictList) { Map<String, String> pictMap = new HashMap<String, String>(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { DocumentBuilder db = dbf.newDocumentBuilder(); File file = new File(ftlFilePath + fileS + xmlfilepath); document = db.parse(file); NodeList nodelist = document.getElementsByTagName("w:p"); Node baseNode = null;// 基於此節點添加節點信息 Node parentNode = null;// 在此節點下添加節點信息 for (int i = 0; i < nodelist.getLength(); i++) { baseNode = nodelist.item(i); Short eNode = Node.ELEMENT_NODE; // 節點類型是節點,而且節點id:basewp if (eNode.equals(baseNode.getNodeType()) && "basewp".equalsIgnoreCase(((Element) baseNode) .getAttribute("id"))) { parentNode = baseNode.getParentNode(); break; } if (i == nodelist.getLength() - 1) { System.out.println("沒有找到指定類型的節點"); return null; } } Map dataMap = new HashMap<String, String>(); int pictNum = 0; // 拼裝要插入的xml節點:富文本是圖文並排,須要屢次插入:解析這個富文本的工做要作,數據庫存儲的是html,須要的是純文本+圖片 for (int i = 0; i < imgs.length; i++) { if (imgs[i] != null) { String xcjqText = imgs[i].replaceAll("<[^<>]*>|<|>", ""); // 添加文本標籤 // 給文本標籤模板賦值 Node node = null; node = XmlUtils.getextNode("檢查狀況文本"+i); parentNode.insertBefore(node, baseNode); pictMap.put("檢查狀況文本"+i,xcjqText); } if (pictList.size() > pictNum) { // 解析圖片:圖片寬高 String pictId = null; String pictWidth = null; String pictHeight = null; Pattern pt = Pattern.compile("http:[^\"']*");// 圖片連接 // http:/i/eg_mouse.jpg?id=xxxxx Matcher m = pt.matcher(pictList.get(pictNum)); while (m.find()) { pictId = m.group().split("=")[1];// 圖片UUID } Pattern ptw = Pattern.compile("width(:|=)\"?\\d*"); Matcher mw = ptw.matcher(pictList.get(pictNum)); while (mw.find()) { pictWidth = mw.group().split("=\"?|:")[1];// 圖片寬度數字部分 } Pattern pth = Pattern.compile("height(:|=)\"?\\d*"); Matcher mh = pth.matcher(pictList.get(pictNum)); while (mh.find()) { pictHeight = mh.group().split("=\"?|:")[1];// 圖片高度數字部分 } // 請求圖片數據轉String(數據庫或文件系統),請求失敗則不添加圖片標籤,go on // 要返回圖片的備用寬度,高度,若是從img標籤中取到,則不用這個寬高,不然使用 String pictD[] = this.getBase64PictStr(pictId); if (pictWidth == null) { pictWidth = pictD[0]; pictHeight = pictD[1]; } // 給圖片模板賦值 // pt=px乘以3/4。 String pictW = Integer.parseInt(pictWidth) * 3 / 4 + ""; String pictH = Integer.parseInt(pictHeight) * 3 / 4 + ""; String puuid = "現場檢查"+pictNum;//UUID.randomUUID().toString() 不能使用uuid其中的字符會被ftl模板錯誤解析 pictMap.put(puuid, pictD[2]); // 添加圖片標籤 Node node = null; node = XmlUtils.getPicNode(puuid, pictW, pictH,(pictNum+1+"")); parentNode.insertBefore(node, baseNode); pictNum++; if (i == imgs.length - 1 && pictNum < pictList.size() - 1) { // 繼續添加圖片 } } } // 利用transformer對象將修改後的文檔從新輸出 TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); DOMSource source = new DOMSource(document); // 將xml文檔保存爲ftl StreamResult result = new StreamResult(new java.io.File(ftlFilePath + File.separator + "xcjc_copy.ftl")); transformer.transform(source, result); } catch (ParserConfigurationException e) { // TODO 自動生成的 catch 塊 // e.printStackTrace(); System.out.println("xml配置有錯誤:ParserConfigurationException"); } catch (SAXException e) { // TODO 自動生成的 catch 塊 // e.printStackTrace(); System.out.println("解析文件xml內容失敗"); } catch (IOException e) { // TODO 自動生成的 catch 塊 // e.printStackTrace(); System.out.println(e + "讀取文件失敗"); } catch (TransformerConfigurationException e) { // TODO 自動生成的 catch 塊 // e.printStackTrace(); System.out.println("轉換對象建立失敗"); } catch (TransformerException e) { // TODO 自動生成的 catch 塊 // e.printStackTrace(); System.out.println("輸出文件失敗"); } return pictMap; }
在這個實現中,調用了2個私有方法,分別是添加文本節點:getextNode和添加圖片節點:getPicNode,其中圖片數據必須是base64碼的:getBase64PictStr
static Node getPicNode(String PictWordID, String picWidth, String picHeight,String picID) { // 一級節點 // wp 節點及屬性 Element wpe = document.createElement("w:p"); wpe.setAttribute("wsp:rsidR", "00172C3B"); wpe.setAttribute("wsp:rsidRDefault", "00B7682D"); wpe.setAttribute("wsp:rsidP", "00172C3B"); // 二級節點 Element wppre = document.createElement("w:pPr"); Element wre = document.createElement("w:r"); wre.setAttribute("wsp:rsidRPr", "00974FA3"); wpe.appendChild(wppre); wpe.appendChild(wre); // 三級節點 Element wwindowcontrole = document.createElement("w:widowControl"); Element wjce = document.createElement("w:jc"); wjce.setAttribute("w:val", "left"); Element wrpre = document.createElement("w:rPr"); Element wrpre2 = document.createElement("w:rPr"); Element wpicte = document.createElement("w:pict"); wppre.appendChild(wwindowcontrole); wppre.appendChild(wjce); wppre.appendChild(wrpre); wre.appendChild(wrpre2); wre.appendChild(wpicte); // 四級節點:處理方式改成順序 Element wrFontse = document.createElement("w:rFonts"); wrFontse.setAttribute("w:ascii", "宋體"); wrFontse.setAttribute("w:h-ansi", "宋體"); wrFontse.setAttribute("w:cs", "宋體"); wrpre.appendChild(wrFontse); Element wxfonte = document.createElement("wx:font"); wxfonte.setAttribute("wx:val", "宋體"); wrpre.appendChild(wxfonte); Element wkerne = document.createElement("w:kern"); wkerne.setAttribute("w:val", "0"); wrpre.appendChild(wkerne); Element wsze = document.createElement("w:sz"); wsze.setAttribute("w:val", "24"); wrpre.appendChild(wsze); Element wszcse = document.createElement("w:sz-cs"); wszcse.setAttribute("w:val", "24"); wrpre.appendChild(wszcse); Element wnoProof = document.createElement("w:noProof"); wrpre2.appendChild(wnoProof); Element vshapetype = document.createElement("v:shapetype"); vshapetype.setAttribute("id", "_x0000_t75");// //////////////////////v:stroke vshapetype.setAttribute("coordsize", "21600,21600"); vshapetype.setAttribute("o:spt", "75"); vshapetype.setAttribute("o:preferrelative", "t"); vshapetype.setAttribute("path", "m@4@5l@4@11@9@11@9@5xe"); vshapetype.setAttribute("filled", "f"); vshapetype.setAttribute("stroked", "f"); wpicte.appendChild(vshapetype); Element vstroke = document.createElement("v:stroke"); vstroke.setAttribute("joinstyle", "miter"); vshapetype.appendChild(vstroke); Element vformulas = document.createElement("v:formulas"); vshapetype.appendChild(vformulas); Element vf = document.createElement("v:f"); vf.setAttribute("eqn", "if lineDrawn pixelLineWidth 0"); vformulas.appendChild(vf); Element vf2 = document.createElement("v:f"); vf2.setAttribute("eqn", "sum @0 1 0"); vformulas.appendChild(vf2); Element vf3 = document.createElement("v:f"); vf3.setAttribute("eqn", "sum 0 0 @1"); vformulas.appendChild(vf3); Element vf4 = document.createElement("v:f"); vf4.setAttribute("eqn", "prod @2 1 2"); vformulas.appendChild(vf4); Element vf5 = document.createElement("v:f"); vf5.setAttribute("eqn", "prod @3 21600 pixelWidth"); vformulas.appendChild(vf5); Element vf6 = document.createElement("v:f"); vf6.setAttribute("eqn", "prod @3 21600 pixelHeight"); vformulas.appendChild(vf6); Element vf7 = document.createElement("v:f"); vf7.setAttribute("eqn", "sum @0 0 1"); vformulas.appendChild(vf7); Element vf8 = document.createElement("v:f"); vf8.setAttribute("eqn", "prod @6 1 2"); vformulas.appendChild(vf8); Element vf9 = document.createElement("v:f"); vf9.setAttribute("eqn", "prod @7 21600 pixelWidth"); vformulas.appendChild(vf9); Element vf10 = document.createElement("v:f"); vf10.setAttribute("eqn", "sum @8 21600 0"); vformulas.appendChild(vf10); Element vf11 = document.createElement("v:f"); vf11.setAttribute("eqn", "prod @7 21600 pixelHeight"); vformulas.appendChild(vf11); Element vf12 = document.createElement("v:f"); vf12.setAttribute("eqn", "sum @10 21600 0"); vformulas.appendChild(vf12); Element vpath = document.createElement("v:path"); vpath.setAttribute("o:extrusionok", "f"); vpath.setAttribute("gradientshapeok", "t"); vpath.setAttribute("o:connecttype", "rect"); vshapetype.appendChild(vpath); Element olock = document.createElement("o:lock"); olock.setAttribute("v:ext", "edit"); olock.setAttribute("aspectratio", "t"); vshapetype.appendChild(olock); Element wbinData = document.createElement("w:binData"); wbinData.setAttribute("w:name", "wordml://0300000"+picID+".png");//修改圖片的src和w:name解決只加載第一張圖片的問題 wbinData.setAttribute("xml:space", "preserve"); wpicte.appendChild(wbinData); Text pictText = document.createTextNode("${" + PictWordID + "}");// 這兒能夠添加圖片定位參數 wbinData.appendChild(pictText); Element vshape = document.createElement("v:shape");// 指定圖片id和寬高 vshape.setAttribute("id", PictWordID);//圖片id須要動態傳值,多個圖片如今只加載同一個 vshape.setAttribute("o:spid", "_x0000_i1028"); vshape.setAttribute("type", "#_x0000_t75");// picWidth // vshape.setAttribute("style","width:358.4pt;height:535.7pt;visibility:visible;mso-wrap-style:square"); vshape.setAttribute("style", "width:" + picWidth + "pt;height:" + picHeight + "pt;visibility:visible;mso-wrap-style:square"); Element vimagedata = document.createElement("v:imagedata"); vimagedata.setAttribute("src", "wordml://0300000"+picID+".png"); vimagedata.setAttribute("o:title", ""); vshape.appendChild(vimagedata); wpicte.appendChild(vshape); return wpe; }
static Node getextNode(String wordtext1) { // 一級節點 // wp 節點及屬性 Element wpe = document.createElement("w:p"); wpe.setAttribute("wsp:rsidP", "008027B0"); wpe.setAttribute("wsp:rsidR", "008027B0"); wpe.setAttribute("wsp:rsidRDefault", "008027B0"); wpe.setAttribute("wsp:rsidRPr", "006B689C"); // 二級節點及屬性 Element wpPr = document.createElement("w:pPr"); Element wr = document.createElement("w:r"); wr.setAttribute("wsp:rsidRPr", "006B689C"); wpe.appendChild(wpPr); wpe.appendChild(wr); // 三級節點 順序添加 //wppr節點及子節點 Element wspacing = document.createElement("w:spacing"); wspacing.setAttribute("w:line", "500"); wspacing.setAttribute("w:line-rule", "exact"); wpPr.appendChild(wspacing); Element wrPr = document.createElement("w:rPr"); wpPr.appendChild(wrPr); Element wrFonts = document.createElement("w:rFonts"); wrFonts.setAttribute("w:ascii", "仿宋_GB2312"); wrFonts.setAttribute("w:cs", "仿宋_GB2312"); wrPr.appendChild(wrFonts); Element wxfont = document.createElement("wx:font"); wxfont.setAttribute("wx:val", "仿宋_GB2312"); wrPr.appendChild(wxfont); Element wu = document.createElement("w:u");//和下面的代碼一塊兒控制文字下面是否有下劃線 wu.setAttribute("w:val", "single");//這裏設置下劃線爲單線 wrPr.appendChild(wu); //wr節點及子節點 Element wrPr1 = document.createElement("w:rPr"); wr.appendChild(wrPr1); Element wrFonts1 = document.createElement("w:rFonts"); wrFonts1.setAttribute("w:ascii", "仿宋_GB2312"); wrFonts1.setAttribute("w:cs", "仿宋_GB2312"); wrPr1.appendChild(wrFonts1); Element wxfont1 = document.createElement("wx:font"); wxfont1.setAttribute("wx:val", "仿宋_GB2312"); wrPr1.appendChild(wxfont1); Element wu1 = document.createElement("w:u");//和下面的代碼一塊兒控制文字下面是否有下劃線 wu1.setAttribute("w:val", "single");//這裏設置下劃線爲單線 wrPr1.appendChild(wu1); Element wt = document.createElement("w:t");//建立要插入文本的節點 Text wordtext=document.createTextNode("${" + wordtext1 + "}");//要插入文本的標記 wt.appendChild(wordtext); wr.appendChild(wt); return wpe; }
public String[] getBase64PictStr(String pictID) { byte[] data = null; String[] wh = new String[3]; Connection conn; Statement stmt = null; PreparedStatement ps = null; ResultSet rs = null; // 這個sql應該是動態獲取的 String sql = "select image from picc where id='"+pictID+"'";//2e30826eba714a909758c2bfff8f4875 DataSource dataSource = DataSourceFactory.defaultFactory .getDataSource("dataSource"); try { conn = dataSource.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { Blob blob = rs.getBlob("image");// IMAGE data = blob.getBytes(1, (int) blob.length()); BufferedImage bis = ImageIO .read(new ByteArrayInputStream(data)); int wid = bis.getWidth(); wh[0] = wid + ""; int hei = bis.getHeight(); wh[1] = hei + ""; BASE64Encoder encoder = new BASE64Encoder(); String str = encoder.encode(data); wh[2] = str;// 編碼後的圖片字符串 // System.out.println(str); } } catch (SQLException e1) { e1.printStackTrace(); System.out.println("建立數據庫鏈接失敗"); } catch (IOException e) { System.out.println("轉換圖片失敗"); // e.printStackTrace(); } return wh; }
下一篇博客要寫下word轉pdf/png的方法,jodconver這個jar包用過2個版本,3.0的很不成熟,不推薦。這個方法生成word如今沒有在項目中使用,有問題互相探討。