我知道这可能有点不寻常,但是我想了解一下PDF文档(字节数组)是否包含特定文本.我使用iText库v2.1.7在Java中自己创建了文档,该库可生成符合PDF 1.4规范的文档.

我最初的天真尝试是这样的:

byte[] target = "the target text".getBytes("UTF-8");
int index = Bytes.indexOf(pdfBytes, target); // Guava lib
System.out.println( index ); // always -1 (not found)

对于这些类型的文档如何编码以弄清楚我需要做什么,我只是不够了解.我想我真正需要找出的是在转换为字节时需要在目标文本上使用哪种编码,以使其与PDF使用的编码匹配.

我创建了一个小的PDF文档样本,除了一个带有单词一二三四五的短语外,什么都没有.如果我在Linux终端中放置该文件(或使用vim进行查看),则该PDF文件的内容将如下所示:

%PDF-1.4
%����
2 0 obj
<>stream
x�+�r
�24U�02I�2P0Q�n�
�F
!i\�y�
%��
%E��
i��E
i�e��!Y0Ů!\�\���
endstream
endobj
4 0 obj
<>>>/Parent 3 0 R/MediaBox[0 0 595 842]>>
endobj
1 0 obj
<>
endobj
3 0 obj
<>
endobj
5 0 obj
<>
endobj
6 0 obj
<>
endobj
xref
0 7
0000000000 65535 f
0000000309 00000 n
0000000015 00000 n
0000000397 00000 n
0000000152 00000 n
0000000460 00000 n
0000000505 00000 n
trailer
<<7bf1bdf9e8d048c5795c7785954d9360>]/Root 5 0 R/Size 7>>
startxref
615
%%EOF

其中某些字符编码在复制和粘贴过程中无法正确翻译,因此,如果您复制并保存在那里看到的内容,则会得到损坏的PDF. Here’s a link到该PDF的副本.

我尝试将目标字符串编码为各种编码,例如CP-1252和WinAnsiEncoding,但是这些是无法识别的字符集.

我原本认为这不会给我带来很多麻烦,但是我还无法弄清楚该怎么做.我确实有一种变通方法,可以获得相同的结果,但这是专门针对iText库的解决方案,即不是在PDF字节数组中搜索文本的通用解决方案.

如果使用iText解析要搜索的字节数组,则可以遍历PDF的每一页并提取文本:

private static boolean doesPDFContain(byte[] pdf, String text) throws Exception {
PdfReader reader = new PdfReader(pdf);
int numPages = reader.getNumberOfPages();
PdfTextExtractor extractor = new PdfTextExtractor(reader);
for (int i=1; i<=numPages; i++) {
if ( extractor.getTextFromPage(i).contains(text) ){
return true;
}
}
return false;
}

我仍然会对是否有可能做我最初尝试的事情感兴趣.

解决方法:

出于多种原因,您的幼稚方法(仅以特定编码查找文本)通常不起作用.

屏幕上显示的文本是您要查找的文本,是通过某些内容流中的文本绘制指令绘制的. (让我们忽略图形看起来像文本但使用矢量或位图图形命令绘制的情况,以及缺少或不正确的字体编码信息的情况.)

>您要查找的文本不一定是由一条指令绘制的.例如,可以使用两个连续的命令编写文本“ Hello”:

(Hel) Tj (lo) Tj

不同的命令甚至不需要在内容流中彼此跟随,它们可以分布在整个内容流中.

> PDF中的每种字体都可以为其字符串使用不同的编码,并且这些编码甚至不需要是标准编码,它们可以是PDF创建程序在运行时临时创建的编码.

>内容流可能(通常确实)需要过滤器进行解码,例如在PDF上方的对象2中,内容流需要FlateDecode过滤(本质上是:解压缩).

>可以对PDF进行加密(在这种情况下,更具体地说是对字符串和流进行加密);即使您可以在PDF查看器中毫不费力地打开的PDF,也可以使用默认密码进行加密(此技术用于编码权限).

因此,要检查内容流的内容,您可能必须

>解密文件;然后

>使用各自适用的过滤器对内容流进行解码;然后

>解析内容流指令以了解每个文本绘制指令

>使用哪种字体绘制文本和

>在何处绘制文本;然后

>根据字体信息解码字符串内容;然后

>根据位置信息对文本片段进行排序,然后将它们作为单个字符串组合在一起.

您最终可以在此字符串中以幼稚的方式搜索文本.