美文网首页
html页面导出为pdf

html页面导出为pdf

作者: 定金喜 | 来源:发表于2020-05-23 08:43 被阅读0次

1.项目需求

项目中有个需求,需要将页面的结果导出为pdf,但是页面结果是html,类似于下图:


报告结果页面(部分)

有两种思路可以解决这个问题:
1.将结果整理成表格形式,使用pdf表单和表格配置成模板,然后使用java操作这个模板可以导出结果,但是只能输出表格形式的pdf,不能导出和页面一样的结果;
2.获取页面的html,利用框架将html转化成pdf,可以得到与页面一模一样的结果
项目中需要导出和页面一样的结果,所以只能采用第二种方案

2.需求难点

html页面是动态的,只有显示的时候才能获取到完整的页面,如果只是导出单个页面,可以在用户详情显示页面加导出按钮导出单个报告的pdf,但是如果要导出多个页面的报告,同时没法显示多个报告详情,所以需要我们按照动态操作html模板(根据报告详情),然后将操作完成的html页面导出。

3.步骤

1.采用html2pdf+jsoup框架实现,增加依赖

<dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>html2pdf</artifactId>
        <version>2.0.2</version>
</dependency>
<dependency>
       <groupId>com.itextpdf</groupId>
       <artifactId>font-asian</artifactId>
       <version>7.1.2</version>
</dependency>
<dependency>
       <groupId>org.jsoup</groupId>
       <artifactId>jsoup</artifactId>
       <version>1.6.2</version>
</dependency>

框架介绍:
jsoup工具介绍:参考https://www.open-open.com/jsoup/
html2pdf工具介绍:参考https://www.jianshu.com/p/62d80d1205f0
2.编写html模板(部分,report-pdf.html)

...
 <ul>
                        <li class="comp-ScrollWrapper-basic-item"><span class="before"></span>身份信息核验<span class="page-order-result-basic-text">验证一致</span></li>
                        <li class="comp-ScrollWrapper-basic-item"><span class="before"></span>司法信息检索<span class="page-order-result-basic-text danger">司法信息检索</span></li>
                        <li class="comp-ScrollWrapper-basic-item"><span class="before"></span>司法信息检索<span class="page-order-result-basic-text">低风险</span></li>
                    </ul>
                    <div class="comp-ScrollWrapper-declare grey">本报告仅用于入职人员背调信息查询</div>
                </div>
                <dl class="page-order-resultPanel-verify">
                    <dt class="page-order-resultPanel-verify-title">
                        身份信息核验
                    </dt>
                    <dd class="page-order-resultPanel-verify-body">
                        <ul class="page-order-resultKey noBottom">
                            <li class="page-order-resultKey-item">
                                <span class="page-order-resultKey-label ">姓名</span>
                                <span class="page-order-resultItem-item-text baseText">
                                    *佳佳
                                </span>
                            </li>
                            <li class="page-order-resultKey-item">
                                <span class="page-order-resultKey-label ">身份证号</span>
                                <span class="page-order-resultItem-item-text baseText">
                                    110103********6789
                                </span>
                            </li>
                        </ul>
                    </dd>
                </dl>
                <dl class="page-order-resultPanel-verify">
                    <dt class="page-order-resultPanel-verify-title">
                        社会行为规范性评估
                    </dt>
                    <dd class="page-order-resultPanel-verify-body">
                        <ul class="page-order-resultKey noBottom">
                            <li class="page-order-resultKey-item">
                                <span class="page-order-resultKey-label">风险得分</span>
                                <span class="page-order-resultItem-item-text green">
                                    <!-- 低green  中yellow  高red-->
                                    低风险
                                </span>
                            </li>
                            <li class="page-order-resultKey-item">
                                <span class="page-order-resultKey-label">核实时间</span>
                                <span class="page-order-resultItem-item-text">
                                    2019年9月2日
                                </span>
                            </li>
                        </ul>
                    </dd>
                </dl>
                <dl class="page-order-resultPanel-verify">
                    <dt class="page-order-resultPanel-verify-title">
                        社会行为规范性评估
                    </dt>
                    <dd class="page-order-resultPanel-verify-body">    
                        <ul>
                            <li>
                                <dl>
                                    <dt class="page-order-resultItem-item title others block">
                                        <div class="page-order-resultItem-item-label">
                                        网贷黑名单检索
                                        </div>
                                        <div class="page-order-resultItem-item-text">
                                            暂无相关信息
                                        </div>
                                    </dt>
                                </dl>
                            </li>
                            <li>
                                <dl>
                                    <dt class="page-order-resultItem-item title others block">
                                        <div class="page-order-resultItem-item-label">
                                        失信公告检索
                                        </div>
                                        <div class="page-order-resultItem-item-text diffText">
                                            存在相关差异
                                        </div>
                                    </dt>
                                    <dd class="page-order-resultItem-item-wrapper">
                                        <ul class="page-order-resultKey noBottom">
                                            <li class="page-order-resultKey-item">
                                                <span class="page-order-resultKey-label">立案时间</span>
                                                <span class="page-order-resultKey-item-text">
                                                    2016年8月11日
                                                </span>
                                            </li>
                                            <li class="page-order-resultKey-item">
                                                <span class="page-order-resultKey-label">概要</span>
                                                <span class="page-order-resultKey-item-text">
                                                        其他有履行能力而拒不履行生效法...
                                                </span>
                                            </li>
                                        </ul>
                                        <div class="page-order-resultKey-matchRatio-warp">
                                            <div class="page-order-resultKey-matchRatio">
                                                <div class="page-order-resultKey-assist-icon"></div>
                                                信息匹配度: 30%
                                            </div>
                                        </div>
                                    </dd>
                                </dl>
                            </li>
                            <li>
                                <dl>
                                    <dt class="page-order-resultItem-item title others block">
                                        <div class="page-order-resultItem-item-label">
                                            执行公告检索
                                        </div>
                                        <div class="page-order-resultItem-item-text diffText">
                                            存在相关差异
                                        </div>
                                    </dt>
                                </dl>
                            </li>
                        </ul>
                    </dd>
                </dl>
