java使用freemarker模板生成html,再生成pdf

1.freemarker模板生成html

  1. 添加Maven依赖

在pom.xml文件中添加以下依赖:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
  1. 创建Freemarker模板

新建一个HTML文件,例如table.ftl,然后在其中编写HTML模板,包括表格的头部、内容和尾部等部分。具体可以参考下面这个例子:

<table border="1">
    <thead>
        <tr>
            <th>IDth>
            <th>Nameth>
            <th>Ageth>
        tr>
    thead>
    <tbody>
    <#list users as user>
        <tr>
            <td>${user.id}td>
            <td>${user.name}td>
            <td>${user.age}td>
        tr>
    #list>
    tbody>
table>

上面的模板中使用了Freemarker的指令语法来实现动态生成表格内容,包括使用<#list>标签来遍历用户列表,并使用${}语法来输出用户信息。

  1. 创建Controller方法

在Controller中编写一个方法来获取用户列表,然后渲染上面的模板并返回HTML内容。示例代码如下:

@Controller
public class UserController {
    @GetMapping("/users")
    public String userList(Model model) {
        List<User> users = new ArrayList<>();
        users.add(new User(1, "Tom", 18));
        users.add(new User(2, "Jerry", 20));
        users.add(new User(3, "John", 22));
        model.addAttribute("users", users);
        return "table";
    }
}

上面的方法使用@GetMapping注解来处理请求,然后创建一个用户列表,并将其添加到模型中。最后返回table字符串,代表要使用的HTML模板文件。

  1. 运行项目

运行Spring Boot应用程序,然后使用浏览器访问http://localhost:8080/users,即可看到动态生成的HTML表格。

注意:上面的例子仅供参考,实际应用中需要根据自己的需求进行修改扩展。

2.利用iText将生成的HTML转换为PDF文件

理解了freemarker生成html的步骤以后,就可以利用iText把html生成pdf文件了。

  1. 编写转换代码
    添加Maven依赖
    在pom.xml文件中添加以下依赖:
<dependency>
<groupId>com.itextpdf.toolgroupId>
	<artifactId>xmlworkerartifactId>
	<version>5.5.1version>
dependency>

<dependency>
	<groupId>com.itextpdfgroupId>
	<artifactId>itext-asianartifactId>
	<version>5.2.0version>
dependency>


<dependency>
	<groupId>org.xhtmlrenderergroupId>
	<artifactId>flying-saucer-pdf-itext5artifactId>
	<version>9.0.9version>
dependency>
 
  1. 编写转换代码
    在SpringBoot应用程序中创建一个Service或者Controller类,然后编写HTML转PDF的代码。

freeMarker转换为html的方法:

public class HtmlGenerator {

    public static String generate(String template, Map<String, Object> variables) throws IOException, TemplateException, IOException {
     
        Configuration config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置
        config.setClassForTemplateLoading(HtmlGenerator.class, "/filePath");
        //读取模板文件地址
        config.setDefaultEncoding("UTF-8");
        //获取模板文件
        Template tp = config.getTemplate(template);
        StringWriter stringWriter = new StringWriter();
        BufferedWriter writer = new BufferedWriter(stringWriter);
        tp.setEncoding("UTF-8");
        //把map数据写入
        tp.process(variables, writer);
        String htmlStr = stringWriter.toString();
        writer.flush();
        writer.close();
        return htmlStr;
    }
}

/filePath为项目中的ftl文件相对路径。
html生成pdf的方法:

public class PdfDocumentGenerator {
    private static final Logger logger = LoggerFactory.getLogger(PdfDocumentGenerator.class);

