iText+freemarker 生成PDF 使用ftl模板

生成样式图片

根据模板生成

模板地址

在这里插入图片描述

maven

		<!--PDF  start-->
		<dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf</artifactId>
            <version>9.1.5</version>
        </dependency>
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-itext5</artifactId>
            <version>9.1.5</version>
        </dependency>
        <!--PDF  end-->
        <!--freemarker-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.26-incubating</version>
        </dependency>
        <!--条形码-->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.3</version>
        </dependency>

PDF生成工具类

import com.lowagie.text.DocumentException;
import freemarker.core.ParseException;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.IOException;
import java.io.OutputStream;

/**
 * @Description PDF生成工具类
 * @author GGBind
 * @date 2021/12/28/10:03
 */
public class PdfUtils {

    /**
     * 生成PDF到输出流中(ServletOutputStream用于下载PDF)
     *
     * @param ftlPath       ftl模板文件的路径(不含文件名)
     * @param ftlName       ftl模板文件的名称(不含路径)
     * @param imageDiskPath 如果PDF中要求图片,那么需要传入图片所在位置的磁盘路径
     * @param data          输入到FTL中的数据
     * @param out      HttpServletResponse
     * @return
     * @throws TemplateNotFoundException
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException
     * @throws DocumentException
     */
    public static OutputStream generateToServletOutputStream(String ftlPath, String ftlName, String imageDiskPath, Object data, OutputStream out) throws IOException, TemplateException, DocumentException {
        PdfHelper pdfHelper = new PdfHelper();
        String html =  pdfHelper.getPdfContent(ftlPath, ftlName, data);
        ITextRenderer render = null;
        render = pdfHelper.getRender();
        render.setDocumentFromString(html);
        if (null != imageDiskPath && !"".equals(imageDiskPath)) {
            //html中如果有图片,图片的路径则使用这里设置的路径的相对路径,这个是作为根路径
            render.getSharedContext().setBaseURL("file:/" + imageDiskPath);
        }
        render.layout();
        render.createPDF(out);
        render.finishPDF();
        return out;
    }
}

PDF生成辅助类

package com.saicmotor.maxus.yuejin.partorder.common.util.pdfUtil;

import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import lombok.extern.slf4j.Slf4j;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Locale;

/**
 * PDF生成辅助类
 * @author GGBind
 * @date 2022/2/09
 */
@Slf4j
@SuppressWarnings("deprecation")
public class PdfHelper {

    public ITextRenderer getRender() throws DocumentException, IOException {
        ITextRenderer render = new ITextRenderer();
        //添加字体,以支持中文
        render.getFontResolver().addFont("/static/fonts/NSimSun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        return render;
    }

    /**
     * 获取要写入PDF的内容
     * @param ftlPath
     * @param ftlName
     * @param o
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    public String getPdfContent(String ftlPath, String ftlName, Object o) throws IOException, TemplateException {
        return useTemplate(ftlPath, ftlName, o);
    }

    /**
     * 使用freemarker得到html内容
     * @param ftlPath
     * @param ftlName
     * @param o
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    public String useTemplate(String ftlPath, String ftlName, Object o) throws IOException, TemplateException {
        String html = null;
        Template tpl = getFreemarkerConfig(ftlPath).getTemplate(ftlName);
        tpl.setEncoding("UTF-8");
        StringWriter writer = new StringWriter();
        tpl.process(o, writer);
        writer.flush();
        html = writer.toString();
        return html;
    }

    /**
     * 获取Freemarker配置
     *
     * @param templatePath
     * @return
     * @throws IOException
     */
    private Configuration getFreemarkerConfig(String templatePath) throws IOException {
        Version version = new Version("2.3.22");
        Configuration config = new Configuration(version);
        config.setClassLoaderForTemplateLoading(Thread.currentThread().getContextClassLoader(), "/templates");
        config.setEncoding(Locale.CHINA, "utf-8");
        return config;
    }


    /**
     * 获取类路径
     *
     * @return
     */
    public String getPath() {
        return PdfHelper.class.getResource("/").getPath().substring(1);
    }

	/**
     * 获取前端response的输出流
     * 并设置response 编码格式
     * @return 输出流out
     */
    public ServletOutputStream setResponse(HttpServletResponse response, String Name) throws Exception{
        response.setContentType("application/pdf");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename="+ new String(Name.getBytes("utf-8"), "ISO8859-1") + ".pdf");
        ServletOutputStream out = response.getOutputStream();
        return out;
    }

}

base64编码工具类

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;

/**
 * @Description
 * @Author GGBond
 * @Date 2022/2/14
 */
@Slf4j
public class BarcodeUtil {