...

导入html模板到内存(该模板是全局共用)

/**
     * html静态模板
     */
    private StringBuilder htmlTemplate;

    /**
     * 转换配置
     */
    private ConverterProperties cp;

    @Override
    public void afterPropertiesSet() throws Exception {

        htmlTemplate = new StringBuilder();
        BufferedReader br = null;
        try {
            InputStream inputStream  = this.getClass().getResourceAsStream("report-pdf.html");

            br = new BufferedReader(new InputStreamReader(inputStream));
            //建立一个对象,它把文件内容转成计算机能读懂的语言
            String line = null;
            while ((line = br.readLine()) != null) {
                htmlTemplate.append(line);
                htmlTemplate.append("\r\n");
            }

            cp = new ConverterProperties();
            //导入字体
            cp.setFontProvider(new DefaultFontProvider(true, true, true));
            cp.setCharset("utf-8");

        } catch (Exception ex) {
            logger.error("PdfExportUtil afterPropertiesSet error,",ex);
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } finally {
            }
        }
    }

3.导入字体和初始化水印
因为该框架默认不支持中文字符,需要导入中文字体

 /**
     * 生成pdf
     * @param referReportVO
     * @param fileName
     * @return
     */

    public Boolean generateBgiReportPdf(ReferReportVO referReportVO, String fileName) {

        Document document = null;
        PdfDocument pd = null;
        //生产文件夹
        int index = fileName.lastIndexOf("/");
        String pathInfo = fileName.substring(0, index + 1);
        File file = new File(pathInfo);
        if (!file.exists()){
            if (!file.mkdir()){
                return false;
            }
        }
        try {
            pd = new PdfDocument(new PdfWriter(fileName));
            document = new Document(pd);
            document.setFont(PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", false));
            WaterMark waterMark = new WaterMark("仅供"+referReportVO.getCorpName()+"公司内部使用");
            pd.addEventHandler(PdfDocumentEvent.END_PAGE, waterMark);

            StringBuilder autoHtml = autoMakeHtml(referReportVO);
            //设置页面边距 必须先设置边距,再添加内容,否则页边距无效
            List<IElement> list = HtmlConverter.convertToElements(autoHtml.toString(), this.cp);
            for (IElement ie : list) {
                if (ie instanceof HtmlPageBreak) {
                    document.add((HtmlPageBreak)ie);

                    //普通块级元素
                } else {
                    document.add((IBlockElement)ie);
                }
            }

        } catch (Exception ex) {
            logger.error("generateBgiReportPdf error,corpId={},reportNo={}", referReportVO.getCorpId(),
                    referReportVO.getReportNo(), ex);
            return false;
        } finally {
            if (document != null) {
                try {
                    document.close();
                }
                catch (Exception ex) {
                    logger.error("生成文件失败",ex);
                    return false;
                }
            }
        }
        return true;
    }

水印类

/**加水印
 * @Author: ding
 * @Date: 2019-09-17 15:21
 */
public class WaterMark implements IEventHandler {

    private static final Logger logger = LoggerFactory.getLogger(PdfExportUtil.class);

    private String waterText;

    public WaterMark(String waterText) {
        this.waterText = waterText;
    }

    @Override
    public void handleEvent(Event event){

        PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
        PdfDocument document = documentEvent.getDocument();
        PdfPage page = documentEvent.getPage();
        PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);
        Rectangle rectangle = page.getPageSize();
        PdfFont pdfFont = null;
        try {
            pdfFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", false);
        } catch (Exception ex){
            logger.error("创建字体失败",ex);
            return;
        }
        Canvas canvas = new Canvas(pdfCanvas, document, page.getPageSize())
                .setFontColor(new DeviceRgb(227,228,228))
                .setFontSize(12)
                .setFont(pdfFont);
        float initX = rectangle.getWidth()/2;
        float initY = rectangle.getHeight()/2;
        Random rd = new Random(System.currentTimeMillis());
        for (int i = -1; i<= 1; i++) {
            for (int j=-2; j<=2; j=j+1) {
                int nextRd = rd.nextInt(50);
                if (nextRd > 20 && nextRd < 45) {
                    canvas.showTextAligned(this.waterText, initX+i*(100+nextRd), initY+j*(150+nextRd),
                            TextAlignment.CENTER, 170);
                }
            }
        }
    }
}

