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> 

HalloWord
41 声望2 粉丝