You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PDFBOX回退PDF的最后一次增量更新(签名场景)

如何用Apache PDFBox回退PDF的最后一次增量更新

我之前刚好解决过类似的需求,用Apache PDFBox回退PDF的最后一次增量更新其实核心就是定位到倒数第二个%%EOF标记,然后截断文件到这个位置就行——毕竟PDF的增量更新就是在原文档末尾追加新内容,每段增量末尾都会带%%EOF。下面是具体的实现思路和代码:

核心原理

PDF的增量更新(比如数字签名后的追加内容)是直接在原始文档字节流的末尾添加新的修订数据,每个完整的修订段末尾都会以%%EOF标记结束。所以要回退最后一次增量,只需要找到文件中倒数第二个%%EOF的位置,然后将文件截断到该位置即可,这样就会丢弃最后一次追加的所有内容,回到上一次修订的状态。

代码实现

这里我们直接操作PDF的原始字节流(避免PDFBox解析重写文档导致的格式变化),结合简单的字符串查找来定位%%EOF标记:

import org.apache.commons.io.IOUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class PdfRollbackLastIncrement {
    public static void rollbackLastIncrement(File inputPdf, File outputPdf) throws IOException {
        // 读取原始PDF的全部字节内容
        byte[] pdfBytes = IOUtils.toByteArray(new FileInputStream(inputPdf));
        String pdfString = new String(pdfBytes);
        
        // 找到所有%%EOF标记的结束位置
        List<Integer> eofEndPositions = new ArrayList<>();
        final String EOF_MARKER = "%%EOF";
        int currentIndex = pdfString.indexOf(EOF_MARKER);
        
        while (currentIndex != -1) {
            // 记录标记的结束索引(标记长度+起始索引)
            eofEndPositions.add(currentIndex + EOF_MARKER.length());
            // 继续查找下一个标记
            currentIndex = pdfString.indexOf(EOF_MARKER, currentIndex + 1);
        }
        
        // 检查是否存在至少两次修订(至少两个%%EOF)
        if (eofEndPositions.size() >= 2) {
            // 取倒数第二个%%EOF的结束位置作为截断点
            int rollbackPoint = eofEndPositions.get(eofEndPositions.size() - 2);
            byte[] rolledBackBytes = new byte[rollbackPoint];
            System.arraycopy(pdfBytes, 0, rolledBackBytes, 0, rollbackPoint);
            
            // 将截断后的字节写入新文件
            try (FileOutputStream fos = new FileOutputStream(outputPdf)) {
                fos.write(rolledBackBytes);
            }
            System.out.println("成功回退最后一次增量更新,输出文件:" + outputPdf.getAbsolutePath());
        } else {
            throw new IllegalArgumentException("该PDF没有足够的增量修订,无法执行回退操作");
        }
    }

    // 测试示例
    public static void main(String[] args) {
        try {
            rollbackLastIncrement(
                new File("signed_document.pdf"),
                new File("restored_original.pdf")
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意事项

  • 依赖说明:代码中用到了org.apache.commons.io.IOUtils来简化文件读取,你需要确保项目中引入了Apache Commons IO依赖,或者替换为原生Java IO的读取方式(比如用FileInputStream逐字节读取)。
  • 特殊情况处理:少数PDF的%%EOF标记可能后面跟着空白字符或注释行,这种情况下可以调整查找逻辑,比如查找%%EOF后面的换行符,确保定位到的是完整的修订结束点。
  • 准确性:这种直接操作字节流的方式不会解析PDF内容,所以能100%保留原始文档的格式和内容,比通过PDFBox加载后再保存的方式更准确(后者会重写整个文档结构)。

内容的提问来源于stack exchange,提问作者Abhishek Dadhich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:01:03