【问题标题】:How to parse untagged pdf file with iText如何使用 iText 解析未标记的 pdf 文件
【发布时间】:2014-01-25 19:43:09
【问题描述】:

我想用 iText 解析这个文件 (http://www.bbm.ca/_documents/top_30_tv_programs_english/2011/nat01032011.pdf)。问题是它没有标记,所以我无法获取 XML 文件。我决定从中提取文本,我认为例如第一行将是:

1\specialCharWJC:PLAYOFFS CANADA\specialCharTSN+\specialCharM.W....\specialChar19:30\specialChar21:57\specialChar5133

我为第一行提取的文本是

1 WJC:PLAYOFFS CANADA TSN+ M.W.... 19:30 21:57 5133

我使用以下方法提取了文本:

PdfReader reader = new PdfReader(filename);
String str = PdfTextExtractor.getTextFromPage(reader, 1);

PDF 查看器如何知道加拿大在第二列而不是第三列。

我目前的解决方案是使用http://www.idrsolutions.com/online-pdf-to-html5-converter/ 将 pdf 文件转换为 html5,http://www.idrsolutions.com/online-pdf-to-html5-converter/ 可以确定每列的文本。

感谢您的回复

【问题讨论】:

    标签: java pdf itext pdf-parsing


    【解决方案1】:

    我编写了 iText 文本提取器。 iText 中有两种提取策略 - 一种是幼稚的(更多的概念证明),它只是在遇到文本时转储文本。另一个(LocationTextExtractionStrategy)更加精致,它使用@Jongware 建议的位置和字体信息构建字符串(它还考虑了所有坐标转换)。如果您只是像您一样调用 getTextFromPage(),则后者是默认策略。

    第 20 行文本显示两次的原因是 b/c 一些 PDF 制作者这样做是为了模拟粗体字形(他们稍微移动字符并重新渲染)。所以这不是一个错误,真的 - 但肯定是一个改进的机会。如果我们检测到大块相同的内容落在彼此的某个缇区域内,我们可能会做一些事情。我们还没有这样做的原因是这可能真的很棘手,b/c 你可能有一个块是整个单词,还有另一组块——每个字母一个块。我们有能力进行子块分析(事实上,这在解析器接口中的某个地方暴露了 - 无法立即回忆 - 如果你需要它,请告诉我,我会追踪它) - 但那会来性能损失相当大,所以我不愿意这样做。

    无论如何,我解决这个特定挑战的方法是设置物理区域并将区域过滤器传递给 LocationTextExtractionStrategy#getResultantText() 调用。

    如果您确实需要根据文本的水平位置插入制表符(或某些列标记),这是非常可行的 - 看看在 LocationTextExtractionStrategy 源代码中调用 isChunkAtWordBoundary() 方法的位置并添加您自己的处理程序,用于在空格之外插入特殊字符。也可以进行某种上下文分析(即注意有一堆块碰巧共享相同的 X 位置和方向,并将该 X 位置指定为制表位)。

    如果您想出了一个很好且通用的想法(即不特定于这个解析任务),请告诉我,我会看看我能做些什么来将它整合到 iText 中。

    【讨论】:

    • 感谢详细的解释!在这种情况下,我确定重复的文本是 not 故意的(查看日期字符串的结束位置)。也许它位于 Excel 中“页面”的底部?它的分块略有不同,暗示了一些微小的字距或间距变化——没有明显的原因!这一定是侥幸,可能是 Excel 绘制页面的独特方式。
    • @Jongware 如果您在内容流中查看它,它会变得更有趣。文本的第一次出现是用一个太小的剪切矩形来绘制的,它会切掉文本的下部。然后在该区域填充一个矩形。然后又是文本,这次是它适合的剪辑路径,并且字符和单词间距设置略有不同。好像有些自动化输出不满足,后来又被人工修正了,修正用的程序引入了一些自己的人物定位思路。
    【解决方案2】:

    这...

    PDF 查看器如何知道加拿大在第二列而不是第三列。

    这是一个错误的问题——但“为什么”包含可能解决方案的提示。

    问题是“错误的”,因为您的“PDF 查看器”不知道文本应该在第二列。 PDF 中“没有”spoon 列:查看器得到 的所有内容都是 (x,y) 位置列表和要在其上显示的文本。它所要做的就是将光标移动到 (x,y) 位置并绘制文本。看?不涉及列。也不是单个 [Tab] 字符(或任何其他类型的魔法 \specialChar,就此而言)。

    一个笨拙的、直接的文本转换器扫描输入文件中的文本运行并立即将它们写出。它可能会测试大于预期的 x 位置,并在必要时插入一个空格 - 事实上,iText 似乎这样做是因为检查您的文件显示在“1”和“WJC:”之间没有存储“空格”字符:季后赛加拿大”。 在同一 y 位置上移动到更大的 x 位置,因此 iText 推断存在“某物”。

    一种可能的解决方案是存储所有文本片段的所有 (x,y) 坐标,对它们进行排序,然后测试每个文本片段的结尾是否在下一个开头的合理距离内。 (这也需要您检索字符宽度。)如果距离或多或少等于空格宽度,则可以输出“空格”。如果更多,您可以输出一个[Tab]。以下是执行此操作的简单 PDF 阅读器的输出:

    1   WJC:PLAYOFFS CANADA     TSN+        M.W.... 19:30   21:57   5133
    2   WJC:PLYOFF CAN PSTGM    TSN+        ..W.... 21:54   22:21   3558
    3   BIG BANG THEORY         CTV Total   ...T... 20:00   20:31   3334
    

    -- 为了清楚起见,我手动对齐了列,因为每列之间只有一个 [Tab]。您的文档很“简单”,因为每一列都包含一些文本。如果不这样做会稍微困难一些(但如果有必要,您可以创建一个 可能 选项卡位置列表,并针对该位置测试每个新文本字符串)。

    简而言之,你不能使用普通函数getTextFromPage,你需要检索正确的x和y位置并处理它们。


    令人惊讶:由于某种未知原因,这条线

    20  LAW AND ORDER:SVU   CTV Total   W   21:00   23:00   1295
    

    在本文档中两次出现在完全相同的位置。我没有预料到,所以在排序之后,我在输出中得到了这个:

    20<FONT ArialMT>20 LALAWW ANANDD ORDEORDER:SR:SVUVU CTCTVV TTotalotal ..WW.... 21:0021:00 23:0023:00 1295<FONT Arial-BoldMT>1295
    

    更简单的解决方案

    ... 将手动创建“广播网点”列表。该列表具有相当可预测的格式:[digits] [Title] [Outlet] ..(等),并且只有 Title 和 Outlet 不遵循特定模式。在这个列表中,我只计算了 4 家不同的广播公司。解析剩余的“列”应该很简单。

    【讨论】:

    • 非常感谢,今天学到了一些东西。对于解决方案,我已经按照您的建议进行了操作。 JPedal 图书馆可以检索这些列,但您必须付费。
    • 你知道如何报告 iText 的错误吗?
    • 我为什么会知道? :-)
    • 1.这不是解析或提取错误;该文本 在 PDF 中存储了两次。 2.我没有使用iText,我从头开始编写了自己的PDF解析器。 - 不知道iText的默认转文本转换器有多聪明;我敢打赌它也会输出这个特定的字符串两次。
    • 关于 2:iText 到文本的转换是模块化的:它有一个基本框架,以预处理的方式进行解析并将操作转发给某个侦听器(例如,带有各种图形的 ctm/tm 的 String s状态信息)。反过来,侦听器在从这些事件派生页面文本字符串的过程中可能更简单或更高级。该框架非常好,iText 包含两个侦听器(Kevin 在他的回答中描述);一个非常简单,另一个更好,可以排序,但仍有一些改进的潜力。
    猜你喜欢
    • 2011-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多