Java- freemarker-pdf

maven:

<dependency>
     <groupId>org.freemarker</groupId>
     <artifactId>freemarker</artifactId>
     <version>2.3.23</version>
 </dependency>
 <dependency>
     <groupId>com.itextpdf</groupId>
     <artifactId>itextpdf</artifactId>
     <version>5.4.1</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>

Java代码:

package com.xtm.seal.utils.pdfUtil;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;

/**
 * @project: xtm-print
 * @description: pdfs生成工具
 * @author: zhangyingbin
 * @create: 2020-06-16 12:57
 **/
public class PdfUtil {


    private static String TEMPLATE = null;
    private static String CONTRACT = null;

    private static String PDFNAME = null;//pdf文件名
    private static String HTMLNAME = null;//html文件名

    static {
        String basePath = new File("").getAbsolutePath();
        TEMPLATE = basePath + "/src/main/resources/templates/";//模板存储路径
        CONTRACT = basePath + "/src/main/resources/templates/result/";
        String name = UUID.randomUUID().toString().replaceAll("-","")+"-"+String.valueOf(System.currentTimeMillis());
        PDFNAME = name;
        HTMLNAME = name;

    }

    /**
    * @Description: 挖域表单模板生成pdf
    * @Param: [templateName, map, request, response]
    * @return: java.io.File
    * @Author: zhangyingbin
    * @Date: 2020/6/29
    */
    public static File AcrobatPdfTemplate(String templateName, Map<String,Object> map) throws IOException, DocumentException {

        Map<String, Object> o = new HashMap();
        o.put("datemap", map);

        String rootPath = new File("").getAbsolutePath();
        //返回tomcat真实路径   Users/a/soft/apache-tomcat-8.5.37/
        System.out.println(rootPath);
        //return rootPath.substring(0, rootPath.lastIndexOf("/") + 1);
        String templatePath = rootPath+"/src/main/resources/templates/"+templateName+".pdf";

        //TODO 生成的新文件路径
        String newPDFPath = rootPath+"/src/main/resources/templates/result/"+ UUID.randomUUID().toString().replaceAll("-","")+"-"+System.currentTimeMillis() +".pdf";
        PdfReader reader;
        FileOutputStream out;
        ByteArrayOutputStream bos;
        PdfStamper stamper;
        //↓↓↓↓↓这个是字体文件
        BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font FontChinese = new Font(bf, 5, Font.NORMAL);
        out = new FileOutputStream(newPDFPath);// 输出流
        reader = new PdfReader(templatePath);// 读取pdf模板
        bos = new ByteArrayOutputStream();
        stamper = new PdfStamper(reader, bos);

        AcroFields form = stamper.getAcroFields();
        Map<String, String> datemap = (Map<String, String>) o.get("datemap");
        form.addSubstitutionFont(bf);

        for (String key : datemap.keySet()) {
            String value = datemap.get(key);
            form.setField(key, value,true);
        }

        //TODO 插入图片
        //获取插入图片的位置的文本域的坐标
//        int page = form.getFieldPositions("peo1_name").get(0).page;
//        System.out.println("page :" + page);
//        Rectangle signReact = form.getFieldPositions("peo1_name").get(0).position;
//        float x = signReact.getLeft();
//        float y = signReact.getBottom();
//        System.out.println("x:" + x + " y:" + y);
//        //插入图片
//        Image image = Image.getInstance("/Users/zhangyingbin/Desktop/zyb.png");
//        //获取图片页面
//        PdfContentByte under = stamper.getOverContent(page);
//        //图片大小自适应
//        image.scaleToFit(signReact.getWidth(), signReact.getHeight());
//        image.setAbsolutePosition(x, y);
//        under.addImage(image);


        stamper.setFormFlattening(true);// 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑
        stamper.close();
        Document doc = new Document();

        //   Font font = new Font(bf, 32);
        PdfCopy copy = new PdfCopy(doc, out);//用于保存原页面内容,然后输出
        doc.open();


        PdfImportedPage importPage = null;
        ///循环是处理成品只显示一页的问题
        for (int i = 0; i < reader.getNumberOfPages(); i++) {
            importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), i + 1);
            copy.addPage(importPage);
        }

        //TODO 复制第二页插入其他过长数据
