如何在提取文本之前识别可能损坏的 pdf 页面?

2024-01-09

TL;DR

我的工作流程:

  1. 下载PDF
  2. 使用将其拆分为页面pdftk https://linux.die.net/man/1/pdftk
  3. 使用提取每个页面的文本pdf转文本 https://linux.die.net/man/1/pdftotext
  4. 对文本进行分类并添加元数据
  5. 以结构化格式发送给客户

我需要提取一致的文本才能从 3 跳转到 4。如果文本是乱码,我必须对其页面进行 OCR。但是,OCR 所有页面是不可能的。如何预先确定哪些页面应该进行 ORed?我试过跑pdffonts https://linux.die.net/man/1/pdffonts and pdf转html https://linux.die.net/man/1/pdftohtml在每一页上。跑起来不是很贵吗subprocess.run每页两次?

损坏的页面是什么意思?

PDF 页面无法从其源中提取文本,可能是由于 to_unicode 转换所致。

描述

我正在构建一个依赖于每天从一千个 PDF 文件中提取文本的应用程序。每个 PDF 中的文本布局都有些结构化,因此调用pdf转文本 https://linux.die.net/man/1/pdftotext在大多数情况下,来自 python 的效果都很好。但是,来自一两个资源的某些 PDF 文件会带来带有问题字体的页面,从而导致文本出现乱码。我认为仅在有问题的页面上使用 OCR 就可以解决这个问题。所以,我的问题是如何在提取文本之前识别哪些页面可能会导致乱码.

首先,我尝试在提取乱码后使用正则表达式(\p{Cc}或拉丁字母之外不太可能的字符),但它不起作用,因为我发现带有有效字符和数字的损坏文本,即AAAAABS12 54c] $( JJJJ Pk ,还有。

二、我尝试识别乱码通话pdffonts https://linux.die.net/man/1/pdffonts- 识别每个页面上的 to_unicode 映射的名称、编码、嵌入性和存在性并解析其输出。在我的测试中,它效果很好。但我发现还需要计算有多少字符使用了可能有问题的字体,pdf转html https://linux.die.net/man/1/pdftohtml- 显示每个文本块p标签及其字体名称 - 在这里拯救了这一天。 @LMC 帮助我弄清楚如何做到这一点,看看answer https://stackoverflow.com/a/68433055/6328506。糟糕的是我最后打了电话subprocess.run每个pdf页两次,超级贵。如果我能绑定这些工具会更便宜 https://github.com/cbrunet/python-poppler/issues/36.

我想知道查看 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$18(/$\x030$5,$\x03&$/$\'2\x03\'(\x03)$5,$6\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

以上文字摘自本文第25页document http://tjdocs.tjgo.jus.br/documentos/584556使用pdftotext。

对于该页面, 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 映射或没有命名为likely有问题的。但是,正如我所说,我还发现使用 ToUnicode 表而不是自定义编码字体的情况也有问题。据我所知,还可以找到例如为损坏字体定义的单个字符,但不会影响页面的整体可读性,因此也许不需要 OCR 该页面。换句话说,如果给定页面中的字体没有进行 ToUnicode 转换,并不意味着该页面的文本完全受到影响。

我正在寻找比正则表达式乱码更好的解决方案。

我必须进行 OCR 的 PDF 页面示例

以下所有页面都包含葡萄牙语文本,但如果您尝试复制文本并粘贴到某处,您会看到普遍的乱码。

  • 第 146 页http://tjdocs.tjgo.jus.br/documentos/584544 http://tjdocs.tjgo.jus.br/documentos/584544
  • 第 26、80、81、82、83 和 84 页http://tjdocs.tjgo.jus.br/documentos/584556 http://tjdocs.tjgo.jus.br/documentos/584556
  • 第 23 页http://tjdocs.tjgo.jus.br/documentos/584589 http://tjdocs.tjgo.jus.br/documentos/584589

到目前为止我做了什么

自从我创建了一个 bash 脚本来迭代页面并将每个页面的 pdftohtml 和 pdffonts 输出合并到单个 HTML 中以来,我避免了在页面上调用 subprocess 两次:

#!/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>"

上面的代码使我能够调用 subprocess 一次并使用解析 htmllxml对于每个页面(考虑到<page>标签)。但仍然需要查看文本内容才能知道文本是否损坏。


A quick bash /questions/tagged/bash函数基于pdftotext

这是一个完整的(rewrited) 功能扫描坏页:

#!/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-Z\047"()[]{}<>,-./+?!$&@#:;%$=_ºÁÃÇÔàáâãçéêíóôõú– ' < <(
            "${pdftxt[@]}" <"$1"
    ))
    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)]

Usage:

findBadPages [-f INTEGER] [-l INTEGER] [-m INTEGER] [-q] <pdf file>

Where

  • -f让你指定第一页.
  • -l for 最后一页.
  • -m对于最小值strange每页找到的字符用于打印状态。
  • -q标志在进度期间抑制页码显示,然后仅显示badpages数字。

Note:

使用的字符串tr -d: 0-9a-zA-Z\047"()[]{}<>,-./:;%$=_ºÁÃÇÔàáâãçéêíóôõú– 是通过对 PDF 文件中使用的字符进行排序而构建的!他们无法匹配另一种语言!也许在未来的某些用途中可能需要添加一些更重音的字符或其他错过的可打印字符。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在提取文本之前识别可能损坏的 pdf 页面? 的相关文章

