JAVA导出PDF,Freemarket+itext.

最近客户有个需求,针对企业的信息整理出一个电子版的报告,要求以pdf格式展示,接到需求后分析了一下需求,整体方案如下,

  1.通过freemarket,将html模板渲染成完整的html页面;

  2.再通过itext将html页面渲染成pdf.

直接上代码:

  1. 准备html 模板.

       

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
	<title>信用评价下载</title>

	<style>
		body{
			width: 100%;
			height: auto;

		}
		.title{
			font-family: -apple-system;
			font-size: 36px;
			font-weight: 400;
			text-align: center;
		}

		table {
			width: 100%;
			font-family: verdana,arial,sans-serif;
			font-size:16px;
			color:#333333;
			border-width: 1px;
			border-color: #ebeef5;
			border-collapse: collapse;
			text-align: center;
		}
		table th {
			border-width: 1px;
			padding: 8px;
			border-style: solid;
			border-color: #666666;
			background-color: #dedede;
		}
		table td {
			border-width: 1px;
			padding: 8px;
			border-style: solid;
			border-color: #ebeef5;
			/*background-color: #ffffff;*/
		}
		table .tr1{
			background-color: #e4dbd0;
			font-weight: bold;
		}

	</style>

</head>
<body >
	<div >
		<div class="title" style="width: 100%">
			 <span>企业信用报告</span>
		</div>

		<div style="height: 400px" >

		</div>

		<div style="text-align: center;font-size: 24px">
			(${(baseInfo.entName)!'--'})
		</div>

		<div style="height: 500px">


		</div>

		<div style="text-align: center">
			 报告生成时间: ${createTime}
		</div>

		<br></br>
		<!--企业基本信息-->
		<div>
			<div style="font-weight: bold">
				<p>一:企业基本信息</p>
			</div>
			<div>
				<table>
					<tbody>
						<tr>
							<td>企业名称</td>
							<td colspan="3">${(baseInfo.entName)!'--'}</td>
						</tr>

						<tr>
							<td>统一社会信用代码</td>
							<td colspan="3">${(baseInfo.uniscid)!'--'}</td>
						</tr>
						<tr>
							<td>经营场所</td>
							<td colspan="3">${(baseInfo.dom)!'--'}</td>
						</tr>

						<tr>
							<td style="width: 25%">法定代表人</td>
							<td style="width: 25%">${(baseInfo.name)!'--'}</td>
							<td style="width: 20%">成立日期</td>
							<td>${(baseInfo.estDate)!'--'}</td>
						</tr>
						<tr>
							<td>注册资本(万)</td>
							<td >${(baseInfo.regcap)!'--'}</td>
							<td>经营状态</td>
							<td>${(baseInfo.regCtateCn)!'--'}</td>
						</tr>
						<tr>
							<td>登记机关</td>
							<td >${(baseInfo.regorgCn)!'--'}</td>
							<td>公司类型</td>
							<td>${(baseInfo.entTypeCn)!'--'}</td>
						</tr>
						<tr>
							<td>经营范围</td>
							<td colspan="3">
								<div >
									${(baseInfo.opScope)!'--'}
								</div>
							</td>
						</tr>
					</tbody>
				</table>
			</div>

		</div>




		<!--高管信息-->
		<br></br>
		<br></br>
		<br></br>
		<!--高管信息-->
		<div>
			<div style="font-weight: bold">
				<p>二:高管信息</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td  >序号</td>
						<td >职位</td>
						<td >人员</td>
					</tr>

					<#if managerInfo?? && (managerInfo?size > 0)>
							<#list managerInfo as value>
						    	<tr>
									<td style="width: 5%">${value_index?if_exists+1}</td>
									<td style="width: 50%">${(value.position)!'--'}</td>
									<td style="width: 45%">${(value.name)!'--'}</td>
								</tr>
						  </#list>
					</#if>
					<tr>
						<td colspan="2" style="text-align: left">共计[${managerInfo?size}]条</td>
					</tr>
					</tbody>
				</table>
			</div>
		</div>

		<!--股东信息-->
		<br></br>
		<br></br>
		<br></br>
		<!--股东信息-->
		<div>
			<div style="font-weight: bold">
				<p>三:股东信息</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td style="width: 5%" >序号</td>
						<td >股东名称</td>
						<td >股东类型</td>
						<td >出资比例</td>
						<td >出资额度</td>
						<td >出资时间</td>
						<td >货种</td>
					</tr>

					<#if enterpriseShareInfo?? && (enterpriseShareInfo?size > 0)>
							<#list enterpriseShareInfo as value>
							    	<tr>
										<td style="width: 2%">${value_index?if_exists+1}</td>
										<td>${(value.shareName)!'--'}</td>
										<td >${(value.shareType)!'--'}</td>
										<td >${(value.moneyPre)!'--'}</td>
										<td >${(value.moneyTotal)!'--'}</td>
										<td >${(value.moneyTime)!'--'}</td>
										<td >${(value.costType)!'--'}</td>
									</tr>
							  </#list>
					</#if>

						<tr>
							<td colspan="6" style="text-align: left">共计[${enterpriseShareInfo?size}]条</td>
						</tr>
					</tbody>
				</table>
			</div>
		</div>



		<!--工商变更-->
		<br></br>
		<br></br>
		<br></br>
		<!--工商变更-->
		<div>
			<div style="font-weight: bold">
				<p>四:工商变更</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td style="width: 5%" >序号</td>
						<td style="width: 15%">变更日期</td>
						<td style="width: 20%">变更事项</td>
						<td style="width: 35%">变更前</td>
						<td style="width: 35%">变更后</td>
					</tr>


					<#if enterpriseChange?? && (enterpriseChange?size > 0)>
					<#list enterpriseChange as value>
					    	<tr>
								<td style="width: 5%">${value_index?if_exists+1}</td>
								<td>${(value.changeDate)!'--'}</td>
								<td >${(value.changeItem)!'--'}</td>
								<td >${(value.beforeChange)!'--'}</td>
								<td >${(value.afterChange)!'--'}</td>
							</tr>
					  </#list>
				</#if>
					<tr>
						<td colspan="4" style="text-align: left">共计[${enterpriseChange?size}]条</td>
					</tr>
					</tbody>
				</table>
			</div>
		</div>


		<!--商标-->
		<!--<br></br>
		<br></br>
		<br></br>
		<!–商标–>
		<div>
			<div style="font-weight: bold">
				<p>五:商标</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td >序号</td>
						<td >申请日期</td>
						<td >注册号</td>
						<td >状态</td>
						<td >商标名</td>
						<td >国际分类</td>
					</tr>

					<tr>
						<td colspan="6" style="text-align: left">共计[0]条</td>
					</tr>
					</tbody>
				</table>
			</div>
		</div>-->



		<!--专利-->
		<br></br>
		<br></br>
		<br></br>
		<!--专利-->
		<div>
			<div style="font-weight: bold">
				<p>五:专利</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td >序号</td>
						<td style="width: 20%">专利名称</td>
						<td style="width: 10%">公开/公布号</td>
						<td style="width: 16%">类型</td>
						<td style="width: 16%">法律状态</td>
						<td style="width: 16%">公开/公布日期</td>
					</tr>

						<#if enterprisePatent?? && (enterprisePatent?size > 0)>
						<#list enterprisePatent as value>
						    	<tr>
									<td>${value_index?if_exists+1}</td>
									<td>${(value.patentName)!'--'}</td>
									<td >${(value.authorizeNum)!'--'}</td>
									<td >${(value.typeName)!'--'}</td>
									<td >${(value.lastStatus)!'--'}</td>
									<td >${(value.authorizeDate)!'--'}</td>
								</tr>
								  </#list>
					</#if>
					<tr>
						<td colspan="5" style="text-align: left">共计[${enterprisePatent?size}]条</td>
					</tr>
					</tbody>
				</table>
			</div>
		</div>



		<br></br>
		<br></br>
		<br></br>
		<!--软件著作权-->
		<div>
			<div style="font-weight: bold">
				<p>六:软件著作权</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td >序号</td>
						<td >登记号</td>
						<td >软件全称</td>
						<td >类型</td>
						<td >版本号</td>
						<td >著作权人</td>
						<td >登记批准日期</td>
					</tr>

					<#if enterpriseCopyRight?? && (enterpriseCopyRight?size > 0)>
						<#list enterpriseCopyRight as value>
						    	<tr>
									<td>${value_index?if_exists+1}</td>
									<td>${(value.number)!'--'}</td>
									<td >${(value.name)!'--'}</td>
									<td >${(value.typeName)!'--'}</td>
									<td >${(value.version)!'--'}</td>
									<td >${(value.company)!'--'}</td>
									<td >${(value.approvalDate)!'--'}</td>
								</tr>
					  </#list>
					</#if>

					<tr>
						<td colspan="6" style="text-align: left">共计[${enterpriseCopyRight?size}]条</td>
					</tr>
					</tbody>
				</table>
			</div>
		</div>


		<!--招投标-->
		<br></br>
		<br></br>
		<br></br>
		<!--招投标-->
		<div>
			<div style="font-weight: bold">
				<p>七:招投标</p>
			</div>
			<div>
				<table>
					<tbody>
					<tr class="tr1">
						<td >序号</td>
						<td >企业名称</td>
						<td >角色</td>
						<td >标题</td>
						<td >所在地区</td>
						<td >招标代理</td>
						<td >发布时间</td>
						<td >状态</td>
					</tr>

					<#if enterpriseBiddingsall?? && (enterpriseBiddingsall?size > 0)>
						<#list enterpriseBiddingsall as value>
						    	<tr>
									<td style="width: 5%">${value_index?if_exists+1}</td>
									<td style="width: 10%">${(value.ename)!'--'}</td>
									<td style="width: 15%">${(value.role)!'--'}</td>
									<td style="width: 15%">${(value.title)!'--'}</td>
									<td style="width: 15%">${(value.areaName)!'--'}</td>
									<td style="width: 15%">${(value.tendeAgent)!'--'}</td>
									<td style="width: 10%">${(value.date)!'--'}</td>
									<td style="width: 15%">${(value.status)!'--'}</td>
								</tr>
						  </#list>
					</#if>

				<tr>
					<td colspan="7" style="text-align: left">共计[${enterpriseBiddingsall?size}]条</td>
				</tr>
					</tbody>
				</table>
			</div>
		</div>
		<br></br>
		<br></br>
		<br></br>
		<div style="margin-bottom: 0px ;color:#626675">
			<span>注:</span>
			<span>各维度数据均按时间排序获取前50条!</span>
		</div>




		<!--分割线-->
	</div>


