如何在提取文本之前识别可能损坏的 pdf 页面?
How to identify likely broken pdf pages before extracting its text?
TL;DR
我的工作流程:
我需要提取一致的文本才能从3跳到4。如果文本是乱码,我必须对其页面进行OCR。但是,OCR 所有页面都没有问题。如何预先确定哪些页面应该被 OCR?我尝试在每个页面上 运行 pdffonts and pdftohtml 。 运行 subprocess.run
两次一页不贵吗?
破页是什么意思?
无法从其来源提取文本的 PDF 页面,可能是由于 to_unicode 转换。
描述
我正在构建一个依赖于每天从上千个 PDF 文件中提取文本的应用程序。每个 PDF 中的文本布局都有些结构化,因此在大多数情况下从 python 调用 pdftotext 效果很好。但是,来自一个或两个资源的某些 PDF 文件会带来字体有问题的页面,从而导致文本出现乱码。我认为只在有问题的页面上使用 OCR 就可以解决这个问题。所以,我的问题是如何在提取文本之前识别哪些页面可能会导致乱码。
首先,我尝试在提取后使用正则表达式(\p{Cc}
或不太可能是拉丁字母以外的字符)来识别乱码文本,但它没有用,因为我发现了包含有效字符和数字的损坏文本,即 AAAAABS12 54c] $( JJJJ Pk
,以及。
其次,我尝试识别乱码调用pdffonts - to identify name, encoding, embeddedness and existence of to_unicode map - on each page and parsing its output. In my tests, it kinda works well. But I found also necessary to count how many chars used likely problematic fonts, pdftohtml - Display each text block in p
tag along with its font name - saved the day here. @LMC helped me to figure out how to do it, take a look at the . The bad part is I ended up calling subprocess.run
two times for each pdf page, what is super expensive. It would be cheaper if I could just bind those tools。
我想知道查看 PDF 源代码并验证某些 CMAP(uni
是且不是自定义字体)是否可能且可行(如果存在),或者其他启发式方法可以在之前找到有问题的字体提取文本或对其进行 OCR。
我的一个 PDF 文件中的乱码示例:
0\n1\n2\n3\n4\n2\n0\n3\n0\n5 6\n6\nÿ\n89 ÿ\n4\n\x0e\n3\nÿ\n\x0f\x10\n\x11\n\x12\nÿ\n5\nÿ\n6\n6\n\x13\n\x11\n\x11\n\x146\n2\n2\n\x15\n\x11\n\x16\n\x12\n\x15\n\x10\n\x11\n\x0e\n\x11\n\x17\n\x12\n\x18\n\x0e\n\x17\n\x19\x0e\n\x1a\n\x16\n2 \x11\n\x10\n\x1b\x12\n\x1c\n\x10\n\x10\n\x15\n\x1d29 2\n\x18\n\x10\n\x16\n89 \x0e\n\x14\n\x13\n\x14\n\x1e\n\x14\n\x1f\n5 \x11\x1f\n\x15\n\x10\n! \x1c\n89 \x1f\n5\n3\n4\n"\n1\n1\n5 \x1c\n89\n#\x15\n\x1d\x1f\n5\n5\n1\n3\n5\n$\n5\n1 5\n2\n5\n%8&&#\'#(8&)\n*+\n\'#&*,\nÿ\n(*ÿ\n-\n./0)\n1\n*\n*//#//8&)\n*ÿ\n#/2#%)\n*,\nÿ\n(*/ÿ\n/#&3#40)\n*/ÿ\n#50&*-\n.()\n%)\n*)\n/ÿ\n+\nÿ\n*#/#\n&\x19\n\x12\nÿ\n\x1cÿ\n,\x1d\n\x12\n\x1b\x10\n\x15\n\x116\nÿ\n\x15\n7\nÿ\n8\n9\n4\n6\nÿ\n%\x10\n\x15\n\x11\n\x166\nÿ\n:\x12\x10;\n2\n*,\n%#26\nÿ\n<\n$\n3\n0\n3\n+\n3\n8\n3\nÿ\n+\nÿ\n=\x15\n\x10\n6\nÿ\n>\n9\n0\n?\nÿ\n4\n3\n3\n1\n+\n8\n9\n3\n<\n@A\nB\nC\nD\nEÿ\nGH\nI\nÿ\nJ\nJ\nK\nL\nJ\nM\nJ\nN\nO\nP\nO\nQ\nI\n#\x1bÿ\n0\n1\nÿ\n\x1c\n\x10\nÿ\n*\x1a\n\x16\n\x18\nÿ\n\x1c\n\x10\nÿ\n0\n3\n0\n5\n\x0e\n/\x10\n\x15\n\x13\x16\n\x12\nÿ\n/\x10\n\x16\n\x1d\x1c\x16\n\x12\n6\nÿ\n* \x19\n\x15\n\x116\nÿ\n\x12\n\x19\n\x11\n\x19\n\x12\n\x16\nÿ\n\x15ÿ\n/*-\n\x0e\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\n(\x10\nÿ\x16\n\x1c\n\x10\n\x1bÿ\n\x1c\n\x12\nÿ\n%\x13\n\x10\n9\n\x10\nÿ\n\x1c\n\x10\nÿ\n\'\x12\n\x1a\x15\n\x10\n\x11\n\x10\nÿ\n\x1c\n\x12\nÿ\n%\x16\n\x16\n\x10\nR\n\x10\n\x1c\x16\n\x12\nÿ\n\'\x10\n\x16\n\x12\n\x18\nÿ\n\x1c\n\x12\nÿ\n-\n\x19\x11\n1\n\x12\nÿ\n\x1cÿ\n#\x11\n\x12\n\x1cÿ\n\x1c\n\x10\nÿ\n*\x18\n\x12\nR\x126\nÿ\n/\x16\n\x12\n\x0e\n& \x10\n\x12\n\x15\n\x12\nÿ\n%\x10\n\x18\x11\n\x16\n\x10\nÿ\n:\x12\x13\n\x12\n\x1c\x0e\nÿ\n*\x19\n\x11\n\x19\n\x10\n+\x10\nÿ\n\x10\nÿ\n&\x10\nR\x11\n\x16\n\x10\n+\x10\nÿ\n\x15ÿ\n/*-\n2\n2\'<\nÿ\n+\nÿ\n#S\n\x11\n\x16\n\x12\n\x17\n\x19\n\x1c \x12\n\x18\nÿ\n*\x1c\n\x1b\x15\x11\n\x16\n\x12\n\x11\n\x1d\x0e\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\n*\x11\n\x10\n\x15 \x12\n\x1b\x10\n\x15\n\x11\n\x10\n6\nTU\nV\nWU\nXÿ\nYXÿ\nTU\nV\nW\nX\nXYZU\n[U\nT\]X\U\nW\nX\nVD\n^\n_\n`\nÿ\nab\nÿ\nXGb\nc\nE^\nd\nO\nP\nO\nQ\nP\ne\nO\nf\nP\nf\nJ\nf\nP\ne\ng\nGb\nh_\nEGI\niaA\nYjTk\nXlm@ YjTk\nXlmX] ]jTk@[Yj] U\nZk]U\nZU\n] X]noU\nW\nX] W@V\n\\nX]\nÿ\n89\nÿ\n89\np ÿ\nq\n(\x10\x14\n\x12\x13\n8r\nIOV\x11\x03\x14\n(VWH\x03GRFXPHQWR\x03p\x03FySLD\x03GR\x03RULJLQDO\x03DVVLQDGR\x03GLJLWDOPHQWH\x03SRU\x03(00(/$\x030,$\x03&$/$\'2\x03\'(\x03),\x036,/9$\x11\x033DUD\x03FRQIHULU\x03R\x03RULJLQDO\x0f\x03DFHVVH\x03R\x03VLWH\x03\x0f\x03LQIRUPH\x03R\x03SURFHVVR\x03\x13\x13\x13\x13\x16\x17\x18\x10\x1a\x18\x11\x15\x13\x15\x14\x11\x1b\x11\x13\x15\x11\x13\x13\x1a\x16\x03H\x03R\x03\nFyGLJR\x03\x17(\x14\x14\x16\x14\x13\x11\x03
以上文字是使用 pdftotext 从本 document 第 25 页提取的。
对于该页面,pdffonts 输出:
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
[none] Type 3 Custom yes no no 13 0
DIIDPF+ArialMT CID TrueType Identity-H yes yes yes 131 0
DIIEDH+Arial CID TrueType Identity-H yes yes no 137 0
DIIEBG+TimesNewRomanPSMT CID TrueType Identity-H yes yes yes 142 0
DIIEDG+Arial CID TrueType Identity-H yes yes no 148 0
Arial TrueType WinAnsi yes no no 159 0
很容易确定 [none]
命名的字体有问题。到目前为止,根据我分析过的数据,我的做法是使用自定义或 identity-h 编码标记字体,没有 to_unicode 映射或 none 命名为 likely[=89= 】 有问题。但是,正如我所说,我还发现使用 ToUnicode table 而不是自定义编码字体的情况也有问题。据我所知,也有可能找到,例如,为损坏的字体定义的单个字符,但不影响页面的整体可读性,因此可能没有必要对该页面进行 OCR。换句话说,如果给定页面中的字体没有ToUnicode转换,并不意味着该页面的文本完全受到影响。
我正在寻找比正则表达式乱码更好的解决方案。
我必须进行 OCR 的 PDF 页面示例
以下所有页面都包含葡萄牙语文本,但如果您尝试复制文本并粘贴到某处,您将看到普遍的乱码。
- 第 146 页,共 http://tjdocs.tjgo.jus.br/documentos/584544
- http://tjdocs.tjgo.jus.br/documentos/584556
的第 26、80、81、82、83 和 84 页
- 第 23 页,共 http://tjdocs.tjgo.jus.br/documentos/584589
到目前为止我做了什么
自从我创建了一个 bash 脚本来迭代页面并将每个页面的 pdftohtml 和 pdffonts 输出合并为一个 HTML 后,我就避免了在一个页面上调用两次子进程:
#!/bin/sh
# Usage: ./font_report.sh -a 1 -b 100 -c foo.pdf
while getopts "a:b:c:" arg; do
case $arg in
a) FIRST_PAGE=$OPTARG;;
b) LAST_PAGE=$OPTARG;;
c) FILENAME=$OPTARG;;
*)
echo 'Error: invalid options' >&2
exit 1
esac
done
: ${FILENAME:?Missing -c}
if ! [ -f "$FILENAME" ]; then
echo "Error: $FILENAME does not exist" >&2
exit 1
fi
echo "<html xmlns='http://www.w3.org/1999/xhtml' lang='' xml:lang=''>" ;
for page in $(seq $FIRST_PAGE $LAST_PAGE)
do
{
echo "<page number=$page>" ;
echo "<pdffonts>" ;
pdffonts -f $page -l $page $FILENAME ;
echo "</pdffonts>" ;
(
pdftohtml -f $page -l $page -s -i -fontfullname -hidden $FILENAME -stdout |
tail -n +35 | # skips head tag and its content
head -n -1 # skips html ending tag
) ;
echo "</page>"
}
done
echo "</html>"
上面的代码使我能够调用一次子进程并为每个页面使用 lxml
解析 html(考虑 <page>
标记)。但是还是需要看文字内容才能知道文字是否有问题。
尝试使用其他模块以正确提取文本;我建议使用 PyPDF2。
这里有一个函数可以解决这个问题:
import PyPDF2
def extract_text(filename, page_number):
# Returns the content of a given page
pdf_file_object = open(filename, 'rb')
pdf_reader = PyPDF2.PdfFileReader(pdf_file_object)
# page_number - 1 below because in python, page 1 is considered as page 0
page_object = pdf_reader.getPage(page_number - 1)
text = page_object.extractText()
pdf_file_object.close()
return text
顺便说一下,PyPDF2 不是 Python 中的预装模块。要安装它,请通过命令行安装 pip(尽管很可能这一点已经完成)和 运行 'pip install PyPDF2'。
@mkl 可能会说使用字典搜索
我尝试了不同的方法来查看最小的第三个示例中检测两个错误页面的一些简单方法,并怀疑它很容易被具有好文本和坏文本的页面击败,因此这不是一个完整的答案,因为几乎肯定需要更多的遍数来完善。
我们必须接受您提出的每个 PDF 质量未知但需要处理的问题。因此,接受我们希望大多数页面都很好的事实,我们 运行 盲目地度过了突发阶段。
一个非常常见的单词结构可以包含 3 个字母的音节“est”
因此,如果我们搜索突发文件,我们会发现第 23 页和第 24 页中缺少这些文件,因此它们很可能会损坏。
同样对于 855 页文件,您说第 146 页是一个问题(通过我之前的仅损坏页面的搜索方法确认,包含 ����,只是那个损坏了)但现在我们可以很容易地看到第一个40 页
页面当然也需要 OCR(包括那些只有图像的页面)
第4、5、8、9、10、35页(35是奇数页?BG图?)
但是第 19、33 页的 2 个页面出现误报(有文本但没有 est 或 EST)
和 20、32、38 具有 EST,因此搜索需要不区分大小写
所以使用不区分大小写的搜索无需修改我们应该得到 95% 的置信度(40 中有 2 个错误)需要 OCR 但我没有深入测试为什么小写 est
只返回了总数 855 中的 275,除非之后有 非常 高百分比的图像需要 OCR。
我之前建议通过查找 ???????? 来更快地搜索第三个 6054 页面文件这给了我们一个更不稳定的使用结果,但确实显示损坏是从 25 到 85
的所有文本页面
那么这会导致什么?
实际上很少有人使用???
损坏的页面通常包含 ???或者 ����
在葡萄牙语中,损坏的页面不太可能包含 /I est
部分损坏的页面可能包含用于 OCR 的大图像,est
或不
上面的 none 会有一些损坏的页面
由于这也是(或主要)性能问题,我建议将您的代码修改为更多多线程解决方案或简单地使用 GNU Parallel
关于它的相当不错的文章 -> link
基于pdftotext
的快速bash函数
这是一个完整的(重写)功能扫描坏页:
#!/bin/bash
findBadPages() {
local line opts progress=true usage="Usage: ${FUNCNAME[0]} [-f first page]"
usage+=' [-l last page] [-m min bad/page] [-q (quiet)]'
local -a pdftxt=(pdftotext -layout - -)
local -ia badpages=()
local -i page=1 limit=10 OPTIND
while getopts "ql:f:m:" opt;do
case $opt in
f ) pdftxt+=(-f $OPTARG); page=$OPTARG ;;
l ) pdftxt+=(-l $OPTARG) ;;
m ) limit=$OPTARG ;;
q ) progress=false ;;
* ) printf >&2 '%s ERROR: Unknown option!\n%s\n' \
"${FUNCNAME[0]}" "$usage";return -1 ;;
esac
done
shift $((OPTIND-1))
while IFS= read -r line; do
[ "$line" = $'\f' ] && page+=1 && $progress && printf %d\r $page
((${#line} > 1 )) && badpages[page]+=${#line}
done < <(
tr -d '0-9a-zA-Z7"()[]{}<>,-./+?!$&@#:;%$=_ºÁÃÇÔàáâãçéêíóôõú– ' < <(
"${pdftxt[@]}" <""
))
for page in ${!badpages[@]} ;do
(( ${badpages[page]} > limit )) && {
$progress && printf "There are %d strange characters in page %d\n" \
${badpages[page]} $page || echo $page ;}
done
}
然后现在:
findBadPages DJE_3254_I_18062021\(1\).pdf
There are 2237 strange characters in page 23
There are 258 strange characters in page 24
There are 20 strange characters in page 32
findBadPages -m 100 -f 40 -l 100 DJE_3254_I_18062021.pdf
There are 623 strange characters in page 80
There are 1068 strange characters in page 81
There are 1258 strange characters in page 82
There are 1269 strange characters in page 83
There are 1245 strange characters in page 84
There are 256 strange characters in page 85
findBadPages DJE_3254_III_18062021.pdf
There are 11 strange characters in page 125
There are 635 strange characters in page 145
findBadPages -qm100 DJE_3254_III_18062021.pdf
145
findBadPages -h
/bin/bash: illegal option -- h
findBadPages ERROR: Unknown option!
Usage: findBadPages [-f first page] [-l last page] [-m min bad/page] [-q (quiet)]
用法:
findBadPages [-f INTEGER] [-l INTEGER] [-m INTEGER] [-q] <pdf file>
在哪里
-f
让你指定第一页.
-l
最后一页。
-m
每页找到最少 奇怪 个字符以打印状态。
-q
标志在进程中抑制页码显示,然后仅显示 badpages 数字。
注:
tr -d
使用的字符串:0-9a-zA-Z7"()[]{}<>,-./:;%$=_ºÁÃÇÔàáâãçéêíóôõú–
是通过对 PDF 文件中使用的字符进行排序而构建的!他们无法匹配另一种语言!也许在未来的某些用途中可能需要添加一些重音字符或其他遗漏的可打印内容。
TL;DR
我的工作流程:
我需要提取一致的文本才能从3跳到4。如果文本是乱码,我必须对其页面进行OCR。但是,OCR 所有页面都没有问题。如何预先确定哪些页面应该被 OCR?我尝试在每个页面上 运行 pdffonts and pdftohtml 。 运行 subprocess.run
两次一页不贵吗?
破页是什么意思?
无法从其来源提取文本的 PDF 页面,可能是由于 to_unicode 转换。
描述
我正在构建一个依赖于每天从上千个 PDF 文件中提取文本的应用程序。每个 PDF 中的文本布局都有些结构化,因此在大多数情况下从 python 调用 pdftotext 效果很好。但是,来自一个或两个资源的某些 PDF 文件会带来字体有问题的页面,从而导致文本出现乱码。我认为只在有问题的页面上使用 OCR 就可以解决这个问题。所以,我的问题是如何在提取文本之前识别哪些页面可能会导致乱码。
首先,我尝试在提取后使用正则表达式(\p{Cc}
或不太可能是拉丁字母以外的字符)来识别乱码文本,但它没有用,因为我发现了包含有效字符和数字的损坏文本,即 AAAAABS12 54c] $( JJJJ Pk
,以及。
其次,我尝试识别乱码调用pdffonts - to identify name, encoding, embeddedness and existence of to_unicode map - on each page and parsing its output. In my tests, it kinda works well. But I found also necessary to count how many chars used likely problematic fonts, pdftohtml - Display each text block in p
tag along with its font name - saved the day here. @LMC helped me to figure out how to do it, take a look at the subprocess.run
two times for each pdf page, what is super expensive. It would be cheaper if I could just bind those tools。
我想知道查看 PDF 源代码并验证某些 CMAP(uni
是且不是自定义字体)是否可能且可行(如果存在),或者其他启发式方法可以在之前找到有问题的字体提取文本或对其进行 OCR。
我的一个 PDF 文件中的乱码示例:
0\n1\n2\n3\n4\n2\n0\n3\n0\n5 6\n6\nÿ\n89 ÿ\n4\n\x0e\n3\nÿ\n\x0f\x10\n\x11\n\x12\nÿ\n5\nÿ\n6\n6\n\x13\n\x11\n\x11\n\x146\n2\n2\n\x15\n\x11\n\x16\n\x12\n\x15\n\x10\n\x11\n\x0e\n\x11\n\x17\n\x12\n\x18\n\x0e\n\x17\n\x19\x0e\n\x1a\n\x16\n2 \x11\n\x10\n\x1b\x12\n\x1c\n\x10\n\x10\n\x15\n\x1d29 2\n\x18\n\x10\n\x16\n89 \x0e\n\x14\n\x13\n\x14\n\x1e\n\x14\n\x1f\n5 \x11\x1f\n\x15\n\x10\n! \x1c\n89 \x1f\n5\n3\n4\n"\n1\n1\n5 \x1c\n89\n#\x15\n\x1d\x1f\n5\n5\n1\n3\n5\n$\n5\n1 5\n2\n5\n%8&&#\'#(8&)\n*+\n\'#&*,\nÿ\n(*ÿ\n-\n./0)\n1\n*\n*//#//8&)\n*ÿ\n#/2#%)\n*,\nÿ\n(*/ÿ\n/#&3#40)\n*/ÿ\n#50&*-\n.()\n%)\n*)\n/ÿ\n+\nÿ\n*#/#\n&\x19\n\x12\nÿ\n\x1cÿ\n,\x1d\n\x12\n\x1b\x10\n\x15\n\x116\nÿ\n\x15\n7\nÿ\n8\n9\n4\n6\nÿ\n%\x10\n\x15\n\x11\n\x166\nÿ\n:\x12\x10;\n2\n*,\n%#26\nÿ\n<\n$\n3\n0\n3\n+\n3\n8\n3\nÿ\n+\nÿ\n=\x15\n\x10\n6\nÿ\n>\n9\n0\n?\nÿ\n4\n3\n3\n1\n+\n8\n9\n3\n<\n@A\nB\nC\nD\nEÿ\nGH\nI\nÿ\nJ\nJ\nK\nL\nJ\nM\nJ\nN\nO\nP\nO\nQ\nI\n#\x1bÿ\n0\n1\nÿ\n\x1c\n\x10\nÿ\n*\x1a\n\x16\n\x18\nÿ\n\x1c\n\x10\nÿ\n0\n3\n0\n5\n\x0e\n/\x10\n\x15\n\x13\x16\n\x12\nÿ\n/\x10\n\x16\n\x1d\x1c\x16\n\x12\n6\nÿ\n* \x19\n\x15\n\x116\nÿ\n\x12\n\x19\n\x11\n\x19\n\x12\n\x16\nÿ\n\x15ÿ\n/*-\n\x0e\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\n(\x10\nÿ\x16\n\x1c\n\x10\n\x1bÿ\n\x1c\n\x12\nÿ\n%\x13\n\x10\n9\n\x10\nÿ\n\x1c\n\x10\nÿ\n\'\x12\n\x1a\x15\n\x10\n\x11\n\x10\nÿ\n\x1c\n\x12\nÿ\n%\x16\n\x16\n\x10\nR\n\x10\n\x1c\x16\n\x12\nÿ\n\'\x10\n\x16\n\x12\n\x18\nÿ\n\x1c\n\x12\nÿ\n-\n\x19\x11\n1\n\x12\nÿ\n\x1cÿ\n#\x11\n\x12\n\x1cÿ\n\x1c\n\x10\nÿ\n*\x18\n\x12\nR\x126\nÿ\n/\x16\n\x12\n\x0e\n& \x10\n\x12\n\x15\n\x12\nÿ\n%\x10\n\x18\x11\n\x16\n\x10\nÿ\n:\x12\x13\n\x12\n\x1c\x0e\nÿ\n*\x19\n\x11\n\x19\n\x10\n+\x10\nÿ\n\x10\nÿ\n&\x10\nR\x11\n\x16\n\x10\n+\x10\nÿ\n\x15ÿ\n/*-\n2\n2\'<\nÿ\n+\nÿ\n#S\n\x11\n\x16\n\x12\n\x17\n\x19\n\x1c \x12\n\x18\nÿ\n*\x1c\n\x1b\x15\x11\n\x16\n\x12\n\x11\n\x1d\x0e\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\nÿ\n*\x11\n\x10\n\x15 \x12\n\x1b\x10\n\x15\n\x11\n\x10\n6\nTU\nV\nWU\nXÿ\nYXÿ\nTU\nV\nW\nX\nXYZU\n[U\nT\]X\U\nW\nX\nVD\n^\n_\n`\nÿ\nab\nÿ\nXGb\nc\nE^\nd\nO\nP\nO\nQ\nP\ne\nO\nf\nP\nf\nJ\nf\nP\ne\ng\nGb\nh_\nEGI\niaA\nYjTk\nXlm@ YjTk\nXlmX] ]jTk@[Yj] U\nZk]U\nZU\n] X]noU\nW\nX] W@V\n\\nX]\nÿ\n89\nÿ\n89\np ÿ\nq\n(\x10\x14\n\x12\x13\n8r\nIOV\x11\x03\x14\n(VWH\x03GRFXPHQWR\x03p\x03FySLD\x03GR\x03RULJLQDO\x03DVVLQDGR\x03GLJLWDOPHQWH\x03SRU\x03(00(/$\x030,$\x03&$/$\'2\x03\'(\x03),\x036,/9$\x11\x033DUD\x03FRQIHULU\x03R\x03RULJLQDO\x0f\x03DFHVVH\x03R\x03VLWH\x03\x0f\x03LQIRUPH\x03R\x03SURFHVVR\x03\x13\x13\x13\x13\x16\x17\x18\x10\x1a\x18\x11\x15\x13\x15\x14\x11\x1b\x11\x13\x15\x11\x13\x13\x1a\x16\x03H\x03R\x03\nFyGLJR\x03\x17(\x14\x14\x16\x14\x13\x11\x03
以上文字是使用 pdftotext 从本 document 第 25 页提取的。
对于该页面,pdffonts 输出:
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
[none] Type 3 Custom yes no no 13 0
DIIDPF+ArialMT CID TrueType Identity-H yes yes yes 131 0
DIIEDH+Arial CID TrueType Identity-H yes yes no 137 0
DIIEBG+TimesNewRomanPSMT CID TrueType Identity-H yes yes yes 142 0
DIIEDG+Arial CID TrueType Identity-H yes yes no 148 0
Arial TrueType WinAnsi yes no no 159 0
很容易确定 [none]
命名的字体有问题。到目前为止,根据我分析过的数据,我的做法是使用自定义或 identity-h 编码标记字体,没有 to_unicode 映射或 none 命名为 likely[=89= 】 有问题。但是,正如我所说,我还发现使用 ToUnicode table 而不是自定义编码字体的情况也有问题。据我所知,也有可能找到,例如,为损坏的字体定义的单个字符,但不影响页面的整体可读性,因此可能没有必要对该页面进行 OCR。换句话说,如果给定页面中的字体没有ToUnicode转换,并不意味着该页面的文本完全受到影响。
我正在寻找比正则表达式乱码更好的解决方案。
我必须进行 OCR 的 PDF 页面示例
以下所有页面都包含葡萄牙语文本,但如果您尝试复制文本并粘贴到某处,您将看到普遍的乱码。
- 第 146 页,共 http://tjdocs.tjgo.jus.br/documentos/584544
- http://tjdocs.tjgo.jus.br/documentos/584556 的第 26、80、81、82、83 和 84 页
- 第 23 页,共 http://tjdocs.tjgo.jus.br/documentos/584589
到目前为止我做了什么
自从我创建了一个 bash 脚本来迭代页面并将每个页面的 pdftohtml 和 pdffonts 输出合并为一个 HTML 后,我就避免了在一个页面上调用两次子进程:
#!/bin/sh
# Usage: ./font_report.sh -a 1 -b 100 -c foo.pdf
while getopts "a:b:c:" arg; do
case $arg in
a) FIRST_PAGE=$OPTARG;;
b) LAST_PAGE=$OPTARG;;
c) FILENAME=$OPTARG;;
*)
echo 'Error: invalid options' >&2
exit 1
esac
done
: ${FILENAME:?Missing -c}
if ! [ -f "$FILENAME" ]; then
echo "Error: $FILENAME does not exist" >&2
exit 1
fi
echo "<html xmlns='http://www.w3.org/1999/xhtml' lang='' xml:lang=''>" ;
for page in $(seq $FIRST_PAGE $LAST_PAGE)
do
{
echo "<page number=$page>" ;
echo "<pdffonts>" ;
pdffonts -f $page -l $page $FILENAME ;
echo "</pdffonts>" ;
(
pdftohtml -f $page -l $page -s -i -fontfullname -hidden $FILENAME -stdout |
tail -n +35 | # skips head tag and its content
head -n -1 # skips html ending tag
) ;
echo "</page>"
}
done
echo "</html>"
上面的代码使我能够调用一次子进程并为每个页面使用 lxml
解析 html(考虑 <page>
标记)。但是还是需要看文字内容才能知道文字是否有问题。
尝试使用其他模块以正确提取文本;我建议使用 PyPDF2。
这里有一个函数可以解决这个问题:
import PyPDF2
def extract_text(filename, page_number):
# Returns the content of a given page
pdf_file_object = open(filename, 'rb')
pdf_reader = PyPDF2.PdfFileReader(pdf_file_object)
# page_number - 1 below because in python, page 1 is considered as page 0
page_object = pdf_reader.getPage(page_number - 1)
text = page_object.extractText()
pdf_file_object.close()
return text
顺便说一下,PyPDF2 不是 Python 中的预装模块。要安装它,请通过命令行安装 pip(尽管很可能这一点已经完成)和 运行 'pip install PyPDF2'。
@mkl 可能会说使用字典搜索
我尝试了不同的方法来查看最小的第三个示例中检测两个错误页面的一些简单方法,并怀疑它很容易被具有好文本和坏文本的页面击败,因此这不是一个完整的答案,因为几乎肯定需要更多的遍数来完善。
我们必须接受您提出的每个 PDF 质量未知但需要处理的问题。因此,接受我们希望大多数页面都很好的事实,我们 运行 盲目地度过了突发阶段。
一个非常常见的单词结构可以包含 3 个字母的音节“est” 因此,如果我们搜索突发文件,我们会发现第 23 页和第 24 页中缺少这些文件,因此它们很可能会损坏。
同样对于 855 页文件,您说第 146 页是一个问题(通过我之前的仅损坏页面的搜索方法确认,包含 ����,只是那个损坏了)但现在我们可以很容易地看到第一个40 页
页面当然也需要 OCR(包括那些只有图像的页面)
第4、5、8、9、10、35页(35是奇数页?BG图?)
但是第 19、33 页的 2 个页面出现误报(有文本但没有 est 或 EST)
和 20、32、38 具有 EST,因此搜索需要不区分大小写
所以使用不区分大小写的搜索无需修改我们应该得到 95% 的置信度(40 中有 2 个错误)需要 OCR 但我没有深入测试为什么小写 est
只返回了总数 855 中的 275,除非之后有 非常 高百分比的图像需要 OCR。
我之前建议通过查找 ???????? 来更快地搜索第三个 6054 页面文件这给了我们一个更不稳定的使用结果,但确实显示损坏是从 25 到 85
的所有文本页面那么这会导致什么?
实际上很少有人使用???
损坏的页面通常包含 ???或者 ����
在葡萄牙语中,损坏的页面不太可能包含 /I est
部分损坏的页面可能包含用于 OCR 的大图像,est
或不
上面的 none 会有一些损坏的页面
由于这也是(或主要)性能问题,我建议将您的代码修改为更多多线程解决方案或简单地使用 GNU Parallel
关于它的相当不错的文章 -> link
基于pdftotext
的快速bash函数
这是一个完整的(重写)功能扫描坏页:
#!/bin/bash
findBadPages() {
local line opts progress=true usage="Usage: ${FUNCNAME[0]} [-f first page]"
usage+=' [-l last page] [-m min bad/page] [-q (quiet)]'
local -a pdftxt=(pdftotext -layout - -)
local -ia badpages=()
local -i page=1 limit=10 OPTIND
while getopts "ql:f:m:" opt;do
case $opt in
f ) pdftxt+=(-f $OPTARG); page=$OPTARG ;;
l ) pdftxt+=(-l $OPTARG) ;;
m ) limit=$OPTARG ;;
q ) progress=false ;;
* ) printf >&2 '%s ERROR: Unknown option!\n%s\n' \
"${FUNCNAME[0]}" "$usage";return -1 ;;
esac
done
shift $((OPTIND-1))
while IFS= read -r line; do
[ "$line" = $'\f' ] && page+=1 && $progress && printf %d\r $page
((${#line} > 1 )) && badpages[page]+=${#line}
done < <(
tr -d '0-9a-zA-Z7"()[]{}<>,-./+?!$&@#:;%$=_ºÁÃÇÔàáâãçéêíóôõú– ' < <(
"${pdftxt[@]}" <""
))
for page in ${!badpages[@]} ;do
(( ${badpages[page]} > limit )) && {
$progress && printf "There are %d strange characters in page %d\n" \
${badpages[page]} $page || echo $page ;}
done
}
然后现在:
findBadPages DJE_3254_I_18062021\(1\).pdf
There are 2237 strange characters in page 23
There are 258 strange characters in page 24
There are 20 strange characters in page 32
findBadPages -m 100 -f 40 -l 100 DJE_3254_I_18062021.pdf
There are 623 strange characters in page 80
There are 1068 strange characters in page 81
There are 1258 strange characters in page 82
There are 1269 strange characters in page 83
There are 1245 strange characters in page 84
There are 256 strange characters in page 85
findBadPages DJE_3254_III_18062021.pdf
There are 11 strange characters in page 125
There are 635 strange characters in page 145
findBadPages -qm100 DJE_3254_III_18062021.pdf
145
findBadPages -h
/bin/bash: illegal option -- h
findBadPages ERROR: Unknown option!
Usage: findBadPages [-f first page] [-l last page] [-m min bad/page] [-q (quiet)]
用法:
findBadPages [-f INTEGER] [-l INTEGER] [-m INTEGER] [-q] <pdf file>
在哪里
-f
让你指定第一页.-l
最后一页。-m
每页找到最少 奇怪 个字符以打印状态。-q
标志在进程中抑制页码显示,然后仅显示 badpages 数字。
注:
tr -d
使用的字符串:0-9a-zA-Z7"()[]{}<>,-./:;%$=_ºÁÃÇÔàáâãçéêíóôõú–
是通过对 PDF 文件中使用的字符进行排序而构建的!他们无法匹配另一种语言!也许在未来的某些用途中可能需要添加一些重音字符或其他遗漏的可打印内容。