HTM模板的样式和实际转pdf样式会有非常大的差距 获取模板的方式也是十分曲折...
这里记录一下便捷的方式
第一步:
在前端调用windows的打印页面 这里的样式完全和html一一致;左侧 目标打印机 选择:另存为PDF
这里保存的pdf样式和html的样式完全一致。
第二步:
把上一步保存好的pdf转换为word 在线转换地址 pdf2word
第三步:
把上一步保存好的word转换为html 在线转换地址 word2html
获得的模板在稍作调整即可
我是这么转换了几次才得到了样式最贴近的模板
如果样式差距大 可以自己调整转换的步骤 省略 or 多转几次
jar包中有default.css文件可能会影响样式
图片支持http链接 直接像普通的字符串一样扔进去就可以
如果样式难搞 字段不多建议使用 pdf模板转换 样式不会改变 缺点:字符长度完全受域限制 图片插入太费劲
首先引入依赖
<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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。