</body>
</html>

  

2. 准备html需要填充的数据

 Map map = new HashMap<>();

        //报告创建时间.
        map.put("createTime",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));

        //获取基本信息.
        map.put("baseInfo",this.queryEnterpriseBaseInfo(uniscid));

        //获取高管信息.
        map.put("managerInfo",this.queryEnterpriseManagerInfo(uniscid));

        //获取股东信息
        map.put("enterpriseShareInfo",this.queryEnterpriseShareInfo(uniscid));

        //获取工商变更信息
        map.put("enterpriseChange",this.queryEnterpriseChangeInfoInfo(uniscid));

        //获取专利信息
        map.put("enterprisePatent",this.queryEnterprisePatentInfo(uniscid));

        //获取著作信息
        map.put("enterpriseCopyRight",this.queryEnterpriseCopyRightInfo(uniscid));

        //获取招标信息
        map.put("enterpriseBiddingsall",this.queryEnterpriseBiddingsallInfo(uniscid));
        //生成报告
        PDFUtil.htmlToPdf(map,uniscid,"dea.html",resp);

 

3.加载html模板,渲染html模板,渲染pdf.

   

package com.audaque.credit;

import com.audaque.component.core.AudaqueContexts;
import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;

/**
 * @ClassName 导出pdf工具类.
 * @Descrription TODO
 * @Author mxk
 * @Date 2021/5/28 下午3:30
 * @Version 1.0
 */
