Java使用IText7动态生成带审批文本框的PDF文档
文章目录
测试结果示例
实现思路
使用Canvas进行相对定位和绝对定位来确定文本框内文字位置,用Rectangle通过坐标转换来确定文本框的位置
xml
<itext.version>7.1.2</itext.version>
xml
<!--itext-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>pdfa</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>pdftest</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>${itext.version}</version>
</dependency>
1.构建第一个框的起始坐标
注意:由于Rectangle构造的坐标为以文档的左下角为xy轴,为了支持计算分页顺序添加需要转换到左上角
java
//第一个文本框(非审批框,高度比较大有特殊自动换行需求)
// 定义文本框坐标和矩形尺寸(左上点坐标)
float xLeftTop = 80; // 左上角x坐标
float yLeftTop = 100; // 左上角y坐标
float width = 450; // 矩形宽度
float height = 300; // 矩形高度
//坐标转换:默认是左下角坐标轴
PdfPage firstPage = pdfDoc.getPage(1);
float yBottom = firstPage.getPageSize().getHeight() - yLeftTop - height;
Rectangle rect = new Rectangle(xLeftTop, yBottom, width, height);
2.渲染第一个框
使用Canvas进行相对定位和绝对定位,来确保文本框内的文字内容位置
为了防止自动换行超出框内,建议Canvas布局的宽度小于文本框的布局
java
private void createIndexFieldText(Document doc, PdfFont font, String text, Rectangle rect) {
// 创建Canvas进行绝对定位
PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
//防止字体出框
Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
// 绘制文本框边框
pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
//相对定位
Paragraph approve = new Paragraph()
.setFont(font)
.setFontSize(12)
.setRelativePosition(10, 10,0,0)
.add("致______________:");
canvas.add(approve);
Paragraph approve1 = new Paragraph()
.setFont(font)
.setFontSize(12).setFirstLineIndent(25)
.setRelativePosition(10, 10,0,0)
.add("________________机构:");
canvas.add(approve1);
Paragraph approve2 = new Paragraph("我方承担的工程," +
"已完成相关准备工作,申请开工,请予以审核,批准。")
.setTextAlignment(TextAlignment.JUSTIFIED)
.setFont(font).setFirstLineIndent(25)
.setRelativePosition(10, 10, 0,0)
.setFontSize(12);
canvas.add(approve2);
// 审批相关绝对定位
Paragraph fieldText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
.add(text);
canvas.add(fieldText);
// 在文本框内添加
Paragraph dateText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
.add("日期: ");
canvas.add(dateText);
canvas.close();
}
3.渲染其他的审批框
原理同大框,需计算是否需要分页,如分页需要重置新页面的起始坐标,且由于大框高度大于其他框,需要重置高度,多个框添加时需计算y轴坐标。
java
/**
* 添加审批文本框
*/
private static void addTextField(Document doc, PdfFont font, String text, Rectangle rect,boolean page) {
// 创建Canvas进行绝对定位
PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
//防止字体出框
Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
if(page){
pdfCanvas = new PdfCanvas(doc.getPdfDocument().addNewPage());
}
Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
// 绘制文本框边框
pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
//审核意见定位
Paragraph approve = new Paragraph()
.setFont(font)
.setFontSize(12)
.setFixedPosition(rect.getX() + 10, rect.getY()+rect.getHeight()-25,200)
.add("审核意见:");
canvas.add(approve);
// 在文本框内添加
Paragraph fieldText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
.add(text);
canvas.add(fieldText);
// 在文本框内添加
Paragraph dateText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
.add("日期: ");
canvas.add(dateText);
canvas.close();
}
4.完整代码
java
package com.moshangshang.pdf.controller;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
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.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("")
public class PDFController {
/**
* 带文本框的审批表生成
*/
@GetMapping(value = "/pdf")
public void test( HttpServletResponse response) throws Exception {
response.setContentType("application/pdf");
OutputStream os = response.getOutputStream();
// 创建 PDF 文档
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(os));
Document document = new Document(pdfDoc,PageSize.A4);
PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", true);
// 设置页面尺寸和边距
document.setMargins(50, 50, 50, 50);
Paragraph title = new Paragraph("审批测试表")
.setTextAlignment(TextAlignment.CENTER)
.setFont(font).setBold().setFontSize(22);
document.add(title);
List<String> unitList = Arrays.asList("测试1","测试2","测试3","测试4","测试5","测试6","测试7","测试8","测试9");
//文本框绘制索引
int fieldTextIndex = 1;
//第一个文本框(非审批框,高度比较大有特殊自动换行需求)
// 定义文本框坐标和矩形尺寸(左上点坐标)
float xLeftTop = 80; // 左上角x坐标
float yLeftTop = 100; // 左上角y坐标
float width = 450; // 矩形宽度
float height = 300; // 矩形高度
//坐标转换:默认是左下角坐标轴
PdfPage firstPage = pdfDoc.getPage(1);
float yBottom = firstPage.getPageSize().getHeight() - yLeftTop - height;
Rectangle rect = new Rectangle(xLeftTop, yBottom, width, height);
for (String name : unitList) {
//定位
if(fieldTextIndex == 1){
//开工报告第一个大框添加
createIndexFieldText(document, font, name+"(签字、印章):", rect,false);
}else {
//其他全为同等审批框,修改高度
height = 150;
//默认第一页三个框,其他页默认四个
if(fieldTextIndex%4==0){
//如果大于单页数量,创建新页,并重置坐标
float yBottom1 = firstPage.getPageSize().getHeight() - yLeftTop - height;
rect = new Rectangle(xLeftTop, yBottom1, width, height);
addTextField(document, font, name+" (签字、印章):", rect,true);
fieldTextIndex++;
continue;
}
rect = new Rectangle(xLeftTop, rect.getY()-height, width, height);
// 绘制文本框边框
addTextField(document, font, name+"(签字、印章):", rect,false);
}
fieldTextIndex++;
}
document.close();
}
/**
*
*/
private void createIndexFieldText(Document doc, PdfFont font, String text, Rectangle rect,boolean page) {
// 创建Canvas进行绝对定位
PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
//防止字体出框
Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
if(page){
pdfCanvas = new PdfCanvas(doc.getPdfDocument().addNewPage());
}
Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
// 绘制文本框边框
pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
//相对定位
Paragraph approve = new Paragraph()
.setFont(font)
.setFontSize(12)
.setRelativePosition(10, 10,0,0)
.add("致______________(单位):");
canvas.add(approve);
Paragraph approve1 = new Paragraph()
.setFont(font)
.setFontSize(12).setFirstLineIndent(25)
.setRelativePosition(10, 10,0,0)
.add("________________机构:");
canvas.add(approve1);
Paragraph approve2 = new Paragraph("我方," +
"已完成相关准备工作,具备开工条件,申请于2025年开工,请予以审核,批准。")
.setTextAlignment(TextAlignment.JUSTIFIED)
.setFont(font).setFirstLineIndent(25)
.setRelativePosition(10, 10, 0,0)
.setFontSize(12);
canvas.add(approve2);
// 审批相关绝对定位
Paragraph fieldText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
.add(text);
canvas.add(fieldText);
// 在文本框内添加
Paragraph dateText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
.add("日期: ");
canvas.add(dateText);
canvas.close();
}
/**
* 添加审批文本框
*/
private static void addTextField(Document doc, PdfFont font, String text, Rectangle rect,boolean page) {
// 创建Canvas进行绝对定位
PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
//防止字体出框
Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
if(page){
pdfCanvas = new PdfCanvas(doc.getPdfDocument().addNewPage());
}
Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
// 绘制文本框边框
pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
//审核意见定位
Paragraph approve = new Paragraph()
.setFont(font)
.setFontSize(12)
.setFixedPosition(rect.getX() + 10, rect.getY()+rect.getHeight()-25,200)
.add("审核意见:");
canvas.add(approve);
// 在文本框内添加
Paragraph fieldText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
.add(text);
canvas.add(fieldText);
// 在文本框内添加
Paragraph dateText = new Paragraph()
.setFont(font)
.setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
.setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
.add("日期: ");
canvas.add(dateText);
canvas.close();
}
}