	/**
	 * Output a pdf to the specified outputstream
	 * 
	 * @param htmlStr
	 *            the htmlstr
	 * @param out
	 *            the specified outputstream
	 * @throws Exception
	 */
	public static void generate(String htmlStr, OutputStream out)
			throws Exception {
        DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
        df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
        df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
        DocumentBuilder builder = df.newDocumentBuilder();
		org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(htmlStr
				.getBytes()));
		ITextRenderer renderer = new ITextRenderer();
		renderer.setDocument(doc, null);
		renderer.layout();
		renderer.createPDF(out);
		out.close();
	}
	
	public static void generatePlus(String htmlStr, OutputStream out) throws IOException, DocumentException {
		final String charsetName = "UTF-8";
		
		Document document = new Document(PageSize.A4, 30, 30, 30, 30);
        document.setMargins(30, 30, 30, 30);
        PdfWriter writer = PdfWriter.getInstance(document, out);
        document.open();
        
        // html内容解析
        HtmlPipelineContext htmlContext = new HtmlPipelineContext(
                new CssAppliersImpl(new XMLWorkerFontProvider() {
                    @Override
                    public Font getFont(String fontname, String encoding,
                                        float size, final int style) {
                        if (fontname == null) {
                       
                            fontname = getChineseFont();      
                        }
                        return super.getFont(fontname, encoding, size,
                                style);
                    }
                })) {
            @Override
            public HtmlPipelineContext clone()
                    throws CloneNotSupportedException {
                HtmlPipelineContext context = super.clone();
                try {
                    ImageProvider imageProvider = this.getImageProvider();
                    context.setImageProvider(imageProvider);
                } catch (NoImageProviderException e) {
                }
                return context;
            }
        };
        
        // 图片解析
        htmlContext.setImageProvider(new AbstractImageProvider() {
        	
        	String rootPath = PdfDocumentGenerator.class.getResource("/").getPath();
        	
            @Override
            public String getImageRootPath() {
            	return rootPath;
            }

            @Override
            public Image retrieve(String src) {
                if (StringUtils.isEmpty(src)) {
                    return null;
                }
                try {
                    Image image = Image.getInstance(new File(rootPath, src).toURI().toString());
                    // 图片显示位置
                    image.setAbsolutePosition(400, 400);		
                    if (image != null) {
                        store(src, image);
                        return image;
                    }
                } catch (Throwable e) {
                	e.printStackTrace();
                }
                return super.retrieve(src);
            }
        });
        htmlContext.setAcceptUnknown(true).autoBookmark(true).setTagFactory(Tags.getHtmlTagProcessorFactory());
        
        // css解析
        CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
        cssResolver.setFileRetrieve(new FileRetrieve() {
            @Override
            public void processFromStream(InputStream in,
                                          ReadingProcessor processor) throws IOException {
                try (
                        InputStreamReader reader = new InputStreamReader(in, charsetName)) {
                    int i = -1;
                    while (-1 != (i = reader.read())) {
                        processor.process(i);
                    }
                } catch (Throwable e) {
                }
            }

            // 解析href
            @Override
            public void processFromHref(String href, ReadingProcessor processor) throws IOException {
            	InputStream is = PdfDocumentGenerator.class.getResourceAsStream("/" + href);
                try (InputStreamReader reader = new InputStreamReader(is,charsetName)) {
                    int i = -1;
                    while (-1 != (i = reader.read())) {
                        processor.process(i);
                    }
                } catch (Throwable e) {
                	e.printStackTrace();
                }
            }
        });

        HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer));
        Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
        XMLWorker worker = null;
        worker = new XMLWorker(pipeline, true);
        XMLParser parser = new XMLParser(true, worker, Charset.forName(charsetName));
        try (InputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes())) {
            parser.parse(inputStream, Charset.forName(charsetName));
        }
        document.close();
	}

    /**
     * 获取中文字体位置 
     * @return
     */
    public static String getChineseFont() {
        String chineseFont = null;
        chineseFont = Object.class.getResource("/").getPath() + "font/simsun.ttc";
        if(!new File(chineseFont).exists()){
            throw new RuntimeException("字体文件不存在!"+chineseFont);
        }

        return chineseFont;
    }
}

运行测试方法:

