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>