目录
一、前期准备
1、中文字体文件
2、maven依赖
二、创建PDF文档方法
三、通过可填充PDF模板将业务参数进行填充
1、 设置可填充的PDF表单
2、代码开干,代码填充可编辑PDF并另存文件
一、前期准备
1、中文字体文件
本演示使用的是iText 7版本,如果没有中文字体,那生成的PDF文档涉及中文的区域都无法显示。
现有查找到的PDF免费下载网址如下:
- 阿里巴巴矢量图标库:除了图标库,该网站还提供了一些免费的字体库供下载和使用。
- 字由:字由是一个专注于中文字体的网站,提供了一些优质的免费字体供下载。
- 字体中国:字体中国是一个提供中文字体下载的网站,包含了许多中文设计师的作品。
- 站长之家字体库:站长之家提供了大量的免费字体库,包含了各种中文字体和英文字体。
2、maven依赖
<dependencies>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.5</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.5</version>
</dependency>
</dependencies>
二、创建PDF文档方法
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
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.Cell;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.properties.HorizontalAlignment;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class CreatePdf {
//文件根目录
public static String RootPath = "D:/files-pdf/";
// 设置字体文件路径
//注意:如果没有中文字体,那PDF内容涉及中文的区域都不显示
public static String fontPath = RootPath + "ZiTiQuanWeiJunHei-W1-2.ttf";
public static void main(String[] args) {
String pdfName = "testContent.pdf";
// 创建 PdfWriter 和 PdfDocument
PdfWriter writer = null;
try {
writer = new PdfWriter(pdfName + ".pdf");
} catch (FileNotFoundException e) {
System.out.println("创建PdfWriter失败。。。。。");
e.printStackTrace();
return;
}
PdfDocument pdfDocument = new PdfDocument(writer);
// 创建 Document
Document document = new Document(pdfDocument);
// 设置页面的边距
documentMargins(document);
// 设置页眉和页脚
headerAndFooter(pdfDocument);
//编写PDF主体的文档内容 , 这一块是主要编写位置
setContent(document);
//添加水印
addWatermark(pdfDocument);
// 关闭对象
document.close(); //document文档要在输出前关闭,不然会提示“java.nio.file.NoSuchFileException: editable.pdf”
pdfDocument.close();
// 将生成的 PDF 文件移动到指定目录下
downloadPdf(RootPath, pdfName);
}
/**
* 获取设置的字体
*
* @return PdfFont 字体
*/
private static PdfFont getFont() {
// 设置中文字体
PdfFont font = null;
try {
font = PdfFontFactory.createFont(fontPath);
} catch (IOException e) {
System.out.println("字体获取失败。。。。。。。。。。。");
e.printStackTrace();
return null;
}
return font;
}
/**
* 设置页面的边距
*
* @param document 内容文档
*/
private static void documentMargins(Document document) {
// 上、右、下、左
int margins = 80;
document.setMargins(margins, margins, margins, margins);
}
/**
* 设置页眉页脚
*
* @param pdfDocument PDF文档
*/
private static void headerAndFooter(PdfDocument pdfDocument) {
pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, new IEventHandler() {
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfPage page = docEvent.getPage();
PdfCanvas canvas = new PdfCanvas(page);
// 创建页眉
Rectangle pageSize = page.getPageSize();
canvas.beginText()
.setFontAndSize(getFont(), 10)
.moveText(pageSize.getWidth() / 2, pageSize.getTop() - 20)
.showText("这是页眉")
.endText();
// 创建页脚
canvas.beginText()
.setFontAndSize(getFont(), 10)
.moveText(pageSize.getWidth() / 2, pageSize.getBottom() + 20)
.showText("这是页脚")
.endText();
canvas.release();
}
});
}
/**
* 添加水印
*
* @param pdfDocument PDF文档
* @throws MalformedURLException
*/
private static void addWatermark(PdfDocument pdfDocument) {
// 加载水印图片
String watermarkImage = RootPath + "zm5.jpg";
Image image = null;
try {
image = new Image(ImageDataFactory.create(watermarkImage));
} catch (MalformedURLException e) {
System.out.println("获取水印图片失败 , e : " + e);
e.printStackTrace();
return;
}
// 获取 PDF 页面的大小,此处是要以页面区域做参考,后续好设置对应的坐标填充水印
Rectangle rectanglePageSize = pdfDocument.getDefaultPageSize();
// 遍历每个页面,添加水印
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
//创建PDF画布
PdfCanvas pdfCanvas = new PdfCanvas(pdfDocument.getPage(i));
// 创建 Canvas 画布对象,设置位置和大小
Canvas canvas = new Canvas(pdfCanvas, rectanglePageSize, true);
// 在水印画布上添加图片,并设置透明度和位置
// 上、右、下、左
image.setOpacity(0.8f).setMargins(600, 200, 0, 300);
canvas.add(image);
// 关闭水印画布
canvas.close();
}
}
/**
* 生成的 PDF 文件移动到指定目录下
*
* @param rootPath 存储目录
* @param pdfName PDF文件名
* @return
*/
private static String downloadPdf(String rootPath, String pdfName) {
// 假设生成的 PDF 文件路径为 sourcePath
Path sourcePath = Paths.get(pdfName + ".pdf");
// 假设目标目录路径为 targetDirectoryPath
Path targetDirectoryPath = Paths.get(rootPath);
// 移动文件到目标目录
Path targetPath = null;
try {
targetPath = Files.move(sourcePath, targetDirectoryPath.resolve(sourcePath.getFileName()), StandardCopyOption.REPLACE_EXISTING);
// 输出成功信息
System.out.println("文件移动成功,目标路径:" + targetPath);
return targetPath.toString();
} catch (IOException e) {
// 输出失败信息
System.out.println("文件移动失败,目标路径:" + targetPath);
e.printStackTrace();
return null;
}
}
/**
* PDF主体内容
*
* @param document 文档
*/
private static void setContent(Document document) {
// 设置中文字体
PdfFont font = getFont();
/******************************** 段落内容由 Paragraph 区域编写 ******************************************/
// 创建段落标题
Paragraph paragraphTitle = new Paragraph().setFont(font);
// setBold:设置粗体,setItalic:斜体,setUnderline:下划线
paragraphTitle.add("这是一份PDF测试文档").setBold().setFontSize(12);
// 设置段落的对齐方式为居中
paragraphTitle.setTextAlignment(TextAlignment.CENTER);
document.add(paragraphTitle);
// 创建一个可编辑的段落
Paragraph nameOfStaff = new Paragraph().setFont(font).setFontSize(10);
nameOfStaff.add("Name of Developer / 开发人员:");
document.add(nameOfStaff);
Paragraph fullname = new Paragraph().setFont(font).setFontSize(10);
fullname.add("____________________(Note: 请写全名)");
document.add(fullname);
Paragraph paragraph1 = new Paragraph().setFont(font).setFontSize(9);
paragraph1.add("这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
"这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
"这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
"这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
"这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!");
document.add(paragraph1);
Paragraph declare = new Paragraph().setFont(font).setFontSize(9);
declare.add("I hereby declare that:");
document.add(declare);
Paragraph section = new Paragraph().setFont(font).setFontSize(9).setBold().setUnderline();
section.add("多选项声明确认!Multiple option declaration confirmation!");
document.add(section);
// 创建一个带有复选框的列表
Paragraph paragraph2 = new Paragraph().setFont(font).setFontSize(9);
paragraph2.add("(Please tick in the box as appropriate)").add("\n");
paragraph2.add("口 声明内容1。。。声明内容1。。。声明内容1。。。声明内容1。。。声明内容1。。。声明内容1。。。").add("\n");
paragraph2.add("口 声明内容2。。。声明内容2。。。声明内容2。。。声明内容2。。。声明内容2。。。声明内容2。。。").add("\n");
document.add(paragraph2);
/******************************** 表格由 Table 区域编写 ******************************************/
// 创建表格并设置列数和默认宽度
Table table = new Table(4);
// table.setMaxWidth(1000); //固定式宽度
// table.setAutoLayout(); //根据内容自适应宽度
table.setWidth(UnitValue.createPercentValue(100)); //页面总宽固定
// 添加表格标题(合并4列)
Cell titleCell = new Cell(1, 4);
// 创建文本对象
titleCell.add(new Paragraph("Table Title"));
titleCell.setTextAlignment(TextAlignment.CENTER);
table.addHeaderCell(titleCell);
// 添加表头
table.addHeaderCell("Header 1");
table.addHeaderCell("Header 2");
table.addHeaderCell("Header 3");
table.addHeaderCell("Header 4");
// 添加表格内容
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
Cell cell = new Cell()
.setFont(font)
.add(new Paragraph("行 " + (i + 1) + ", Col " + (j + 1)))
.setWidth(UnitValue.createPercentValue(25));
table.addCell(cell);
}
}
// 设置表格样式
table.setHorizontalAlignment(HorizontalAlignment.CENTER);
// 将表格添加到文档中
document.add(table);
/******************************** 内容区域编写END ******************************************/
}
}
三、通过可填充PDF模板将业务参数进行填充
1、 设置可填充的PDF表单
至于编辑器自行查找,免费的基本大多会添加水印。
设置成功后,如下图,可编辑区高亮显示
2、代码开干,代码填充可编辑PDF并另存文件
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class FillingPdfTemplate {
//文件根目录
public static String RootPath = "D:/files-pdf/";
// 设置字体文件路径
//注意:如果没有中文字体,那PDF内容涉及中文的区域都不显示
public static String fontPath = RootPath + "ZiTiQuanWeiJunHei-W1-2.ttf";
public static void main(String[] args) {
Map<String, String> mapParam = new HashMap<>();
mapParam.put("fullname", "某某某");
mapParam.put("Check Box1","On");
mapParam.put("Check Box2", "Off");
mapParam.put("account1", "account1");
mapParam.put("broker1", "broker1");
mapParam.put("number1", "number1");
mapParam.put("security1", "security1");
mapParam.put("sharehold1", "sharehold1");
mapParam.put("clp1", "clp1");
mapParam.put("shares1", "shares1");
mapParam.put("sharehold3", "");
mapParam.put("clp3", "");
mapParam.put("shares3", "");
mapParam.put("name1", "某某某");
mapParam.put("position", "XXX高级");
mapParam.put("company", "深圳市XXX科技有限公司");
mapParam.put("date", "2023-05-24");
String templatePdfPath = RootPath + "Acrobat-demo.pdf";
String destPdfPath = RootPath + "Acrobat-demo-result.pdf";
replaceTextFieldPdf(templatePdfPath, destPdfPath, mapParam);
}
/**
* 获取设置的字体
*
* @return PdfFont 字体
*/
private static PdfFont getFont() {
// 设置中文字体
PdfFont font = null;
try {
font = PdfFontFactory.createFont(fontPath);
} catch (IOException e) {
System.out.println("字体获取失败。。。。。。。。。。。");
e.printStackTrace();
return null;
}
return font;
}
/**
* 替换PDF文本表单域变量
*
* @param templatePdfPath 要替换的pdf全路径
* @param params 替换参数
* @param destPdfPath 替换后保存的PDF全路径
* @throws IOException
*/
public static final void replaceTextFieldPdf(String templatePdfPath, String destPdfPath,
Map<String, String> params) {
PdfDocument pdfDoc = null;
try {
pdfDoc = new PdfDocument(new PdfReader(templatePdfPath), new PdfWriter(destPdfPath));
} catch (IOException e) {
e.printStackTrace();
}
//获取表单信息
PdfAcroForm pdfAcroForm = PdfAcroForm.getAcroForm(pdfDoc, true);
//遍历填充预设的值
Set<Map.Entry<String, String>> entries = params.entrySet();
entries.stream().forEach(entry -> {
if(pdfAcroForm.getField(entry.getKey()) != null){
// 设置表单字段的值
//复选框类型的:1勾选,2圆圈,3叉叉,4菱形,5方块,6星星
//如果不想复选框被选中,要设置为"Off",选中设置为"On",注意大小写
pdfAcroForm.getField(entry.getKey()).setCheckType(1).setValue(entry.getValue() , true).setFont(getFont());
}
});
// 添加表单字段
// PdfTextFormField textField = PdfFormField.createText(pdfDoc, new Rectangle(100, 100, 200, 20), "newField", "");
// pdfAcroForm.addField(textField);
//表单扁平化,设置后生成的文档不可再编辑
// pdfAcroForm.flattenFields();
pdfDoc.close();
}
}