java - 阅读 PDF Literal String 解析困境

标签 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

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

'H T M L E x a m p l e' 实际上是 0H0T0M0L0[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]

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

< ¬ ¬ ¬...> 实际上是 <0[32][32]¬0[32][32]¬0[32][32]¬...> 其中 [32]¬ 的组合是€

我面临的问题不是我使用的转换本身: 新字符串(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对象中,但其他渲染器仍然可以很好地渲染它!

有什么想法吗?

最佳答案

I use: new String(sb.toString().getBytes("UTF-8"),"UTF-16BE")

The problem is to know when to apply it and when to keep the UTF-8.

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

这个假设是错误的。

PDF 允许一些标准的单字节编码(MacRomanEncodingMacExpertEncodingWinAnsiEncoding),其中没有一个是 UTF-8(由于不同编码之间的关系,尤其是 ASCII、Latin1 和 UTF-8,在样本有限的情况下,它们可能会相互混淆)。此外,还允许使用许多预定义的多字节编码,其中一些确实与 UTF-16 相关..

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

例如本次文字绘制操作

(ABCCD) Tj

对于使用这种编码的简单字体:

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

显示单词 Hello!

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

此外,OP当前的解决方案

If your font object has a CMap, then you treat it as a UTF-16, otherwise not.

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

a) 简单字体(使用单字节编码)也可以提供一个ToUnicode CMap 和 b) 复合字体 CMap 也不需要类似 UTF,而是可以使用混合多字节编码。

因此,无法深入分析使用的字体信息,cf。 PDF specification 的 9.5..9.9 ISO 32000-1。

PS 关于 OP 的一些评论:

this: new String(sb.toString().getBytes("UTF-8"),"UTF-16BE") was an example to the how the problem is solved not a solution! The solution is done while fetching the glyphs whether I treat the data as 16-bit or 8-bit

the ToUnicode map is 16-bit(The only ones I've seen) per key,

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

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

解释为

Figure 6 shows how the codespace definition in this example comprises two single-byte linear ranges of codes (<00> to <80> and <A0> to <DF>) and two double-byte rectangular ranges of codes (<8140> to <9FFC> and <E040> to <FBFC>). The first two-byte region comprises all codes bounded by first-byte values of 81 through 9F and second-byte values of 40 through FC. Thus, the input code <86A9> is within the region because both bytes are within bounds. That code is valid. The input code <8210> is not within the region, even though its first byte is between 81 and 9F, because its second byte is not within bounds. That code is invalid. The second two-byte region is similarly bounded.

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

关于java - 阅读 PDF Literal String 解析困境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26351052/

相关文章:

java - 在 netbeans 中使用 .class 文件

java - Android - 调用自定义 View 方法时出现空指针异常

java - 将 rowspan 与 itext pdf 一起使用时无法显示背景颜色

c# - 在 C# 中确定字符串的编码

java - 使用 JAXB 解析器解析韩语字符串 작동불 时获取字符串 '???'

java - 打印字符串中唯一元音的数量,Java

java - 双三元Integer初始化导致空指针

pdf - SVG linestroke-dasharray 无法正确打印到 PDF

r - ggplot 与 ggplot2 : pdf very slow to display

Python - 读取奇怪的 utf-16 格式的文本文件