public class Pdfdest {
    public static void main(String[] args) throws Exception {
        String outputFile = "d:/test1.pdf";
        Map<String, Object> map = new HashMap<>();
        map.put("XXX", "测试");
        //生成工具,下面有代码
        String htmlStr = HtmlGenerator.generate("test.ftl", map);
        //生成工具,下面有代码
        OutputStream out = new FileOutputStream(outputFile);
        PdfDocumentGenerator.generatePlus(htmlStr,out);
    }
}

准备一个test.ftl放到resource/filePath下,当然字体最好也放到resource/font下,运行时需要使用。
test.ftl的代码如下:

DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">meta>
    <style>

        @page {
            @top-center { content: element(header) }
        }
        @page {
            @bottom-center { content: element(footer) }
        }
        .apply {
            margin: 0 auto;
            padding: 0 30px;
        }
        .title {
            margin-top: 40px ;
            text-align: center;
            font-weight: bold;
        //字体需要和后台对应上
        font-family: SimSun;
            font-weight: bold;
            font-size: 20px;
            color: #333333;
            letter-spacing: 0;
        }
        .table {
            border-collapse: collapse;
            width: 100%;
            margin-top: 30px;
            font-family: SimSun;
            font-size: 14px;
            color: #111111;
            letter-spacing: 0.54px;
        }
        .label {
            background-color: #E6E6E6;
            width: 20%;
        }
        .normaltd {
            padding: 10px 0;
        }
        .maxtd {
            height: 250px;
        }
        .value {
            width: 30%;
            padding-left: 10px;
        }
        .apply {
            margin: 0 auto;
            padding: 0 30px;
        }
        .title {
            margin-top: 40px ;
            text-align: center;
            font-weight: bold;
        //字体需要和后台对应上
        font-family: SimSun;
            font-weight: bold;
            font-size: 20px;
            color: #333333;
            letter-spacing: 0;
        }
        .table {
            width: 100%;
            margin-top: 30px;
            font-family: SimSun;
            font-size: 14px;
            color: #111111;
            letter-spacing: 0.54px;
        }
        .label {
            background-color: #E6E6E6;
            width: 20%;
        }
        .normaltd {
            padding: 10px 0;
        }
        .maxtd {
            height: 250px;
        }
        .value {
            width: 30%;
            padding-left: 10px;
        }

        tr {
            page-break-inside: avoid;
            page-break-after: auto;
        }
    style>
head>

<body style="font-family: SimSun">
<div class="apply">
    <p class="title">申请单p>
    <table border="1" cellspacing="0" class="table">
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">XXXtd>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr >
            <td valign="middle" colspan="1" class="label maxtd" align="center">XXXtd>
            <td valign="middle" colspan="3" class="maxtd value">${XXX}td>
        tr>
        <tr>
            <td colspan="1" class="label normaltd" align="center">XXXtd>
            <td colspan="3" class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td colspan="1" class="label normaltd" align="center">XXXtd>
            <td colspan="3" class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td colspan="1" class="label normaltd" align="center">XXXtd>
            <td colspan="3" class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">XXXtd>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr >
            <td valign="middle" colspan="1" class="label maxtd" align="center">XXXtd>
            <td valign="middle" colspan="3" class="maxtd value">${XXX}td>
        tr>
        <tr>
            <td colspan="1" class="label normaltd" align="center">XXXtd>
            <td colspan="3" class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td colspan="1" class="label normaltd" align="center">XXXtd>
            <td colspan="3" class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td colspan="1" class="label normaltd" align="center">XXXtd>
            <td colspan="3" class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">XXXtd>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
        <tr>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
            <td class="label normaltd" align="center">XXXtd>
            <td class="normaltd value">${XXX}td>
        tr>
    table>
div>
body>
html>

执行就可以看到生成的pdf文件了,文件路径在d:/test1.pdf。

你可能感兴趣的:(java,html,pdf,freemarker)