4.使用jsoup根据报告数据操作html(部分代码)

...
/**
     * 动态生成html
     * @param referReportVO
     * @return
     */
    private StringBuilder autoMakeHtml(ReferReportVO referReportVO) throws Exception{

        org.jsoup.nodes.Document document = Jsoup.parse(this.htmlTemplate.toString(),"utf-8");

        String userName = referReportVO.getRespondentName();
        //用户名称图片展示
        Element userNameImgEle = document.getElementById("bg-user-name-1");
        userNameImgEle.text(userName);

        //用户名称
        Element userNameStrEle = document.getElementById("bg-user-name-2");
        userNameStrEle.text(userName);

        //企业名称
        Element corpNameStrEle = document.getElementById("bg-corp-name");
        corpNameStrEle.text(referReportVO.getCorpName());

        List<ItemReportVO> reportVOList = referReportVO.getReferItemReportList();
        if (CollectionUtils.isEmpty(reportVOList)) {
            return new StringBuilder(document.toString());
        }

        //差异图片渲染显示
        Element diffImgEle = null;
        Integer reportLevel = referReportVO.getReportLevel();
        if (reportLevel.equals(ReportLevelEnum.HAVE_DEFFERENCE.getCode().intValue())) {
            diffImgEle = document.getElementById("bg-img-has-difference");
        } else if (reportLevel.equals(ReportLevelEnum.PART_DEFFERENCE.getCode().intValue())) {
            diffImgEle = document.getElementById("bgi-img-part-difference");
        } else {
            diffImgEle = document.getElementById("bg-img-no-difference");
        }
        diffImgEle.attr("style", "display:block");


        //结果概述
        Element conItemListEle = document.getElementById("bgi-ul-conclusion-list");
        String conLi = null;
        for (ItemReportVO itemReportVO : reportVOList) {
            String conclusion = itemReportVO.getConclusion();
            String conclusionDesc = itemReportVO.getConclusionDesc();
            conclusionDesc = conclusionDesc.replaceAll("x","*").replaceAll("X","*");
            if (conclusion.equalsIgnoreCase("0")) {
                conLi = "<li class=\"comp-ScrollWrapper-basic-item\"><span class=\"before\"></span><span>"+itemReportVO.getItemName()+
                        "</span><span class=\"page-order-result-basic-text\">"+conclusionDesc + "</span></li>";
            } else {
                conLi = "<li class=\"comp-ScrollWrapper-basic-item\"><span class=\"before\"></span><span>"+itemReportVO.getItemName()+
                        "</span><span  class=\"page-order-result-basic-text-difftext\">"+conclusionDesc + "</span></li>";
            }

            conItemListEle.append(conLi);
        }

        Map<String, ItemReportVO> itemNameToReportVOMap = reportVOList.stream().collect(
                Collectors.toMap(ItemReportVO::getItemName, s -> s, (k1, k2) -> k1));

        //身份信息校验
        ItemReportVO itemReportVO = itemNameToReportVOMap.get("身份信息核验");
        if (itemReportVO == null) {
            logger.error("报告内容存在问题,具体内容为:{}", JSON.toJSONString(referReportVO));
            return null;
        }
        Element identityCheck = document.getElementById("bgi-ul-identity-check");
        generateItemFields(identityCheck, itemReportVO.getFiledList());

        Element allProductsEle = document.getElementById("all-products-contents");

        //社会行为规范性评估
        itemReportVO = itemNameToReportVOMap.get("社会行为规范性评估");
        if (itemReportVO != null) {
            generateProductItem(allProductsEle, itemReportVO.getItemName(), "bgi-ul-social-item");
            Element socialCheckUl = document.getElementById("bgi-ul-social-item");
            generateItemFields(socialCheckUl, itemReportVO.getFiledList());
        }

        //司法信息检索
        itemReportVO = itemNameToReportVOMap.get("司法信息检索");
        if (itemReportVO != null) {
            generateProductItem(allProductsEle, itemReportVO.getItemName(), "bgi-ul-law-item");
            Element lawCheckUl = document.getElementById("bgi-ul-law-item");
            generateProductFieldItems(itemReportVO.getProductNo(), lawCheckUl, itemReportVO.getFiledList(), "bgi-dl-law-search", true);
        }

        //商业利益冲突核验
        itemReportVO = itemNameToReportVOMap.get("商业利益冲突核验");
        if (itemReportVO != null) {
            generateProductItem(allProductsEle, itemReportVO.getItemName(), "bgi-ul-busi-item");
            Element busiCheckUl = document.getElementById("bgi-ul-busi-item");
            generateProductFieldItems(itemReportVO.getProductNo(), busiCheckUl, itemReportVO.getFiledList(), "bgi-dl-busi-search", false);
        }

        //职业资格证书核验
        itemReportVO = itemNameToReportVOMap.get("职业资格证书核验");
        if (itemReportVO != null) {
            generateProductItem(allProductsEle, itemReportVO.getItemName(), "bgi-ul-position-item");
            Element positionCheckUl = document.getElementById("bgi-ul-position-item");
            generateProductFieldItems(itemReportVO.getProductNo(), positionCheckUl, itemReportVO.getFiledList(), "bgi-dl-position-search", false);
        }

        return new StringBuilder(document.toString());
    }

    private void generateProductItem(Element parentEle, String productName, String idName) {

        String productStr = "<dl class=\"page-order-resultPanel-verify\">\n" +
                "<dt class=\"page-order-resultPanel-verify-title\">\n" + productName+
                "</dt>\n<dd class=\"page-order-resultPanel-verify-body\">\n" +
                "<ul class=\"page-order-resultKey noBottom\" id=\""+idName+"\">\n" +
                "</ul>\n</dd>\n</dl>";
        parentEle.append(productStr);
    }


    private void generateItemFields(Element parentEle, List<ItemFieldVO> filedList) {
        String identityLi = null;
        if (!CollectionUtils.isEmpty(filedList)) {
            for (ItemFieldVO itemFieldVO : filedList) {
                identityLi = "<li class=\"page-order-resultKey-item\">\n<span class=\"page-order-resultKey-label \">"+ itemFieldVO.getFieldName()+
                        "</span>\n<span class=\"page-order-resultItem-item-text baseText\">\n" + itemFieldVO.getFieldValue()+ "\n</span>\n</li>";
                parentEle.append(identityLi);
            }
        }
    }
...

5.查看导出的结果


pdf导出结果

基本上与html页面相差无几
有不理解的欢迎私信联系我

相关文章

网友评论

      本文标题:html页面导出为pdf

      本文链接:https://www.haomeiwen.com/subject/oczfahtx.html