【问题标题】:Reading legacy Word forms checkboxes converted to PDF阅读转换为 PDF 的旧 Word 表单复选框
【发布时间】:2016-11-14 23:52:45
【问题描述】:

我们的客户以 PDF 表单的形式向我们发送订单,该表单是从使用旧表单构建的 Word 文档生成的。

目前,我们客户中心的人员正在将订单输入我们的系统,但我们决定尝试将这项任务自动化。

我可以通过一个简单的 PdfReader 每页阅读 PDF 的内容:

    public static string GetPdfText(string path)
    { 
        var text = string.Empty;
        using (var reader = new PdfReader(path))
        {
            for (var page = 1; page <= reader.NumberOfPages; page++)
            {
                text += PdfTextExtractor.GetTextFromPage(reader, page);
            }
        }
        return text;
    }

但不是复选框...

在遍历 PDF 中的每个对象时,我能够将复选框检测为字典,但我无法将它们与其他对象区分开来或读取值...

    public static IEnumerable<PdfDictionary> ReadCheckboxes(string path)
    {
        using (var reader = new PdfReader(path))
        {
            var checkboxes = new List<PdfDictionary>();
            for (var i = 0; i < reader.XrefSize; i++)
            {
                var pdfObject = reader.GetPdfObject(i);
                checkboxes.Add((PdfDictionary) pdfObject);
            }
            return checkboxes;
        }
    }

我错过了什么?我也试过阅读 AcroFields,但它们是空的......

我已上传带有旧复选框 here 的示例 PDF。

目前没有选项可以在我们的系统之间集成或对基础 PDF 或 Word 文档进行任何更改。

【问题讨论】:

  • 你的 PDF 没有任何交互式复选框,只是画线看起来像复选框。可能可以识别内容流中的框,但这并非易事。
  • ahoibakk,我认为提取类似复选框的图形及其分别表示的状态作为数据并不难,例如“位置x0,y0的复选框,选中;位置x1,y1的复选框,不是已检查;……”这样就够了吗? IE。您的“表格”是否足够静态,以便这些位置可以识别复选框的含义?
  • 我查看了您的 PDF 的内容流以及我在 Word 中创建的一些文件,说明看起来非常一致。一个空复选框由宽度和高度相同的单个“re”(矩形)运算符呈现。选中(Xed)框是相同的,只是它们后面紧跟着两个“m”(moveTo)/“l”(lineTo)运算符。找到这些会很容易,弄清楚它们接近的单词将是真正棘手的部分。
  • @mkl 是的,我的表格是静态的,这样就可以了!任何例子都非常感谢!
  • @JoelGeraci 谢谢,我会在周一早上看看内容流!

标签: c# pdf checkbox itext


【解决方案1】:

OP 在 cmets 中指出,一个解决方案返回一个输出,如“位置 x0、y0,选中;位置 x1、y1 处的复选框,未选中;。 ..”就足够了,即他的“表格”足够静态,因此这些位置允许识别各个复选框的含义。因此,这里是这个变体的一个实现。

我刚刚看到问题被标记为,而我已经使用 Java 实现了搜索。这应该不是太大的问题,代码应该很容易移植。如果移植有问题,我会在此处添加 C# 版本。

由于复选框是使用矢量图形绘制的,因此 OP 已经使用的文本提取找不到它们。不过幸运的是,iText 解析框架也可用于查找矢量图形。

因此,我们首先需要一个收集盒子的ExtRenderListener(iTextSharp 中的IExtRenderListener)。它只有接口方法modifyPath 和renderPath 的重要实现:

@Override
public void modifyPath(PathConstructionRenderInfo renderInfo)
{
    switch (renderInfo.getOperation())
    {
    case PathConstructionRenderInfo.RECT:
    {
        float x = renderInfo.getSegmentData().get(0);
        float y = renderInfo.getSegmentData().get(1);
        float w = renderInfo.getSegmentData().get(2);
        float h = renderInfo.getSegmentData().get(3);
        rectangle = new Rectangle(x, y, x+w, y+h);
    }
    case PathConstructionRenderInfo.MOVETO:
    {
        float x = renderInfo.getSegmentData().get(0);
        float y = renderInfo.getSegmentData().get(1);
        moveToVector = new Vector(x, y, 1);
        lineToVector = null;
        break;
    }
    case PathConstructionRenderInfo.LINETO:
    {
        if (moveToVector != null)
        {
            float x = renderInfo.getSegmentData().get(0);
            float y = renderInfo.getSegmentData().get(1);
            lineToVector = new Vector(x, y, 1);
        }
        break;
    }
    default:
        moveToVector = null;
        lineToVector = null;
    }
}

