【问题标题】:How to find table border lines in pdf using PDFBox?如何使用 PDFBox 在 pdf 中查找表格边框线?
【发布时间】:2016-02-15 12:22:16
【问题描述】:

我正在尝试在 pdf 中查找表格边框线。我用PrintTextLocations pdfBox 类来造词。现在我正在寻找形成表格的不同线条的坐标。我尝试使用org.apache.pdfbox.pdfviewer.PageDrawer,但找不到包含这些行的任何字符/图形。我尝试了两种方法:

第一:

Graphics g = null;
Dimension d = new Dimension();
d.setSize(700, 700);
PageDrawer pageDrawer = new PageDrawer();
pageDrawer.drawPage(g, myPage, d);

它给了我空指针异常。所以其次,我试图覆盖processStream 函数,但我无法获得任何中风。请帮帮我。我愿意使用任何其他提供表格中线条坐标的库。另一个快速的问题是,pdfbox 中的那些表格边框线是什么类型的对象?这些是图形还是这些字符?

这是我要解析的示例 pdf 的链接: http://stats.bls.gov/news.release/pdf/empsit.pdf 并尝试获取第 8 页上的表格行。

编辑:我遇到了另一个问题,在解析这个 pdf 的第 1 页时,我无法得到任何行,因为 printPath() 函数中的 pathIterator 是空的,尽管 strokePath() 函数为每一行调用。如何处理这个 pdf?

【问题讨论】:

  • @Jongware 在编辑后的帖子中找到链接。谢谢
  • 您使用哪个 PDFBox 版本?特别是,它是某个 1.8.X 版本还是当前的 2.0.0 候选版本?关于您的第一段代码:当您设置Graphics g = null 然后执行pageDrawer.drawPage(g, myPage, d) 时,预计会出现NullPointerException。你显然必须提供一个图形上下文来绘制......
  • 我的方法是获取页面的图形,因为 PDPage 不提供任何图形。我正在考虑调用drawPage 函数并获取一个可能包含线条图形的图形对象。
  • 我使用的是 1.8X 版本的 pdfBox
  • 关于另一个文档 cpid1511.pdf - 所有这些矢量图形操作都来自表格......但这些操作不仅仅是绘制 ,其中大多数实际上是绘制的表格内容的字母! (通常您使用包含精确绘图操作的字体的文本操作来绘制文本,但这里的字母直接是使用矢量图形绘制的......)

标签: pdf pdfbox


【解决方案1】:

在 1.8.* 版本中,PDFBox 解析功能以一种不太通用的方式实现,特别是 OperatorProcessor 实现与特定解析器类紧密相关,例如处理路径绘制操作的实现假定与PageDrawer 实例交互。

因此,除非想要复制和粘贴所有 OperatorProcessor 类并进行细微更改,否则必须从这样一个特定的解析器类派生。

因此,在您的情况下,我们也将从PageDrawer 派生我们的解析器,毕竟我们对路径绘制操作感兴趣:

public class PrintPaths extends PageDrawer
{
    //
    // constructor
    //
    public PrintPaths() throws IOException
    {
        super();
    }

    //
    // method overrides for mere path observation
    //
    // ignore text
    @Override
    protected void processTextPosition(TextPosition text) { }

    // ignore bitmaps
    @Override
    public void drawImage(Image awtImage, AffineTransform at) { }

    // ignore shadings
    @Override
    public void shFill(COSName shadingName) throws IOException { }

    @Override
    public void processStream(PDPage aPage, PDResources resources, COSStream cosStream) throws IOException
    {
        PDRectangle cropBox = aPage.findCropBox();
        this.pageSize = cropBox.createDimension();
        super.processStream(aPage, resources, cosStream);
    }

    @Override
    public void fillPath(int windingRule) throws IOException
    {
        printPath();
        System.out.printf("Fill; windingrule: %s\n\n", windingRule);
        getLinePath().reset();
    }

    @Override
    public void strokePath() throws IOException
    {
        printPath();
        System.out.printf("Stroke; unscaled width: %s\n\n", getGraphicsState().getLineWidth());
        getLinePath().reset();
    }

