精华内容
下载资源
问答
  • Java iText+FreeMarker生成PDF(HTML转PDF) 1.背景 在某些业务场景中,需要提供相关的电子凭证,比如网银/支付宝中转账的电子回单,签约的电子合同等。方便用户查看,下载,打印。目前常用的解决方案是,把相关数据...

    Java iText+FreeMarker生成PDF(HTML转PDF)
    1.背景
    在某些业务场景中,需要提供相关的电子凭证,比如网银/支付宝中转账的电子回单,签约的电子合同等。方便用户查看,下载,打印。目前常用的解决方案是,把相关数据信息,生成对应的pdf文件返回给用户。
    在这里插入图片描述

    本文源码:http://git.oschina.net/lujianing/java_pdf_demo
    2.iText
    iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
    iText 官网:http://itextpdf.com/
    iText 开发文档: http://developers.itextpdf.com/developers-home
    iText目前有两套版本iText5和iText7。iText5应该是网上用的比较多的一个版本。iText5因为是很多开发者参与贡献代码, 因此在一些规范和设计上存在不合理的地方。iText7是后来官方针对iText5的重构,两个版本差别还是挺大的。不过在实际使用中,一般用到的都比较 简单,所以不用特别拘泥于使用哪个版本。比如我们在http://mvnrepository.com/中搜索iText,出来的都是iText5的依赖。
    来个最简单的例子:
    添加依赖:

    com.itextpdf itextpdf 5.5.11 测试代码:JavaToPdf package com.lujianing.test;

    import com.itextpdf.text.Document;
    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.Paragraph;
    import com.itextpdf.text.pdf.PdfWriter;

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;

    /**

    • Created by lujianing on 2017/5/7.
      */
      public class JavaToPdf {

      private static final String DEST = “target/HelloWorld.pdf”;

      public static void main(String[] args) throws FileNotFoundException, DocumentException {
      Document document = new Document();
      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));
      document.open();
      document.add(new Paragraph(“hello world”));
      document.close();
      writer.close();
      }
      }
      运行结果:
      在这里插入图片描述

    3.iText-中文支持
    iText默认是不支持中文的,因此需要添加对应的中文字体,比如黑体simhei.ttf
    可参考文档:http://developers.itextpdf.com/examples/font-examples/using-fonts#1227-tengwarquenya1.java
    测试代码:JavaToPdfCN
    package com.lujianing.test;

    import com.itextpdf.text.Document;
    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.Font;
    import com.itextpdf.text.FontFactory;
    import com.itextpdf.text.Paragraph;
    import com.itextpdf.text.pdf.BaseFont;
    import com.itextpdf.text.pdf.PdfWriter;

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;

    /**

    • Created by lujianing on 2017/5/7.
      */
      public class JavaToPdfCN {

      private static final String DEST = “target/HelloWorld_CN.pdf”;
      private static final String FONT = “simhei.ttf”;

      public static void main(String[] args) throws FileNotFoundException, DocumentException {
      Document document = new Document();
      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));
      document.open();
      Font f1 = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
      document.add(new Paragraph(“hello world,我是鲁家宁”, f1));
      document.close();
      writer.close();
      }
      }
      输出结果:
      在这里插入图片描述

    4.iText-Html渲染
    在一些比较复杂的pdf布局中,我们可以通过html去生成pdf
    可参考文档:http://developers.itextpdf.com/examples/xml-worker-itext5/xml-worker-examples
    添加依赖:

    com.itextpdf.tool xmlworker 5.5.11 添加模板:template.html Title
    你好,鲁家宁
    测试代码:JavaToPdfHtml package com.lujianing.test;

    import com.itextpdf.text.Document;
    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.pdf.PdfWriter;
    import com.itextpdf.tool.xml.XMLWorkerFontProvider;
    import com.itextpdf.tool.xml.XMLWorkerHelper;
    import com.lujianing.test.util.PathUtil;

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.charset.Charset;

    /**

    • Created by lujianing on 2017/5/7.
      */
      public class JavaToPdfHtml {

      private static final String DEST = “target/HelloWorld_CN_HTML.pdf”;
      private static final String HTML = PathUtil.getCurrentPath()+"/template.html";
      private static final String FONT = “simhei.ttf”;

      public static void main(String[] args) throws IOException, DocumentException {
      // step 1
      Document document = new Document();
      // step 2
      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));
      // step 3
      document.open();
      // step 4
      XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
      fontImp.register(FONT);
      XMLWorkerHelper.getInstance().parseXHtml(writer, document,
      new FileInputStream(HTML), null, Charset.forName(“UTF-8”), fontImp);
      // step 5
      document.close();
      }
      }
      输出结果:
      在这里插入图片描述

    需要注意:
    1.html中必须使用标准的语法,标签一定需要闭合
    2.html中如果有中文,需要在样式中添加对应字体的样式

    5.iText-Html-Freemarker渲染
    在实际使用中,html内容都是动态渲染的,因此我们需要加入模板引擎支持,可以使用FreeMarker/Velocity,这里使用FreeMarker举例
    添加FreeMarke依赖:

    org.freemarker freemarker 2.3.30 添加模板:template_freemarker.html Title
    你好,${name}
    测试代码:JavaToPdfHtmlFreeMarker package com.lujianing.test;

    import com.itextpdf.text.Document;
    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.pdf.PdfWriter;
    import com.itextpdf.tool.xml.XMLWorkerFontProvider;
    import com.itextpdf.tool.xml.XMLWorkerHelper;
    import com.lujianing.test.util.PathUtil;

    import freemarker.template.Configuration;
    import freemarker.template.Template;

    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.nio.charset.Charset;
    import java.util.HashMap;
    import java.util.Map;

    /**

    • Created by lujianing on 2017/5/7.
      */
      public class JavaToPdfHtmlFreeMarker {

      private static final String DEST = “target/HelloWorld_CN_HTML_FREEMARKER.pdf”;
      private static final String HTML = “template_freemarker.html”;
      private static final String FONT = “simhei.ttf”;

      private static Configuration freemarkerCfg = null;

      static {
      freemarkerCfg =new Configuration();
      //freemarker的模板目录
      try {
      freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
      } catch (IOException e) {
      e.printStackTrace();
      }
      }

      public static void main(String[] args) throws IOException, DocumentException {
      Map<String,Object> data = new HashMap();
      data.put(“name”,“鲁家宁”);
      String content = JavaToPdfHtmlFreeMarker.freeMarkerRender(data,HTML);
      JavaToPdfHtmlFreeMarker.createPdf(content,DEST);
      }

      public static void createPdf(String content,String dest) throws IOException, DocumentException {
      // step 1
      Document document = new Document();
      // step 2
      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
      // step 3
      document.open();
      // step 4
      XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
      fontImp.register(FONT);
      XMLWorkerHelper.getInstance().parseXHtml(writer, document,
      new ByteArrayInputStream(content.getBytes()), null, Charset.forName(“UTF-8”), fontImp);
      // step 5
      document.close();

      }

      /**

      • freemarker渲染html
        */
        public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
        Writer out = new StringWriter();
        try {
        // 获取模板,并设置编码方式
        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;
        }
        }
        输出结果:
        在这里插入图片描述

    目前为止,我们已经实现了iText通过Html模板生成Pdf的功能,但是实际应用中,我们发现iText并不能对高级的CSS样式进行解析,比如CSS中的position属性等,因此我们要引入新的组件
    若中文变量还是不显示,则在pom.xml里添加如下:

      
        
          src/main/resources
          true
        
      
      
      
        
          org.apache.maven.plugins
          maven-resources-plugin
          2.7
          
          UTF-8
            
            ttf
            
          
        
      

    备注:工具类的方法 PathUtil.getCurrentPath() = “src/main/resources/”,(这个对新手有用)
    6.Flying Saucer-CSS高级特性支持
    Flying Saucer is a pure-Java library for rendering arbitrary well-formed XML (or XHTML) using CSS 2.1 for layout and formatting, output to Swing panels, PDF, and images.
    Flying Saucer是基于iText的,支持对CSS高级特性的解析。
    添加依赖:

    org.xhtmlrenderer flying-saucer-pdf 9.1.5 org.xhtmlrenderer flying-saucer-pdf-itext5 9.1.5 添加模板:template_freemarker_fs.html Title
    你好,${name}
    测试代码:JavaToPdfHtmlFreeMarker package com.lujianing.test.flyingsaucer;

    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.pdf.BaseFont;
    import com.lujianing.test.util.PathUtil;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import org.xhtmlrenderer.pdf.ITextFontResolver;
    import org.xhtmlrenderer.pdf.ITextRenderer;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.util.HashMap;
    import java.util.Map;

    /**

    • Created by lujianing on 2017/5/7.
      */
      public class JavaToPdfHtmlFreeMarker {

      private static final String DEST = “target/HelloWorld_CN_HTML_FREEMARKER_FS.pdf”;
      private static final String HTML = “template_freemarker_fs.html”;
      private static final String FONT = “simhei.ttf”;
      private static final String LOGO_PATH = “file://”+PathUtil.getCurrentPath()+"/logo.png";

      private static Configuration freemarkerCfg = null;

      static {
      freemarkerCfg =new Configuration();
      //freemarker的模板目录
      try {
      freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
      } catch (IOException e) {
      e.printStackTrace();
      }
      }

      public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {
      Map<String,Object> data = new HashMap();
      data.put(“name”,“鲁家宁”);
      String content = JavaToPdfHtmlFreeMarker.freeMarkerRender(data,HTML);
      JavaToPdfHtmlFreeMarker.createPdf(content,DEST);
      }

      /**

      • freemarker渲染html
        */
        public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
        Writer out = new StringWriter();
        try {
        // 获取模板,并设置编码方式
        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;
        }

      public static void createPdf(String content,String dest) throws IOException, DocumentException, com.lowagie.text.DocumentException {
      ITextRenderer render = new ITextRenderer();
      ITextFontResolver fontResolver = render.getFontResolver();
      fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
      // 解析html生成pdf
      render.setDocumentFromString(content);
      //解决图片相对路径的问题
      render.getSharedContext().setBaseURL(LOGO_PATH);
      render.layout();
      render.createPDF(new FileOutputStream(dest));
      }
      }
      输出结果:
      在这里插入图片描述

    在某些场景下,html中的静态资源是在本地,我们可以使用render.getSharedContext().setBaseURL()加载文件资源,注意资源URL需要使用文件协议 “file://”。
    对于生成的pdf页面大小,可以用css的@page属性设置。

    7.PDF转图片
    在某些场景中,我们可能只需要返回图片格式的电子凭证,我们可以使用Jpedal组件,把pdf转成图片
    添加依赖:

    org.jpedal jpedal-lgpl 4.74b27 测试代码:JavaToPdfImgHtmlFreeMarker package com.lujianing.test.flyingsaucer;

    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.pdf.BaseFont;
    import com.lujianing.test.util.PathUtil;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import org.jpedal.PdfDecoder;
    import org.jpedal.exception.PdfException;
    import org.jpedal.fonts.FontMappings;
    import org.xhtmlrenderer.pdf.ITextFontResolver;
    import org.xhtmlrenderer.pdf.ITextRenderer;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.util.HashMap;
    import java.util.Map;

    import javax.imageio.ImageIO;

    /**

    • Created by lujianing on 2017/5/7.
      */
      public class JavaToPdfImgHtmlFreeMarker {

      private static final String DEST = “target/HelloWorld_CN_HTML_FREEMARKER_FS_IMG.png”;
      private static final String HTML = “template_freemarker_fs.html”;
      private static final String FONT = “simhei.ttf”;
      private static final String LOGO_PATH = “file://”+PathUtil.getCurrentPath()+"/logo.png";
      private static final String IMG_EXT = “png”;

      private static Configuration freemarkerCfg = null;

      static {
      freemarkerCfg =new Configuration();
      //freemarker的模板目录
      try {
      freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
      } catch (IOException e) {
      e.printStackTrace();
      }
      }

      public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {
      Map<String,Object> data = new HashMap();
      data.put(“name”,“鲁家宁”);

       String content = JavaToPdfImgHtmlFreeMarker.freeMarkerRender(data,HTML);
       ByteArrayOutputStream pdfStream = JavaToPdfImgHtmlFreeMarker.createPdf(content);
       ByteArrayOutputStream imgSteam = JavaToPdfImgHtmlFreeMarker.pdfToImg(pdfStream.toByteArray(),2,1,IMG_EXT);
      
       FileOutputStream fileStream = new FileOutputStream(new File(DEST));
       fileStream.write(imgSteam.toByteArray());
       fileStream.close();
      

      }

      /**

      • freemarker渲染html
        */
        public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
        Writer out = new StringWriter();
        try {
        // 获取模板,并设置编码方式
        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;
        }

      /**

      • 根据模板生成pdf文件流
        */
        public static ByteArrayOutputStream createPdf(String content) {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        ITextRenderer render = new ITextRenderer();
        ITextFontResolver fontResolver = render.getFontResolver();
        try {
        fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } catch (com.lowagie.text.DocumentException e) {
        e.printStackTrace();
        } catch (IOException e) {
        e.printStackTrace();
        }
        // 解析html生成pdf
        render.setDocumentFromString(content);
        //解决图片相对路径的问题
        render.getSharedContext().setBaseURL(LOGO_PATH);
        render.layout();
        try {
        render.createPDF(outStream);
        return outStream;
        } catch (com.lowagie.text.DocumentException e) {
        e.printStackTrace();
        } finally {
        try {
        outStream.close();
        } catch (IOException e) {
        e.printStackTrace();
        }
        }
        return null;
        }

      /**

      • 根据pdf二进制文件 生成图片文件

      • @param bytes pdf二进制

      • @param scaling 清晰度

      • @param pageNum 页数
        */
        public static ByteArrayOutputStream pdfToImg(byte[] bytes, float scaling, int pageNum,String formatName) {
        //推荐的方法打开PdfDecoder
        PdfDecoder pdfDecoder = new PdfDecoder(true);
        FontMappings.setFontReplacements();
        //修改图片的清晰度
        pdfDecoder.scaling = scaling;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
        //打开pdf文件,生成PdfDecoder对象
        pdfDecoder.openPdfArray(bytes); //bytes is byte[] array with PDF
        //获取第pageNum页的pdf
        BufferedImage img = pdfDecoder.getPageAsImage(pageNum);

         ImageIO.write(img, formatName, out);
        

        } catch (PdfException e) {
        e.printStackTrace();
        } catch (IOException e){
        e.printStackTrace();
        }

        return out;
        }
        }
        输出结果:
        在这里插入图片描述

    Jpedal支持将指定页Pdf生成图片,pdfDecoder.scaling设置图片的分辨率(不同分辨率下文件大小不同) ,支持多种图片格式,具体更多可自行研究
    8.总结
    对于电子凭证的技术方案,总结如下:
    1.html模板+model数据,通过freemarker进行渲染,便于维护和修改
    2.渲染后的html流,可通过Flying Saucer组件生成pdf文件流,或者生成pdf后再转成jpg文件流
    3.在Web项目中,对应的文件流,可以通过ContentType设置,在线查看/下载,不需通过附件服务

    9.纯前端解决方案
    还有一种解决方案是使用PhantomJS
    git地址: https://github.com/ariya/phantomjs
    PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。 PhantomJS 可以用于 页面自动化 , 网络监测 , 网页截屏 ,以及 无界面测试 等。
    具体方法可自行查询。

    10:个人使用注意事项:freewarker中不支持span标签的使用

    展开全文
  • java生成pdf

    2018-12-03 10:06:34
    iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
  • java 根据html生成PDF

    热门讨论 2013-10-16 14:48:22
    flying saucer把html生成PDF,感觉还不错,支持css样式,itext 和 pd4ml好像对于复杂样式不能支持
  • * 基于freemarker生成pdf * Author:杨杰超 * Date:2019年11月15日 下午3:33:07 * Copyright (c) 2019, yangjiechao@dingtalk.com All Rights Reserved.<br/> * */ public class GenPdfTest { ...

    首先在maven中引入相关依赖

    		<dependency>
    			<groupId>org.freemarker</groupId>
    			<artifactId>freemarker</artifactId>
    			<version>2.3.22</version>
    		</dependency>
    		<dependency>
    			<groupId>org.xhtmlrenderer</groupId>
    			<artifactId>flying-saucer-pdf</artifactId>
    			<version>9.1.11</version>
    		</dependency>

    其中freemarker版本随意,flying-saucer-pdf架包建议使用最新的,因为老版本对html不友好,新版本做了很多兼容,还原度较高。

    干活在这里

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.StringWriter;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    
    import org.xhtmlrenderer.pdf.ITextRenderer;
    
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.pdf.BaseFont;
    
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.Version;
    
    /**
     * 基于freemarker生成pdf<br/>
     * Author:杨杰超<br/>
     * Date:2019年11月15日 下午3:33:07 <br/>
     * Copyright (c) 2019, yangjiechao@dingtalk.com All Rights Reserved.<br/>
     *
     */
    public class GenPdfTest {
    
        /**
         * 生成测试数据
         */
        private static Map<String, Object> newData() {
            Map<String, Object> data = new HashMap<String, Object>();
            // 存入一个集合
            List<Object> cureseList = new ArrayList<Object>();
            cureseList.add(newCureseData("▲公共必修课1", "数理统计", "1/春", "2.5", "85"));
            cureseList.add(newCureseData("▲公共必修课2", "数理统计", "1/春", "2.5", "85"));
            cureseList.add(newCureseData("▲公共必修课3", "数理统计", "1/春", "2.5", "85"));
            cureseList.add(newCureseData("▲公共必修课4", "数理统计", "1/春", "2.5", "85"));
            cureseList.add(newCureseData("▲公共必修课5", "数理统计", "1/春", "2.5", "85"));
            // 课程加权平均成绩
            String avgAchievement = "-";
            String stuName = "Jc";
            String stuNo = "1111111";
            String stuGrade = "2019";
            String stuType = "研究生";
            String stuCollege = "软件学院";
            String stuTeacher = "何XX";
            String stuMajor = "计算机科学与技术";
    
            data.put("courseList", cureseList);
            data.put("avgAchievement", avgAchievement);
            data.put("stuName", stuName);
            data.put("stuNo", stuNo);
            data.put("stuGrade", stuGrade);
            data.put("stuType", stuType);
            data.put("stuCollege", stuCollege);
            data.put("stuTeacher", stuTeacher);
            data.put("stuMajor", stuMajor);
    
            return data;
        }
    
        /**
         * 生成测试数据
         * 
         * @param type
         * @param name
         * @param semester
         * @param credit
         * @param achievement
         * @return
         */
        private static Map<String, Object> newCureseData(String type, String name, String semester, String credit,
                String achievement) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("type", type);
            map.put("name", name);
            map.put("semester", semester);
            map.put("credit", credit);
            map.put("achievement", achievement);
            return map;
        }
    
        public static void main(String[] args) throws Exception {
    
            Map<String, Object> data = newData();
    
            // 根路径
            String path = GenPdfTest.class.getResource("/").getPath().substring(1);
            // 模板名称
            String ftlName = "pdftest.ftl";
            // 图片路径
            String imageDiskPath = path + "imgs/";
            // 文件生成全路径
            String outputFile = "D:\\pdftest.pdf";
    
            generateToFile(path, ftlName, imageDiskPath, data, outputFile);
        }
    
        /**
         * 获取freemarker模板对象
         * 
         * @param ftlPath
         *            FTL模板路径
         * @param ftlName
         *            FTL模板名称
         * @return
         * @throws IOException
         */
        public static Template generateTemplate(String ftlPath, String ftlName) throws IOException {
            Configuration config = new Configuration(new Version("2.3.22"));
            config.setDirectoryForTemplateLoading(new File(ftlPath));
            config.setEncoding(Locale.CHINA, "UTF-8");
    
            Template template = config.getTemplate(ftlName);
            return template;
        }
    
        /**
         * 获取ITextRenderer渲染器
         * 
         * @param path
         *            根路径
         * @return
         * @throws DocumentException
         * @throws IOException
         */
        public static ITextRenderer generateITextRenderer(String path) throws DocumentException, IOException {
            ITextRenderer render = new ITextRenderer();
            // 添加字体,以支持中文
            render.getFontResolver().addFont(path + "fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            render.getFontResolver().addFont(path + "fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
    
            return render;
        }
    
        /**
         * 生成PDF到文件
         * 
         * @param ath
         *            根路径
         * @param ftlName
         *            模板文件吗(不含路径)
         * @param imageDiskPath
         *            图片的磁盘路径
         * @param data
         *            数据
         * @param outputFile
         *            目标文件(全路径名称)
         * @throws Exception
         */
        public static void generateToFile(String path, String ftlName, String imageDiskPath, Object data, String outputFile)
                throws Exception {
    
            Template tpl = generateTemplate(path + "ftl/", ftlName);
    
            StringWriter writer = new StringWriter();
            tpl.process(data, writer);
            writer.flush();
            String html = writer.toString();
    
            ITextRenderer render = generateITextRenderer(path);
    
            OutputStream out = new FileOutputStream(outputFile);
            render.setDocumentFromString(html);
            if (imageDiskPath != null && !"".equals(imageDiskPath)) {
                render.getSharedContext().setBaseURL("file:/" + imageDiskPath);
            }
            render.layout();
            render.createPDF(out);
            render.finishPDF();
            render = null;
            out.close();
        }
    }
    

    freemarker模板ftl文件 print.ftl

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    
        <style type="text/css">
        body {
            font-family: Arial Unicode MS;
            background-image: url(BGforPrint.gif);
            width:650px;
            height:978px;
        }
        table,table tr th, table tr td { border: 1px solid #000000; }
        table { width: 100%; text-align: center; border-collapse: collapse;}   
        </style>
    </head>
    
    <body>
        <center>
            <strong><span><font size="5">XXXX成绩表</font></span></strong>
     
            <br/>
            <br/>
    
            <table>
                <tr>
                    <td>姓名</td>
                    <td>${stuName}</td>
                    <td>学号</td>
                    <td>${stuNo}</td>
                    <td>年级</td>
                    <td>${stuGrade}</td>
                </tr>
                <tr>
                    <td>学生类型</td>
                    <td>${stuType}</td>
                    <td>学院</td>
                    <td colspan="3">${stuCollege}</td>
                </tr>
                <tr>
                    <td>指导教师</td>
                    <td>${stuTeacher}</td>
                    <td>专业</td>
                    <td colspan="3">${stuMajor}</td>
                </tr>
            </table>
            <br/>
            <table>
                <tr>
                    <td colspan="7">成绩表</td>
                </tr>
                <tr>
                    <td>课程类别</td>
                    <td colspan="3">课程名称</td>
                    <td>上课学年/学期</td>
                    <td>学分</td>
                    <td>成绩</td>
                </tr>
                
                <#list courseList as course>
                <tr>
                    <td>${course.type}</td>
                    <td colspan="3">${course.name}</td>
                    <td>${course.semester}</td>
                    <td>${course.credit}</td>
                    <td>${course.achievement}</td>
                </tr>
                </#list>    
                
            </table>
        </center>
    </body>
    </html>

    项目结构图:


    最终生成pdf:
    其中使用到了arialuni.ttf与simsum.ttc2个字体文件,这个在网上找找吧,就不放上来了。

    就是这样,搞完收工~

    展开全文
  • javahtml生成pdf

    2020-06-29 15:33:28
    html生成pdf使用itext尝试将html生成图片使用wkhtmltox 使用itext <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.4.3<...

    使用itext

    <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itextpdf</artifactId>
                <version>5.4.3</version>
            </dependency>
            <dependency>
                <groupId>com.itextpdf.tool</groupId>
                <artifactId>xmlworker</artifactId>
                <version>5.5.5</version>
            </dependency>
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext-asian</artifactId>
                <version>5.2.0</version>
            </dependency>
            <dependency>
                <groupId>org.xhtmlrenderer</groupId>
                <artifactId>core-renderer</artifactId>
                <version>R8</version>
            </dependency>
    

    java代码

    public void downPdf() {
            PdfUtils pdfUtils = new PdfUtils();
            //html文件路径
            String htmlFilePath = "/testechart.html";
            // 中文字体存储路径
            String chineseFontPath = "/simsun.ttc";
            String fileName = "2.pdf";
            OutputStream outputStream;
            outputStream = null;
            try {
                // 防止中文乱码
                fileName = URLEncoder.encode(fileName, "UTF-8");
                //生成pdf文件
                pdfUtils.html2pdf(htmlFilePath,"/2.pdf",chineseFontPath);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    import com.lowagie.text.pdf.BaseFont;
    import lombok.extern.slf4j.Slf4j;
    import org.xhtmlrenderer.pdf.ITextFontResolver;
    import org.xhtmlrenderer.pdf.ITextRenderer;
    
    import java.io.*;
    import java.net.MalformedURLException;
    
    /**
     * @author :zyt
     * @date :Created in 2020-06-24 11:12
     * @description:
     * @modified By:
     * @version: 1.0.0
     */
    @Slf4j
    public class PdfUtils {
        public static void html2pdf(String htmlFile, String pdfFile, String chineseFontPath)  {
            // step 1
            String url;
            OutputStream os = null;
            try {
                url = new File(htmlFile).toURI().toURL().toString();
                os = new FileOutputStream(pdfFile);
                ITextRenderer renderer = new ITextRenderer();
    
                renderer.setDocument(url);
                // 解决中文不显示问题
                ITextFontResolver fontResolver = renderer.getFontResolver();
                fontResolver.addFont(chineseFontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
                renderer.layout();
                renderer.createPDF(os);
            } catch (MalformedURLException e) {
                System.out.println(e.toString());
            } catch (FileNotFoundException e) {
                System.out.println(e.toString());
    
            } catch (com.lowagie.text.DocumentException e) {
                System.out.println(e.toString());
    
            } catch (IOException e) {
                System.out.println(e.toString());
    
            } finally {
                if(os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        System.out.println(e.toString());
                    }
                }
            }
        }
    
    }
    

    普通html都可以转,大家可以手动试试,但是有一个问题,echart类型的图表不能直接转,因为echart本身不支持导出pdf,只有将echart在网页上渲染过之后,才可以获取渲染后的数据再生成pdf。

    尝试将html生成图片

    既然echart不能生成pdf,那么是否可以生成图片呢?尝试一下吧,代码如下

         <dependency>
                <groupId>gui.ava</groupId>
                <artifactId>html2image</artifactId>
                <version>0.9</version>
            </dependency>
    
    public void downImage() throws FileNotFoundException, MalformedURLException {
            //html文件路径
            String htmlFilePath = "/testechart3.html";
    //imageHtml为获取的html源码字符串
            HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
    
           URL url = new File(htmlFilePath).toURI().toURL();
    
            String imageName = "/echart"+".png";
            imageGenerator.loadUrl(url);//也可以根据html url引用 loadUrl的方式加载
            //Thread.sleep(1000); //有时会有加载图片延迟,因此这里设置下延时
            imageGenerator.getBufferedImage();
            //Thread.sleep(2000);
            imageGenerator.saveAsImage(imageName);
    
        }
    

    经过尝试,echart依然不能生成图片,唯一使用echart生成图片的方式为
    https://blog.csdn.net/tjj3027/article/details/80421170?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-9.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-9.nonecase
    从前端获取echart的base64码,传回后端再生成图片。

    使用wkhtmltox

    这个是个额外的软件,windows,mac os,linux的版本都有,官网地址为:https://wkhtmltopdf.org/downloads.html
    在这里插入图片描述我尝试过mac os和linux CentOS7的,效果都还不错
    mac os的直接下载安装包,安装即可。
    linux的需要的依赖则比较多,步骤如下,官网上面比较难下载,我会把我用到的安装包都放在我的资源中,大家可以自行获取。

    1.安装
    rpm -ivh wkhtmltox-0.12.6-1.centos7.x86_64.rpm
    2.安装依赖字体
    yum install xorg-x11-fonts-75dpi.noarch
    3.生成pdf
    报错:/lib64/libcrypto.so.10: version `OPENSSL_1.0.2’ not found (required by wkhtm)
    升级OPENSSL版本
    4.强迫安装
    rpm -ivh openssl-libs-1.0.2k-12.el7.x86_64.rpm —force
    5.导出全部乱码
    sudo yum install urw-fonts
    数字可以显示
    6.中文没有展示
    拷贝simsun.ttc到centos的/usr/share/fonts/

    展开全文
  • java 生成pdf

    2021-05-18 14:26:16
    技术选型,java生成pdf最终选择了,itext7+pdfhtml+freemarker, freemarker是个模板引擎用来填充数据; pdfhtml,是itext官方套件,用来将html转为pdf itext是itext套件的核心、基础组件,底层是它来生成pdf; 技术...

    java生成pdf技术选型

    技术选型

    java生成pdf最终选择itext7+pdfhtml+freemarker。为什么使用这个组合呢?生成pdf,通常会用html转pdf,这是因为html+css便于调节样式,否则使用原生库,用java直接生成pdf,太繁琐,改一点样式都要改java代码;所以首先要用html,那就要找个模板引擎将数据填充到html,如果不用模板引擎,用js填充的话,就得用到浏览器引擎解析js,js发送http request获取数据,挺麻烦的,所以不如用模板引擎;有了模板引擎,还要有html-pdf转化器,可以将填充后的html转化为pdf;基于以上分析,必须要用到html+css、模板引擎、html-pdf转化器,相应的最终选择itext7+pdfhtml+freemarker。

    • itext7是itext的最新版本,功能丰富并较itext5做了很大优化;
    • pdfhtml是itext的套件之一,底层依赖itext,它支持html5、css3,能将html转换为pdf;
    • freemarker是apache的模板引擎,用来将数据填充到html;

    itext7+pdfhtml+freemarker vs flyingsaucer+itext5 vs wkhtmlTopdf

    • flyingsaucer与pdfhtml作用相同,用来将html转为pdf,但只支持css2,并且底层依赖itext5(最新的是itext7),非官方的,所以放弃;
    • wkhtmlTopdf使用浏览器引擎解析html,然后将html转换为pdf,所以还原度较高,但是命令行式的,并且要预先安装,每次导出pdf就是执行一次shell命令,这就等于从jvm开了一个进程,从性能、繁琐性考虑,放弃;

    字体

    itext分为3种字体,Standard Type 1 Fonts (14种)、shipped fonts(12种)、系统字体,Standard Type 1 Fonts 因为版权问题不允许嵌套到pdf中,

    Standard Type 1 Fonts

    字体 变种
    Times Times-Roman
    Times Times-Bold
    Times Times-Italic
    Times Times-BoldItalic
    Helvetica Helvetica
    Helvetica Helvetica-Bold
    Helvetica Helvetica-Oblique
    Helvetica Helvetica-BoldOblique
    Courier Courier
    Courier Courier-Bold
    Courier Courier-Oblique
    Courier Courier-BoldOblique
    Symbol Symbol
    ZapfDingbats ZapfDingbats

    可以看到Times 、Helvetica、Courier每种都有4中变种分别是常规、加粗、倾斜、加粗倾斜,共12种,再加上Symbol、ZapfDingbats一共14种,这些字体只包含了西方字符,所以用这些字体生成汉语肯定是不显示的;因为版权问题,这些字体不能嵌套到pdf中,itext假设pdf 阅读器会搭载这些字体或者操作系统有这些字体;

    shipped fonts

    shipped fonts含有sans 、serif 、monospaced 3种字体,类似的每种有4种变体,常规字体、加粗字体、倾斜字体、加粗倾斜字体,默认嵌套。

    系统字体

    系统字体指的是,操作系统上的字体,比如在windows中,字体路径是C:\Windows\Fonts,最好不要注册系统字体,因为会加载很多字体,这会消耗资源且顺序不可控,所以,在不同操作系统上,pdf可能表现不一样。

    html生成pdf关键代码

    springboot中,生成pdf的核心代码

    ConverterProperties converterProperties = new ConverterProperties();
          DefaultFontProvider fontProvider = new DefaultFontProvider(false,
                  false,false,"simfang");
          Class<MyController> myControllerClass = MyController.class;
          logger.info("TreatmentController的ClassLoader是:{}",myControllerClass.getClassLoader());
          String fontPath = myControllerClass.getResource("/simfang.ttf").toString();
          fontProvider.addFont(fontPath);
          //注意templates以/结尾表示文件夹
           String baseUri = myControllerClass.getResource("/templates/").toString();
          logger.info("itext的baseUri是:{}",baseUri);
          converterProperties.setBaseUri(baseUri);
          converterProperties.setFontProvider(fontProvider);
          HtmlConverter.convertToPdf(byteArrayInputStream,
                  response.getOutputStream(), converterProperties);
    

    注意,

    DefaultFontProvider(false,
                  false,false,"simfang");
    

    利用了构造方法

    DefaultFontProvider(registerStandardPdfFonts,
                  registerShippedFonts,registerSystemFonts,defaultFontFamily);
    

    表示不注册标准字体、不注册搭载字体、不注册系统字体、默认字体是仿宋,推荐这样使用,可确保字体的唯一性。

    设置baseuri

    springboot中设置图片、字体等的baseuri,注意文件夹以/结尾

    //注意templates以/结尾表示文件夹
           String baseUri = myControllerClass.getResource("/templates/").toString();
          logger.info("itext的baseUri是:{}",baseUri);
    

    常见问题

    pdf分页

    pdf分页用css来实现,在想分页的地方添加

    page-break-after:always;
    

    pdf页码和页脚

    用css来实现

     @page {
        @bottom-left {
            font-size: 12px;
            content: "这就是页脚";
        }
        @bottom-right {
            /*这是当前页、总页数*/
            content: counter(page) "/" counter(pages);
        }
        margin-bottom: 70px;
    }
    

    在这里插入图片描述

    如果首页不想要页脚和页码,则利用:first伪元素

    @page:first{
       @bottom-left {
           content: none;
       }
       @bottom-right {
           content: none;
       }
    }
    

    页码不正确,多了一页

    可能是在最后一页,设置了分页导致,去掉就好了。

    pdf不显示汉字、乱码

    • 不显示,不显示有两种原因

      • 字体用的不对。不显示是因为字体用的不对,比如用了Helvetica字体,这个字体只有英语字母、英文符号等字符,汉字用这个字体,肯定不会显示。
      • 字体未嵌入到pdf,字体包含目标字符,但未嵌入到pdf中,在其他电脑上打开时,恰巧这个电脑没有所使用的字体那么也不会显示,这个时候就要将字体内嵌到pdf中,当然pdf阅读器也可能使用相近字体替代,这时“替代字体”就是“实际字体”;
    • 乱码
      乱码指的是,对字体使用了错误的编码。比如仿宋,你用Helvetica去编码肯定不行。编码指的是字符编码,iso8859-1、gbk、unicode之类的

    怎样查看pdf字体?

    • 查看pdf使用的全部字体

      • 使用Adobe Acrobat打开pdf,查看pdf字体,文件---->属性—>字体或者使用快捷键ctrl+d
      • 在这里插入图片描述
    • 查看某个文字或者段落使用的字体

      • 在选中目标文字,右击编辑文本,右侧显示字体。
      • 在这里插入图片描述

    windows上生成的pdf与linux生成的pdf,字体不一样

    查看pdf字体,发现使用了很多字体,怀疑是用了系统字体,将registerSystemFonts设置为false后修复。

    type1、ttf、ottf、cid名字解释

    • type1就是上文提到的Standard Type 1 Fonts,是adobe公司推出的,也叫PostScript Type 1 简称ps1,不再推荐使用,基于adobe pdf阅读器已经搭载了该字体的假设,不允许内嵌到pdf。
    • true type是apple和微软开发的,晚于type1,并且逐渐成为主流,adobe目前转向true type,比type1支持更多的字符集、布局,ttf就是 true type font的缩写。
    • open type 跨平台的, Adobe and Microsoft出品,adobe已经将他的字体库全部转为open typeotf就是open type font的缩写,跨平台支持苹果和windows,支持更多的字符集和排版特性。基于true type的open type字体结尾是ttf
    • eof= Embedded OpenType,它是对OpenType 字体的压缩版,被微软设计用来在web网页中嵌入使用。只有微软的IE浏览器支持,跟WOFF正好相反
    • woff=Web Open Font Format ,在web页面中的一种字体格式,它其实是 OpenType 或者 TrueType被压缩了并且带有一些元数据。woff主要有两个目的,第一它是专门用来当作web字体的而不是桌面字体,第二减少web字体在网络传输时的延迟。woff2是woff的下一代字体,比woff平均大小能小30%,传输速度更快。
    • cid是character id的意思,一般是type1、true type
    • type1、ttf、otf、woff、woff2的不同

    文档

    itext官网---->developer---->knowledge base—>Ebooks

    https://stackoverflow.com/questions/83320/what-is-the-difference-between-truetype-fonts-and-type-1-fonts

    展开全文
  • Java生成PDF文档

    2019-12-04 13:16:44
    背景 需求内容生成xlxs和pdf 过程 把xlxs转成pdf java 实现Word或Excel 转Pdf 利用java实现excel转pdf...Java生成PDF文档 iText使用PDF模板一 java根据模板导出pdf 动态生成数据太麻烦。 java根据模板HTML动态...
  • java 中实现HTML 生成pdf

    千次阅读 2017-01-23 10:44:09
    JAVA生成pdf 的方法很多,iText是一个生成PDF文档的开源Java库。但是用iText生成pdf,有时候很难控制页面的样式。听说flying-saucer 可以对样式进行简单的支持。由于项目需要,用flying-saucer 体验 了一把,感觉...
  • Java 生成 pdf

    千次阅读 2019-01-12 21:34:04
    Java 可以通过 IText 直接把内容生成 pdf 文件,下面的 Demo 主要演示了文件属性、页眉页脚、表格、段落文字、图片的生成。 先上代码: package html2PDF; import java.io.File; import java.io....
  • JAVA將頁面HTML生成PDF

    2013-07-22 11:09:44
    個人上傳以備參考 JAVA 將頁面 HTML 生成 PDF
  • java批量生成pdf

    2021-02-07 11:49:50
    生成pdf 到年底了,公司是不是要求开始生成大量的pdf支持预览,下载,批量生成,烧脑的想法难为烧脑的人!!! 一:实现pdf预览 ...java 生成pdf的方式有很多itext,freeemark,无奈需求需要按照不同pdf模版生产pdf, ...
  • Java代码示例: tools.pdf.ITextTest xhtml代码示例: template.xhtml
  • java 读取html生成pdf

    2016-04-21 15:51:52
    import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import com.lowagie.text.pdf.BaseFont;   /** HTML文件路径*/ public static final String HTML = "D:\\" +
  • import org.anyline.net.HttpResult; import org.anyline.net.HttpUtil;...public class HtmlToPdf { /** * htmlpdf *toPdfTool:D\:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf * @param srcPath ...
  • java html生成pdf的问题

    2019-11-15 11:05:01
    使用的springboot+thyemleft模板+xmlworker+itext+...从第二行TableRowElement错误可以看出是因为模板生成html页面中table标签有问题,结果一看,果然<div> <tr th:each="v,status:${f.errors}"> &l...
  • Java生成PDF文件

    千次阅读 2019-06-27 13:59:11
    Java生成PDF文件 一、前言  最近在做也导出试卷的功能,刚开始是导出为doc,可是导出来格式都有变化,最后说直接将word转为pdf,可是各种不稳定,各种报错、最后想到直接将文件写入pdf(参考:...
  • java工程中将word格式模板类型在浏览器中以pdf格式下载或预览,使用thymeleaf前端框架,maven工程的demo。
  • java 生成pdf DEMO 开发过程中遇到的坑 /** * 切记 css 要定义在head 里,否则解析失败 * css 要定义字体 * 字体中英文 https://www.cnblogs.com/chbyiming-bky/articles/9789869.html * 例如宋体 style=“font-...
  • Java html模板生成PDF

    2019-10-09 03:43:18
    * 根据模板生成pdf * * @param data 传入到freemarker模板里的数据 * @param out 生成的pdf文件流 */ public void createPDF(Object data, OutputStream out) throws Exception { // 创建一个...
  • 转载自 Java实现HTML代码生成PDF文档1、IText实现html2pdf,速度快,纠错能力差,支持中文(要求HTML使用unicode编码),但中支持一种中文字体,开源。2、Flying Sauser实现html2pdf,纠错能力差,支持多种中文字体...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,155
精华内容 462
关键字:

html生成pdfjava

java 订阅