2020-10-13

iText + freemarker 根据html模板导出pdf 带页眉,页眉横线,页脚

1.引入依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>

<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.0.3</version>
</dependency>

2.html模板:
<!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">
<style>
.line {
text-decoration: underline;
}
.headCenter {
height: 40px;
}
body {
margin-left:30px;
margin-right:30px;
font-size: 16px;
font-family: SimSun;
}
.tdCenter {
text-align: center;
}
.fontsize5 {
font-size: 14px;
margin-top: 15px;
}
div {
line-height: 30px;
}
table, th, td{
border:1px solid #000000;
height:30px;
}
table {
border-collapse: collapse;
table-layout: fixed;
word-break:break-all;
font-size: 10px;
width: 100%;
}
td {
width: 60px;
word-break:break-all;
word-wrap : break-word;
}
.spanWeight {
font-weight: bold;
}
.div5Padding {
padding: 5px 0px;
}
p {
text-indent:1.5em;
line-height: 30px;
margin:0px auto
}
th {
text-align:center
}
.borderNone {
border:0px solid black;
}
</style>

<body>
<div class="headCenter"></div>
<div class="headCenter"></div>
<table cellspacing="0" cellpadding="0" class="borderNone" style="margin-top: 20px;">
<tr>
<td class="borderNone"><strong>合同编号:</strong><span class="line">{(test)!}</span></td> <td class="borderNone" colspan="2"><span class="spanWeight">签订时间:</span><span class="line">{(test)!} </span></td>
</tr>
</table>
<#--list 多层嵌套-->
<#if testList??>
<#list testList as item>
<p>
{item.test} <#if item.testList ?? && (item.testList ?size > 0)> <#list item.getTestList() as subItem> <p style="text-indent:2.5em;">{subItem.test}
</p>
</#list>
</#if>
</p>
</#list>
</#if>
</body>

</html>

3.导出工具类

注意:数据名称要和模板里的字段名称对上。我这里的object是一个实体类。

另外在项目的resources新建一个templates放置模板,在linux部署也可以找到该模板。
public class PdfUtil {
@Resource
FreeMarkerConfigurer freeMarkerConfigurer;

public void doExport(HttpServletResponse response,String htmlString,String fileName) throws Exception {
    response.setContentType("application/pdf");
    response.setCharacterEncoding("utf-8");
    response.setHeader("Pragma", "public");
    response.setHeader("Cache-Control", "max-age=30");
    response.setHeader("Content-disposition","attachment;filename=" + new
            String(fileName.getBytes("utf-8"),"ISO-8859-1" )+ ".pdf");
    OutputStream out;
    out = response.getOutputStream();
    //设置文档大小
    Document document = new Document();
    document.setMargins(40,40,60,60);
    PdfWriter writer = PdfWriter.getInstance(document, out);
    writer.setStrictImageSequence(true);
    //输出为PDF文件
    convertToPDF(writer,document,htmlString);
}
/**
 * @description PDF文件生成
 */
private void convertToPDF(PdfWriter writer,Document document,String htmlString){
    writer.setPageEvent(new PDFBuilder());
    document.open();
    MyFontsProvider fontProvider = new MyFontsProvider();
    fontProvider.addFontSubstitute("lowagie", "simsun");
    fontProvider.setUseUnicode(true);
    CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
    HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
    htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
    try {
        XMLWorkerHelper.getInstance().parseXHtml(writer,document,
                new ByteArrayInputStream(htmlString.getBytes("UTF-8")),
                XMLWorkerHelper.class.getResourceAsStream("/default.css"),
                Charset.forName("UTF-8"),fontProvider);
    } catch (IOException e) {
        e.printStackTrace();
        throw new BusinessException("PDF文件生成异常");
    }finally {
        document.close();
    }
}

/**
 * @description 获取模板并填充数据
 */
public String getContent(String fileName, Object data) throws Exception{
    // 创建一个Configuration对象
    Configuration configuration = new Configuration(Configuration.getVersion());
    // 告诉config对象模板文件存放的路径。
    configuration.setDefaultEncoding("utf-8");
    //从config对象中获得模板对象。需要制定一个模板文件的名字。
    Template template = freeMarkerConfigurer.getConfiguration().getTemplate(fileName+".ftl");
    StringWriter writer = new StringWriter();
    //模版和数据匹配
    template.process(data, writer);
    writer.flush();
    writer.close();
    String html = writer.toString();
    return html;
}

/**
 * 设置字符集
 */
public static class MyFontsProvider extends XMLWorkerFontProvider {
    public MyFontsProvider(){
        super(null, null);
    }
    @Override
    public Font getFont(final String fontname, String encoding, float size, final int style) {
        String fntnames = fontname;
        Font FontChinese = null;
        if (fntnames == null) {
            fntnames = "宋体";
        }
        if (size == 0) {
            size = 4;
        }
        try{
            //注意这里有一个,1
            BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
            FontChinese = new Font(bfChinese);
        }catch (Exception e){
            e.printStackTrace();
        }
        if(FontChinese==null){
            FontChinese = super.getFont(fntnames, encoding, size, style);
        }
        return FontChinese;
    }
}

}

设置页眉页脚等

