为什么字符 ID 160 在 PDFMiner 中不被识别为 Unicode?

Why character ID 160 is not recognised as Unicode in PDFMiner?

我正在使用 PDFMiner.

将 .pdf 文件转换为 .xml 文件

对于 .pdf 文件中的每个单词,PDFMiner 会检查它是否是 Unicode(以及许多其他内容)。如果是,它 returns 字符,如果不是,它引发异常和 returns 字符串 "(cid:%d)" 其中 %d 是字符 ID,我认为是Unicode 十进制。

这个问题的编辑部分对此有很好的解释: What is this (cid:51) in the output of pdf2txt?。为了方便,我在这里报告代码:

def render_char(self, matrix, font, fontsize, scaling, rise, cid):
    try:
        text = font.to_unichr(cid)
        assert isinstance(text, unicode), text
    except PDFUnicodeNotDefined:
        text = self.handle_undefined_char(font, cid)


def handle_undefined_char(self, font, cid):
    if self.debug:
        print >>sys.stderr, 'undefined: %r, %r' % (font, cid)
    return '(cid:%d)' % cid

对于用西里尔文编写的 .pdf 文件,我通常会遇到此异常。但是,有一个使用简单英语的文件,我在其中得到了非中断 spaces(具有 cid=160)的异常。我不明白为什么这个字符不被识别为 Unicode,而同一文件中的所有其他字符都是。

如果在相同的环境中,我在控制台中 运行 isinstance(u'160', unicode) 我得到 True,而(显然)等效命令返回 False 运行 在 PDFMiner 中。

如果我调试,我发现字体被正确识别,即我得到:

cid = 160
font =  <PDFType1Font: basefont='Helvetica'>

PDFMiner 接受编解码器作为参数。我选择了 utf-8,它有 160 作为不间断的 Unicode Decimal space (http://dev.networkerror.org/utf8/).

如果有帮助,这里是 to_unichr 的代码:

def to_unichr(self, cid):
    if self.unicode_map:
        try:
            return self.unicode_map.get_unichr(cid)
        except KeyError:
            pass
    try:
        return self.cid2unicode[cid]
    except KeyError:
        raise PDFUnicodeNotDefined(None, cid)

有没有办法set/change代码识别的字符映射?

您认为我应该更改什么,或者您认为我应该调查哪里,以便 cid=160 不会引发异常?

示例文档中的相关字体是简单字体并使用 WinAnsiEncoding。此编码在 PDF 规范 ISO 32000-1 中定义为附件 D.2 拉丁字符集和编码 中 table 中的四种特殊编码之一。 table WIN 列中包含 240(= 十进制 160。table 条目以八进制数字给出!)的条目.

此 table 被提取为 latin_enc.py 中的 ENCODING 数组,并从该数组中生成这四种编码的映射encodingdb.py 然后使用,例如对于具有这种编码的字体,请参见 pdffont.py.

中的 PDFSimpleFont

因此,PdfMiner 无法将代码 160 识别为在 WinAnsiEncoding 中具有任何关联字符。这会导致您的问题。


只看table似乎是正确的,但如果阅读table下面的注释,就会发现:

  1. The SPACE character shall also be encoded as 312 in MacRomanEncoding and as 240 in WinAnsiEncoding. This duplicate code shall signify a nonbreaking space; it shall be typographically the same as (U+003A) SPACE.

PdfMiner 开发似乎忽略了这一点。

可以通过为 space

添加第二个条目来修复此疏忽
('nbspace', None, 202, 160, None)

ENCODING数组(使用十进制数);如果您愿意,可以改用 space

(我说 可能 是因为我不喜欢 Python 编程,因此无法检查,尤其是无法检查不需要的副作用。)

对于出现上述错误的人,以下代码可能会对您有所帮助。

import minecart
from PIL import Image
import io

pdffile = open('sample.pdf', 'rb')
doc = minecart.Document(pdffile)

for page in doc.iter_pages():
    im = page.images[0]#taking only one image per page
    byteArray = im.obj.get_data()
    image = Image.open(io.BytesIO(byteArray))
    image.show()

希望对您有所帮助!!

请参考https://github.com/felipeochoa/minecart/issues/16 .

对于不同文件中的相似字符,对我有用的一个解决方案是使用 ftfy.fix_text()。我被这个包修复了 mojibake 烘焙成 pdf 的 unicode,基本上是不同编码之间典型的卷曲引用 hijinks。 Pdfminer 将它们捕获为“(cid:146)”等,但我想进一步清理它们。这个 class 目前在那个文件上工作;它包括使其打印某些东西的最小值,但工作模块中可能会有更多 pdfminer 元素。如果使用 pdf2txt.py,也许可以将副本放在安全的地方,将 pdfminer.high_level.extract_text_to_fp(fp, **locals()) 行重定向到该模块的安全副本,将此 class 添加到该模块的末尾,然后交换它继承了基础class。我刚刚完成了 HTMLConverter,但其他的可能也可以类似地处理。

from pdfminer.converter import HTMLConverter
from io                 import BytesIO
class HTMLConvertOre(HTMLConverter):
    import ftfy, six
    from pdfminer.layout    import LTChar
    from pdfminer.pdffont   import PDFUnicodeNotDefined
    def __init__(self, rsrcmgr, outfp, codec='utf-8', pageno=1, laparams=None,
                 scale=1, fontscale=1.0, layoutmode='normal', showpageno=True,
                 pagemargin=50, imagewriter=None, debug=0,
                 rect_colors={'curve': 'black', 'page': 'gray'},
                 text_colors={'char': 'black'}):
        """Initialize pdfminer.converter HTMLConverter."""
        HTMLConverter.__init__(**locals())
    def render_char(self, matrix, font, fontsize, scaling, rise, cid, ncs,
                    graphicstate):
        """Mod invoking ftfy.fix_text() to possibly rescue bad cids."""
        try:
            text = font.to_unichr(cid)
            assert isinstance(text, six.text_type), str(type(text))
        except PDFUnicodeNotDefined:
            try:
                text = ftfy.fix_text(chr(cid), uncurl_quotes=False)
                assert isinstance(text, six.text_type), str(type(text))
                cid=ord(text)
            except PDFUnicodeNotDefined:
                text = self.handle_undefined_char(font, cid)
        textwidth = font.char_width(cid)
        textdisp = font.char_disp(cid)
        item = LTChar(matrix, font, fontsize, scaling, rise, text, textwidth,
                      textdisp, ncs, graphicstate)
        self.cur_item.add(item)
        return item.adv
if __name__ == '__main__':
    rsrcmgr = PDFResourceManager()
    outfp = BytesIO()
    device = HTMLConvertOre(rsrcmgr, outfp)
    print(device)