最近在使用freemaker做一個word模板,裏面包含大量表格、截圖、超鏈接等數據、歷時一週多,遇到很多坑,現在想想都後怕,現在簡單總結一下,希望給以後的小夥伴提供幫助,少走彎路!
坑一:word打不開
可能原因:
1、往xml文件中添加數據佔位時,不細心導致文件中出現多餘的{、}、;、#等字符,導致xml校驗錯誤,會導致生成的word打不開。
2、word中有超鏈接,鏈接中包含多個參數時,會用&進行連接,然而在xml中&屬於特殊字符,如若不處理,導致xml校驗錯誤,會導致生成的word打不開;處理方式有兩種如下
第一種:使用CDATA包含此鏈接,使得xml不解析,比如<![CDATA[www.test.com?param=1&num=2]]>
第二種:將特殊字符轉義,&對應的轉義爲& 其他特殊字符轉義可自行去網上查詢,轉義後的鏈接就變成www.test.com?param=1&num=2,這種xml是可以解析的。
3、xml校驗沒有問題(xml校驗器https://www.runoob.com/xml/xml-validator.html),此時生成的docx文檔,使用wps是可以打開的,但是使用office確打不開,解決方案是直接生成doc格式的文檔,這樣wps跟office都能打開了,具體原因不詳,可能跟freemarker對docx跟doc支持不一樣。
坑二:批量添加圖片
由於第一次往word文檔中加圖片,而且是動態批量添加,沒有頭緒,網上查詢了資料也都沒有太好的方法,後面想着直接動態循環生成rId,沒成想直接成功了,非常開心,但也產生一個新問題,就是圖片變形了(下面在詳細說),實現方式如下:
往word中加圖片,首先在模板中需要插入一個圖片進行佔位,轉換成xml後,關於圖片的地方有三點,我自己理解的,描述的可能不太對,見諒!
1、圖片的源定義
<Relationship Id="rId99" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image99.jpeg"/>
2、圖片的base64碼,用變量替換
<pkg:part pkg:name="/word/media/image99.jpeg" pkg:contentType="image/jpeg" pkg:compression="store">
<pkg:binaryData>${img_gqctt!"--"}</pkg:binaryData>
</pkg:part>
3、圖片展示
<w:p w:rsidR="005A1D92" w:rsidRDefault="00785A68" w:rsidP="005A1D92">
<w:pPr>
<w:adjustRightInd w:val="0"/>
<w:spacing w:line="360" w:lineRule="auto"/>
<w:contextualSpacing/>
<w:rPr>
<w:rFonts w:ascii="等線" w:eastAsia="等線" w:hAnsi="等線"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:ascii="等線" w:eastAsia="等線" w:hAnsi="等線" w:cs="等線" w:hint="eastAsia"/>
<w:bCs/>
<w:noProof/>
<w:sz w:val="24"/>
</w:rPr>
<w:drawing>
<wp:inline distT="0" distB="0" distL="114300" distR="114300" wp14:anchorId="46AD878E" wp14:editId="0A36392B">
<wp:extent cx="5274945" cy="4326255"/>
<wp:effectExtent l="0" t="0" r="5080" b="14605"/>
<wp:docPr id="2" name="圖片 2" descr="1589945007(1)"/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/>
</wp:cNvGraphicFramePr>
<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="2" name="圖片 2" descr="1589945007(1)"/>
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1"/>
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId99"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="5274945" cy="4326255"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
<w:r w:rsidR="005A1D92">
<w:rPr>
<w:rFonts w:ascii="等線" w:eastAsia="等線" w:hAnsi="等線" w:hint="eastAsia"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t xml:space="preserve"></w:t>
</w:r>
</w:p>
1跟2是通過image99.jpeg關聯,1跟3是通過rId99關聯
具體實現方式:
<#list img_glrcyry as glrcyry>
<Relationship Id="rId${glrcyry_index+200}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image${glrcyry_index+200}.jpeg"/>
</#list>
爲什麼要+200,是爲了防止id重複,我這文檔中包含大量的圖片
<#list img_glrcyry as glrcyry>
<pkg:part pkg:name="/word/media/image${glrcyry_index+200}.jpeg" pkg:contentType="image/jpeg" pkg:compression="store">
<pkg:binaryData>${glrcyry.img_glrcyry}</pkg:binaryData>
</pkg:part>
</#list>
<#list img_glrcyry as glrcyry>
<w:p w:rsidR="005A1D92" w:rsidRDefault="005A1D92" w:rsidP="005A1D92">
<w:pPr>
<w:adjustRightInd w:val="0"/>
<w:spacing w:line="360" w:lineRule="auto"/>
<w:contextualSpacing/>
<w:rPr>
<w:rFonts w:ascii="等線" w:eastAsia="等線" w:hAnsi="等線"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:noProof/>
</w:rPr>
<w:lastRenderedPageBreak/>
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="5274945" cy="4326255"/>
<wp:effectExtent l="0" t="0" r="1905" b="0"/>
<wp:docPr id="${glrcyry_index+200}" name="圖片 ${glrcyry_index+200}" descr="C:\Users\hewh\AppData\Local\Temp\ksohtml14508\wps40.jpg"/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/>
</wp:cNvGraphicFramePr>
<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="0" name="Picture 906" descr="C:\Users\hewh\AppData\Local\Temp\ksohtml14508\wps40.jpg"/>
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId${glrcyry_index+200}">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi
xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
</a:ext>
</a:extLst>
</a:blip>
<a:srcRect/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="5274945" cy="4326255"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln>
<a:noFill/>
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
<w:r>
<w:rPr>
<w:rFonts w:ascii="等線" w:eastAsia="等線" w:hAnsi="等線" w:hint="eastAsia"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t xml:space="preserve"></w:t>
</w:r>
</w:p>
</#list>
這樣就能將圖片批量動態的添加到word模板中了。
坑三:圖片變形問題
因爲事先用了一個圖片進行佔位,所有在模板中,圖片的大小固定,如果新插入的圖片跟佔位大小不一樣,就會導致圖片變形,壓縮或者拉伸。下面看一下圖片位置裏面代碼有兩個核心的地方:
<wp:extent cx="5274945" cy="4326255"/>
<a:ext cx="5274945" cy="4326255"/>
第一個是圖片在word中的區域,長跟寬的大小,第二個是圖片本身的通過像素值轉換的長跟寬(我自己理解的,可能描述的不太對),網上有說圖片像素值轉換成這個大小的計算邏輯,是 像素值*914400/100,然後去替換長跟寬,後面我經過反覆試驗驗證,發現不是很準確,這樣會導致圖片變形。
我的思路:圖片變形無非就是長寬比例不一致導致,所以我們根據要插入的圖片的寬跟高,計算寬高比,來改變模板裏這個寬高值,保證圖片在模板裏跟原圖是同樣的寬高比,這樣圖片就不會變形。
因爲一個圖片在word中只能佔一頁,而實際上有的圖片過長,有的圖片過寬,所以我的思路是,如果圖片過長,就固定長度,壓縮寬度,如果圖片過寬,就固定寬度,壓縮長度。我自己在word中用一個圖片拉滿,佔滿一頁,寬度大概是5275000,長度大概是8780000,我就以這個爲基準,以圖片時間寬高計算要在模板中展示的實際寬高,部分代碼如下:
BufferedImage bufferedImage = ImgBase64Util.base64String2BufferedImage(imgData);
double wordProportion = (double) 5275000/8780000;
double realProportion = (double) bufferedImage.getWidth()/bufferedImage.getHeight();
// 過寬,以寬度爲基準
if(realProportion >= wordProportion){
root.put("img_gqctt_width", "5275000");
long realHeight = 5275000L * bufferedImage.getHeight() / bufferedImage.getWidth();
root.put("img_gqctt_height", String.valueOf(realHeight));
}else{
root.put("img_gqctt_height", "8780000");
long realWidth = 8780000L * bufferedImage.getWidth() / bufferedImage.getHeight();
root.put("img_gqctt_width", String.valueOf(realWidth));
}
這樣到時候直接替換模板中的寬高,如果是批量添加圖片,需要計算出每個圖片的在模板中的實際寬高
<wp:extent cx="${img_gqctt_width}" cy="${img_gqctt_height}"/>
<a:ext cx="${img_gqctt_width}" cy="${img_gqctt_height}"/>
至此就能保證圖片在word中不變形了!
一週多的時間,中間遇到各種問題,曾一度要奔潰,好在後來慢慢問題全部解決,收穫頗豐!繼續加油!共勉!
寫在最後:freemarker對高版本的docx支持不是很好,建議模板生成xml時,選擇word 2003 XML文檔,不建議選擇word XML文檔,最後生成word文檔時,直接生成doc格式的,能避免很多坑!