我有数千份文件,其中一些被扫描了。所以我需要一个脚本来测试属于一个目录的所有 PDF 文件。有没有一种简单的方法可以做到这一点?
- 大多数 PDF 是报告。因此,他们有很多文字。
它们非常不同,但是由于与扫描相关的不稳定的 OCR 过程,如下所述的扫描的可以找到一些文本。
下面评论中由于 Sudodus 的提议似乎很有趣。查看已扫描的 PDF 与未扫描的 PDF 之间的区别:
扫描:
grep --color -a 'Image' AR-G1002.pdf
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 340615/Name/Obj13/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 40452/Name/Obj18/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 41680/Name/Obj23/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 41432/Name/Obj28/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 59084/Name/Obj33/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 472681/Name/Obj38/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 469340/Name/Obj43/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 371863/Name/Obj48/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 344092/Name/Obj53/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 59416/Name/Obj58/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 48308/Name/Obj63/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 51564/Name/Obj68/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 63184/Name/Obj73/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 40824/Name/Obj78/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 23320/Name/Obj83/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 31504/Name/Obj93/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 18996/Name/Obj98/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 292932/Name/Obj103/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 27720/Name/Obj108/Subtype/Image/Type/XObject/Width 1698>>stream
<rdf:li xml:lang="x-default">Image</rdf:li>
<rdf:li xml:lang="x-default">Image</rdf:li>
未扫描:
grep --color -a 'Image' AR-G1003.pdf
<</Lang(en-US)/MarkInfo<</Marked true>>/Metadata 167 0 R/Pages 2 0 R/StructTreeR<</Contents 4 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F3 9 0 R/F4 11 0 R/F5 13 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/StructParents 0/Tabs/S/Type/<</Filter/FlateDecode/Length 5463>>stream
<</BaseFont/Times#20New#20Roman,Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontD<</Ascent 891/AvgWidth 427/CapHeight 677/Descent -216/Flags 32/FontBBox[-558 -216 2000 677]/FontName/Times#20New#20Roman,Bold/FontWeight 700/ItalicAngle 0/Leadi<</BaseFont/Times#20New#20Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescri<</Ascent 891/AvgWidth 401/CapHeight 693/Descent -216/Flags 32/FontBBox[-568 -216 2000 693]/FontName/Times#20New#20Roman/FontWeight 400/ItalicAngle 0/Leading 42<</BaseFont/Arial,Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 10 0<</Ascent 905/AvgWidth 479/CapHeight 728/Descent -210/Flags 32/FontBBox[-628 -210 2000 728]/FontName/Arial,Bold/FontWeight 700/ItalicAngle 0/Leading 33/MaxWidth<</BaseFont/Times#20New#20Roman,Italic/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 12 0 R/LastChar 118/Name/F4/Subtype/TrueType/Type/Font/Widths 164 0 <</Ascent 891/AvgWidth 402/CapHeight 694/Descent -216/Flags 32/FontBBox[-498 -216 1333 694]/FontName/Times#20New#20Roman,Italic/FontWeight 400/ItalicAngle -16.4<</BaseFont/Arial/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 14 0 R/La<</Ascent 905/AvgWidth 441/CapHeight 728/Descent -210/Flags 32/FontBBox[-665 -210 2000 728]/FontName/Arial/FontWeight 400/ItalicAngle 0/Leading 33/MaxWidth 2665<</Contents 16 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R>>/ProcSet[<</Filter/FlateDecode/Length 7534>>streamarents 1/Tabs/S/Type/Page>>
<</Contents 18 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R>>/ProcSet[<</Filter/FlateDecode/Length 6137>>streamarents 2/Tabs/S/Type/Page>>
<</Contents 20 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R/F6 21 0 R><</Filter/FlateDecode/Length 6533>>stream>>/StructParents 3/Tabs/S/Type/Page>>
<</BaseFont/Times#20New#20Roman/DescendantFonts 22 0 R/Encoding/Identity-H/Subty<</BaseFont/Times#20New#20Roman/CIDSystemInfo 24 0 R/CIDToGIDMap/Identity/DW 100<</Ascent 891/AvgWidth 401/CapHeight 693/Descent -216/Flags 32/FontBBox[-568 -216 2000 693]/FontFile2 160 0 R/FontName/Times#20New#20Roman/FontWeight 400/Italic<</Contents 27 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</ExtGState<</GS28 28 0 R/GS29 29 0 R>>/Font<</F1 5 0 R/F2 7 0 R/F3 9 0 R/F5 13 0 R/F6 21 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC<</Filter/FlateDecode/Length 5369>>streamge>>
每页的图像数量要大得多(大约每页一张)!
cd <path to dir>
所有 pdf 扫描文件将保留在文件夹中,其他文件将移动到另一个文件夹。
脚本
如果
pdf
文件包含图像(与文本或整个页面一起插入到文档中,“扫描的 pdf”),则该文件通常(可能总是)包含字符串/Image/
.以同样的方式,您可以搜索字符串
/Text
以判断 pdf 文件是否包含文本(未扫描)。我制作了 shellscript
pdf-text-or-image
,在大多数情况下它可能适用于您的文件。shellscript在文件/Image/
中查找文本字符串。/Text
pdf
使 shellscript 可执行,
将目录更改为您拥有
pdf
文件的位置并运行 shellscript。识别的文件被移动到以下子目录
scanned
text
s-and-t
(对于同时包含 [扫描?] 图像和文本内容的文档)未识别的文件对象“UFO”保留在当前目录中。
测试
我用你的两个文件
AR-G1002.pdf
和AR-G1003.pdf
, 和一些自己的pdf
文件(我使用 Libre Office Impress 创建的)测试了 shellscript。让我们希望
如果这更多是关于实际检测 PDF是否是通过扫描创建的,而不是pdf 具有图像而不是文本,那么您可能需要深入研究文件的元数据,而不仅仅是内容。
一般来说,对于我可以在我的计算机上找到的文件和您的测试文件,以下是正确的:
我目前使用的是 Windows,所以我使用
node.js
了以下示例:要运行它,你需要安装 Node.js(应该是一个命令),你还需要调用:
用法:
这个例子不被认为是完成的解决方案,但是通过
debug
标志,您可以深入了解文件的元信息:我编写的简单函数在我可以在我的计算机上找到的文档(包括您的示例)上取得了 100% 的成功。我根据运行程序之前的状态为文件命名,以便查看结果是否正确。
您可以使用调试模式和一点点编程来极大地改善您的结果。您可以将程序的输出传递给其他程序,每行始终有一个完整路径。
我创建了一个脚本来检测 PDF 是否为 OCRd。主要思想:在 OCRd PDF 中,文本是不可见的。
测试给定 PDF (
f1
) 是否为 OCRd 的算法:f1
创建记为的副本f2
f2
f1
和f2
f1
f1
如果和的所有图像都相同,则为 OCRdf2
。https://github.com/jfilter/pdf-scripts/blob/master/is_ocrd_pdf.sh
据我所知,没有万无一失的方法,但有一些策略。
pdf 可能嵌入了一些文本,但这可能不是您要查找的文本。例如,一些出版公司,例如 Jstor,即使在 pdf 没有 ocred 的情况下,也会在 pdf 上放置一些与版权相关的文本信息。
因此,一个好的策略是将 pdf 提供给 pdf 到 txt 转换器并计算字数。如果这个数字太低(完全主观的指标),那么可以合理地预期它没有 ocr。
下面我们有一个 bash one 衬垫,它为几个文件模拟了这一点(需要并行和 poppler):
这将计算您目录中每个 pdf 文件的字数。然后,您可以过滤下面的内容,例如 100 个单词,然后将其提供给脚本,例如
ocrmypdf
对它们进行 ocr。如果文档集合的扫描文档没有添加带有光学字符识别 (OCR) 的文本,Hobbyist 提供了一个很好的解决方案。如果这是可能的,您可能需要编写一些脚本来读取输出
pdfinfo -meta
并检查用于创建文件的工具,或者使用使用 Python 库之一来检查它们的 Python 例程。使用类似的工具搜索文本strings
是不可靠的,因为 PDF 内容可以被压缩。并且检查创建工具也不是万无一失的,因为 PDF 页面可以合并;我经常将 PDF 文本文档与扫描的图像结合起来,以便将它们放在一起。很抱歉,我无法提供具体建议。我已经有一段时间没有研究 PDF 的内部结构了,但是根据您的要求有多严格,您可能想知道它有点复杂。祝你好运!
我能想到的2种方法:
使用选择文本工具:如果您使用的是扫描的 PDF,则无法选择文本,而是会出现一个框。您可以使用此事实来创建脚本。我知道在 C++ QT 中有一种方法,但在 Linux 中不确定。
在文件中搜索单词:在非扫描的 PDF 中,您的搜索将有效,但在扫描的文件中无效。您只需要找到所有 PDF 共有的一些单词,或者我宁愿说在所有 PDF 中搜索字母“e”。它具有最高的频率分布,因此您很可能会在所有包含文本的文档中找到它(除非它的gadsby)
例如
使用任何文本处理工具