記一次 HTML 模板 轉 PDF

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>
相關文章
相關標籤/搜索