提取具有更大字体粗细的文本

Extracting text with greater font weight

我有一些文档预测了某些文本的位置,我正在尝试提取这些文本。在大多数情况下,它工作得很好,但我在处理文本稍粗的部分文档时遇到了困难。

细文本:

粗文本:

我知道在这个分辨率下很难区分,但如果你看一下 MO DAY YEAR TIME (2400) 部分,你会发现第二个更厚。

精简的文字完全符合我的预期:

2015 年 9 月 28 日 0820

然而,粗版本给了我每个字符的三元组,每个重复字符之间有白色 space:

1 1 11 1 1/ / /1 1 19 9 9/ / / 2 2 20 0 01 1 15 5 5 1 1 17 7 70 0 02 2 2

我正在使用以下代码从文档中提取文本:

public static Document GetDocumentInfo(string fileName)
{
    // Using 11 in x 8.5 in dimensions at 72 dpi.
    var boudingBoxes = new[]
    {
        new RectangleJ(446, 727, 85, 14),
        new RectangleJ(396, 702, 43, 14),
        new RectangleJ(306, 680, 58, 7),
        new RectangleJ(378, 680, 58, 7),
        new RectangleJ(446, 680, 45, 7),
        new RectangleJ(130, 727, 29, 10),
        new RectangleJ(130, 702, 29, 10)
    };

    var data = GetPdfData(fileName, 1, boudingBoxes);

    // I would populated the new document with extracted data
    // here, but it's not important for the example.
    var doc = new Document();
    return doc;
}

public static string[] GetPdfData(string fileName, int pageNum, RectangleJ[] boundingBoxes)
{
    // Omitted safety checks, as they're not important for the example.

    var data = new string[boundingBoxes.Length];

    using (var reader = new PdfReader(fileName))
    {
        if (reader.NumberOfPages < 1)
        {
            return null;
        }

        RenderFilter filter;
        ITextExtractionStrategy strategy;

        for (var i = 0; i < boundingBoxes.Length; ++i)
        {
            filter = new RegionTextRenderFilter(boundingBoxes[i]);
            strategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), filter);
            data[i] = PdfTextExtractor.GetTextFromPage(reader, pageNum, strategy);
        }

        return data;
    }
}

显然,如果没有别的办法,我可以在读入它们后去掉重复的字符,因为有一个非常明显的模式,但我宁愿找到一个正确的方法而不是 hack。在过去的几个小时里,我试着四处寻找,但找不到遇到类似问题的人。

编辑:

我终于遇到了这个问题:

Text Extraction Duplicate Bold Text

...并且在评论中指出,一些质量较低的 PDF 制作者会复制文本以模拟粗体,因此这是可能发生的事情之一。但是,提到在该位置省略重复文本,我不知道如何实现,因为我的这部分代码...

data[i] = PdfTextExtractor.GetTextFromPage(reader, pageNum, strategy);

...在任何指定位置完全读入重复的文本。

编辑:

我现在遇到过将内容复制多达四次以模拟厚度的文档。这是一种非常奇怪的做事方式,但我相信这种方法的设计者有他们的理由。

编辑:

我制作了 A 解决方案(见我的回答)。它在数据已经提取并删除所有重复项后对其进行处理。理想情况下,这应该在提取过程中完成,但它可能会变得非常复杂,而且这似乎是一种非常干净和简单的方法来完成同样的任务。

正如@mkl 所建议的那样,解决此问题的一种方法是覆盖 LocationExtractionStrategy;然而,事情变得相当复杂,因为它需要比较在特定边界找到的每个字符的位置。我尝试做一些研究以实现这一目标,但由于文档不完善,它有点失控了。

因此,我创建了一个 post-processing 方法,大致基于@TheMuffinMan 的建议,以清除任何重复。我决定不处理像素,而是处理已知静态位置的字符数异常。就我而言,我知道提取的第二个数据片段永远不会超过三个字符,所以这对我来说是一个很好的比较点。如果您知道文档布局,则可以在其上使用任何您知道长度始终固定的内容。

用我原来post中列出的方法提取数据后,我检查第二条数据的长度是否大于三。如果它 returns 为真,那么我将给定的长度除以三,因为这是它可以拥有的最多字符,并且由于所有重复的长度都是偶数,我知道我会得到偶数的重复案例:

var data = GetPdfData(fileName, 1, boudingBoxes);

if (data[1].Length > 3)
{
    var count = data[1].Length / 3;
    for (var i = 0; i < data.Length; ++i)
    {
        data[i] = RemoveRepetitions(data[i], count);
    }
}

如您所见,我然后遍历数据并将每个片段传递给 RemoveRepetitions() 方法:

public static string RemoveRepetitions(string original, int count)
{
    if (original.Length % count != 0)
    {
        return null;
    }
    var temp = new char[original.Length / count];
    for (int i = 0; i < original.Length; i += count)
    {
        temp[i / count] = original[i];
    }

    return new string(temp);
}

此方法采用我们之前计算的字符串和预期重复次数。需要注意的一件事是,我不必担心重复过程中插入的空格,如原始 post 中的示例所示,因为 count 将代表总数应该只有一个的字符。