public class PDFUtil {


    private static final String FONT = AudaqueContexts.getProperty("pdffreemarket.templete")+File.separator+"font"+File.separator+"simsun.ttf";

    private static Configuration freemarkerCfg = null;

    static {
        String templetePath = AudaqueContexts.getProperty("pdffreemarket.templete");
        freemarkerCfg =new Configuration();
        //freemarker的模板目录
        try {
            freemarkerCfg.setDirectoryForTemplateLoading(new File(templetePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 将freemark渲染为html,转换成pdf
     * @param t 动态数据
     * @param
     * @param <T>
     */
    public static <T> void htmlToPdf(T t, String fileName, String template, HttpServletResponse resp) {
        // 渲染html内容
        String content = PDFUtil.freeMarkerRender(t, template);
//        System.out.println("解析之后的数据=>>>>"+content);
        Rectangle rectangle = new Rectangle(PageSize.A4);
        Document document = new Document(rectangle);
        try {

            resp.setCharacterEncoding("UTF-8");
            resp.setHeader("content-Type", "application/pdf");
            resp.setHeader("Content-Disposition",
                    "inline;filename=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));
            // 建立书写器
            PdfWriter writer = PdfWriter.getInstance(document, resp.getOutputStream());
            writer.setPageEvent(new TextWaterMarkPdfPageEvent());
            document.open();
            XMLWorkerFontProvider fontImp = new MyFontsProvider();
            fontImp.register(FONT);
            XMLWorkerHelper.getInstance().parseXHtml(writer, document,
                    new ByteArrayInputStream(content.getBytes("utf-8")), null, Charset.forName("UTF-8"), fontImp);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } finally {
            document.close();
        }
    }

    /**
     * 设置字体
     */
    public static class TextWaterMarkPdfPageEvent extends PdfPageEventHelper {
        protected PdfTemplate total;
        protected BaseFont helv;

        public void onOpenDocument(PdfWriter writer, Document document) {
            total = writer.getDirectContent().createTemplate(100, 100);
            total.setBoundingBox(new Rectangle(-20, -20, 100, 100));
            try {
                helv = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            } catch (Exception e) {
                throw new ExceptionConverter(e);
            }
        }

        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            PdfContentByte cb = writer.getDirectContent();
            cb.beginText();
            try {

                // 设置水印字体参数及大小   (字体参数,字体编码格式,是否将字体信息嵌入到pdf中(一般不需要嵌入),字体大小)
                cb.setFontAndSize(helv, 50);
                PdfGState gs = new PdfGState();
                gs.setFillOpacity(0.3f);
                gs.setStrokeOpacity(0.3f);
                // 设置透明度
                cb.setGState(gs);
                // 设置水印对齐方式 水印内容 X坐标 Y坐标 旋转角度
                cb.showTextAligned(Element.ALIGN_RIGHT, AudaqueContexts.getProperty("pdffreemarket.waterMarketText"), 385, 500, 45);
                // 设置水印颜色(灰色)
                cb.setColorFill(BaseColor.GRAY);
                //结束
                cb.endText();
            } catch (Exception e) {
                e.printStackTrace();
            }
            super.onEndPage(writer, document);
        }
    }


    /**
     * 重写 字符设置方法,解决中文乱码问题
     */
    public static class MyFontsProvider extends XMLWorkerFontProvider {

        public Font getFont(final String fontname, final String encoding, final boolean embedded,
                            final float size, final int style, final BaseColor color) {
            BaseFont bf = null;
            try {
                bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            } catch (Exception e) {

            }
            Font font = new Font(bf, size, style, color);
            font.setColor(color);
            return font;
        }
    }

    /**
     * freemarker渲染html
     */
    public static <T> String freeMarkerRender(T data, String htmlTmp) {
        Writer out = new StringWriter();
        try {
            freemarkerCfg.setDefaultEncoding("UTF-8");
            // 获取模板,并设置编码方式
            Template template = freemarkerCfg.getTemplate(htmlTmp);
            template.setEncoding("UTF-8");
            // 合并数据模型与模板,将合并后的数据和模板写入到流中,这里使用的字符流
            template.process(data, out);
            out.flush();
            return out.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }
}

 

写在最后,网络上关于java导出pdf 的教程与样例代码太多了,但是基本上都是一个板子刻出来的.我想要加个通过freemarket渲染后(结构完美), 再在itext渲染的过程中增加水印,网络大神们都建议采用两步走.先将渲染后的html存储本地,然后读取出来

再用itext渲染,这不是托裤子放屁么,就没个完整的方案,于是就去翻itext api 发现了一个神奇的类PdfPageEventHelper 发现这个类是在itext渲染html 到pdf过程中,每一次分页的过程中均调用,那我为什不能在这里添加水印呢,于是乎一个很完美的方案

出来了,自定义一个类继承PdfPageEventHelper,然后将这个类注册到书写器中,这样在渲染每一页之后再增加一个水印.,最后附上我生成的一个效果,晚上又可以加个鸡腿了.

 

 

 

posted on 2021-06-02 09:54  康仔☆  阅读(239)  评论(6编辑  收藏  举报

导航