阅读PDF Literal String解析困境

时间:2014-10-14 00:47:23

标签: java pdf encoding character-encoding pdf-parsing

我在同一个PDF页面中有以下内容,位于不同的ObjectX:

第一

[(some text)] TJ ET Q
[(some other text)] TJ ET Q

到目前为止非常简单和基本......

第二个

[( H T M L   E x a m p l e)] TJ ET Q
[( S o m e   s p e c i a l   c h a r a c t e r s :   <   ¬   ¬   ¬   &   ט   ט   ©   >   \\ s l a s h   \\ \\ d o u b l e - s l a s h   \\ \\ \\ t r i p l e - s l a s h  )] TJ ET Q

注意:在上面的文字中并不明显,但是:

'HTMLE xample'实际上是 0H0T0M0L 0 [32] 0E0x0a0m0p0l0e 其中每个0都是文字值0 == ((char)0)所以如果我忽略所有的0值,这实际上就像上面的例子......

一些字节:

htmlexample == [0, 72, 0, 84, 0, 77, 0, 76, 0, 32, 0, 69, 0, 120, 0, 97, 0, 109, 0, 112, 0, 108, 0, 101]
<content>  == [0, 32, 32, -84, 0, 32, 32, -84, 0, 32, 32, -84, 0, 32, 0, 38, 0, 32, 0, -24, 0, 32, 0, -24, 0, 32, 0, -87, 0, 32, 0]

但是在下一行中,由于以下原因,我需要将每两个字节组合成一个char:

&LT; ¬¬¬...&gt;实际上&lt; 0 [32] [32]¬0[32] [32]¬0[32] [32]¬...&gt;其中[32]¬的组合是€

我面临的问题不是我使用的转换本身:     new String(sb.toString()。getBytes(“UTF-8”),“UTF-16BE”)

问题是知道何时应用它以及何时保留UTF-8。

== 更新 ==

用于有问题的对象的字体是:

#7 0# {
    'Name' : "F4"
    'BaseFont' : "AAAAAE+DejaVuSans-Bold"
    'Subtype' : "Type0"
    'ToUnicode' : #41 0# {
        'Filter' : "FlateDecode"
        'Length' : 1679.0f
    } + Stream(5771 bytes)
    'Encoding' : "Identity-H"
    'DescendantFonts' : [#42 0# {
        'FontDescriptor' : #43 0# {
            'MaxWidth' : 2016.0f
            'AvgWidth' : 573.0f
            'FontBBox' : [-1069.0f, -415.0f, 1975.0f, 1174.0f]
            'MissingWidth' : 600.0f
            'FontName' : "AAAAAE+DejaVuSans-Bold"
            'Type' : "FontDescriptor"
            'CapHeight' : 729.0f
            'StemV' : 60.0f
            'Leading' : 0.0f
            'FontFile2' : #34 0# {
                'Filter' : "FlateDecode"
                'Length1' : 83036.0f
                'Length' : 34117.0f
            } + Stream(83036 bytes)
            'Ascent' : 928.0f
            'Descent' : -236.0f
            'XHeight' : 547.0f
            'StemH' : 26.0f
            'Flags' : 32.0f
            'ItalicAngle' : 0.0f
        }
        'Subtype' : "CIDFontType2"
        'W' : [32.0f, [348.0f, 456.0f, 521.0f, 838.0f, 696.0f, 1002.0f, 872.0f, 306.0f, 457.0f, 457.0f, 523.0f, 838.0f, 380.0f, 415.0f, 380.0f, 365.0f], 48.0f, 57.0f, 696.0f, 58.0f, 59.0f, 400.0f, 60.0f, 62.0f, 838.0f, 63.0f, [580.0f, 1000.0f, 774.0f, 762.0f, 734.0f, 830.0f, 683.0f, 683.0f, 821.0f, 837.0f, 372.0f, 372.0f, 775.0f, 637.0f, 995.0f, 837.0f, 850.0f, 733.0f, 850.0f, 770.0f, 720.0f, 682.0f, 812.0f, 774.0f, 1103.0f, 771.0f, 724.0f, 725.0f, 457.0f, 365.0f, 457.0f, 838.0f, 500.0f, 500.0f, 675.0f, 716.0f, 593.0f, 716.0f, 678.0f, 435.0f, 716.0f, 712.0f, 343.0f, 343.0f, 665.0f, 343.0f, 1042.0f, 712.0f, 687.0f, 716.0f, 716.0f, 493.0f, 595.0f, 478.0f, 712.0f, 652.0f, 924.0f, 645.0f, 652.0f, 582.0f, 712.0f, 365.0f, 712.0f, 838.0f], 160.0f, [348.0f, 456.0f, 696.0f, 696.0f, 636.0f, 696.0f, 365.0f, 500.0f, 500.0f, 1000.0f, 564.0f, 646.0f, 838.0f, 415.0f, 1000.0f, 500.0f, 500.0f, 838.0f, 438.0f, 438.0f, 500.0f, 736.0f, 636.0f, 380.0f, 500.0f, 438.0f, 564.0f, 646.0f], 188.0f, 190.0f, 1035.0f, 191.0f, 191.0f, 580.0f, 192.0f, 197.0f, 774.0f, 198.0f, [1085.0f, 734.0f], 200.0f, 203.0f, 683.0f, 204.0f, 207.0f, 372.0f, 208.0f, [838.0f, 837.0f], 210.0f, 214.0f, 850.0f, 215.0f, [838.0f, 850.0f], 217.0f, 220.0f, 812.0f, 221.0f, [724.0f, 738.0f, 719.0f], 224.0f, 229.0f, 675.0f, 230.0f, [1048.0f, 593.0f], 232.0f, 235.0f, 678.0f, 236.0f, 239.0f, 343.0f, 240.0f, [687.0f, 712.0f, 687.0f, 687.0f, 687.0f, 687.0f, 687.0f], 247.0f, [838.0f, 687.0f], 249.0f, 252.0f, 712.0f, 253.0f, [652.0f, 716.0f]]
        'Type' : "Font"
        'BaseFont' : "AAAAAE+DejaVuSans-Bold"
        'CIDSystemInfo' : {
            'Supplement' : 0.0f
            'Ordering' : "Identity" + Stream(8 bytes)
            'Registry' : "Adobe" + Stream(5 bytes)
        }
        'DW' : 600.0f
        'CIDToGIDMap' : #44 0# {
            'Filter' : "FlateDecode"
            'Length' : 10200.0f
        } + Stream(131072 bytes)
    }]
    'Type' : "Font"
}

