HTML转PDF

简介

由于业务需求要根据合同模板和线上的数据实时生成合同,避免客户打印了模板还要去填数据的问题须要导出PDF,发现了IText这个东西。下面就简单的介绍一下一些简单的使用和一些常见的问题css

咱们能够建立让用户本身建立模板,模板是使用Freemarker存储的,导出PDF的时候就是取数据——>渲染Freemarker模板——>导出PDF。html

经常使用的元素

最经常使用的就是Paragraph和PdfTable了,这个难度都不大,随便搜索一下都能找到不少资料和实例。java

这里咱们强调2个问题:spring

  1. 中文问题
  2. 水印问题

中文问题也很好解决设置BaseFont就能够了。有一些须要使用FontProvider实现以下:apache

static class MyFontProvide implements FontProvider {
        @Override
        public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color) {
            BaseFont bFont = null;
            try {
                bFont = createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
             //bFont = BaseFont.createFont(fontname, encoding, embedded);
            } catch (Exception e) {
                e.printStackTrace();
            }
           //return new Font(bFont, 8, Font.NORMAL);
            return new Font(bFont,size,style,color);
        }
        @Override
        public boolean isRegistered(String arg0) {
            return false;
        }
    }

具体的更多的能够看BaseFont类。api

不少要添加水印都只添加了第一页,为了解决这个问题可使用PdfPageEvent,下面来一个实现看一下:安全

static class MyPdfPageEvent implements PdfPageEvent{

        @Override
        public void onStartPage(PdfWriter writer, Document document) {
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.5f);
            gs.setStrokeOpacity(0.2f);
            String waterMarkName = "合同预览";
            PdfContentByte under = writer.getDirectContentUnder();
            under.beginText();
            under.setColorFill(BaseColor.LIGHT_GRAY);
            under.setFontAndSize(baseFont, 100);
            under.setTextMatrix(0, 0);
            under.setGState(gs);
            under.showTextAligned(Element.ALIGN_CENTER,waterMarkName,200,200,45);
            under.setTextMatrix(50, 50);
            under.showTextAligned(Element.ALIGN_CENTER,waterMarkName,400,400,-45);
            under.endText();
        }

        @Override
        public void onOpenDocument(PdfWriter writer, Document document) { }

        @Override
        public void onEndPage(PdfWriter writer, Document document) {

        }

        @Override
        public void onCloseDocument(PdfWriter writer, Document document) { }

        @Override
        public void onParagraph(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title) { }

        @Override
        public void onChapterEnd(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title) { }

        @Override
        public void onSectionEnd(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) { }
    }

能够看到有不少事件,咱们只须要在开始新的一页的时候添加水印就能够了。网络

完整的测试代码

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.itextpdf.text.pdf.BaseFont.createFont;
import static freemarker.template.Configuration.VERSION_2_3_23;

public class ContractHtmlPdfTest {

    private static BaseFont baseFont;

