【问题标题】:pdfbox 2.0.2 > Calling of PageDrawer.processPage method caught exceptionspdfbox 2.0.2 > PageDrawer.processPage 方法的调用捕获异常
【发布时间】:2016-08-13 09:45:39
【问题描述】:

作为 pdfbox 2.0.2 (https://github.com/apache/pdfbox/tree/2.0.2) 用户的新手,我想获取页面 (PDPage) 的所有描边线(例如表格的列和行边框),因此我创建了以下类: 包org.apache.pdfbox.rendering;

import java.awt.geom.GeneralPath;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;

import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.rendering.PageDrawer;
import org.apache.pdfbox.rendering.PageDrawerParameters;

public class LineCatcher {
    private PageDrawer pageDrawer;
    private PDDocument document;
    private PDFRenderer pdfRenderer;
    private PDPage page;

    public LineCatcher(URI pdfSrcURI) throws IllegalArgumentException, 
        MalformedURLException, IOException {
        this.document = PDDocument.load(IOUtils.toByteArray(pdfSrcURI));
        this.pdfRenderer = new PDFRenderer(this.document);
    }
    public GeneralPath getLinePath(int pageIndex) throws IOException {
        this.page = this.document.getPage(pageIndex);
        PageDrawerParameters parameters = new PageDrawerParameters (this.pdfRenderer, this.page);
        this.pageDrawer = new PageDrawer(parameters);
        this.pageDrawer.processPage(this.page); //catches exception here
        return this.pageDrawer.getLinePath();
    }
}

根据我的理解,为了得到一个页面的行路径,首先要处理这个页面,所以我在该行中调用了processPage方法,这里我标记了“catch exception here”。它意外地在提到的行中捕获了 NullPointer Excpetions。异常信息如下:

java.lang.NullPointerException
  at org.apache.pdfbox.rendering.PageDrawer.fillPath(PageDrawer.java:599)
  at org.apache.pdfbox.contentstream.operator.graphics.FillNonZeroRule.process(FillNonZeroRule.java:36)
  at org.apache.pdfbox.contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:815)
  at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:472)
  at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:446)
  at org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:149)
  at org.apache.pdfbox.rendering.LineCatcher.getLinePath(LineCatcher.java:33)
  at org.apache.pdfbox.rendering.TestLineCatcher.testGetLinePath(TestLineCatcher.java:21)

有没有人可以就我的逻辑提供一些建议或帮助调试代码?提前致谢

【问题讨论】:

  • 肯定错了……getLinePath()就是在处理页面的时候获取当前行路径。每次填充/描边后,它会重置为空。这不是您想的那样,即包含页面所有行的路径。我会看看我是否能想出更好的东西,例如抓住中风算子。

标签: java pdfbox


【解决方案1】:

扩展 PageDrawer 并没有真正起作用,所以我扩展了 PDFGraphicsStreamEngine,结果如下。我做了一些在 PageDrawer 中完成的事情。要收集线条,请在 strokePath() 中评估形状,或者在我包含 println 的其他方法中收集点和线条。

public class LineCatcher extends PDFGraphicsStreamEngine
{
    private final GeneralPath linePath = new GeneralPath();
    private int clipWindingRule = -1;

    public LineCatcher(PDPage page)
    {
        super(page);
    }

    public static void main(String[] args) throws IOException
    {
        try (PDDocument document = PDDocument.load(new File("Test.pdf")))
        {
            PDPage page = document.getPage(0);
            LineCatcher test = new LineCatcher(page);
            test.processPage(page);
        }
    }

    @Override
    public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException
    {
        System.out.println("appendRectangle");
        // to ensure that the path is created in the right direction, we have to create
        // it by combining single lines instead of creating a simple rectangle
        linePath.moveTo((float) p0.getX(), (float) p0.getY());
        linePath.lineTo((float) p1.getX(), (float) p1.getY());
        linePath.lineTo((float) p2.getX(), (float) p2.getY());
        linePath.lineTo((float) p3.getX(), (float) p3.getY());

        // close the subpath instead of adding the last line so that a possible set line
        // cap style isn't taken into account at the "beginning" of the rectangle
        linePath.closePath();
    }

    @Override
    public void drawImage(PDImage pdi) throws IOException
    {
    }

    @Override
    public void clip(int windingRule) throws IOException
    {
        // the clipping path will not be updated until the succeeding painting operator is called
        clipWindingRule = windingRule;

    }

    @Override
    public void moveTo(float x, float y) throws IOException
    {
        linePath.moveTo(x, y);
        System.out.println("moveTo");
    }

    @Override
    public void lineTo(float x, float y) throws IOException
    {
        linePath.lineTo(x, y);
        System.out.println("lineTo");
    }

    @Override
    public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
    {
        linePath.curveTo(x1, y1, x2, y2, x3, y3);
        System.out.println("curveTo");
    }

    @Override
    public Point2D getCurrentPoint() throws IOException
    {
        return linePath.getCurrentPoint();
    }

    @Override
    public void closePath() throws IOException
    {
        linePath.closePath();
    }

    @Override
    public void endPath() throws IOException
    {
        if (clipWindingRule != -1)
        {
            linePath.setWindingRule(clipWindingRule);
            getGraphicsState().intersectClippingPath(linePath);
            clipWindingRule = -1;
        }
        linePath.reset();

    }

    @Override
    public void strokePath() throws IOException
    {
        // do stuff
        System.out.println(linePath.getBounds2D());

        linePath.reset();
    }

    @Override
    public void fillPath(int windingRule) throws IOException
    {
        linePath.reset();
    }

    @Override
    public void fillAndStrokePath(int windingRule) throws IOException
    {
        linePath.reset();
    }

    @Override
    public void shadingFill(COSName cosn) throws IOException
    {
    }
}

2019 年 3 月 19 日更新:另见 mkl here 的后续回答。

【讨论】:

  • 非常感谢您的大力帮助 :) 请问linePath 是final 的原因是什么?
  • 这是 Netbeans 的推荐,因为它永远不会被覆盖。这并不重要。
  • 啊哈,与List 如此相似?我记得列表应该被初始化为最终的。 wìndingrule 是什么?似乎这无关紧要,但最好能有所了解。而且,应该有返回linePath的方法吧?
  • 重新缠绕规则,请查看 PDF 32000 规范(谷歌)。它可能与您无关,因为您只对线条感兴趣,而不是填充形状。重新返回 linepath,您可以更改代码以将 linePath 替换为 List,然后在 stroke 方法中添加 linePath。我的回答旨在解释如何获取原始内容,您需要将其调整为您尝试对表格执行的操作。如果您愿意,我可以将我的答案更改为我刚才提到的内容。只要说出来,我就去做。
  • 在方法 - strokePath()、fillPath() 和 endPath() 中,调用 GeneralPath.reset() 方法。为什么只在这 3 种方法中调用它们?我仍然无法理解它的逻辑。此外,如果我想将 GeneralPath 添加到列表中,我必须将 GeneralPath.reset 调用替换为新的 GeneralPath 调用,对吗?
最近更新 更多