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

使用PDFBox移除PDF全页边框/边距及解决提取格式丢失问题

嘿,我来帮你搞定这两个PDFBox相关的问题,都是项目里常碰到的场景,给你详细拆解解决办法:

一、使用PDFBox移除PDF所有页面的边框/边距

PDF里的“边框/边距”通常分两种情况,得针对性处理:

情况1:边距是页面裁剪框(CropBox)导致的

很多PDF会通过设置CropBox来限制页面显示/打印的区域,看起来像是有边距。这种情况直接调整CropBox和页面的媒体框(MediaBox)一致就行,代码示例如下:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import java.io.File;
import java.io.IOException;

public class RemoveMargins {
    public static void main(String[] args) throws IOException {
        try (PDDocument document = PDDocument.load(new File("input.pdf"))) {
            for (PDPage page : document.getPages()) {
                // 将CropBox设置为和MediaBox一样大,移除裁剪边距
                page.setCropBox(page.getMediaBox());
                // 如果是想自定义裁剪区域,也可以手动设置CropBox的坐标,比如:
                // page.setCropBox(new PDRectangle(0, 0, page.getMediaBox().getWidth(), page.getMediaBox().getHeight()));
            }
            document.save("output_no_margins.pdf");
        }
    }
}

情况2:边框是内容层绘制的线条

如果边框是PDF内容里直接绘制的线条(比如模板里的装饰框),就得解析页面的内容流,过滤掉绘制边框的指令。可以通过继承PDFStreamEngine来实现:

import org.apache.pdfbox.contentstream.PDFStreamEngine;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class RemoveBorderLines extends PDFStreamEngine {
    @Override
    protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
        String opName = operator.getName();
        // 过滤掉绘制路径并填充/描边的指令,比如"f"(填充)、"S"(描边)、"B"(填充+描边)
        // 注意:如果页面还有其他需要保留的线条,得根据线条的坐标/宽度等特征做更精准的判断
        if (!opName.equals("f") && !opName.equals("S") && !opName.equals("B")) {
            super.processOperator(operator, operands);
        }
    }

    public static void main(String[] args) throws IOException {
        try (PDDocument document = PDDocument.load(new File("input.pdf"))) {
            RemoveBorderLines remover = new RemoveBorderLines();
            for (PDPage page : document.getPages()) {
                remover.processPage(page);
                // 替换页面的内容流为处理后的版本
                page.setContents(remover.getPageStream());
            }
            document.save("output_no_borders.pdf");
        }
    }
}

提示:如果需要精准过滤边框,得先分析边框线条的坐标范围、线宽等特征,在processOperator里增加判断逻辑,避免误删其他有用线条。

二、解决非结构化表格导致的文本提取格式丢失问题

这个问题太常见了!很多PDF模板里的表格根本不是结构化的表格元素,只是用绝对定位的文本块+绘制的线条拼出来的。PDFBox默认的PDFTextStripper只会按文本在内容流里的读取顺序输出,完全不管它们的位置关系,所以提取后表格格式全乱了。

给你两个实用的解决方案:

方案1:基于文本坐标还原表格结构

通过继承PDFTextStripper,重写processTextPosition方法收集每个文本块的坐标信息,然后按行(Y坐标分组)、列(X坐标排序)来整理内容:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TableTextExtractor extends PDFTextStripper {
    private List<TextPosition> textPositions = new ArrayList<>();

    public TableTextExtractor() throws IOException {
        super();
    }

    @Override
    protected void processTextPosition(TextPosition text) {
        textPositions.add(text);
    }

    public static void main(String[] args) throws IOException {
        try (PDDocument document = PDDocument.load(new File("input_with_table.pdf"))) {
            TableTextExtractor extractor = new TableTextExtractor();
            extractor.getText(document);

            // 按Y坐标分组(同一行的文本Y坐标接近),这里的阈值可以根据实际PDF调整
            Map<Float, List<TextPosition>> rows = extractor.textPositions.stream()
                    .collect(Collectors.groupingBy(t -> Math.round(t.getY() * 100) / 100.0f));

            // 遍历每一行,按X坐标排序,拼接成表格行
            try (FileWriter writer = new FileWriter("output_table.txt")) {
                rows.entrySet().stream()
                        .sorted(Comparator.comparingDouble(Map.Entry::getKey).reversed()) // PDF的Y坐标是从下往上的,所以倒序
                        .forEach(entry -> {
                            List<String> rowTexts = entry.getValue().stream()
                                    .sorted(Comparator.comparingDouble(TextPosition::getX))
                                    .map(TextPosition::getUnicode)
                                    .collect(Collectors.toList());
                            try {
                                writer.write(String.join("\t", rowTexts) + "\n");
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
            }
        }
    }
}

这个方法能把同一行的文本按左右顺序排列,用制表符分隔,还原表格的列结构。

方案2:结合表格线条定位单元格

如果PDF里有表格的边框线条,可以先解析页面的路径找到单元格的边界,再把文本块匹配到对应的单元格里,这样精度更高。核心思路是:

  1. 用PDFStreamEngine解析页面的路径,记录所有单元格的矩形区域;
  2. 收集文本块的坐标,判断每个文本属于哪个单元格;
  3. 按单元格的行和列顺序整理输出。

这个方案逻辑稍复杂,但处理复杂表格时效果更好,你可以根据实际需求调整细节。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:46:34