设置页眉地下的横线的时候,无论如何只能使得一边没有,所有加一张图片盖住另一边,有会的同学可以告诉我。
public class PDFBuilder extends PdfPageEventHelper {
/**
* 页眉
*/
public String header = "";

/**
 * 文档字体大小,页脚页眉最好和文本大小一致
 */
public int presentFontSize = 10;

/**
 * 文档页面大小,最好前面传入,否则默认为A4纸张
 */
public Rectangle pageSize = PageSize.A4;

/**
 * 模板
 */
public PdfTemplate total;

/**
 * 基础字体对象
 */
public BaseFont bf = null;

/**
 * 利用基础字体生成的字体对象,一般用于生成中文文字
 */
public Font fontDetail = null;

/**
 * 利用基础字体生成的字体对象,一般用于生成中文文字
 */
public Font fontDetail1 = null;


public void setHeader(String header) {
    this.header = header;
}

/**
 *
 * @see PdfPageEventHelper#onOpenDocument(PdfWriter,
 *      Document)
 */
@Override
public void onOpenDocument(PdfWriter writer, Document document) {
    // 共 页 的矩形的长宽高
    total = writer.getDirectContent().createTemplate(50, 50);
}

/**
 *
 *关闭每页的时候,写入页眉,写入'第几页共'这几个字。
 * @see PdfPageEventHelper#onEndPage(PdfWriter,
 *      Document)
 */
@Override
public void onEndPage(PdfWriter writer, Document document) {
    this.addPage(writer, document);
}

/**
 * 加分页
 */
@SneakyThrows
public void addPage(PdfWriter writer, Document document){
    InputStream is = null;
    ByteArrayOutputStream baos = null;
    InputStream is1 = null;
    ByteArrayOutputStream baos1 = null;

    //设置分页页眉页脚字体
    Image image = null;
    Image whiteImage = null;
    try {
        final String picPath = "img/img001.png";
        final String picPath1 = "img/img002.png";
        Resource resource = new ClassPathResource(picPath);
        Resource resource1 = new ClassPathResource(picPath1);
        is = resource.getInputStream();
        baos = new ByteArrayOutputStream();
        int i;
        while ((i = is.read()) != -1) {
            baos.write(i);
        }
        is1 = resource1.getInputStream();
        baos1 = new ByteArrayOutputStream();
        int j;
        while ((j = is1.read()) != -1) {
            baos1.write(j);
        }
        // 页眉logo图片实例
        image = Image.getInstance(baos.toByteArray());
        whiteImage = Image.getInstance(baos1.toByteArray());
        if (bf == null) {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
        }
        if (fontDetail == null) {
            // 数据体字体
            fontDetail = new Font(bf, presentFontSize, Font.NORMAL);
        }
        if (fontDetail1 == null) {
            // 数据体字体
            fontDetail1 = new Font(bf, 16, Font.BOLD);
        }
    } catch (DocumentException | IOException e) {
        e.printStackTrace();
    }finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (baos != null) {
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    PdfContentByte directContent = writer.getDirectContent();


    //1、生成右侧页眉
    // document.right(-20)  方法设置页眉的左右间距
    // document.top(+20)  方法设置页眉的上下间距
    ColumnText.showTextAligned(directContent,
            Element.ALIGN_RIGHT, new Phrase(" 测试", fontDetail),
            document.right(150), document.top(-13), 0);

    //设置线条
    Phrase p2 = new Phrase();
    p2.add(new Chunk(new LineSeparator()));
    ColumnText.showTextAligned(directContent,
            Element.ALIGN_LEFT, p2,
            document.left(), document.top(-8), 0);

    Phrase p3 = new Phrase();
    p3.add(new Chunk(whiteImage, 0, -30));

    ColumnText.showTextAligned(directContent,
            Element.ALIGN_RIGHT, p3,
            document.right(-45), document.top(-8), 0);

    //  !!!! 最重要的是这个, 如果页眉需要设置图片的话,需要在Phrase对象中添加一个Chunk对象,在Chunk对象中添加图片信息即可
    Phrase p1 = new Phrase("", fontDetail);
    p1.add(new Chunk(image, 0, -30));
    // 1、写入左侧页眉
    ColumnText.showTextAligned(directContent,
            Element.ALIGN_LEFT, p1,
            document.left(50), document.top(-40), 0);
    // 2、生成右侧页脚
    ColumnText.showTextAligned(directContent,
            Element.ALIGN_LEFT, new Phrase("第" + document.getPageNumber() + "页", fontDetail),
            document.left(230), document.bottom(-30), 0);

    //设置线条
    ColumnText.showTextAligned(directContent,
            Element.ALIGN_LEFT, p2,
            document.left(), document.bottom(-20), 0);
    //设置图片遮住多余的线条
    ColumnText.showTextAligned(directContent,
            Element.ALIGN_RIGHT, p3,
            document.right(-45), document.bottom(-20), 0);

    //加这个为了模板的替换
    directContent.addTemplate(total, document.left(260), document.bottom() - 30);

    //第一页
     if(document.getPageNumber()==1) {
         ColumnText.showTextAligned(directContent,
                 Element.ALIGN_LEFT, new Phrase("测试测试测试", fontDetail1),
                 document.left(150), document.top(30), 0);

         ColumnText.showTextAligned(directContent,
                 Element.ALIGN_CENTER, new Phrase("测试", fontDetail1),
                 document.left(260), document.top(60), 0);
     }
}

/**
 *
 *关闭文档时,替换模板,完成整个页眉页脚组件
 * @see PdfPageEventHelper#onCloseDocument(PdfWriter,
 *      Document)
 */
@Override
public void onCloseDocument(PdfWriter writer, Document document) {
    // 7.最后一步了,就是关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
    total.beginText();
    // 生成的模版的字体、颜色
    total.setFontAndSize(bf, presentFontSize);
    //页脚内容拼接  如  第1页/共2页
    String foot2 = "总" + (writer.getPageNumber()) + " 页";
    // 模版显示的内容
    total.showText(foot2);
    total.endText();
    total.closePath();
}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。