    static {
        try {
            baseFont = createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    ;

    public static String getHtml(String templateStr, Map<String, Object> root) throws Exception {
        Configuration config=new Configuration(VERSION_2_3_23);
        Template template= new Template("htmlTemplate", new StringReader(templateStr), config);
        StringWriter writer = new StringWriter();
        template.process(root,writer);
        return writer.toString();
    }


    @Test
    public void htmlToPdf() throws Exception {
        Document document = new Document();
        FileOutputStream fos = new FileOutputStream("F:\\tmp\\contract.pdf");
        PdfWriter writer = PdfWriter.getInstance(document, fos);
        writer.setPageEvent(new MyPdfPageEvent());//添加水印
        InputStream contractHtml = ContractHtmlPdfTest.class.getResourceAsStream("/contract.html");
        int len = 1024 * 1024;
        byte [] buffer = new byte[len];
        int read = IOUtils.read(contractHtml, buffer);
        byte [] cb = new byte[read];
        System.arraycopy(buffer,0,cb,0,read);
        String html = new String(cb);
//        System.out.println(html);
        String rs = getHtml(html, getMap());
//        System.out.println(rs);
        ByteArrayInputStream bais = new ByteArrayInputStream(rs.getBytes());
        InputStream contractCss = ContractHtmlPdfTest.class.getResourceAsStream("/contract.css");
        document.open();
        XMLWorkerHelper xmlWorkerHelper = XMLWorkerHelper.getInstance();
        xmlWorkerHelper.parseXHtml(writer, document, bais,contractCss,new MyFontProvide());
        document.add(getPdfTable());
        document.close();
    }

    private Map<String,Object> getMap(){
        HashMap<String, Object> result = new HashMap<>();
        result.put("firstPartyName","xx有限责任公司");
        result.put("firstPartyAddr","上海");
        result.put("firstPartyContact","李四");
        result.put("firstPartyPhone","13888888888");
        result.put("secondPartyName","YY有限责任公司");
        result.put("secondPartyAddr","北京");
        result.put("secondPartyContact","张三");
        result.put("secondPartyPhone","13877777777");
        return result;
    }

    private PdfPTable getPdfTable(){
        //建立一个有3列的表格
        PdfPTable table = new PdfPTable(3);
        table.setWidthPercentage(100);
        table.setHorizontalAlignment(Element.ALIGN_CENTER);
        PdfPCell cell = new PdfPCell(new Paragraph("Title"));
        //合并3个单元格
        cell.setColspan(3);
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(cell);
        for(int i = 0;i<9;i++) {
            table.addCell(UUID.randomUUID().toString().substring(0,8));
        }

        cell = new PdfPCell(new Paragraph("setBorderColor"));
        cell.setBorderColor(BaseColor.BLUE);
        table.addCell(cell);

        cell = new PdfPCell(new Paragraph("setBackgroundColor"));
        cell.setColspan(2);
        cell.setBackgroundColor(BaseColor.GRAY);
        table.addCell(cell);
        return table;
    }

    static class MyFontProvide implements FontProvider {
        @Override
        public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color) {
            BaseFont bFont = null;
            try {
                bFont = createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
//                bFont = BaseFont.createFont(fontname, encoding, embedded);
            } catch (Exception e) {
                e.printStackTrace();
            }
//            return new Font(bFont, 8, Font.NORMAL);
            return new Font(bFont,size,style,color);
        }

        @Override
        public boolean isRegistered(String arg0) {
            return false;
        }
    }

    static class MyPdfPageEvent implements PdfPageEvent{

        @Override
        public void onStartPage(PdfWriter writer, Document document) {
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.5f);
            gs.setStrokeOpacity(0.2f);
            String waterMarkName = "合同预览";
            PdfContentByte under = writer.getDirectContentUnder();
            under.beginText();
            under.setColorFill(BaseColor.LIGHT_GRAY);
            under.setFontAndSize(baseFont, 100);
            under.setTextMatrix(0, 0);
            under.setGState(gs);
            under.showTextAligned(Element.ALIGN_CENTER,waterMarkName,200,200,45);
            under.setTextMatrix(50, 50);
            under.showTextAligned(Element.ALIGN_CENTER,waterMarkName,400,400,-45);
            under.endText();
        }

        @Override
        public void onOpenDocument(PdfWriter writer, Document document) { }

        @Override
        public void onEndPage(PdfWriter writer, Document document) {

        }

        @Override
        public void onCloseDocument(PdfWriter writer, Document document) { }

        @Override
        public void onParagraph(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title) { }

        @Override
        public void onChapterEnd(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title) { }

        @Override
        public void onSectionEnd(PdfWriter writer, Document document, float paragraphPosition) { }

        @Override
        public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) { }
    }
}

后面有测试中用到的文件,放在项目的classpath就能够了。dom

生成的PDF截图大概是这样的:maven

导出样例

contract.html 文件

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>contract</title>
    <link rel="stylesheet" type="text/css" href="contract.css" />
</head>
<body>
<div id="title">电影拍摄合做协议</div>
<div class="subjectWrap">
    <div class="subject">甲方:    ${firstPartyName}   </div>
    <div class="subject">注册地址:${firstPartyAddr}   </div>
    <div class="subject">联系人:  ${firstPartyContact}</div>
    <div class="subject">联系电话:${firstPartyPhone}  </div>
</div>

