Apache PDFBox案例

wen java案例 2

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

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