【问题标题】:How do I reconcile these text positions and line positions with PDFBox?如何使用 PDFBox 协调这些文本位置和行位置?
【发布时间】:2017-09-29 16:47:00
【问题描述】:

我为表格中的行返回的 y 坐标似乎超出了文本的坐标。似乎有一些转变正在进行,但我找不到它。如果可能的话,我想在下面扩展的 PDFGraphicsStreamEngine 范围内解决问题,而不必回到绘图板上使用 PDFBox 中可用的其他输入流。

我已扩展PDFTextStripper 以获取页面上每个文本字形的位置:

public class MyPDFTextStripper extends PDFTextStripper {

    private List<TextPosition> tps;

    public MyPDFTextStripper() throws IOException {
        tps = new ArrayList<>();
    }

    @Override
    protected void writeString
            (String text,
             List<TextPosition> textPositions)
            throws IOException {
        tps.addAll(textPositions);
    }

    List<TextPosition> getTps() {
        return tps;
    }
}

我已经扩展了PDFGraphicsStreamEngine 以将页面上的每一行提取为Line2D

public class LineCatcher extends PDFGraphicsStreamEngine
{
    private final GeneralPath linePath = new GeneralPath();
    private List<Line2D> lines;

    LineCatcher(PDPage page)
    {
        super(page);
        lines = new ArrayList<>();
    }

    List<Line2D> getLines() {
        return lines;
    }

    @Override
    public void strokePath() throws IOException
    {
        Rectangle2D rect = linePath.getBounds2D();
        Line2D line = new Line2D.Double(rect.getX(), rect.getY(),
                rect.getX() + rect.getWidth(),
                rect.getY() + rect.getHeight());
        lines.add(line);
        linePath.reset();
    }

    @Override
    public void moveTo(float x, float y) throws IOException
    {linePath.moveTo(x, y);}
    @Override
    public void lineTo(float x, float y) throws IOException
    {linePath.lineTo(x, y);}
    @Override
    public Point2D getCurrentPoint() throws IOException
    {return linePath.getCurrentPoint();}

    //all other overridden methods can be left empty for the purposes of this problem.
}

我写了一个简单的程序来演示这个问题:

public class PageAnalysis {
    public static void main(String[] args) {
        try (PDDocument doc = PDDocument.load(new File("onePage.pdf"))) {
            PDPage page = doc.getPage(0);

            MyPDFTextStripper ts = new MyPDFTextStripper();
            ts.getText(doc);
            List<TextPosition> tps = ts.getTps();

            System.out.println("Y coordinates in text:");
            Set<Integer> ySet = new HashSet<>();
            for (TextPosition tp: tps) {
                ySet.add((int)tp.getY());
            }
            List<Integer> yList = new ArrayList<>(ySet);
            Collections.sort(yList);
            for (int y: yList){
                System.out.print(y + "\t");
            }
            System.out.println();


            System.out.println("Y coordinates in lines:");
            LineCatcher lineCatcher = new LineCatcher(page);
            lineCatcher.processPage(page);
            List<Line2D> lines = lineCatcher.getLines();
            ySet = new HashSet<>();
            for (Line2D line: lines) {
                ySet.add((int)line.getY2());
            }
            yList = new ArrayList<>(ySet);
            Collections.sort(yList);
            for (int y: yList){
                System.out.print(y + "\t");
            }
            System.out.println();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

由此产生的输出是:

Y coordinates in text:
66  79  106 118 141 153 171 189 207 225 243 261 279 297 315 333 351 370 388 406 424 442 460 478 496 514 780 
Y coordinates in lines:
322 340 358 376 394 412 430 448 466 484 502 520 538 556 574 593 611 629 647 665 683 713

文本列表中的最后一个数字对应于底部页码的 y 坐标。我找不到线条的 y 坐标发生了什么,尽管它似乎是那些已经被转换的(媒体框在这里与文本相同,并且适合文本位置) .当前的变换矩阵对于 yScaling 也有 1.0。

【问题讨论】:

    标签: java pdf pdfbox


    【解决方案1】:

    确实,PDFTextStripper 有将坐标转换为非常非 PDF 的坐标系的坏习惯,原点位于页面左上角,y 坐标向下增加。

    因此,对于TextPosition tp,您应该使用

    tp.getY()
    

    改为

    tp.getTextMatrix().getTranslateY()
    

    不幸的是,即使它们更接近实际的 PDF 默认坐标系,这些坐标仍可能会被转换,参见。 this answer: 这些坐标仍然被转换为原点在裁剪框的左下角。

    因此,你真的需要这样的东西:

    tp.getTextMatrix().getTranslateY() + cropBox.getLowerLeftY()
    

    其中cropBoxPDRectangle 检索为

    PDRectangle cropBox = doc.getPage(n).getCropBox();
    

    其中n 是包含该内容的页面的编号。

    【讨论】:

    • 谢谢。我一直在使用TextPosition 很长时间,我忘记了它不是默认坐标系。确实,它在应用程序中是如此根深蒂固,我将看看我是否可以扭转您建议的转变以引导我远离它。它可能不是 PDF,但它可能是目前最简单的解决方案。非常感谢您的回答。
    • 好吧,在文本位置反转转换更容易,因为已经可以从其中访问几乎原始的坐标。不过,也可以检索转换细节并转换线坐标。如果您的应用程序依赖于特定的文本位置坐标系,您可以尝试使用它。
    • 工作完成。我采纳了您最初的建议,并在应用程序的这个小角落中行坐标发挥作用,我转换了 TextPosition 坐标以进行比较。非常感谢您的彻底回答。
    猜你喜欢
    • 1970-01-01
    • 2013-12-17
    • 2019-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-30
    • 2013-08-30
    相关资源
    最近更新 更多