<div class="subjectWrap">
    <div class="subject">乙方:    ${secondPartyName}   </div>
    <div class="subject">注册地址:${secondPartyAddr}   </div>
    <div class="subject">联系人:  ${secondPartyContact}</div>
    <div class="subject">联系电话: ${secondPartyPhone}  </div>
</div>
<div id="content">
    风险提示:<br /><br />合做的方式多种多样,如合做设立公司、合做开发软件、合做购销产品等等,不一样合做方式涉及到不一样的项目内容,相应的协议条款可能大不相同。<br /><br />本协议的条款设置创建在特定项目的基础上,仅供参考。实践中,须要根据双方实际的合做方式、项目内容、权利义务等,修改或从新拟定条款。<br /><br />双方经友好协商,就共同投资摄制一部放映时间为xxxx分钟的xxxx毫米彩色故事片《xxxx》,达成以下协议:<br /><br />第一条、剧本和生产许可<br /><br />一、本片剧本由乙方提供,经甲、乙双方赞成并获中国电影行政主管部门批准拍摄。<br /><br />二、双方一致赞成,本片经中国电影行政主管部门批准立项后,将严格按照审查经过的剧本拍摄,如要对剧本进行情节变动,需征得双方赞成,并经中国电影合做制片公司报送中国电影行政主管部门从新审批。<br /><br />三、甲方承担并办理与本片有关的合拍申请、报批影片送审、取得放映许可证的相关手续。审查影片及取得发行放映许可证的费用由甲方承担并预先支付(仅包括管理部门按正式文件规定收取的剧本审查、节目立项、双片审查费用,必须提供有效证实)。<br /><br />四、本片在艺术处理上应符合中国国情,尊重中华民族的风俗习惯。<br /><br />第二条、预算、投资和投资比例及投资方式<br /><br />一、本片由甲、乙双方共同投资联合拍摄,投资拍摄资金为xxxx元,投资比例为甲方xxxx元(大写:xxxx),乙方xxxx元(大写:xxxx)。<br /><br />二、以上投资款,支付至双方指定帐户:xxxx,开户行:xxxx,帐号:xxxx。<br /><br />三、合同签定后xxxx个工做日内,双方即付定金xxxx元(大写:xxxx)。若是一方未能按时支付定金,另外一方有权单方面终止本合同书,并与第三方签定相似合同书。<br /><br />四、项目肯定、《拍摄许可证》取得后,开机前xx周内,甲、乙双方即付清投资余款,不然,视为违约,守约方有权向违约方要求赔偿损失。<br /><br />第三条、摄制组人员组成<br /><br />一、本片摄制组由甲、乙双方共同组成。主创人员名单应在本片开机前由甲方报中国电影合做制片公司并经中国电影主管部门赞成。<br /><br />二、双方商定本片出品人甲方为xxxx,乙方为xxxx。<br /><br />三、本片导演为xxxx。<br /><br />四、本片演员均由乙方指定并提供,经甲方赞成,并按规定程序将有关资料报批。<br /><br />五、甲方指定xxxx为该片项目负责人。项目负责人表明甲方协调处理有关事项,监督进出关及影片拍摄状况,酬金享受本片制片待遇(甲方详列工做内容及薪酬标准)。他们在协助协调本片拍摄的各项工做所发生的食、宿、交通、电讯等费用由摄制组承担,实报实销。<br /><br />六、摄制组全体人员、设备及重要拍摄场地的安全进行保险,保险费由摄制组承担。<br /><br />第四条、拍摄和后期制做<br /><br />一、本片的拍摄周期,甲、乙双方商定自xxxx年xxxxx月至xxxx年xxxx月底完成。<br /><br />(1)开机时间:xxxx。<br /><br />(2)停机时间:xxxx。<br /><br />(3)双片送审时间:xxxx。<br /><br />(4)出片时间:xxx。<br /><br />二、本片的拍摄地点为xxx。如需移至他地拍摄,甲、乙双方应及时通知中国电影合做制片公司。<br /><br />三、本片的底、样片冲印及后期制做应在中国内地完成。如因技术等特殊缘由,需在境外进行部分后期制做,应由双方提出申请,经中制公司报送中国电影行政主管部门批准。<br /><br />四、本片在中国内地拍摄期间,摄制组人员应遵照中华人民共和国法律、法规,尊重拍摄地风俗习惯。<br /><br />第五条、影片送审<br /><br />一、本片的混录双片完成后,经甲、乙双方审定,由甲方负责送中国电影合做制片公司和国家广电总局电影审查委员会审查,并严格遵守审查修改意见进行修改。<br /><br />二、本片经国家广电总局电影审查委员会审查经过并取得《电影片公映许可证》后,方可在中国境内外发行放映、销售或以任何其余方式在公众领域进行映出或作商业性推销传播活动。<br /><br />三、本片经国家广电总局电影审查经过后为该片的惟一版本。本片取得《电影片公映许可证》后,任何一方不得擅自修改,制做其它版本,若有情节变动、名称变动,应当从新向中国电影合做制片公司和中国电影行政主管部门报审。<br /><br />第六条、版权和发行收益<br /><br />一、本片以及与本片有关的全部载体的著做权归甲、乙双方共同拥有。<br /><br />二、本片中国内地的院线发行权、电视播映权及其收益归甲方全部。包括:(可选择)在中国内地(除香港、澳门、台湾地 区外)音像权、35mm、16mm、数字电影,电影频道,有线电视,无线电视、网络传输的发行权)。其它全部权均为乙方全部。包括:(可选择)港、澳、台 地区及海外全部权(家庭影院、电影发行、有线及无线电视播映、网络传输等全部权)及收益。家庭影院:指录像带、ld、vcd、dvd、evd等以及未来发 明的一切家庭式播放录像产品。<br /><br />三、除前款规定外,因本片或因行使本权利而产生的其余一切权利,按双方投资比例分红享有;双方全部广告收益归各方全部。<br /><br />四、上述权限期限为2019-01-01。<br /><br />第七条、发行销售<br /><br />一、 本片发行后送交两部标准拷贝(中国电影行政主管部门一部,中国电影资料馆一部)和大1/2录像带四盘(中国电影行政主管部门三盘,中国电影合做制片公司一 盘),及向国家广电总局审查委员会上缴betacam影片节目带一盘。为保证投资方的权益,上缴的拷贝录像带和betacam影片节目带可印上“样带”字 样;此项资料拷贝费用由摄制组承担。<br /><br />二、甲、乙方如出售、转让本片的发行权,则必须在其合同中注明:所出售、转让的权利不包括选送本片参加各种国际(地区)电影节、电影展等活动。<br /><br />三、 本片片头字幕的排列以及本片的一切副产品的片头字幕的排列(包括有关本片的包装及文字印刷品之上的说明等),若在中国内地发行销售,应为甲方在前,乙方在 后;若在中国内地之外的国家或地区(含港、澳、台地区)发行销售,应为乙方在前,甲方在后;根据中国电影行政主管部门规定“中国电影合做制片公司协助拍 摄”应以独立画幅排列在甲乙或乙甲两方以后。不管影片在什么时候、何地发行、放映、参加国际(地区)电影节、展,影片字幕署名不得变动、删减。<br /><br />四、乙方赞成2018-08-08向甲方提供本片故事梗概、演职员资料、剧照、海报等宣传资料,做为甲方存档和电影发行之用。<br /><br />第八条、报关和签证<br /><br />一、 根据中国《电影管理条例》规定,为拍摄本片所需临时入出境的设备、器材和胶片等物品的报关手续由甲方指派具备表明甲方资格的专职人员负责办理。该人员职责 应从设备、器材和胶片等物品入境之日起至所有出境物品结关手续办理结束之日止。乙方摄制人员以及受聘人员的入境签证手续,由甲方代为办理。<br /><br />二、乙方应如实向中国海关申报进出境的一切物品,若发现申报不实,乙方应对此负责;若由上述缘由而形成甲方经济损失,乙方应给予赔偿。<br /><br />三、为本片办理器材、胶片等物品临时进、出境海关手续所发生的包装、储运、报关费用由摄制组承担;报关人员的食、宿、交通、电讯、劳务、补助等费用均由摄制组承担。<br /><br />四、乙方为拍摄本片临时入境的设备、器材和胶片等物品,在本片拍摄结束后,应按中国海关的有关规定在5个月以内(自入境之日起)所有复运出境。逾期不出者,海关所以而收取的一切税、费由乙方缴纳。<br /><br />第九条、参赛参展和获奖<br /><br />一、经中国电影合做制片公司报中国电影行政主管部门批准,甲、乙双方凭本片可参加各种国内外电影节、展等活动,其表明团成员的活动经费由甲、乙方各自负责。<br /><br />二、 本片获奖荣誉双方共享。在中国内地若获影片奖,奖品和获奖证书原件归甲方,奖金分配原则为甲方占50%,乙方占50%。在中国内地之外(含港、澳地区)获影片奖,奖金、奖品、获奖证书归甲方,向另外一方提供奖品和获奖证书复制品。若获个 人单项奖的奖金,奖品和证书归获奖者我的全部。<br /><br />第十条、不可抗力<br /><br />本片在摄制过程当中,如遇不可抗力的因素而形成摄制工做暂停或终止,甲、乙双方应及时将状况通告中国电影合做公司。<br /><br />第十一条、违约责任<br /><br />风险提示:<br /><br />合同的约定虽然细致,但没法保证合做方不违约。所以,必须明确约定违约条款,一旦一方违约,另外一方则可以以此做为追偿依据。<br /><br />一、 本合同任何一方在合同签字后不执行或违反本合同第一条第1、2、四款;第三条第1、二款;第四条第三款;第五条第三款;第六条第一款;第七条第4、五款; 第九条第一款之规定,或将本合同擅自转让给任何非本合同签约方,视为该方对本合同的重大违约。违约方应承担违约责任,并接受中国电影行政主管部门根据中国 《电影管理条例》对违约方做出处罚。<br /><br />二、若本合同任何一方违约而形成另外一方经济损失,受损失方有权追究违约方的责任并提出赔偿要求。<br /><br />三、违约方的损失赔偿额应当至关于因违约形成的损失,包括合同履行后能够得到的利益。<br /><br />第十二条、法律适用和管辖<br /><br />一、本合同以及构成本合同组成部分的附件的解释、效力和补充,均应适用中华人民共和国法律。<br /><br />二、全部因履行本合同或与本合同有关的争议,应由中华人民共和国有管辖权的法院管辖并依该管辖法院适用的程序法加以解决。<br /><br />三、全部因履行本合同义务或合同有关的一切争议,应首先由争议各方经过友好协商加以解决;若争议各方没法提出解决方案并达成协议。<br /><br /><br />第十三条、补充协议<br /><br />本合同若有未尽事宜,双方应友好协商解决,必要时另行签定补充协议书。补充协议与本合同具备同等法律效力。<br /><br />第十四条、生效和终止<br /><br />一、本合同由甲、乙双方法定表明签字,自签字之日起生效。<br /><br />二、本合同以中文写成,一式2份,双方各执1份,具备同等的权威和效力。
