Apache PDFBox 实战案例详解
Apache PDFBox 是 Java 中处理 PDF 文件最常用的开源库之一,下面通过几个完整的实战案例来展示它的核心功能。

创建带格式的 PDF 文档
场景
生成一份包含标题、段落、表格和图片的员工入职通知书。
完整代码
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.font.*;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import java.io.IOException;
public class CreatePDF {
public static void main(String[] args) throws IOException {
// 1. 创建空白文档
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4); // A4 纸张
document.addPage(page);
// 2. 创建内容流
PDPageContentStream content = new PDPageContentStream(document, page);
// 3. 设置字体(中文需要加载系统字体)
PDType0Font font = PDType0Font.load(document,
new java.io.File("C:/Windows/Fonts/simhei.ttf")); // 黑体
// 4. 写入标题
content.beginText();
content.setFont(font, 20);
content.setLeading(30); // 行距
content.newLineAtOffset(50, 750); // 起始位置
content.showText("员工入职通知书");
content.newLine();
content.showText("尊敬的张三先生:");
content.endText();
// 5. 添加段落文字
String paragraph = "恭喜您通过我司面试!请您于2024年3月1日携带相关证件";
content.beginText();
content.setFont(font, 12);
content.newLineAtOffset(50, 700);
content.showText(paragraph);
content.newLine();
content.showText("到公司人力资源部报到。");
content.endText();
// 6. 画一条分隔线
content.setLineWidth(1f);
content.moveTo(50, 680);
content.lineTo(545, 680);
content.stroke();
// 7. 插入图片(Logo)
PDImageXObject logo = PDImageXObject.createFromFile("logo.png", document);
content.drawImage(logo, 470, 740, 80, 50);
// 8. 关闭内容流
content.close();
// 9. 保存文件
document.save("offer_letter.pdf");
document.close();
System.out.println("PDF 创建成功!");
}
}
读取并提取 PDF 文本
场景
从大量 PDF 文件中提取文本内容用于数据分析。
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import java.io.File;
import java.io.IOException;
public class ReadPDF {
public static void main(String[] args) throws IOException {
File pdfFile = new File("employee_contract.pdf");
// 打开 PDF 文档
try (PDDocument document = PDDocument.load(pdfFile)) {
// 检查加密状态
if (document.isEncrypted()) {
System.out.println("PDF 文件已加密,需要密码");
return;
}
// 全文提取
PDFTextStripper stripper = new PDFTextStripper();
stripper.setStartPage(1); // 从第1页开始
stripper.setEndPage(2); // 到第2页结束
String text = stripper.getText(document);
System.out.println("提取的文本内容:");
System.out.println(text);
// 按区域提取(适用于表格等特定区域)
PDFTextStripperByArea areaStripper = new PDFTextStripperByArea();
areaStripper.setSortByPosition(true);
// 获取页面信息
System.out.println("PDF 页数: " + document.getNumberOfPages());
System.out.println("页面尺寸: " + document.getPage(0).getMediaBox());
}
}
}
PDF 表单填写与生成
场景
批量填写电子合同或申请表。
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.form.*;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class FillForm {
public static void main(String[] args) throws IOException {
// 1. 加载模板
try (PDDocument document = PDDocument.load(new File("employee_form.pdf"))) {
// 2. 获取表单
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
if (form == null) {
System.out.println("该 PDF 不包含表单!");
return;
}
// 3. 获取所有表单字段
System.out.println("可用的表单字段:");
form.getFields().forEach(field ->
System.out.println(" - " + field.getFullyQualifiedName()));
// 4. 填写表单数据
Map<String, String> data = new HashMap<>();
data.put("name", "张三");
data.put("idNumber", "110101199001011234");
data.put("position", "Java 高级工程师");
data.put("salary", "25000");
data.put("startDate", "2024-03-01");
// 5. 逐个字段赋值
for (Map.Entry<String, String> entry : data.entrySet()) {
PDField field = form.getField(entry.getKey());
if (field != null) {
field.setValue(entry.getValue());
// 针对复选框特殊处理
if (field instanceof PDCheckBox) {
((PDCheckBox) field).check();
}
}
}
// 6. 保存填写后的新文件
document.save("filled_form.pdf");
System.out.println("表单填写完成,已保存为 filled_form.pdf");
}
}
}
PDF 加密与权限控制
场景
为 PDF 文件设置密码并限制打印、复制等操作。
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import java.io.File;
import java.io.IOException;
public class ProtectPDF {
public static void main(String[] args) throws IOException {
try (PDDocument document = PDDocument.load(new File("sensitive.docx"))) {
// 1. 设置访问权限
AccessPermission ap = new AccessPermission();
ap.setCanPrint(false); // 禁止打印
ap.setCanModify(false); // 禁止修改
ap.setCanCopy(false); // 禁止复制
ap.setCanExtractContent(true); // 允许提取内容
// 2. 创建加密策略
StandardProtectionPolicy policy = new StandardProtectionPolicy(
"owner123", // 所有者密码(最高权限)
"user123", // 用户密码(受限权限)
ap
);
// 3. 设置加密强度(128-bit AES)
policy.setEncryptionKeyLength(128);
// 4. 应用加密
document.protect(policy);
// 5. 保存加密后的文件
document.save("protected.pdf");
System.out.println("PDF 加密完成,文件已保护!");
} catch (Exception e) {
System.err.println("加密失败: " + e.getMessage());
}
}
}
批量文档处理(合并/拆分)
场景
批量合并多个 PDF 合同为一个文件。
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.multipdf.Splitter;
import org.apache.pdfbox.pdmodel.PDDocument;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class BatchProcess {
// 合并多个 PDF
public static void mergePDFs(List<String> filePaths, String outputPath) throws IOException {
PDFMergerUtility merger = new PDFMergerUtility();
// 添加要合并的文件
for (String path : filePaths) {
merger.addSource(new File(path));
}
// 设置输出文件
merger.setDestinationFileName(outputPath);
// 执行合并
merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
System.out.println("已合并 " + filePaths.size() + " 个 PDF 文件");
}
// 拆分 PDF 为单独页面
public static void splitPDF(String filePath, String outputDir) throws IOException {
try (PDDocument document = PDDocument.load(new File(filePath))) {
Splitter splitter = new Splitter();
List<PDDocument> pages = splitter.split(document);
// 保存每一页为独立文件
for (int i = 0; i < pages.size(); i++) {
String outputPath = outputDir + "/page_" + (i+1) + ".pdf";
pages.get(i).save(outputPath);
pages.get(i).close();
System.out.println("已生成: " + outputPath);
}
}
}
public static void main(String[] args) throws IOException {
// 合并示例
mergePDFs(
Arrays.asList("contract1.pdf", "contract2.pdf", "contract3.pdf"),
"merged_contracts.pdf"
);
// 拆分示例
splitPDF("resource_plan.pdf", "output_pages");
}
}
复杂布局 - 生成财务报表
场景
生成带图形元素的数据报表。
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import java.awt.Color;
import java.io.IOException;
public class FinancialReport {
public static void main(String[] args) throws IOException {
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDPageContentStream cos = new PDPageContentStream(document, page);
// 绘制表格线
cos.setStrokingColor(Color.BLACK);
cos.setLineWidth(1f);
// 表头
cos.setNonStrokingColor(Color.LIGHT_GRAY);
cos.addRect(50, 700, 150, 30);
cos.fill();
cos.setNonStrokingColor(Color.WHITE);
// 表头文字
cos.beginText();
cos.setFont(PDType1Font.HELVETICA_BOLD, 12);
cos.newLineAtOffset(60, 712);
cos.showText("项目名称");
cos.endText();
// 更多表头...
// 柱状图示例
int[] salesData = {120, 180, 150, 200, 240, 300, 280};
String[] months = {"1月", "2月", "3月", "4月", "5月", "6月", "7月"};
int baseY = 300;
int barWidth = 40;
int spacing = 20;
for (int i = 0; i < salesData.length; i++) {
int x = 100 + i * (barWidth + spacing);
int barHeight = salesData[i];
// 柱体
cos.setNonStrokingColor(new Color(52, 152, 219));
cos.addRect(x, baseY, barWidth, barHeight);
cos.fill();
// 月份标签
cos.beginText();
cos.setFont(PDType1Font.HELVETICA, 10);
cos.newLineAtOffset(x + 5, baseY - 20);
cos.showText(months[i]);
cos.endText();
// 数值标签
cos.beginText();
cos.newLineAtOffset(x + 5, baseY + barHeight + 5);
cos.showText(String.valueOf(salesData[i]));
cos.endText();
}
// 坐标轴
cos.setLineWidth(2f);
cos.moveTo(50, baseY - 30); // X 轴
cos.lineTo(550, baseY - 30);
cos.stroke();
cos.moveTo(50, 100); // Y 轴
cos.lineTo(50, 600);
cos.stroke();
cos.close();
document.save("monthly_sales_report.pdf");
}
}
}
中文支持
// 正确加载中文字体(必须!)
PDType0Font.load(document, new File("C:/Windows/Fonts/simhei.ttf"));
内存优化
// 大文件处理时使用流式加载 PDDocument.load(file, MemoryUsageSetting.setupTempFileOnly());
异常处理
// 统一处理 PDF 相关异常
try (PDDocument doc = new PDDocument()) {
// 操作...
} catch (IOException e) {
System.err.println("PDF 处理错误: " + e.getMessage());
// 记录日志并恢复
}
版本兼容
- PDFBox 2.x 使用
PDPageContentStream - 升级到 3.x 后注意 API 变更
常见问题解决
| 问题现象 | 解决方案 |
|---|---|
| 中文乱码 | 加载系统 TTF 字体文件 |
| 内存溢出 | 使用临时文件模式 |
| 加密无法读取 | 提供密码参数 |
| 表单字段无法填写 | 检查字段是否被锁定 |
通过这些案例,你可以快速掌握 PDFBox 的主要功能,建议在实际项目中结合 Apache POI 处理 Excel 数据,iText 处理复杂 PDF 生成,实现更强大的文档处理能力。