HTM模板的樣式和實際轉pdf樣式會有很是大的差距 獲取模板的方式也是十分曲折...
這裏記錄一下便捷的方式css
第一步:
在前端調用windows的打印頁面 這裏的樣式徹底和html一一致;左側 目標打印機 選擇:另存爲PDF
這裏保存的pdf樣式和html的樣式徹底一致。html
第二步:
把上一步保存好的pdf轉換爲word 在線轉換地址 pdf2word前端
第三步:
把上一步保存好的word轉換爲html 在線轉換地址 word2html
得到的模板在稍做調整便可java
我是這麼轉換了幾回才獲得了樣式最貼近的模板
若是樣式差距大 能夠本身調整轉換的步驟 省略 or 多轉幾回apache
jar包中有default.css文件可能會影響樣式windows
圖片支持http連接 直接像普通的字符串同樣扔進去就能夠服務器
若是樣式難搞 字段很少建議使用 pdf模板轉換 樣式不會改變 缺點:字符長度徹底受域限制 圖片插入太費勁app
首先引入依賴ide
<dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.13</version> </dependency> <dependency> <groupId> com.itextpdf</groupId > <artifactId>itextpdf</artifactId > <version>5.5.9</version> </dependency> <dependency> <groupId> com.itextpdf</groupId > <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency>
如下爲批量導出的代碼 忽略業務代碼字體
@RequestMapping(value="/exportPrintList") public void exportPdf(String keyword, String status, String createStartTime, String createEndTime, HttpServletResponse response) { FileUtil.createDir(pdfFilePath,false); //業務代碼 查詢須要導出的數據 AppHealthtestEx ahe = new AppHealthtestEx(); ahe.setType(HealthTestType.HealthTest.getType()); ahe.setKeywords(keyword); ahe.setCreateStartTime(createStartTime); ahe.setCreateEndTime(createEndTime); if (StringUtils.isNotBlank(status)) { ahe.setStatus(Integer.parseInt(status)); } ahe.setOrgOid(getSearchOrgOid()); List<AppHealthtestEx> testNumbers = service.findPrintHealthtestEx(ahe); int corePoolSize = 5; if(testNumbers.size()<corePoolSize){ corePoolSize = testNumbers.size(); } if(corePoolSize>10){ corePoolSize = 10; } // 批量導出 多個任務 ExecutorService executorService = new ThreadPoolExecutor(corePoolSize, testNumbers.size(), 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3)); List<Future> result = Lists.newArrayList(); AppPrintMode findPrintMode = new AppPrintMode(); List<AppPrintMode> appPrintModes = appPrintModeService.find(findPrintMode); if(CollectionUtils.isNotEmpty(appPrintModes)){ findPrintMode = appPrintModes.get(0); } int printMode = 1; if(findPrintMode.getPrintPlateType()!=null){ printMode = findPrintMode.getPrintPlateType(); } String template = ""; if(printMode == 1){ template = templatePath+"PrintAll.html"; }else{ template = templatePath +"PrintConcis.html"; } for (AppHealthtestEx ex : testNumbers) { result.add(executorService.submit(new PrintThread(pdfFilePath,ex.getTestNumber(),service,template,ex.getName()))); } executorService.shutdown(); try { executorService.awaitTermination(600,TimeUnit.SECONDS); FileUtil.compressToZip(pdfFilePath,filePath,"體檢報告導出.zip"); service.downloadPdf(response, "體檢報告導出.zip"); }catch (Exception e){ log.error("export pdf error :",e); } }
線程類:
import com.itextpdf.text.Document; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.tool.xml.XMLWorkerHelper; import com.itextpdf.tool.xml.html.CssAppliers; import com.itextpdf.tool.xml.html.CssAppliersImpl; import com.itextpdf.tool.xml.html.Tags; import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.Map; import java.util.concurrent.Callable; public class PrintThread implements Callable{ public Logger log = LoggerFactory.getLogger(this.getClass()); private String fileFilePath; private String testNumber; private String templatePath; private AppHealthtestService appHealthtestService; private String fileName; public PrintThread(String fileFilePath, String testNumber, AppHealthtestService appHealthtestService,String templatePath,String fileName) { this.fileFilePath = fileFilePath; this.testNumber = testNumber; this.appHealthtestService = appHealthtestService; this.templatePath = templatePath; this.fileName = fileName; } @Override public Object call(){ FileOutputStream fos = null; Document document = null; try { // 獲取全部佔位符對應的值 Map<String, Object> stringObjectMap = appHealthtestService.exportPreview(testNumber); String htmlContent = htmlSetValue(readString(templatePath),stringObjectMap); ByteArrayInputStream in = new ByteArrayInputStream(htmlContent.getBytes("UTF-8")); document = new Document(PageSize.A4); PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(fileFilePath+ fileName+testNumber + ".pdf")); document.open(); document.addTitle("xxx報告"); // 使用咱們的字體提供器,並將其設置爲unicode字體樣式 AsianFontProvider fontProvider = new AsianFontProvider(); fontProvider.addFontSubstitute("lowagie", "garamond"); // 生產環境是windows 我本地也是windows 若是不加這句 在我本地是沒問題 在服務器上就是亂碼 ... 坑 fontProvider.setUseUnicode(true); CssAppliers cssAppliers = new CssAppliersImpl(fontProvider); HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers); htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory()); XMLWorkerHelper.getInstance().getDefaultCssResolver(true); XMLWorkerHelper.getInstance().parseXHtml(pdfWriter, document, in, null, Charset.forName("UTF-8"), fontProvider); // XMLWorkerHelper.getInstance().parseXHtml(pdfWriter, document, in, Charset.forName("UTF-8"),new AsianFontProvider()); document.close(); } catch (Exception e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (document != null) { document.close(); } } return "success"; } // 讀html模板代碼 private static String readString(String filePath) { File file = new File(filePath); String result = ""; try { FileInputStream in = new FileInputStream(file); // size 爲字串的長度 ,這裏一次性讀完 int size = in.available(); byte[] buffer = new byte[size]; in.read(buffer); in.close(); result = new String(buffer,"UTF-8"); } catch (IOException e) { e.printStackTrace(); } return result; } /** * 替換掉模板中的佔位符 * @param html 模板代碼 * @param map 替換模板的值 * @return */ private String htmlSetValue(String html,Map<String,Object> map){ if(StringUtils.isBlank(html) || MapUtils.isEmpty(map)){ return html; } for (String key : map.keySet()) { html = html.replaceAll("#"+key,MapUtils.getString(map,key,"")); } return html; }
字體類:
public class AsianFontProvider extends XMLWorkerFontProvider { public AsianFontProvider() { super(null,null); } @Override public Font getFont(final String fontname, String encoding, float size, final int style) { String fntname = fontname; if (fntname == null) { fntname = "宋體"; } if (size == 0) { size = 4; } return super.getFont(fntname, encoding, size, style); } }
HTML模板太大 貼一小段 使用 #key 在替換的時候把全部的佔位符替換爲本身想要的值
好比 map中的鍵值對爲: #addres,北京市朝陽區 這樣在html中要顯示地址的地方寫 #address便可:
<td style="vertical-align:bottom; width:20pt"> <p style="line-height:9pt; margin:0pt"><span style="font-family:'Microsoft YaHei'; font-size:8pt">地</span></p> </td> <td style="vertical-align:bottom; width:17pt" colspan="6"> <p style="line-height:7pt; margin:0pt 0pt 0pt 10pt"><span style="font-family:'Microsoft YaHei'; font-size:7pt">址:#address</span></p> </td> </tr> <tr style="height:9pt"> <td style="vertical-align:bottom; width:20pt"> <p style="line-height:9pt; margin:0pt"><span style="font-family:'Microsoft YaHei'; font-size:8pt">身</span></p> </td> <td style="vertical-align:bottom; width:87pt"> <p style="line-height:9pt; margin:0pt 0pt 0pt 10pt"><span style="font-family:'Microsoft YaHei'; font-size:8pt">高:</span><span style="font-family:'Microsoft YaHei'; font-size:8pt">#heightcm</span></p> </td> <td style="vertical-align:bottom; width:44pt"> <p style="line-height:9pt; margin:0pt 0pt 0pt 24pt"><span style="font-family:'Microsoft YaHei'; font-size:8pt">體</span></p> </td> <td style="vertical-align:bottom; width:102pt"> <p style="line-height:9pt; margin:0pt 0pt 0pt 9pt"><span style="font-family:'Microsoft YaHei'; font-size:8pt">重:</span><span style="font-family:'Microsoft YaHei'; font-size:8pt">#weightkg</span></p> </td>