Apache PDFBox案例

wen java案例 113

Apache PDFBox 实战案例详解

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

Apache PDFBox案例


创建带格式的 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 生成,实现更强大的文档处理能力。

抱歉,评论功能暂时关闭!

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