    void printPath()
    {
        GeneralPath path = getLinePath();
        PathIterator pathIterator = path.getPathIterator(null);

        double x = 0, y = 0;
        double coords[] = new double[6];
        while (!pathIterator.isDone()) {
            switch (pathIterator.currentSegment(coords)) {
            case PathIterator.SEG_MOVETO:
                System.out.printf("Move to (%s %s)\n", coords[0], fixY(coords[1]));
                x = coords[0];
                y = coords[1];
                break;
            case PathIterator.SEG_LINETO:
                double width = getEffectiveWidth(coords[0] - x, coords[1] - y);
                System.out.printf("Line to (%s %s), scaled width %s\n", coords[0], fixY(coords[1]), width);
                x = coords[0];
                y = coords[1];
                break;
            case PathIterator.SEG_QUADTO:
                System.out.printf("Quad along (%s %s) and (%s %s)\n", coords[0], fixY(coords[1]), coords[2], fixY(coords[3]));
                x = coords[2];
                y = coords[3];
                break;
            case PathIterator.SEG_CUBICTO:
                System.out.printf("Cubic along (%s %s), (%s %s), and (%s %s)\n", coords[0], fixY(coords[1]), coords[2], fixY(coords[3]), coords[4], fixY(coords[5]));
                x = coords[4];
                y = coords[5];
                break;
            case PathIterator.SEG_CLOSE:
                System.out.println("Close path");
            }
            pathIterator.next();
        }
    }

    double getEffectiveWidth(double dirX, double dirY)
    {
        if (dirX == 0 && dirY == 0)
            return 0;
        Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
        double widthX = dirY;
        double widthY = -dirX;
        double widthXTransformed = widthX * ctm.getValue(0, 0) + widthY * ctm.getValue(1, 0);
        double widthYTransformed = widthX * ctm.getValue(0, 1) + widthY * ctm.getValue(1, 1);
        double factor = Math.sqrt((widthXTransformed*widthXTransformed + widthYTransformed*widthYTransformed) / (widthX*widthX + widthY*widthY));
        return getGraphicsState().getLineWidth() * factor;
    }
}

(PrintPaths.java)

由于我们不想真正绘制页面,而只是提取将要绘制的路径,因此我们必须像这样剥离PageDrawer

此示例解析器输出路径绘制操作以显示如何执行此操作。显然,您可以将它们收集起来进行自动化处理...

你可以像这样使用解析器:

PDDocument document = PDDocument.load(resource);
List<?> allPages = document.getDocumentCatalog().getAllPages();
int i = 7; // page 8

System.out.println("\n\nPage " + (i+1));
PrintPaths printPaths = new PrintPaths();

PDPage page = (PDPage) allPages.get(i);
PDStream contents = page.getContents();
if (contents != null)
{
    printPaths.processStream(page, page.findResources(), page.getContents().getStream());
}

(ExtractPaths.java)

输出是:

Page 8
Move to (35.92070007324219 724.6490478515625)
Line to (574.72998046875 724.6490478515625), scaled width 0.5981000089123845
Stroke; unscaled width: 5.981

Move to (35.92070007324219 694.4660034179688)
Line to (574.72998046875 694.4660034179688), scaled width 0.5981000089123845
Stroke; unscaled width: 5.981

Move to (292.2610168457031 468.677001953125)
Line to (292.8590087890625 468.677001953125), scaled width 512.9430076434463
Stroke; unscaled width: 5129.43

Move to (348.9360046386719 468.677001953125)
Line to (349.53399658203125 468.677001953125), scaled width 512.9430076434463
Stroke; unscaled width: 5129.43

Move to (405.6090087890625 468.677001953125)
Line to (406.2070007324219 468.677001953125), scaled width 512.9430076434463
Stroke; unscaled width: 5129.43

Move to (462.281982421875 468.677001953125)
Line to (462.8799743652344 468.677001953125), scaled width 512.9430076434463
Stroke; unscaled width: 5129.43

Move to (518.9549560546875 468.677001953125)
Line to (519.553955078125 468.677001953125), scaled width 512.9430076434463
Stroke; unscaled width: 5129.43

Move to (35.92070007324219 725.447998046875)
Line to (574.72998046875 725.447998046875), scaled width 0.5981000089123845
Stroke; unscaled width: 5.981

Move to (35.92070007324219 212.5050048828125)
Line to (574.72998046875 212.5050048828125), scaled width 0.5981000089123845
Stroke; unscaled width: 5.981

非常奇特:垂直线实际上画得很短(约 0.6 单位)很粗(约 513 单位)水平线...

【讨论】:

  • 请问Scaled Width是什么意思?
  • 这里的“未缩放宽度”简单来说就是Graphics状态下的线宽值。但这不一定是您最终在输出中感知到的宽度,因为就像所有其他内容行一样,也受当前转换矩阵的影响。这里的“缩放宽度”值是尝试计算您最终会在输出中感知到的宽度。由于变换不仅可以缩放,而且实际上还可以旋转和剪切,getEffectiveWidth 计算这个值的方法不仅仅是一个标量乘法。
猜你喜欢
  • 1970-01-01
  • 2013-01-14
  • 1970-01-01
  • 2011-02-11
  • 2023-04-07
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多