Java 用html模板生成 Pdf 加水印

生成PDF报表是不少企业系统常见的需求, 有些对外提供报表的系统还须要对生成的pdf文件添加水印, 本文将介绍以上2个问题简单又免费的技术方案 ( 商业收费可见: 最新版ItextPdf )html

依赖

免费方案要用到的 第三方依赖有:前端

  • thymleaf : 用来生成html, 你也能够换成其余的模板引擎, 如: freemarker
  • itextpdf 5 : 用来将html渲染成pdf文件

本文的方案基于spring boot开发, 简化了许多thymleaf的配置, 固然你也能够手动配置.vue

如下是maven依赖,java

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.11</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>com.itextpdf.tool</groupId>
    <artifactId>xmlworker</artifactId>
    <version>5.5.11</version>
</dependency>
复制代码

或Grade依赖 :react

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'com.itextpdf:itextpdf:5.5.11'
implementation 'com.itextpdf:itext-asian:5.2.0'
implementation 'com.itextpdf.tool:xmlworker:5.5.11'
复制代码

生成html

html = html模板 + 参数git

thymleaf的模板语法与大多数模板引擎的语法都比较相似, 对于前端框架如vue、angular、react的开发者只须要一点点查阅手册就能够迅速上手, 此处是 thymleaf模板语法文档.spring

spring boot 引入thymleaf的方式很是简单, 后文会提供必要的配置, 下方是使用thymleaf模板引擎生成html的代码片断:前端框架

public String generateHtml(String templateName, Map<String, Object> data) {
    Context ctx = new Context();
    ctx.setVariables(data);

    return templateEngine.process(templateName, ctx);
}
复制代码

其中: templateEngineorg.thymeleaf.TemplateEngine 实例, 经过依赖注入取得.框架

templateName 是模板的名字, 与模板文件的名称对应, 模板文件的路径经过下面这段代码片断来配置, 这也是thymleaf所须要的全部配置.maven

@Configuration
public class ThymeleafConfiguration {
    @Bean
    public ClassLoaderTemplateResolver emailTemplateResolver() {
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setPrefix("templates/");
        templateResolver.setTemplateMode("HTML5");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setOrder(1);
        return templateResolver;
    }
}
复制代码

这段配置会扫描 /src/main/resources/templates路径下, 后缀为.html的文件做为html模板.

例如: String html = generateHtml("pdf/report", context); 则会将模板文件/src/main/resources/templates/pdf/reprot.htmlcontext中的数据, 合成html内容.

生成pdf

pdf = render(html)

利用com.itextpdf.text.pdf.PdfWriter 将html 渲染成pdf, 下方的代码片断已是所有代码.

private File render(String html) throws IOException, DocumentException {
    FileOutputStream os = null;
    try {
        File outputFile = File.createTempFile("temp.pdf");
        os = new FileOutputStream(outputFile);

        Document document = new Document(PageSize.A4);

        PdfWriter pdfWriter = PdfWriter.getInstance(document, os);
        pdfWriter.setPageEvent(new Header());
        document.setMargins(30, 30, 40, 50);
        document.open();

        InputStream htmlStream = new ByteArrayInputStream(html.getBytes("UTF-8"));
        XMLWorkerHelper.getInstance().parseXHtml(
            pdfWriter, 
            document,
            htmlStream,
            Charset.forName("UTF-8"), 
            fontProvider);
        document.close();
        
        return outputFile;
    } finally {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) { /*ignore*/ }
        }
    }
}

private FontProvider fontProvider = new FontProvider() {
    @Override
    public boolean isRegistered(String s) {
        return false;
    }

    @Override
    public Font getFont(String fontFamily, String charset, boolean arg2, float size, int style, BaseColor color) {
        BaseFont chinese = null;
        try {
            chinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
        }
        return new Font(chinese, size, style, color);
    }
};
复制代码

值得注意的地方是, itextpdf对于中文支持不是十分友好, 若是不手动设置中文字体, 渲染出来的中文会变成空白.

上面的代码片断中fontProvider展现了自定义字体的方法.

添加水印

水印PDF = paint(原PDF)

public static File paint(File file) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(file.getPath());
    File dest = File.createTempFile("withMask", "pdf", file.getParentFile());
  
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));

    int n = reader.getNumberOfPages();
    stamper.setRotateContents(false);
    // text watermark
    BaseFont chinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
    Font f = new Font(chinese, 80);
    Font fontHeader = new Font(chinese, 10);

    // transparency
    PdfGState gs1 = new PdfGState();
    gs1.setFillOpacity(0.1f);

    // properties
    PdfContentByte over;
    Rectangle pagesize;
    float x, y;
    // loop over every page
    for (int i = 1; i <= n; i++) {
        pagesize = reader.getPageSize(i);

        over = stamper.getOverContent(i);
        over.saveState();
        over.setGState(gs1);

        x = (pagesize.getLeft() + pagesize.getRight()) / 2 - 25;
        y = (pagesize.getTop() + pagesize.getBottom()) / 2 + 60;

        Phrase p = new Phrase("水印文字", f);
        ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 45);

        over.restoreState();
    }
    stamper.close();
    reader.close();
    return dest;
}

复制代码
相关文章
相关标签/搜索