IText7使用总结
核心概念
- PdfWriter
- PdfWriter类负责将Pdf内容写入到输出流中,它是与物理文件或输出流关联的底层Pdf生成器;
- 创建一个pdf之前需要先创建一个PdfWriter实例;
- PdfDocument
- PdfDocument是更高层次的抽象,它封装了一个PdfWriter实例和其它与文档有关的数据;
- PdfDocument 还可以用来读取现有的 PDF 文件,进行修改后再保存;
- Document
- Document 类是 iText 7 中用于布局内容的主要类。它不是直接操作 PDF 的低层类,而是提供了一个更友好的 API 来添加文本、图像、表格等内容;
- 将 PdfDocument 传递给 Document 构造器,然后使用 Document 来添加内容,如段落、列表、表格等;
- Document 负责自动布局和分页,使得内容能够适配到 PDF 页面上;
- Paragraph
- Paragraph 类代表一个段落,它可以包含文本、图像、链接等元素;
- 可以向 Paragraph 添加多个 Text 对象,或者嵌入其他类型的元素,如 Image 或 List;
- Paragraph 是 Document 中最常用的元素之一,用于添加文本内容;
使用
引入依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.0.4</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>pdfa</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>pdftest</artifactId>
<version>7.0.4</version>
</dependency>
创建pdf
保存在项目根目录下
@Operation(summary = "简单Pdf创建")
@GetMapping(value = "/createPdf")
public String createPdf() throws FileNotFoundException {
PdfWriter writer = new PdfWriter("hello1.pdf");
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf);
document.add(new Paragraph("Hello World!"));
document.close();
return "success create pdf";
}
效果
添加列表
@Operation(summary = "Pdf中添加列表")
@GetMapping("/generatePdf")
public ResponseEntity<byte[]> generatePdf() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter pdfWriter = new PdfWriter(baos);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument);
//字体
PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
document.add(new Paragraph("Hello World!")).setFont(font);
//列表
List list = new List().setSymbolIndent(12).setListSymbol("\u2022").setFont(font);
list.add(new ListItem("Item 1"))
.add(new ListItem("Item 2"))
.add(new ListItem("Item 3"))
.add(new ListItem("Item 4"));
document.add(list);
document.close();
byte[] bytes = baos.toByteArray();
//浏览器会将文件保存为test.pdf,而不是打开
// return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM)
// .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"test.pdf\"")
// .body(bytes);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "test.pdf");
return ResponseEntity.ok()
.headers(headers)
.body(bytes);
//浏览器会识别出这是pdf文件,并尝试在浏览器窗口中打开它,或者根据浏览器的设置进行下载
// return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF)
// .body(bytes);
}
效果如下
添加图片
@Operation(summary = "带图片的pdf")
@GetMapping("/generate-pdf2")
public ResponseEntity<byte[]> generatePdf2() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter pdfWriter = new PdfWriter(baos);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument);
//图片
Image img1 = new Image(ImageDataFactory.create("C:\\Users\\DELL\\Desktop\\BeanFactory.png"));
Image img2 = new Image(ImageDataFactory.create("C:\\Users\\DELL\\Desktop\\BeanFactory.png"));
Paragraph paragraph = new Paragraph("This is img2:").add(img1).add("This is img2: ").add(img2);
document.add(paragraph);
document.close();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "test.pdf");
byte[] bytes = baos.toByteArray();
return ResponseEntity.ok().headers(headers).body(bytes);
}
添加表格
@Operation(summary = "带表格的pdf")
@GetMapping("/generate-table")
public ResponseEntity<byte[]> generateTablePdf() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter pdfWriter = new PdfWriter(baos);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
//第二个参数用来定义页面的大小:默认是A4,这里也是A4只不过旋转了页面
Document document = new Document(pdfDocument, PageSize.A4.rotate());
//默认的情况下,iText使用36个用户单位作为页边距(半英寸)。我们把所有的页边距都改为20个用户单位
document.setMargins(20, 20, 20, 20);
PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA);
PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
//定义了一个列的相对宽度。第1列是第2列的4倍宽。第3列是第2列的3倍宽等
Table table = new Table(new float[]{4, 1, 3, 4, 3, 3, 3, 3, 1});
BufferedReader br = new BufferedReader(new FileReader(new File("C:\\Users\\DELL\\Desktop\\united_states.csv")));
String line = br.readLine();
//创建表格的标题行
process(table, line, bold, true);
while ((line = br.readLine()) != null) {
process(table, line, font, false);
}
br.close();
document.add(table);
document.close();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "test.pdf");
byte[] bytes = baos.toByteArray();
return ResponseEntity.ok().headers(headers).body(bytes);
}
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)));
}
}
}
效果如下
pdf中创建表单
表单中的字段是由注解表示的,当创建一个字段时,这个小部件注解是隐式创建的。
@Operation(summary = "pdf中创建表单")
@GetMapping(value ="/edit-pdf2")
public void editPdf2() throws IOException {
//PdfReader是一个允许iText访问PDF文件并读取存储在PDF文件中的不同PDF对象的类
PdfWriter pdfWriter = new PdfWriter("hello.pdf");
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument);
//表单
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, true);
//加载中文字体
String font = this.getClass().getClassLoader().getResource("msyh.ttc").getPath();
PdfFont f2 = PdfFontFactory.createFont(font + ",0", PdfEncodings.IDENTITY_H, true);
//添加表单中的字段
PdfTextFormField textFormField = PdfTextFormField.createText(pdfDocument, new Rectangle(99, 753, 425, 30),
"name", "张三");
textFormField.setVisibility(PdfFormField.VISIBLE).setFont(f2);
acroForm.addField(textFormField);
//单选按钮
PdfButtonFormField group = PdfFormField.createRadioGroup(
pdfDocument, "language", "");
PdfFormField.createRadioButton(pdfDocument,
new Rectangle(130, 728, 15, 15), group, "English");
PdfFormField.createRadioButton(pdfDocument,
new Rectangle(200, 728, 15, 15), group, "French");
PdfFormField.createRadioButton(pdfDocument,
new Rectangle(260, 728, 15, 15), group, "German");
PdfFormField.createRadioButton(pdfDocument,
new Rectangle(330, 728, 15, 15), group, "Russian");
PdfFormField.createRadioButton(pdfDocument,
new Rectangle(400, 728, 15, 15), group, "Spanish");
acroForm.addField(group);
//复选框
for (int i = 0; i < 3; i++) {
PdfButtonFormField checkField = PdfFormField.createCheckBox(
pdfDocument, new Rectangle(119 + i * 69, 701, 15, 15),
"experience".concat(String.valueOf(i+1)), "Off",
PdfFormField.TYPE_CHECK);
acroForm.addField(checkField);
}
//下拉列表
String[] options = {"Any", "6.30 am - 2.30 pm", "1.30 pm - 9.30 pm"};
PdfChoiceFormField choiceField = PdfFormField.createComboBox(
pdfDocument, new Rectangle(163, 676, 115, 25),
"shift", "Any", options);
acroForm.addField(choiceField);
//多行文本框
PdfTextFormField infoField = PdfTextFormField.createMultilineText(
pdfDocument, new Rectangle(158, 625, 366, 40), "info", "");
infoField.setFont(f2);
acroForm.addField(infoField);
//重置按钮
PdfButtonFormField button = PdfFormField.createPushButton(pdfDocument,
new Rectangle(479, 594, 45, 15), "reset", "RESET");
button.setAction(PdfAction.createResetForm(
new String[] {"name", "language", "experience1", "experience2",
"experience3", "shift", "info"}, 0));
acroForm.addField(button);
Map<String, PdfFormField> fields = acroForm.getFormFields();
fields.get("name").setValue("James Bond 张三");
fields.get("language").setValue("English");
fields.get("experience1").setValue("Off");
fields.get("experience2").setValue("Yes");
fields.get("experience3").setValue("Yes");
fields.get("shift").setValue("Any");
fields.get("info").setValue("I was 38 years old when I became an MI6 agent.");
document.close();
}
需要下载中文字体msyh.ttc放到项目的resource目录下,否则无法显示中文。
往pdf模版中填充数据
使用前面创建的表单作为pdf,往其中填充数据。
@Operation(summary = "操纵现有的pdf")
@GetMapping(value ="/edit-pdf3")
public void editPdf3() throws IOException {
PdfWriter writer = new PdfWriter("hello2.pdf");
PdfDocument pdfDocument = new PdfDocument(new PdfReader("hello.pdf"), writer);
//向pdf中添加注解
PdfAnnotation ann = new PdfTextAnnotation(new Rectangle(400, 795, 0, 0))
.setTitle(new PdfString("iText"))
.setContents("Please, fill out the form.")
.setOpen(true);
pdfDocument.getFirstPage().addAnnotation(ann);
//加载中文字体
String font = this.getClass().getClassLoader().getResource("msyh.ttc").getPath();
PdfFont f2 = PdfFontFactory.createFont(font + ",0", PdfEncodings.IDENTITY_H, true);
//修改pdf中表单的的字段的值
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, true);
Map<String, PdfFormField> fields = acroForm.getFormFields();
fields.get("name").setValue("James 李四").setVisibility(PdfFormField.VISIBLE).setFont(f2);
fields.get("language").setValue("English");
fields.get("experience1").setValue("Off");
fields.get("experience2").setValue("Yes");
fields.get("experience3").setValue("Yes");
fields.get("shift").setValue("Any");
fields.get("info").setValue("job job job");
//将可编辑的表单变为不可编辑的
acroForm.flattenFields();
pdfDocument.close();
return;
}
效果如下