    /**
     * 给前端产生一个条形码
     *
     * @param number 编码
     * @param width  宽度
     * @param height 高度
     */
    public static String getCode(String number, Integer width, Integer height) {
        // 生成条形码
        BufferedImage image = getBarCode(number, width, height);
        // 使用流的方式
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", out);
        } catch (Exception e) {
             log.info("generate code error! error message:{}", "出现问题!");
            e.printStackTrace();
        }
        // 将流转成数组
        byte[] bytes = out.toByteArray();
        BASE64Encoder encoder = new BASE64Encoder();
        // 把生成的编码返回去
        return  encoder.encodeBuffer(bytes).trim();
    }

    /**
     * 产生条形码的方法
     *
     * @param number 编码
     * @param width  宽度
     * @param height 高度
     */
    public static BufferedImage getBarCode(String number, Integer width, Integer height) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(number, BarcodeFormat.CODE_128, width, height);
            return MatrixToImageWriter.toBufferedImage(bitMatrix);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 调用测试
     */
    public static void main(String[] args) {
        String code = getCode("1120220202", 50, 50);
        System.out.println(code);
    }
}

service方法调用

PdfHelper pdfHelper = new PdfHelper();
        List<StockCheckDetailPdf> lists = baseMapper.getStockCheckDetailPdf(dto);
        ServletOutputStream out = pdfHelper.setResponse(response, "盘点单据");
        String path = pdfHelper.getPath();
        Map<String,Object> map = new HashMap<>(16);
        map.put("list",lists);
        String code = BarcodeUtil.getCode(dto.getInventoryNumber(), 80, 20);
        map.put("picture", code);
        map.put("builder",builder.toString());
        map.put("inWarehouse",documentCounting.getInWarehouse());
        map.put("printDate",String.valueOf(DateUtil.getCurrentDate()));
        //这里我们需要将生成的base64图片编码传到工具类里,进行单独设置,静态图片也是如此
        PdfUtils.generateToServletOutputStream(path, "/documentCountingPDF.ftl", code, map, out);
        out.flush();
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>PDF</title>
    <style>
    	/*设置编码格式*/
        body {
            font-family: NSimSun
        }
		/*设置纸张大小*/
        @page {
            size: a4
        }

        @media print {
            div.header-right {
                display: block;
                position: running(header-right);
            }
			/*设置每页结束的是表格,还是图片,还是其他的*/
            /*img{page-break-inside:avoid;}*/
            table {
                page-break-inside: avoid;
            }
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        section {
            display: block;
            margin: 20px 10px;
        }

        .preface p {
            line-height: 30px;
        }

        section > table {
            table-layout: fixed;
            width: 100%;
            margin: 20px 0px;
            text-align:center;
            word-wrap:break-word;
            border-collapse: collapse;
            border-style: solid;
            border-width: 1px 0px 0px 1px;
        }

        section table td {
            padding: 5px 0px;
            border-style: solid;
            border-width: 0px 1px 1px 0px;
            margin: 0;
            padding: 0;
        }

        .center {
            width: 5.8%;
        }
        .part {
            width: 159.1px;
            word-break: keep-all;
            white-space: nowrap;
        }
    </style>
</head>
<body>
<section>
	<#--该list 是由service层的map对应,数据使用占位符去匹配${}-->
    <#list list as item>
        <#if item_index % 14 == 0 || item_index == 0>
            <table cellSpacing="0" cellpadding="0" border="1" width="650px" align="center" >
            <colgroup>
                <col class="center"></col>
                <col class="part"></col>
                <col class="part"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col class="center"></col>
                <col style="width: 70px;word-break: keep-all;white-space: nowrap"></col>
            </colgroup>
        </#if>
        <#if item_index == 0>
            <tr style="width: 100%;">
                <td class="center" colspan="2"><img src="data:image/png;base64,${picture}"/></td>
                <td class="center" colspan="7"><h4>实物盘点记录表</h4></td>
                <td class="center" colspan="7"><h1>${builder}</h1></td>
            </tr>
            <tr style="width: 100%;">
                <td class="center" colspan="4">填报单位:</td>
                <td class="center" colspan="2">存货类别:</td>
                <td class="center" colspan="9">仓库名称(代码): ${inWarehouse}</td>
                <td style="word-break: keep-all;white-space: nowrap;">${printDate}</td>
            </tr>
        </#if>
        <#if item_index % 14 == 0 || item_index == 0>
            <tr style="width: 100%;">
                <td class="center" rowspan="2">序号</td>
                <td class="part" rowspan="2">件号</td>
                <td class="part" rowspan="2">名称</td>
                <td class="center" rowspan="2">规格</td>
                <td class="center" rowspan="2">项目类</td>
                <td class="center" rowspan="2">计量单位</td>
                <td class="center" rowspan="2">外借量</td>
                <td class="center" rowspan="2">库位</td>
                <td class="center" rowspan="2">合计</td>
                <td class="center" rowspan="2">货位</td>
                <td class="center" colspan="3">实盘数量</td>
                <td class="center" rowspan="2">盘点人</td>
                <td class="center" rowspan="2">监盘人</td>
                <td class="center" rowspan="2">备注</td>
            </tr>
            <tr style="width: 100%;">
                <td class="center">合计</td>
                <td class="center">企业库存</td>
                <td class="center">供应商库存</td>
            </tr>
        </#if>
        <tr style="width: 100%;">
            <td class="center">${item_index +1}</td>
            <td class="part">${item.accessoryItemNo}</td>
            <td class="part">
                <#if item.partsChineseName?length < 8 >
                    ${item.partsChineseName}
                <#else>
                    ${item.partsChineseName?substring(0,7)}
                </#if>
            </td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">${item.unit}</td>
            <td class="center">
                ${item.lendingVolume?default(0)}
            </td>
            <td class="center">
                <#if item.atWarehouse?exists>
                    ${item.atWarehouse!}
                </#if>
            </td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">${item.theoreticalInventory}</td>
            <td class="center">${(item.theoreticalInventory?default(0)) - (item.lendingVolume?default(0))}</td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">&nbsp;&nbsp;</td>
            <td class="center">
                <#if item.remark?exists>
                    ${item.remark!}
                </#if>
            </td>
        </tr>
        <#--用来判断每页展示多少条数据,最后需要加上 “table” 标签-->
        <#if (item_index+1) % 14 == 0 || (item_index+1) == list?size>
                </table>
        </#if>
    </#list>
</section>
</body>
</html>

总结

需要注意的是:当前此方法不能控制表格中一行数据变长,自动向下延申后,每页的格式问题;

例如: 当前页14条数据占满本页后,如果表格中字段较长,14行表格会向下延申到第二页,甚至第三页,才会进行下一个表格的展示;目前本方案解决办法是将一页展示的数量变小了,给预留出足够的空白页让其展示;目前还在寻找方法中
因备注字数过长,分页控制就失效了
在这里插入图片描述

目前为止如果表格不够复杂,是够用的,其他的问题暂时还没碰到;

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,您想了解关于使用iTextFreeMarker生成HTML并将其转换为Word文档的内容。首先,您需要了解几个概念: 1. iText是一个用于生成PDF文档的Java库,但它也可以用于生成其他类型的文档,例如Word文档。 2. FreeMarker是一个用于生成动态内容的Java模板引擎,它可以将数据模型与模板文件结合使用生成静态文本文件。 接下来,您可以按照以下步骤来实现将HTML转换为Word文档: 1. 创建一个FreeMarker模板文件,其中包含您要生成的HTML内容,并将其保存为.ftl文件。 2. 在Java代码中,使用FreeMarker来读取.ftl文件并将数据模型填充到模板中,生成HTML字符串。 3. 使用iText将HTML字符串转换为Word文档。您可以使用iText的XMLWorkerHelper类来实现此功能。 以下是一个简单的示例代码,用于将HTML字符串转换为Word文档: ``` // Step 1: Load the FreeMarker template Configuration cfg = new Configuration(Configuration.VERSION_2_3_28); cfg.setDirectoryForTemplateLoading(new File("templates")); Template template = cfg.getTemplate("my_template.ftl"); // Step 2: Generate HTML string from the template and data model Map<String, Object> data = new HashMap<>(); data.put("name", "John Doe"); StringWriter out = new StringWriter(); template.process(data, out); String html = out.getBuffer().toString(); // Step 3: Convert HTML to Word document using iText ByteArrayOutputStream baos = new ByteArrayOutputStream(); Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, baos); document.open(); XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(html.getBytes())); document.close(); // Save the Word document to file FileOutputStream fos = new FileOutputStream("output.docx"); fos.write(baos.toByteArray()); fos.close(); ``` 请注意,此示例代码仅用于说明概念,并且可能需要进行修改以适合您的特定需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值