【问题标题】:PHP Filter FlateDecode PDF stream returning offset charactersPHP过滤器FlateDecode PDF流返回偏移字符
【发布时间】:2023-04-01 17:11:02
【问题描述】:

我有使用 filetotext 类从 PDF 中提取文本的代码。一直工作到上周,当生成的 pdf 发生变化时。奇怪的是,一旦我将 29 添加到字符的序号中,字符似乎就在那里并且正确。

示例响应调试打印输出:

/F1 7.31 Tf
0 0 0 rg
1 0 0 1 195.16 597.4 Tm
($PRXQW)Tj
ET
BT

代码在 pdf 的流部分使用 gzuncompress。 $PRXQW 是金额,将 29dec 添加到每个字符的序号就可以了。但有时一个字符不会被这个精确翻译,比如应该是什么的a ) 在文本中似乎是两个字节的5C66。

只是想知道现在 PDF 中出现的这种编码环类型的字符,是否有人见过这种东西?

【问题讨论】:

  • /F1 引用的字体引入了某种子集或自定义编码,PDF 中该字体的定义可能会有所启发,或查看 PDF 生成时的字体设置。
  • @dwarring,谢谢,这帮助了我前进。

标签: php pdf character-encoding text-extraction


【解决方案1】:

Tj 操作的字符串参数的编码完全取决于所使用的 PDF 字体(本例中为 F1):

文本显示运算符的字符串操作数应被解释为标识要绘制的字形的字符代码序列。

对于简单字体,字符串的每个字节都应被视为一个单独的字符代码。然后应在字体的编码中查找字符代码以选择字形,如 9.6.6 “字符编码”中所述。

对于复合字体 (PDF 1.2),可以使用多字节代码来选择字形。在这种情况下,字符串的一个或多个连续字节应被视为单个字符代码。代码长度和从代码到字形的映射在称为 CMap 的数据结构中定义,如 9.7 “复合字体”中所述。

ISO 32000-1 中的第 9.4.3 节“文本显示运算符”)

OP 的代码似乎采用标准编码,如 MacRomanEncodingWinAnsiEncoding,但这些只是特殊情况。如上面的引用所示,编码也可能是一些特殊的混合多字节编码。

稍后部分的 PDF 规范描述了如何正确提取文本:

符合标准的读者可以使用这些方法,按照给定的优先级,将字符代码映射到 Unicode 值。带标签的 PDF 文档尤其应提供这些方法中的至少一种(参见 14.8.2.4.2,“带标签的 PDF 中的 Unicode 映射”):

  • 如果字体字典包含 ToUnicode CMap(参见 9.10.3,“ToUnicode CMaps”),请使用该 CMap 将字符代码转换为 Unicode。

    李>
  • 如果字体是使用预定义编码 MacRomanEncodingMacExpertEncodingWinAnsiEncoding 之一的简单字体,或者具有编码其差异数组仅包含取自 Adob​​e 标准拉丁字符集的字符名称和 Symbol 字体中的命名字符集(参见附录 D):

    a) 根据表 D.1 和字体的 Differences 数组将字符代码映射到字符名称。

    b) 在 Adobe 字形列表(参见参考书目)中查找字符名称以获得对应的 Unicode 值。

  • 如果字体是使用表 118 中列出的预定义 CMap 之一的复合字体(Identity-H 和 Identity-V 除外)或其后代 CIDFont 使用 Adob​​e-GB1、Adobe-CNS1、Adobe-Japan1 或Adobe-Korea1 字符集:

    a) 根据字体的 CMap 将字符代码映射到字符标识符 (CID)。

    b) 从其 CIDSystemInfo 字典中获取字体的 CMap(例如 Adob​​e 和 Japan1)使用的字符集合的注册表和排序。

    c) 通过连接在步骤 (b) 中获得的注册表和排序,以注册表-排序-UCS2 格式(例如,Adobe-Japan1-UCS2)构造第二个 CMap 名称。

    d) 获取具有在步骤 (c) 中构建的名称的 CMap(可从 ASN 网站获得;参见参考书目)。

    e) 根据步骤(d)中获得的CMap映射步骤(a)中获得的CID,产生Unicode值。