没有迹象表明字体的编码类型。

== 更新 ==

至于ToUnicode对象,在这些字体的情况下,它本身就不应该是Identity-H,而是X == X映射,这里有一些例子,直到FFFF:

<0000> <00ff> <0000>
<0100> <01ff> <0100>
<0200> <02ff> <0200>
<0300> <03ff> <0300>
<0400> <04ff> <0400>
<0500> <05ff> <0500>
<0600> <06ff> <0600>
<0700> <07ff> <0700>
<0800> <08ff> <0800>
<0900> <09ff> <0900>
<0a00> <0aff> <0a00>
<0b00> <0bff> <0b00>
<0c00> <0cff> <0c00>
<0d00> <0dff> <0d00>
<0e00> <0eff> <0e00>
<0f00> <0fff> <0f00>
<1000> <10ff> <1000>
<1100> <11ff> <1100>
....
....
....
<fc00> <fcff> <fc00>
<fd00> <fdff> <fd00>
<fe00> <feff> <fe00>
<ff00> <ffff> <ff00>

因此映射不在ToUnicode对象中,但其他渲染器仍可以很好地渲染它!

任何想法?

2 个答案:

答案 0 :(得分:3)

  

我使用:new String(sb.toString()。getBytes(“UTF-8”),“UTF-16BE”)

     

问题是知道何时应用它以及何时保留UTF-8。

OP可能会在检查一些示例PDF文件后假设PDF内容流中的字符串使用UTF-8或UTF-16BE进行编码。

这个假设是错误的。

PDF允许一些标准的单字节编码( MacRomanEncoding MacExpertEncoding WinAnsiEncoding ),其中没有一个是UTF-8(由于不同编码之间的关系,特别是ASCII,Latin1和UTF-8,当面对有限的样本时,它们可能会相互混淆)。此外,还允许许多预定义的多字节编码,其中一些确实与UTF-16相关..

但PDF也允许使用完全自定义编码,包括单字节和多字节!

E.g。本文绘制操作

(ABCCD) Tj

表示使用此编码的简单字体:

<<
 /Type /Encoding
 /Differences [ 65 /H /e /l /o ] 
>>

显示单词 Hello

虽然这可能看起来像是一个人为构造的示例,但是创建这样的自定义编码的过程(即通过将某些起始值向上的代码按照它们首次出现在页面上或文档中的顺序分配给字形经常使用。

此外,OP的当前解决方案

  

如果您的字体对象有CMap,那么您将其视为UTF-16,否则不会。

仅适用于极少数文档,因为

a)简单字体(使用单字节编码)也可以提供 ToUnicode CMap和 b)复合字体CMaps也不需要像UTF那样,而是可以使用混合的多字节编码。

因此,无法对所使用的字体信息进行深入分析,参见PDF specification ISO 32000-1的9.5..9.9。

PS 关于OP的一些评论:

  

this: new String(sb.toString()。getBytes(“UTF-8”),“UTF-16BE”)是问题如何解决的一个例子一个办法!无论我将数据视为16位还是8位,都可以在获取字形时完成解决方案

  

每个键的ToUnicode映射是16位(我见过的唯一),

数据可以是混合数据,例如看看Adobe CMap and CIDFont Files Specification,这里CMap示例9包含

部分
4 begincodespacerange
<00> <80>
<8140> <9ffc>
<a0> <de>
<e040> <fbec>
endcodespacerange

解释为

  

图6显示了此示例中的代码空间定义如何包含两个单字节线性范围的代码(<00><80><A0><DF>)和两个双字节的长方形代码范围(<8140><9FFC><E040><FBFC>)。第一个双字节区域包括由第一字节值81到9F和第二字节值40到FC限定的所有代码。因此,输入代码<86A9>在区域内,因为两个字节都在边界内。该代码有效。输入代码<8210>不在该区域内,即使其第一个字节在81到9F之间,因为它的第二个字节不在边界内。该代码无效。第二个双字节区域具有类似的界限。

Figure 6 Codespace ranges for the 83pv-RKSJ-H charset encoding

答案 1 :(得分:0)

好的,因为这似乎很复杂,而且这个错误的原因很愚蠢,特别是在我的最后,但是有关何时将这些字符视为 UTF-16的教训,何时不去。

我的问题不是在解析字体时,而是在渲染它们时。根据Font对象中指定的详细信息,您可以确定字体的类型并对其应用正确的逻辑。