spring boot:用itextpdf处理pdf表格文件(spring boot 2.3.2)
一,什么是itextpdf?
1,itextpdf的用途
itextpdf是用来生成PDF文档的一个java类库,
通过iText可以生成PDF文档,
还可以把XML/Html文件转化为PDF文件
2,官方网站:
https://itextpdf.com/en
3,itextpdf使用中的几个问题:
使用中文字体
插入表格
插入图片时设置图片宽度
浏览器直接显示pdf
说明:刘宏缔的架构森林是一个专注架构的博客,
网站:https://blog.imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/05/24/springboot-yong-itextpdf-chu-li-pdf-biao-ge-wen-jian-springboot232/
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,代码地址:
https://github.com/liuhongdi/exportpdf
2,功能说明:
直接显示pdf
把数据保存成pdf文件
pdf文件下载
3,项目结构:如图:
三,配置文件说明
1,pom.xml
<!--pdf begin--> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.1</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> <!--pdf end--> <!--mybatis begin--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--mybatis end--> <!--mysql begin--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mysql end-->
说明:要引入itextpdf
2,把自己要使用的字体文件,复制到
resources/font目录下供访问
3,数据表建表sql
CREATE TABLE `goods` ( `goodsId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `goodsName` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'name', `subject` varchar(200) NOT NULL DEFAULT '' COMMENT '标题', `price` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '价格', `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock', PRIMARY KEY (`goodsId`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'
四,java代码说明
1,AbstractITextPdfView.java
/** 新建一个pdfview,主要是为了避免AbstractPdfView中使用的pdf库太旧的问题 AbstractPdfView只支持到 com.lowagie.itext的2.1.7版本, 版本太旧,文档也缺少 修改后可以支持itextpdf库的类, 新增AbstractITextPdfView后此问题完美解决 by liuhongdi */ public abstract class AbstractITextPdfView extends AbstractView { public AbstractITextPdfView() { setContentType("application/pdf"); } @Override protected boolean generatesDownloadContent() { return true; } @Override protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // 获得流 ByteArrayOutputStream baos = createTemporaryOutputStream(); Document document = newDocument(); PdfWriter writer = newWriter(document, baos); prepareWriter(model, writer, request); buildPdfMetadata(model, document, request); buildPdfDocument(model, document, writer, request, response); writeToResponse(response, baos); } protected Document newDocument() { return new Document(PageSize.A4); } protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException { return PdfWriter.getInstance(document, os); } protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request) throws DocumentException { writer.setViewerPreferences(getViewerPreferences()); } protected int getViewerPreferences() { return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage; } protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) { } protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception; }
说明:如果在浏览器的页面上直接显示pdf,而不是下载文件后再打开,
则需要使用AbstractPdfView,但spring boot默认支持的itext库代码太旧,
注释中已做了说明,所以我们另外自己定义一个
2, ViewPdfUtil.java
public class ViewPdfUtil extends AbstractITextPdfView { //文件名 private String fileName; public String getFileName() { return this.fileName; } public void setFileName(String fileName) { this.fileName = fileName; } //指定一个类型,方便知道调用哪个类处理 private String pdfType; public String getPdfType() { return this.pdfType; } public void setPdfType(String pdfType) { this.pdfType = pdfType; } //生成pdf的document并显示出来 @Override protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setCharacterEncoding("UTF-8"); response.setContentType("application/pdf"); response.setHeader("Content-Disposition","filename=" + URLEncoder.encode(this.fileName, "UTF-8")); List<Goods> products = (List<Goods>) model.get("sheet"); if (this.pdfType.equals("goods")) { PdfTableService pdfTableService = new PdfTableServiceImpl(); //不保存成文件,直接显示,所以不指定保存路径 pdfTableService.createPDF(document, products,""); } } }
说明:主要是实现buildPdfDocument方法,供ModelAndView调用时直接显示到浏览器页面
3,PdfTableServiceImpl.java
@Service public class PdfTableServiceImpl implements PdfTableService { //创建pdf文件, // savePath是保存路径,如果是空字串,则直接输出到document //document:pdf内容 //goods:写入到pdf表格中的数据 @Override public void createPDF(Document document, List<Goods> goods,String savePath) { try { if (!savePath.equals("")) { PdfWriter.getInstance(document, new FileOutputStream(savePath)); } document.addTitle("商品库存统计表"); document.addAuthor("老刘"); document.addSubject("2020年商品库存统计"); document.addKeywords("商品库存"); document.open(); Paragraph para = getParagraphText("整个白酒行业从2012年开始,都迅速下滑,销量和利润都是大跌。2014年和2015年,茅台的股价涨得不错,但也没有超过同期的白马股太多,加上利润增速一直没有恢复塑化剂之前的状态,我就一直没有再买入"); document.add(para); String imagePath = "/data/springboot2/logo.jpg"; // 图片的绝对路径 Image image = Image.getInstance(imagePath); // 取得图片对象 //计算得到目标宽高 File gifFile = new File(imagePath); int origWidth = 0; int origHeight = 0; try { BufferedImage imageBuffer = ImageIO.read(gifFile); if (imageBuffer != null) {//如果image=null 表示上传的不是图片格式 origWidth = imageBuffer.getWidth(); origHeight = imageBuffer.getHeight(); } } catch (IOException e) { e.printStackTrace(); } System.out.println("width:"+document.getPageSize().getWidth()); System.out.println("margin:"+document.leftMargin()); //得到新的高度和新的宽度 float newwidth = document.getPageSize().getWidth()-document.leftMargin()-document.rightMargin(); float newHeight = (newwidth*origHeight) / origWidth; image.scaleAbsolute(newwidth, newHeight); document.add(image); PdfPTable table = createTable(goods); document.add(table); } catch ( IOException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } finally { if (document.isOpen()) { document.close(); } } } //从text得到可以添加到document的Paragraph public static Paragraph getParagraphText(String text) { try { Font font = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED)); font.setColor(BaseColor.GRAY); Paragraph para = new Paragraph(text,font); return para; } catch ( IOException | DocumentException e) { e.printStackTrace(); return null; } } //创建PdfTable public static PdfPTable createTable(List<Goods> products) throws IOException, DocumentException { PdfPTable table = new PdfPTable(4);//生成一个4列的表格 int widths[] = { 10,40,40,10 };//指定各列的宽度百分比 table.setWidthPercentage(100); table.setSpacingBefore(10); table.setWidths(widths); PdfPCell cell; int size = 20; Font font = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED)); font.setColor(BaseColor.BLACK); Font font_head = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED)); font_head.setColor(BaseColor.BLUE); Font font_title = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED)); font_title.setColor(BaseColor.GREEN); font_title.setSize(36); cell = new PdfPCell(new Phrase("商品库存信息表",font_title)); cell.setColspan(4);//设置所占列数 cell.setFixedHeight(50);//设置高度 cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中 table.addCell(cell); cell = new PdfPCell(new Phrase("ID",font_head));//商品编号 cell.setFixedHeight(size); table.addCell(cell); cell = new PdfPCell(new Phrase("商品名称",font_head));//商品名称 cell.setFixedHeight(size); table.addCell(cell); cell = new PdfPCell(new Phrase("描述",font_head));//描述 cell.setFixedHeight(size); table.addCell(cell); cell = new PdfPCell(new Phrase("价格",font_head));//商品价格 cell.setFixedHeight(size); table.addCell(cell); for(int i = 0;i<products.size();i++) { cell = new PdfPCell(new Phrase(String.valueOf(products.get(i).getGoodsId()),font));//商品编号 cell.setFixedHeight(size); table.addCell(cell); cell = new PdfPCell(new Phrase(products.get(i).getGoodsName(),font));//商品名称 cell.setFixedHeight(size); table.addCell(cell); cell = new PdfPCell(new Phrase(products.get(i).getSubject(),font));//描述 cell.setFixedHeight(size); table.addCell(cell); cell = new PdfPCell(new Phrase(products.get(i).getPrice()+"",font));//商品价格 cell.setFixedHeight(size); table.addCell(cell); } return table; } }
用途:把数据添加到pdf的 document,注意对中文字体的引用
另外注意插入图片时得到默认宽度的计算,需要减掉两侧的margin
4,HomeController.java
@RestController @RequestMapping("/home") public class HomeController { @Resource private GoodsMapper goodsMapper; @Resource PdfTableService pdfTableService; //把数据保存到pdf文件 @GetMapping("/savepdf") public String savepdf() { List<Goods> goodsList = goodsMapper.selectAllGoods(); String savePath = "/data/springboot2/goodslist.pdf"; pdfTableService.createPDF(new Document(PageSize.A4), goodsList,savePath); return "pdf saveed"; } //从浏览器直接显示pdf @GetMapping("/viewpdf") public ModelAndView viewpdf() { List<Goods> goodsList = goodsMapper.selectAllGoods(); Map<String, Object> model = new HashMap<>(); model.put("sheet", goodsList); ViewPdfUtil viewPdf = new ViewPdfUtil(); viewPdf.setFileName("测试.pdf"); viewPdf.setPdfType("goods"); return new ModelAndView(viewPdf, model); } //下载pdf文件 @GetMapping("/downpdf") public void downpdf() { String filepath = "/data/springboot2/goodslist.pdf"; PdfUtil.downPdfFile(filepath); } }
三个功能:直接显示,保存成文件,下载
五,效果测试
1,直接显示:
访问:
http://127.0.0.1:8080/home/viewpdf
如图:
2,直接保存成pdf文件:
访问:
http://127.0.0.1:8080/home/savepdf
效果如图:
3,下载pdf文件:
访问:
http://127.0.0.1:8080/home/downpdf
六,查看spring boot的版本:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.2.RELEASE)