</div>


<div id="signature">
<div id="first">
    <div class="subject">甲方:    ${firstPartyName}</div>
    <div class="subject">受权表明:</div>
    <div class="subject">日期:    </div>
</div>
<div id="second">
    <div class="subject">乙方:    ${secondPartyName}</div>
    <div class="subject">受权表明:</div>
    <div class="subject">日期:    </div>
</div>
</div>


</body>
</html>

contract.css文件

#first{
    float: left;
    width: 250px;
    height:200px;
    margin-top: 100px;
}
#second{
    float: right;
    margin-top: 100px;
    width: 250px;
    height:200px;
    /*margin-right: 40%;*/
    margin-right: 700px;
}
#title{
    text-align: center;
    font-size: 35px;
    margin-bottom: 20px;
    padding: 20px;
}

.subjectWrap{
    margin-bottom: 20px;
    width: 400px;
    height: 135px;
}
.subject{
    display: block;
    padding: 4px;
}
#content{
    /*line-height: 35px;*/
}

table,th,td{
    border: 1px solid #a0c6e5;
    border-collapse:collapse;
}

pom依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cn.freemethod</groupId>
    <artifactId>ipdf</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <name>ipdf</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.6.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.2</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.11</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.2</version>
        </dependency>
        
        <!--test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.2.13.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
相关文章
相关标签/搜索