🐙iTextPdf学习笔记
ITextPdf
html转pdf演示:https://itextpdf.com/demos/convert-html-css-to-pdf-free-online
Github地址:https://github.com/itext/itext7
本文示例代码GitHub地址:https://github.com/zhangzhixi0305/itextpdf.git
一、介绍
iText 是一个用于处理 PDF 文档的 Java 库。它提供了丰富的功能,帮助开发人员创建、编辑和处理 PDF 文档,包括添加文本、图像、表格、链接等元素,以及设置页面布局和样式。iText 主要包含以下几个模块:
- 1. Core:核心模块,提供了创建、编辑和解析 PDF 文档的基本功能。
- 2. Text:文本模块,提供了处理文本内容的功能,如设置字体、颜色、段落格式等。
- 3. Graphics:图形模块,提供了处理图形元素的功能,如绘制线条、矩形、圆形等。
- 4. Images:图像模块,提供了处理图像的功能,如插入、缩放和旋转图像等。
- 5.Tables:表格模块,提供了处理表格的功能,如创建、编辑和格式化表格等。
- 6. XML:XML 模块,提供了将 XML 文档转换为 PDF 文档的功能。
- 7. HTML:HTML 模块,提供了将 HTML 文档转换为 PDF 文档的功能。
- 8. CSS:CSS 模块,提供了应用 CSS 样式的功能,以便在 PDF 文档中设置字体、颜色和布局等。
- 9. Security:安全模块,提供了对 PDF 文档进行加密、解密的功能。
- 10. Advanced:高级模块,提供了一些高级功能,如处理数学公式、导入外部内容等。
- 11. German:德语特殊处理模块,提供了处理德语特殊字符和排版的功能。
- 12. Japanese:日文特殊处理模块,提供了处理日文特殊字符和排版的功能。
- 13. AsianFonts:亚洲字体模块,提供了支持亚洲语言的字体和排版功能。
要使用 iText 处理 PDF 文档,首先需要在项目中引入相应的依赖。对于 Maven 项目,可以在 pom.xml 文件中添加以下依赖:
<!-- 始终需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>kernel</artifactId> <version>7.0.3</version> </dependency> <!-- 始终需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>io</artifactId> <version>7.0.3</version> </dependency> <!-- 始终需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>layout</artifactId> <version>7.0.3</version> </dependency> <!-- 仅在需要表单功能时需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>forms</artifactId> <version>7.0.3</version> </dependency> <!-- 仅在需要PDF/A格式支持时需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>pdfa</artifactId> <version>7.0.3</version> </dependency> <!-- 仅在需要数字签名功能时需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>sign</artifactId> <version>7.0.3</version> </dependency> <!-- 仅在需要条形码功能时需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>barcodes</artifactId> <version>7.0.3</version> </dependency> <!-- 仅在需要亚洲字体支持时需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.0.3</version> </dependency> <!-- 仅在需要断字功能时需要 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>hyph</artifactId> <version>7.0.3</version> </dependency>
ITextPdf5和ITextPdf7区别
iTextPDF 5和iTextPDF 7是iText公司开发的两个不同版本的PDF生成和处理库。虽然它们的核心功能都是创建、填充、阅读、修改和操作PDF文档,但两者之间存在显著的区别:
-
API设计与结构:
- iText 5具有较为传统的Java API设计,随着版本迭代,其代码结构逐渐变得复杂。
- iText 7对API进行了全面重构,采用了更模块化的设计,提供了清晰的分层架构,使开发者能够根据需求选择加载特定的功能模块。
-
性能与效率:
- iText 7在性能上有所优化,特别是在处理大规模和复杂文档时表现更好。
- iText 7引入了新的数据模型和布局引擎,增强了对PDF标准的支持,特别是对于PDF/A、PDF/UA等合规性格式的支持。
-
功能增强:
- iText 7增加了对PDF 2.0规范的支持,并且支持更多的高级特性,如数字签名、表单填写、条形码生成、亚洲字体支持以及更加丰富的文本布局选项。
- iText 7将一些额外功能拆分为独立的模块,例如表格处理(
itext.layout
)、表单处理(itext.forms
)、数字签名(itext.sign
)等,这样可以根据实际项目需求按需引入依赖,避免不必要的包体积增大。
-
兼容性与维护:
- iText 5已经停止维护更新,这意味着后续的PDF标准更新或安全修复可能无法得到支持。
- iText 7是当前iText公司的主力版本,持续进行维护和新功能开发,能够更好地适应不断发展的PDF技术标准和市场需求。
-
许可模式:
- iText 5的开源许可证在某个版本后发生了变化,从AGPL到GPL/LGPL混合模式,这影响了商业使用场景下的许可费用问题。
- iText 7同样有多种许可模式,包括免费社区版(有限制)和商业版,以满足不同的用户需求。
总的来说,iText 7相较于iText 5,在功能扩展、性能提升、可维护性和兼容性方面都具有明显优势,推荐在新项目中使用iText 7来处理PDF相关任务。
二、itext-1
2.1.入门案例:HelloWorld
import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Paragraph; import lombok.extern.slf4j.Slf4j; import java.io.IOException; /** * 简单的Hello World示例 * @author zhixi */ @Slf4j public class ITextPdf1 { /** * 输出文件路径 */ public static final String DEST = "results/chapter01/hello_world.pdf"; public static void main(String[] args) { //生成文件 try { new ITextPdf1().createPdf(DEST); } catch (IOException e) { log.error("生成文件失败", e); } } public void createPdf(String dest) throws IOException { // 1、创建一个PdfWriter对象,将文档写入到文件中 PdfWriter writer = new PdfWriter(dest); // 2、初始化一个PdfDocument对象 PdfDocument pdf = new PdfDocument(writer); // 3、初始化一个Document对象 Document document = new Document(pdf); // 4、向文档中添加内容 document.add(new Paragraph("Hello World!")); // 5、关闭文档 document.close(); } }
创建PdfWriter实例,PdfWriter是一个可以写PDF文件的对象,它不需要了解它要写的pdf的实际内容是什么,PdfWriter不需要知道文档是什么,一旦文件结构完成,它就写不同的文件部分,不同的对象,构成一个有效的文档。 dfWriter的初始化参数可以是文件名或者Stream。 PdfWriter了解它需要写什么内容,因为它监听PdfDocument的动态。 PdfWriter负责管理添加的内容,并把内容分布到不同的页面上,并跟踪有关页面内容的所有信息。 PdfDocument和PdfWriter创建以后,我们把PdfDocument传入Docment,并对Document对象操作。 创建Paragraph,包含"Hello World"字符串,并把这个短语加入Document独享中。 关闭Document。PDF文档创建完成 我们在pdf中创建一个Hello World的字符如下图1所示:
2.1.添加缩进无序段落
/** * 简单列表示例。 * * @author zhixi */ public class ITextPdf2 { public static final String DEST = "results/chapter01/rick_astley.pdf"; public static void main(String[] args) throws IOException { //生成文件 new ITextPdf2().createPdf(DEST); } /** * 创建一个简单的列表 * @param dest 输出文件路径 * @throws IOException IO异常 */ public void createPdf(String dest) throws IOException { PdfWriter writer = new PdfWriter(dest); PdfDocument pdf = new PdfDocument(writer); try (Document document = new Document(pdf);) { // 创建字体 PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN); // 添加一个段落 document.add(new Paragraph("iText is:").setFont(font)); // 创建一个列表 List list = new List() .setSymbolIndent(4) // 设置列表符号缩进 .setListSymbol(ListNumberingType.DECIMAL) // 设置列表符号 //.setListSymbol("•") .setFont(font);// 设置字体 list.add(new ListItem("Never gonna give you up")) .add(new ListItem("Never gonna let you down")) .add(new ListItem("Never gonna run around and desert you")) .add(new ListItem("Never gonna make you cry")) .add(new ListItem("Never gonna say goodbye")) .add(new ListItem("Never gonna tell a lie and hurt you")); document.add(list); } } }
符号列表枚举:ListNumberingType.java释义
这个枚举类型 ListNumberingType 定义了用于列表编号的不同格式或样式: DECIMAL: 十进制数字,例如:1, 2, 3, ... DECIMAL_LEADING_ZERO: 前导零的十进制数字,例如:01, 02, 03, ... ROMAN_LOWER: 小写罗马数字,例如:i, ii, iii, iv, v, ... ROMAN_UPPER: 大写罗马数字,例如:I, II, III, IV, V, ... ENGLISH_LOWER: 英文字母(小写),例如:a, b, c, d, ... ENGLISH_UPPER: 英文字母(大写),例如:A, B, C, D, ... GREEK_LOWER: 希腊字母(小写),例如:α, β, γ, δ, ... GREEK_UPPER: 希腊字母(大写),例如:Α, Β, Γ, Δ, ... ZAPF_DINGBATS_1, ZAPF_DINGBATS_2, ZAPF_DINGBATS_3, ZAPF_DINGBATS_4: 这些代表的是 Zapf Dingbats 字体中的字符范围,这是一种特殊符号字体,包含了各种装饰性符号和图形。具体来说: ZAPF_DINGBATS_1: 使用 Zapfdingbats 字体中从字符编码172到181的符号作为列表编号。 ZAPF_DINGBATS_2: 使用 Zapfdingbats 字体中从字符编码182到191的符号。 ZAPF_DINGBATS_3: 使用 Zapfdingbats 字体中从字符编码192到201的符号。 ZAPF_DINGBATS_4: 使用 Zapfdingbats 字体中从字符编码202到221的符号。
字体枚举:FontConstants.java释义
Courier:等宽字体(即每个字符占据相同的空间),通常用于显示代码或打字机风格的文本。 Courier-Bold:Courier字体的加粗版本,同样为等宽字体,但笔画更粗重。 Courier-Oblique:Courier字体的倾斜版本,字符有斜体效果,仍保持等宽特性。 Courier-BoldOblique:Courier字体的加粗且倾斜版本,具有粗体和斜体特征。 Helvetica:一种无衬线字体,简洁现代,适合正文及标题,常用于网页设计和其他需要清晰易读的地方。 Helvetica-Bold:Helvetica字体的加粗版本。 Helvetica-Oblique:Helvetica字体的倾斜版本。 Helvetica-BoldOblique:Helvetica字体的加粗且倾斜版本。 Symbol:符号字体,包含了许多数学符号、希腊字母以及其它特殊符号。 Times-Roman:衬线字体,传统而优雅,适合正文阅读和印刷,源自Times New Roman字体家族。 Times-Bold:Times字体的加粗版本。 Times-Italic:Times字体的斜体版本。 Times-BoldItalic:Times字体的加粗且斜体版本。 ZapfDingbats:一种特殊的符号字体,包含了各种装饰性图形和 dingbat 图形,例如箭头、星星、花饰等。 Times:这个可能是对 "Times-Roman" 的简化引用,也可能是在某些上下文中特指 Times 字体系列。
2.3.简单图像示例
import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Image; import com.itextpdf.layout.element.Paragraph; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Objects; /** * 简单的图像示例。 */ public class ITextPdf3 { private static String RESOURCE_PATH; static { RESOURCE_PATH = Objects.requireNonNull(ITextPdf3.class.getClassLoader().getResource("")).getPath(); // 去除路径中的前缀"/",以适应Windows系统 if (RESOURCE_PATH.startsWith("/")) { RESOURCE_PATH = RESOURCE_PATH.substring(1); } } public static final String DEST = "results/chapter01/quick_brown_fox.pdf"; private static final String FOX = RESOURCE_PATH + "img/fox.jpg"; private static final String DOG = RESOURCE_PATH + "img/dog.jpg"; public static void main(String[] args) throws IOException { new ITextPdf3().createPdf(DEST); } public void createPdf(String dest) throws IOException { try (// 1、创建一个PdfWriter对象 PdfWriter writer = new PdfWriter(dest); // 2、初始化一个PdfDocument对象 PdfDocument pdf = new PdfDocument(writer); // 3、初始化一个Document对象 Document document = new Document(pdf)) { byte[] foxBytes = Files.readAllBytes(Paths.get(FOX)); byte[] dogBytes = Files.readAllBytes(Paths.get(DOG)); Image fox = new Image(ImageDataFactory.create(foxBytes)); Image dog = new Image(ImageDataFactory.create(dogBytes)); // 新建一个段落 Paragraph p = new Paragraph("The quick brown ") .add(fox) .add(" jumps over the lazy ") .add(dog); // 4、向文档中添加段落 document.add(p); } } }
2.4.简单表格示例
import com.itextpdf.io.font.FontConstants; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Cell; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.element.Table; import com.itextpdf.layout.property.TextAlignment; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; /** * 简单的表格示例。 */ public class ITextPdf4 { public static final String DATA = "itext-1/src/main/resources/data/united_states.csv"; public static final String DEST = "results/chapter01/united_states.pdf"; public static void main(String[] args) throws IOException { new ITextPdf4().createPdfToCsv(DEST); new ITextPdf4().createPdfToDb(DEST); } /** * 从数据库创建PDF * * @param dest 输出文件路径 * @throws IOException IO异常 */ public void createPdfToDb(String dest) throws IOException { try (PdfWriter writer = new PdfWriter(dest); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf); ) { // 设置文档的页边距 document.setMargins(20, 20, 20, 20); // 创建只有两列的表格,各占50%宽度 Table table = new Table(new float[]{50f, 50f}); // 设置表格宽度为页面宽度的100% table.setWidthPercent(100); // 设置表格文字居中 table.setTextAlignment(TextAlignment.CENTER); // DB List<Student> students = new ArrayList<>(); Student student = new Student(); student.setName("zhangsan"); student.setAge(18); Student student2 = new Student(); student2.setName("李四"); student2.setAge(20); students.add(student); students.add(student2); // 创建字体(中文) PdfFont fontGarbled = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false); // 添加表头 table.addHeaderCell(new Cell().add(new Paragraph("姓名").setFont(fontGarbled)).setWidth(50f)); table.addHeaderCell(new Cell().add(new Paragraph("年龄").setFont(fontGarbled)).setWidth(50f)); // 添加学生信息 for (Student s : students) { table.addCell(new Cell().add(new Paragraph(s.getName()).setFont(fontGarbled)).setWidth(50f)); table.addCell(new Cell().add(new Paragraph(String.valueOf(s.getAge())).setFont(fontGarbled)).setWidth(50f)); } document.add(table); } } /** * 从CSV文件创建PDF * * @param dest 输出文件路径 * @throws IOException IO异常 */ public void createPdfToCsv(String dest) throws IOException { try (PdfWriter writer = new PdfWriter(dest); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf, PageSize.A4.rotate()); BufferedReader br = new BufferedReader(new FileReader(DATA)); ) { // 设置文档的页边距 document.setMargins(20, 20, 20, 20); // 创建字体:普通字体和加粗字体 PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA); PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD); // 创建表格:设置列宽(百分比) Table table = new Table(new float[]{4, 1, 3, 4, 3, 3, 3, 3, 1}); // 设置表格宽度 table.setWidthPercent(100); // 读取数据并添加到表格中 String line = br.readLine(); process(table, line, bold, true); while ((line = br.readLine()) != null) { process(table, line, font, false); } document.add(table); } } /** * 处理表格数据 * * @param table 表格 * @param line 行数据 * @param font 字体 * @param isHeader 是否是表头 */ public void process(Table table, String line, PdfFont font, boolean isHeader) { StringTokenizer tokenizer = new StringTokenizer(line, ";"); while (tokenizer.hasMoreTokens()) { if (isHeader) { table.addHeaderCell(new Cell().add(new Paragraph(tokenizer.nextToken()).setFont(font))); } else { table.addCell(new Cell().add(new Paragraph(tokenizer.nextToken()).setFont(font))); } } } class Student { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } private int age; } }
三、Web环境下导出PDF下载
很简单,无非就三步:
1、生成pdf到本地
2、下载pdf
3、删除生成的pdf文件
import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Paragraph; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; @Controller @RequestMapping("/pdf") public class PdfController { @GetMapping("/download") public void downloadPdf(HttpServletResponse response) throws IOException { String fileName = "example.pdf"; String filePath = "results/chapter01/" + fileName; // 1、生成pdf generatePdf(filePath); // 2、发送响应(下载pdf) sendPdfResponse(response, fileName, filePath); // 3、删除pdf Files.delete(Paths.get(filePath)); } private void sendPdfResponse(HttpServletResponse response, String fileName, String filePath) throws IOException { // 2.1、设置响应头 File file = new File(filePath); response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename=" + fileName); response.setContentLength((int) file.length()); // 2.2、将pdf文件写入响应输出流 try (OutputStream out = response.getOutputStream()) { Files.copy(Paths.get(filePath), out); } catch (IOException e) { e.printStackTrace(); } } /** * 生成pdf(Service层) * * @param filePath pdf文件路径 * @throws FileNotFoundException 文件未找到异常 */ public void generatePdf(String filePath) throws FileNotFoundException { PdfWriter writer = new PdfWriter(filePath); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf); document.add(new Paragraph("Hello, World!")); document.close(); } }