PHP过滤FlateDecode PDF流返回偏移字符

时间:2015-08-13 23:33:45

标签: php pdf character-encoding text-extraction

我有使用filetotext类从PDF中提取文本的代码。工作到上周,当生成pdf时发生了变化。奇怪的是,一旦我将角色添加到角色中,它就会出现并且正确。

示例响应调试打印输出:

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

代码在pdf的stream部分使用gzuncompress。 $ PRXQW是Amount,在每个角色的ord中加入29dec就可以了。但有时一个字符不会是这种精确的翻译,例如应该是a)在文本中看起来是5C66的两个字节。

只是想知道这个代码环类型的角色来自PDF现在,如果有人见过这种事情?

2 个答案:

答案 0 :(得分:2)

Tj 操作的字符串参数的编码完全取决于使用的PDF字体(在当前情况下 F1 ):

  

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

     

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

     

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

     

ISO 32000-1中第9.4.3节“文字显示操作员”)

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

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

  

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

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

  •   
  • 如果字体是使用预定义编码 MacRomanEncoding MacExpertEncoding WinAnsiEncoding 之一的简单字体,或者有一个编码,其差异数组只包含取自Adobe标准拉丁字符集的字符名称和符号字体中的命名字符集(见附录D):

         

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

         

    b)在 Adob​​e Glyph List 中查找字符名称(参见参考书目)以获取相应的Unicode值。

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

         

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

         

    b)从 CIDSystemInfo 字典中获取字体CMap(例如,Adobe和Japan1)使用的字符集的注册表和顺序。

         

    c)通过以注册表排序-UCS2格式(例如,Adobe-Japan1-UCS2)连接步骤(b)中获得的注册表和排序来构造第二个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的映射。

答案 1 :(得分:0)

在最常见的情况下,你想要解码神秘字符串的是所选字体的/编码字段,在你的情况下是字体/ 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位对是单个字符的映射。例如&lt; 0003&gt; (0x0003)被映射到&lt; 0020&gt;。 (0x0020),这是空格字符。

beginbfrange / endbfrange之间的16位三元组是字符范围的映射。例如来自&lt; 0055&gt;的字符。 (第一)至&lt; 0059&gt; (最后)被映射到&lt; 0072&gt;,&lt; 0073&gt;,&lt; 0074&gt;,&lt; 0075&gt;。和&lt; 0076&gt; (UTF16和ASCII中的'r'到'v')。