@Override
public Path renderPath(PathPaintingRenderInfo renderInfo)
{
    if (renderInfo.getOperation() != PathPaintingRenderInfo.NO_OP)
    {
        if (rectangle != null)
        {
            Vector a = new Vector(rectangle.getLeft(), rectangle.getBottom(), 1).cross(renderInfo.getCtm());
            Vector b = new Vector(rectangle.getRight(), rectangle.getBottom(), 1).cross(renderInfo.getCtm());
            Vector c = new Vector(rectangle.getRight(), rectangle.getTop(), 1).cross(renderInfo.getCtm());
            Vector d = new Vector(rectangle.getLeft(), rectangle.getTop(), 1).cross(renderInfo.getCtm());

            Box box = new Box(new LineSegment(a, c), new LineSegment(b, d));
            boxes.add(box);

        }
        if (moveToVector != null && lineToVector != null)
        {
            if (!boxes.isEmpty())
            {
                Vector from = moveToVector.cross(renderInfo.getCtm());
                Vector to = lineToVector.cross(renderInfo.getCtm());

                boxes.get(boxes.size() - 1).selectDiagonal(new LineSegment(from, to));
            }
        }
    }

    moveToVector = null;
    lineToVector = null;
    rectangle = null;
    return null;
}

Vector moveToVector = null;
Vector lineToVector = null;
Rectangle rectangle = null;

public Iterable<Box> getBoxes()
{
    return boxes;
}

final List<Box> boxes = new ArrayList<Box>();

(来自CheckBoxExtractionStrategy.java)

它使用一个辅助类 Box 使用它们各自的对角线对复选框进行建模:

public class Box
{
    public LineSegment getDiagonal()
    {
        return diagonalA;
    }

    public boolean isChecked()
    {
        return selectedA && selectedB;
    }

    Box(LineSegment diagonalA, LineSegment diagonalB)
    {
        this.diagonalA = diagonalA;
        this.diagonalB = diagonalB;
    }

    void selectDiagonal(LineSegment diagonal)
    {
        if (approximatelyEquals(diagonal, diagonalA))
            selectedA = true;
        else if (approximatelyEquals(diagonal, diagonalB))
            selectedB = true;
    }

    boolean approximatelyEquals(LineSegment a, LineSegment b)
    {
        float permissiveness = a.getLength() / 10.0f;
        if (approximatelyEquals(a.getStartPoint(), b.getStartPoint(), permissiveness) &&
                approximatelyEquals(a.getEndPoint(), b.getEndPoint(), permissiveness))
            return true;
        if (approximatelyEquals(a.getStartPoint(), b.getEndPoint(), permissiveness) &&
                approximatelyEquals(a.getEndPoint(), b.getStartPoint(), permissiveness))
            return true;
        return false;
    }

    boolean approximatelyEquals(Vector a, Vector b, float permissiveness)
    {
        return a.subtract(b).length() < permissiveness;
    }

    boolean selectedA = false;
    boolean selectedB = false;
    final LineSegment diagonalA, diagonalB;
}

(CheckBoxExtractionStrategy.java 中的内部类)

像这样将它应用到示例文档中:

for (int page = 1; page <= pdfReader.getNumberOfPages(); page++)
{
    System.out.printf("\nPage %s\n====\n", page);

    CheckBoxExtractionStrategy strategy = new CheckBoxExtractionStrategy();
    PdfReaderContentParser parser = new PdfReaderContentParser(pdfReader);
    parser.processContent(page, strategy);

    for (Box box : strategy.getBoxes())
    {
        Vector basePoint = box.getDiagonal().getStartPoint();
        System.out.printf("at %s, %s - %s\n", basePoint.get(Vector.I1), basePoint.get(Vector.I2),
                box.isChecked() ? "checked" : "unchecked");
    }
}

一个人得到输出

Page 1
====
at 73.104, 757.8 - checked
at 86.544, 757.8 - checked
at 99.984, 757.8 - unchecked

对于 OP 的文档

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    • 2016-02-21
    • 2015-11-26
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 2010-12-31
    相关资源
    最近更新 更多