//        ByteArrayOutputStream bosArray[] = new ByteArrayOutputStream[reader.getNumberOfPages()];
//        //复制两次
//        for (int j = 0; j < 2; j++) {
//            PdfReader reader1 = new PdfReader(templatePath);// 重新读取pdf模板
//            bosArray[j] = new ByteArrayOutputStream();
//            reader1.selectPages(String.valueOf(reader1.getNumberOfPages()));//截取模板的最后页
//            PdfStamper stamper1 = new PdfStamper(reader1, bosArray[j]);
//
//            AcroFields fields = stamper1.getAcroFields();
//            fields.addSubstitutionFont(bf);
//            fields.setField("bianhao", "12345");
//            fields.setField("type", "测试活动");
//            fields.setField("content", "这是第" + j + "张图片");
//
//            importPage = copy.getImportedPage(reader1, 1);//输出当前页
//            copy.addPage(importPage);
//        }

        doc.close();
        out.close();
        System.out.println("生成pdf文件完成~~~~~~~~~~");
        return new File(newPDFPath);
    }

    /**
    * @Description: ftl模板生成pdf
    * @Param: [templateName, paramMap]
    * @return: java.io.File
    * @Author: zhangyingbin
    * @Date: 2020/6/29
    */
    public static File FtlPdfTemplate(String templateName,
                                       Map<String, Object> paramMap) throws Exception {
        // 获取本地模板存储路径、合同文件存储路径
        String templatePath = TEMPLATE;
        String contractPath = CONTRACT;
        // 组装html和pdf合同的全路径URL
        String localHtmlUrl = contractPath  + HTMLNAME + ".html";
        String localPdfPath = contractPath + "/";
        // 判断本地路径是否存在如果不存在则创建
        File localFile = new File(localPdfPath);
        if (!localFile.exists()) {
            localFile.mkdirs();
        }
        String localPdfUrl = localFile + "/" + PDFNAME + ".pdf";
        templateName = templateName + ".ftl";
        htmHandler(templatePath, templateName, localHtmlUrl, paramMap);// 生成html合同
        pdfHandler(localHtmlUrl, localPdfUrl);// 根据html合同生成pdf合同
        deleteFile(localHtmlUrl);// 删除html格式合同

        System.out.println("PDF生成成功");

        return new File(localPdfUrl);
    }

    private static void htmHandler(String templatePath, String templateName,
                                   String htmUrl, Map<String, Object> paramMap) throws Exception {
        Configuration cfg = new Configuration();
        cfg.setDefaultEncoding("UTF-8");
        cfg.setDirectoryForTemplateLoading(new File(templatePath));

        Template template = cfg.getTemplate(templateName);

        File outHtmFile = new File(htmUrl);

        Writer out = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(outHtmFile)));
        template.process(paramMap, out);

        out.close();
    }

    private static void pdfHandler(String htmUrl, String pdfUrl)
            throws DocumentException, IOException {
        File htmFile = new File(htmUrl);
        File pdfFile = new File(pdfUrl);

        String url = htmFile.toURI().toURL().toString();

        OutputStream os = new FileOutputStream(pdfFile);

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(url);

        org.xhtmlrenderer.pdf.ITextFontResolver fontResolver = renderer
                .getFontResolver();
        // 解决中文支持问题
        try {
            fontResolver.addFont(TEMPLATE + "fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            fontResolver.addFont(TEMPLATE + "fonts/SIMFANG.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            fontResolver.addFont(TEMPLATE + "fonts/MSYH.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            fontResolver.addFont(TEMPLATE + "fonts/MSYHBD.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            fontResolver.addFont(TEMPLATE + "fonts/MSYHL.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            fontResolver.addFont(TEMPLATE + "fonts/SIMLI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } catch (com.lowagie.text.DocumentException e) {
            e.printStackTrace();
        }

        renderer.getSharedContext().setBaseURL("file:/" + TEMPLATE + "image");
        renderer.layout();
        try {
            renderer.createPDF(os);
        } catch (com.lowagie.text.DocumentException e) {
            e.printStackTrace();
        }
        os.close();
    }

    /**
    * @Description: 删除文件
    * @Param: [fileUrl]
    * @return: void
    * @Author: zhangyingbin
    * @Date: 2020/6/30
    */
    public static void deleteFile(String fileUrl) {
        File file = new File(fileUrl);
        file.delete();
    }

    /**
    * @Description: 获得pdf总页数
    * @Param: [file] file:pdf文件
    * @return: int 返回总页码
    * @Author: zhangyingbin
    * @Date: 2020/6/30
    */
    public static int pdfPageCount(File file) throws IOException {
        PdfReader pdfReader = new PdfReader(new FileInputStream(file));
        int pageCount = pdfReader.getNumberOfPages();
        return pageCount;
    }


    /**
    * @Description: pdf 转图片
    * @Param: [file, imgType] file:pdf文件,imgType:要生成图片类型(png,jpg等)
    * @return: java.util.List<java.lang.String> 返回生成的图片名称集合
    * @Author: zhangyingbin
    * @Date: 2020/6/30
    */
    public static java.util.List<String> pdfToImage(File file, String imgType) throws IOException {

        List<String> pdfImages = new ArrayList<>();

        PDDocument doc = PDDocument.load(file);
        PDFRenderer renderer = new PDFRenderer(doc);
        int pageCount = doc.getNumberOfPages();
        for (int i = 0; i < pageCount; i++) {
            BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPI
            // BufferedImage srcImage = resize(image, 240, 240);//产生缩略图
            String pdfImageName = UUID.randomUUID().toString().replaceAll("-", "") + "." + imgType;
            ImageIO.write(image, imgType, new File(TEMPLATE + "pdfImage/" + pdfImageName));

            pdfImages.add(pdfImageName);
        }

        return pdfImages;
    }

    /**
    * @Description: 多个pdf合并成一个pdf
    * @Param: [files]
    * @return: java.io.File
    * @Author: zhangyingbin
    * @Date: 2020/6/30
    */
    public static File mulPdfToOne(List<File> files) throws IOException {
        // pdf合并工具类
        PDFMergerUtility mergePdf = new PDFMergerUtility();
        for (File f : files) {
            if (f.exists() && f.isFile()) {
                // 循环添加要合并的pdf
                mergePdf.addSource(f);
            }
        }
        String onePdfName =TEMPLATE+"mulPdf/"+ UUID.randomUUID().toString().replaceAll("-","")+".pdf";
        // 设置合并生成pdf文件名称
        mergePdf.setDestinationFileName(onePdfName);
        // 合并pdf
        mergePdf.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
        return new File(onePdfName);
    }

}

ftl模板:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/html">
<head>
    <style type="text/css">
        /*解决html转pdf文件中文不显示的问题*/
        body {
            font-family: FangSong;
        }

        /*设定纸张大小*/
        /* A4纸 */
        /* @page{size:210mm*297mm} */
        @page {
            size: a4;

            margin-left: 115px;
            margin-right: 120px;
            margin-top: 120px;
            @bottom-center {
                content: element(footer)
            };
        }

        #info {
            color: black;
            font-family: FangSong;
            line-height: 150%;
            margin: 0;
            font-size: 18px;
        }

        span {
            line-height: 150%;
        }

        .h {
            margin-top: 9px;
            margin-bottom: 9px;
            font-family: Microsoft YaHei;
            font-size: 18px;
            font-weight: 400;
        }

        img {
            width: 440px;
            height: auto;
            /*max-width: 440px;*/
            /*max-height: 100%;*/
        }
        .footer {
            position: running(footer)
        }
        #pagenumber:before {
            content: counter(page);
        }
        #pagecount:before {content: counter(pages);
        }

    </style>

