OpenGL ES 应用
包含 EJB 的 J2EE 应用
基于 JSF 的WEB应用
FileSystem Driver (Windows & Linux)
编译器及代码分析工具
教育信息化咨询、自适应测试平台、联机教学环境
PDFBox是一个相对简洁实用的处理PDF文件的Java类库。PDFBox具有丰富的功能,但我只关心利用它来抽取PDF文件中的文本,这也非常简单,PDFBox作者已经考虑到这种用法并提供了很好的支持,这是PDFBox相对iText对我更具吸引力的重要原因,另外它的体积也比iText小多了。
不过PDFBox处理中文文件有问题,google了一下,抱怨的一堆,解决方案没找着,还是自己动手解决吧。还好,不是太复杂,在小黑屋里关了一天就找到了原因了。以下就是寻找解决方案过程的记录。
PDF Box的总体结构
1、Mysoo使用PDF Box涉及三个层次
PDFTextStripper ==> PD Model ==> COS Model
COS Model是对PDF文件格式的直接映射,PD Model则是对COS Model的OO封装,其关系如下图所示:

上半部份是PDModel,每个PDModel对象都是对于特定COSModel对象的封装,下半部份是COSModel,每个COSModel对象都直接对应PDF文档的一段内容。
PDFTextStripper是一辅助工具,它封装了通过PDModel取得文档的所有页面,并从每一页可能包含有文本的对象中取出字符的操作。
2、PDF Box取出文本的处理过程
PDFTextStripper.writeText()
文本处理的实际内容就在 PDFStreamEngine.showString() 里,在这之前 SetGraphicsStateParameters已经分析并保存了PDF的当前绘制格式参数,其中与文本处理相关的就是字体PDFont。ShowString()会利用这个PDFont对源自PDF文件的字节串进行解码,形成对应的字符串。
3、PDFStreamEngine的字符解码过程
PDFStreamEngine.showString(byte[] string)
for each byte in input string
firtly try PDFont.encode( string[offset], 1 byte )
if return null, then try PDFont.encode( string[offset], 2 bytes)
PDFont.encode( bytes, count ), convert bytes into a single char string
PDFont try to get CMap
if the font has embbed TO_UNICODE Cmap then parse it
else retrieve Cmap based on ENCODING attribute of the font
// 测试中cn.pdf是 Type0字体,encodingName GBK-EUC-H,
// GBK-EUC-H 代表 MS CP936 (lfCharSet 0x86), GBK charset, GBK encoding
// 第一块文本处理时,cmapObjects为空,
PDFont perform cmap name subtitution
PDFont parseCmap from Resource/cmap/cmapName (GBK-EUC-H)
CmapParser.parse(resStream) 分析并创建cmap实例
将cmap对象注册到cmapObjects {encodingName, cmap}
如果1字节字符识别返回null,则尝试2字符
cmap.lookup( bytes, count )
如果字符表里没找到
PDFont.getEncoding()
从font ENCODING属性中取得encoding (GBK-EUC-H)
EncodingManager.getEncoding( encoding )
Manager从内部ENCODINGS表中名字代表的Encoding对象
这张表是在Manger类static块里初始化的
加ENCODINGS.put( COSName.GBK_EUC_H_ENCODING, new GbkEucHEncoding() );
PDFont.getCodeFromArray(bytes, count)
'前'字-1字节时应该返回0xC7,两字节时应该返回0xC7B0
Encoding.getCharacter(code)
如果Encoding也处理不了就getStringFromArray()
faint!
PDFont:
protected int getCodeFromArray( byte[] data, int offset, int length )
{
int code = 0;
for( int i=0; i<length; i++ )
{
code <<= 8;
code = (data[offset+i]+256)%256; //这行应该是 |=
}
return code;
}
//
注:PDFont.java
395~401,if分支的代码貌似多余
// cmap = (CMap)cmapObjects.get( encodingName );
// if( cmap != null )
// {
// cmap = (CMap)cmapObjects.get( encodingName );
// }
// else
// { ... }
PDF文件中字体描述部份:
0020de0: 626a 0d3c 3c20 0d2f 5479 7065 202f 466f bj.<< ./Type /Fo
0020df0: 6e74 200d 2f53 7562 7479 7065 202f 5479 nt ./Subtype /Ty
0020e00: 7065 3020 0d2f 4e61 6d65 202f 4631 200d pe0 ./Name /F1 .
0020e10: 2f42 6173 6546 6f6e 7420 2f23 4241 2344 /BaseFont /#BA#D
0020e20: 4123 4343 2345 3520 0d2f 4465 7363 656e A#CC#E5 ./Descen
0020e30: 6461 6e74 466f 6e74 7320 5b20 3230 3720 dantFonts [ 207
0020e40: 3020 5220 5d20 0d2f 456e 636f 6469 6e67 0 R ] ./Encoding
0020e50: 202f 4742 4b2d 4555 432d 4820 0d3e 3e20 /GBK-EUC-H .>>
Type0复合字体:基本字体名 BADA CCE5 是 黑体,编码是 GBK-EUC-H,CID子字体210 0 R (PDF对象ID)
0020e60: 0d65 6e64 6f62 6a0d 3230 3720 3020 6f62 .endobj.207 0 ob
0020e70: 6a0d 3c3c 200d 2f54 7970 6520 2f46 6f6e j.<< ./Type /Fon
0020e80: 7420 0d2f 5375 6274 7970 6520 2f43 4944 t ./Subtype /CID
CID Type2 字体:基本字体 黑体,WinCharSet 0x86,描述符208 0 R(PDF对象ID)
0020e90: 466f 6e74 5479 7065 3220 0d2f 4261 7365 FontType2 ./Base
0020ea0: 466f 6e74 202f 2342 4123 4441 2343 4323 Font /#BA#DA#CC#
0020eb0: 4535 200d 2f57 696e 4368 6172 5365 7420 E5 ./WinCharSet
0020ec0: 3133 3420 0d2f 466f 6e74 4465 7363 7269 134 ./FontDescri
0020ed0: 7074 6f72 2032 3038 2030 2052 200d 2f43 ptor 208 0 R ./C
0020ee0: 4944 5379 7374 656d 496e 666f 203c 3c20 IDSystemInfo <<
0020ef0: 2f52 6567 6973 7472 7920 284b 77b0 6789 /Registry (Kw.g.
0020f00: 292f 4f72 6465 7269 6e67 2028 4d51 ee29 )/Ordering (MQ.)
0020f10: 2f53 7570 706c 656d 656e 7420 3220 3e3e /Supplement 2 >>
0020f20: 200d 2f44 5720 3130 3030 200d 2f57 205b ./DW 1000 ./W [
0020f30: 2038 3134 2039 3037 2035 3030 2037 3731 814 907 500 771
0020f40: 3620 3737 3136 2035 3030 205d 200d 3e3e 6 7716 500 ] .>>
0020f50: 200d 656e 646f 626a 0d32 3038 2030 206f .endobj.208 0 o
问题求解
没有GBK-EUC-H对应的encoding
Holly(http://www.jsfsoft.com:8080/beyond-pebble/lee)认为可以有更为优雅的解决方案并做了一个patch,patch已经提交给pdfbox项目,在官方版本更新之前,你可以直接从这里获得这个patch(http://sourceforge.net/tracker/index.php?func=detail&aid=1640071&group_id=78314&atid=552834)。