随机推荐

  • 手动安装 Perl 模块

    我已经下载了该模块Digest SHA1并将其解压到目录 Digest SHA1 2 13 然后将所有 SHA1 文件复制到 Digest SHA1 2 13 Digest 在perl脚本中 我做了 use Digest SHA1 像这样启
  • 查找 R 中分组数据帧中前 n 个表示的条目

    我是 R 的初学者 非常感谢您的回复 因为我被困在这段代码上 这是我解决问题的尝试 但它不起作用 personal spotify df lt fromJSON data StreamingHistory0 json personal sp
  • 无法读取 Ionic 中未定义错误的属性“清单”

    我正在尝试使用 Firebase 登录 Facebook 我已经安装了所需的所有库并完成了所需的配置 尽管如此 每当我尝试启动我的应用程序时 它都会给我这个错误 node 11160 UnhandledPromiseRejectionWar
  • Rspec 渲染文本

    我有这个代码 if temp user save sign in user temp user render text OK else render text render to string partial gt errors end 我
  • ORACLE 触发器错误

    我有一个性感的触发器 它从字段 REFERENT 中获取值 解析它 在某个视图中找到一行 并将一些数据填充到需要插入的原始行 然后我还重写 REFERENT 字段并消除额外的数据 REFERENT 字段如下所示 XXX 123 其中 123
  • 使一个类的一个实例与另一个类的实例相等。 – 如何取消?

    I have instance1 instance2 如何将它们彼此断开 以便更改其中一个不会影响另一个 编辑 我希望它们引用同一个对象 所以我无法克隆 后来 不是 但我仍然想要该类的两个实例 所以我不能将它们 清空 Thanks EDIT
  • 了解画布和表面概念

    我正在努力理解绘图的过程SurfaceView因此整个Surface Canvas Bitmap系统 用于Android 我已经阅读了所有文章和 API 文档页面 我可以在 android developers 网站上找到它们 一些 and
  • 在android中从类的一些字段创建parcelable

    我有以下课程 我打算将其从一项活动传递到另一项活动 public class Ad extends ListItem implements parcelable private String type private String reco
  • .Net 3.5 上的 Web 服务客户端应用程序的 SSL 和过时的 TLS(1.0 和 1.1)

    根据 PCI 我们需要从 2016 年 6 月 30 日起停止使用 SSL 和 TLS 在某些实施中为 1 0 和 1 1 http blog securitymetrics com 2015 04 pci 3 1 ssl and tls
  • 当数据库日志记录信息应用于每个 Crystal Reports 部分时,应用程序运行缓慢

    目前 我使用以下方法将连接信息分配给所有报告部分 但由于报告中有很多部分 因此报告会在大约 10 秒后显示 这看起来真的很慢 当安装在客户端时 是否有其他方法可以一次性为每个 CR 设置登录信息 JFYI 所有 CR 使用相同的登录凭据连接
  • unique_ptr 具有需要原始指针的 API?

    使用托管内存和函数式语言大约 10 年之后 我终于回到了 C 智能指针让我感到困惑 一半的文档仍然是关于已弃用的auto ptr 我正在尝试实现这个相当简单的项目符号 你好世界 http bulletphysics org mediawik
  • 具有通配符类型参数的 Map 上的 flatMap

    我正在尝试写这样的东西 trait Typed T trait Test def testMap Map Typed Int def test testMap flatMap case typed size gt Seq fill size
  • 如何在Vuex中深度克隆状态并回滚?

    在 Vuex 中 我想拍摄树中对象属性的快照 克隆 修改它 然后可能回滚到以前的快照 背景 在应用程序中 用户可以在应用某些更改之前尝试它们 应用更改时 它们应该影响主 vuex 树 用户还可以单击 取消 放弃更改并返回到之前的状态 Exa
  • 相关查找字段外键在内联 Django 中不起作用

    我的 tabularinline 字段有问题 我有这样的模型 class Product models Model class Pemesanan models Model produks models ManyToManyField Pr
  • axios 在 componentDidMount 中获取数据后如何拍摄笑话快照?

    要测试的组件 class Carousel extends React Component state slides null componentDidMount gt axios get https s3 amazonaws com ra
  • 拖动数据点并提交值

    On page jqPlot http www jqplot com deploy dist examples customHighlighterCursorTrendline html有一个在 jqPlot 图表上拖动数据点的示例 我如何
  • Ajax v. 在 HTML 中包含数据

    我将 JavaScript 与 jQuery 结合使用 与 Django 后端对话 有一些 UI 需求需要 Ajax 因为在用户提供一些输入之前我们无法知道要发送哪些数据 然而 还有其他在模板时已知的数据 直接将该数据包含在模板中而不是使用
  • PHP ↔ Perl 接口或绑定

    您推荐哪种 PHP Perl 接口或绑定 我需要能够从 PHP 脚本运行位于 Perl 文件中的 Perl 函数并获取返回值 我已经找到了PECL perl 包 http pecl php net package perl但我不确定它的可靠
  • 64 位上 int 与 size_t

    将代码从 32 位移植到 64 位 很多地方都有 int len strlen pstr 这些现在都会生成警告 因为 strlen 返回 64 位的 size t 而 int 仍然是 32 位 所以我一直用它们替换 size t len s
  • 如何在提取文本之前识别可能损坏的 pdf 页面?

    TL DR 我的工作流程 下载PDF 使用将其拆分为页面pdftk https linux die net man 1 pdftk 使用提取每个页面的文本pdf转文本 https linux die net man 1 pdftotext