itext7 html转pdf实现
公司最近做一个交易所项目,里面涉及一个需求就是将html模板,在填充数据后转换为pdf,这样防止数据更改,下面是具体实现
1 pom文件
<dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.1.2</version> </dependency>
2 html转pdf
itext7进行html转换使用类:com.itextpdf.html2pdf.HtmlConverter
它主要有三类操作:convertToPdf直接转换为pdf文件
convertToDocument转为document文档,这样有利于进行pdf页面调整
convertToElements拆解pdf标签
我这里因为html转换后会有多页,这里通过convertToDocument调整页面大小,在一页上显示所有内容;
同时我使用ByteArrayOutputStream类,这个的好处是不在本地生成文件,减少磁盘操作,但是这种方式也有人说效率不高,使用者可以斟酌后使用.
因为spring boot存在打包后resources目录文件获取不到的问题,所以我将pdf依赖的字体文件放到项目的根路径下(跟src目录同级).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | package com.ssth.exchanage.excenter.common.uitls; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import com.itextpdf.html2pdf.ConverterProperties; import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; import com.itextpdf.kernel.colors.Color; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.kernel.pdf.canvas.draw.ILineDrawer; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.LineSeparator; public class PDFUtil { private static final String FONT = "./pdf/font/NotoSansCJKsc-Regular.otf" ; /** * @Description 将html转换为pdf文件 * @param html html页面字符串 * @return * @throws FileNotFoundException * @throws IOException */ public ByteArrayOutputStream html2Pdf(String html) throws FileNotFoundException, IOException { ConverterProperties props = new ConverterProperties(); DefaultFontProvider defaultFontProvider = new DefaultFontProvider( false , false , false ); defaultFontProvider.addFont(font); props.setFontProvider(defaultFontProvider); ByteArrayOutputStream bao = new ByteArrayOutputStream(); PdfWriter writer = new PdfWriter(bao); PdfDocument pdf = new PdfDocument(writer); pdf.setDefaultPageSize( new PageSize( 595 , 14400 )); Document document = HtmlConverter.convertToDocument(html, pdf, props); EndPosition endPosition = new EndPosition(); LineSeparator separator = new LineSeparator(endPosition); document.add(separator); document.getRenderer().close(); PdfPage page = pdf.getPage( 1 ); float y = endPosition.getY() - 36 ; page.setMediaBox( new Rectangle( 0 , y, 595 , 14400 - y)); document.close(); return bao; } /** * 定义操作区域 */ class EndPosition implements ILineDrawer { // y坐标 protected float y; /** * @Description: 获取y坐标 * @return */ public float getY() { return y; } /** * @Description: 操作画布特定区域 * @param pdfCanvas:操作画布 * @param rect:操作区域 */ @Override public void draw(PdfCanvas pdfCanvas, Rectangle rect) { this .y = rect.getY(); } /** * @Description: 获取行颜色 * @return */ @Override public Color getColor() { return null ; } /** * @Description: 获取行宽 * @return */ @Override public float getLineWidth() { return 0 ; } /** * @Description: 设置行颜色 * @param color */ @Override public void setColor(Color color) { } /** * @Description: 设置行宽 * @param lineWidth:宽度 */ @Override public void setLineWidth( float lineWidth) { } } } |
3 流响应
核心是通过ByteArrayOutputStream.writeTo(HttpServletResponse.getOutputStream())方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package com.huishi; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Service; import com.ssth.exchanage.excenter.common.exception.ResException; import com.ssth.exchanage.excenter.common.uitls.DateUtil; import com.ssth.exchanage.excenter.common.uitls.PDFUtil; import com.ssth.exchanage.excenter.controller.response.RecordApplyParamRep; import com.ssth.exchanage.excenter.service.ProductRecordService; @Service public class ProductRecordServiceImp implements ProductRecordService { /** * @Description 打印备案申请书 * @param res http响应 */ @Override public void printRecord(HttpServletResponse res){ try { String htmlStr = FileUtil.readFile( null ); PDFUtil pdfUtil = new PDFUtil(); ByteArrayOutputStream stream = pdfUtil.html2Pdf(htmlStr); res.setHeader( "Expires" , "0" ); res.setHeader( "Cache-Control" , "must-revalidate, post-check=0, pre-check=0" ); res.setHeader( "Pragma" , "public" ); res.setContentType( "application/pdf" ); OutputStream os = res.getOutputStream(); stream.writeTo(os); os.flush(); os.close(); } catch (IOException e) { log.error( "printRecord {}" ,e.getMessage()); throw new ResException( "-1" , "服务内部错误,请稍后再试!" ); } } } |
备注:
itext7解决中文显示问题有两种解决方式:
1 2 3 4 | 1 引入对应的语言包,火狐浏览器预览生成的pdf可能存在部分中文乱码问题,同时因为加载了语言包,生成的pdf更大 使用NotoSansCJKsc-Regular.otf,同时在pom中引入com.itextpdf.font-asian包. 2 设置字体:通过默认字体生成,pdf文件和html大小几乎相同,不存在浏览器预览乱码问题 PdfFont font = PdfFontFactory.createFont( "STSongStd-Light" , "UniGB-UCS2-H" , false ); |
使用示例链接: https://github.com/liulei3/html2pdf
参考文件:
html转pdf: https://developers.itextpdf.com/content/itext-7-examples/itext-7-converting-html-pdf
流响应:https://developers.itextpdf.com/content/best-itext-questions-stackoverview/general-questions-about-itext/itext7-how-can-i-serve-pdf-browser-without-storing-file-server-side
喜欢关注一下,不喜欢点评一下
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构