如果这些方法无法生成 Unicode 值,则无法确定字符代码代表什么,在这种情况下,符合标准的读者可以选择他们选择的字符代码。

ISO 32000-1 中的第 9.10.2 节“将字符代码映射到 Unicode 值”)

因此:

只是想知道现在从 PDF 中出来的这种代码环类型的字符,是否有人见过这种东西?

是的,在野外的 PDF 中,文本绘图运算符字符串参数的编码与 ASCII'ish 完全不同是相当普遍的。正如上面第二段引文中的最后一段所暗示的那样,有些情况根本不允许提取文本(即没有 OCR),即使还有其他地方可以查找到 Unicode 的映射。

【讨论】:

  • 感谢您的详细解释。在对我的原始帖子发表评论后,我获得了可以工作的原始文档,并通过文档属性发现字体部分中的编码显示出差异。工作文件是“Ansi”,新文件现在有“Identity-H”。
  • 我会挖掘正确的映射,或者最终使用我迄今为止破译的内容。看起来新文件现在每个字符使用两个字节,很可能来自这种“Identity-H”编码。
  • 对,Identity-H是两字节编码。它将从 0 到 65,535 的 2 字节字符代码映射到相同的 2 字节 CID 值,首先解释高位字节。
  • 所有这些都为我指明了正确的方向。我在 PDF 中找到了映射数据。这个特定的文件中有 5 个,将它们放在一起似乎给了我我需要的东西。这些映射表确认了我的小数加 29 映射,:)。
【解决方案2】:

在最一般的情况下,您要解码神秘字符串的是所选字体的 /Encoding 字段,在您的情况下是字体 /F1。编码方案很可能是 /Identity-H,它可以包含 PDF 字符串中的 16 位字符到 UTF-16 字符的任意映射。

这是我正在编写的 PDF 解析器的一个示例。每个页面都包含一个资源字典,其中包含一个字体字典:

[&3|0] => Array [
   [/Type] => |/Page|
   [/Resources] => Array [
      [/Font] => Array [
         [/F1] => |&5|0|
         [/F2] => |&7|0|
         [/F3] => |&9|0|
         [/F4] => |&14|0|
         [/F5] => |&16|0|
      ]
   ]
   [/Contents] => |&4|0|
]

在我的例子中,/F3 产生了不可用的文本,所以看看 /F3:

[&9|0] => Array [
    [/Type] => |/Font|
    [/Subtype] => |/Type0|
    [/BaseFont] => |/Arial|
    [/Encoding] => |/Identity-H|
    [/DescendantFonts] => |&10|0|
    [/ToUnicode] => |&96|0|
]

这里可以看到 /Encoding 类型是 /Identity-H。 /F3 中使用的解码字符的字符解码映射存储在 /ToUnicode 引用的流中。以下是 '&96|0' (96 0 R) 引用的流中的相关文本 - 其余部分作为样板被省略,可以忽略:

...
beginbfchar
<0003> <0020>
<000F> <002C>
<0015> <0032>
<001B> <0038>
<002C> <0049>
<003A> <0057>
endbfchar
...
beginbfrange
<0044> <0045> <0061>
<0047> <004C> <0064>
<004F> <0053> <006C>
<0055> <0059> <0072>
endbfrange
...
beginbfchar
<005C> <0079>
<00B1> <2013>
<00B6> <2019>
endbfchar
...

beginbfchar/endbfchar 之间的 16 位对是单个字符的映射。例如 (0x0003) 映射到 (0x0020),即空格字符。

beginbfrange/endbfrange 之间的 16 位三元组是字符范围的映射。例如,从 (第一个)到 (最后一个)的字符被映射到 、、、 和 (UTF16 中的“r”到“v”和ASCII)。

【讨论】:

    猜你喜欢
    • 2015-04-10
    • 2016-02-14
    • 2012-07-28
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 2011-04-01
    • 1970-01-01
    相关资源
    最近更新 更多