</head>
<body style="font-family: FangSong;line-height:1">
<div class='footer' style="width: 100%">
    <p style="text-align: center">-<span id="pagenumber"></span>-</p>
    <br/>
</div>
<div style="width:100%;line-height:1">
    <div>
        <p style="text-align: center;line-height: 115%;font-weight: bold;font-family: SimSun;font-size: 29px;">
            活动基本信息</p>
    </div>

    <div style="line-height: 150%;width:100%;">
        <h2 class="h">活动类型</h2>
        <p id="info">${activityType!"   "}</p>
        <h2 class="h">活动参加人数</h2>
        <p id="info">${activityPeopleCount!"   "}</p>
        <h2 class="h">活动举办地所在区</h2>
        <p id="info">${activityCounties!"   "}</p>
        <h2 class="h">活动名称</h2>
        <p id="info">${activityName!"   "}</p>
        <h2 class="h">活动时间</h2>
        <p id="info">${activityTime!"   "}</p>
        <h2 class="h">活动地点</h2>
        <p id="info">${activityLocation!"   "}</p>

        <h2 class="h">活动拟发票数量</h2>
        <p id="info">${activityTicketCount!"   "}</p>
        <h2 class="h">售票座位图</h2>
        <div style="width: 440px">
            <#list siteImage as image>
                <img src='${image!"data:image/png;base64"}' alt="" />
            </#list>
        </div>
        <h2 class="h">安全工作人员数量</h2>
        <p id="info">${securityWorkerCount!"   "}</p>
        <h2 class="h">活动概况</h2>
        <div id="info">${activityInfo!"   "}</div>
    </div>

</div>
</body>
</html>

ftl模板技巧:
模板分页:

.pageNext {
            page-break-after: always;
        }
<div class='pageNext'></div><!--在需要分页的位置添加-->

页眉页脚设置:
参考:https://cloud.tencent.com/developer/ask/44759

<html>
<head>
<style>
div.header {
    display: block; text-align: center; 
    position: running(header);
}
div.footer {
    display: block; text-align: center;
    position: running(footer);
}
div.content {page-break-after: always;}
@page {
     @top-center { content: element(header) }
}
@page { 
    @bottom-center { content: element(footer) }
}
</style>
</head>
<body>
    <div class='header'>Header</div>
    <div class='footer'>Footer</div>
    <div class='content'>Page1</div>
    <div>Page2</div>
</body>
</html>

页脚添加页码:

 @page {
            size: a4;

            margin-left: 115px;
            margin-right: 120px;
            margin-top: 120px;
            @bottom-center {
                content: element(footer) //页脚设置
            };
        }
  .footer {
            position: running(footer)
        }
  #pagenumber:before {
       content: counter(page);
   }
   #pagecount:before {content: counter(pages);
   }
   
<div class='footer' style="width: 100%">
    <p style="text-align: center"><span id="pagecount"></span>页,第<span id="pagenumber"></span>页面</p>
    <br/>
</div>

表格内容过长分页换行:
引用 :https://blog.youkuaiyun.com/qq_31980421/article/details/79662988

table{
 page-break-inside:auto;-fs-table-paginate:paginate;border-spacing:0;table-layout:fixed;       word- break:break- strict; cellspacing:0;cellpadding:0 ;border: solid 1px #ccc; padding: 2px 2px;}

  tr { page-break-inside:avoid; page-break-after:auto;}默认换行
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值