使用zlib从pdf中提取文本
Extract text from pdf using zlib
我使用该函数在pdf文件中查找文本并将该文本替换为另一个文本。问题是,当我进行膨胀,然后更改文本和收缩时,在最终的pdf中有时会遗漏一些文本或图形。这是一个错误在我的代码或zlib库不支持这个压缩或什么?
// Open the PDF source file:
FILE *pdfFile = fopen([sourceFile cStringUsingEncoding:NSUTF8StringEncoding], "rb");
if (pdfFile) {
// Get the file length:
int fseekres = fseek(pdfFile, 0, SEEK_END);
if (fseekres != 0) {
fclose(pdfFile);
return nil;
}
long filelen = ftell(pdfFile);
fseekres = fseek(pdfFile, 0, SEEK_SET);
if (fseekres != 0) {
fclose(pdfFile);
return nil;
}
char *buffer = new char[filelen];
size_t actualread = fread(buffer, filelen, 1, pdfFile);
if (actualread != 1) {
fclose(pdfFile);
return nil;
}
bool morestreams = true;
while (morestreams) {
size_t streamstart = [self findStringInBuffer:buffer search:(char *)"stream" buffersize:filelen];
size_t streamend = [self findStringInBuffer:buffer search:(char *)"endstream" buffersize:filelen];
[self saveFile:buffer len:streamstart + 7 fileName:[destFile cStringUsingEncoding:NSUTF8StringEncoding]];
if (streamstart > 0 && streamend > streamstart) {
streamstart += 6;
if (buffer[streamstart] == 0x0d && buffer[streamstart + 1] == 0x0a) {
streamstart += 2;
} else if (buffer[streamstart] == 0x0a) {
streamstart++;
}
if (buffer[streamend - 2] == 0x0d && buffer[streamend - 1] == 0x0a) {
streamend -= 2;
} else if (buffer[streamend - 1] == 0x0a) {
streamend--;
}
size_t outsize = (streamend - streamstart) * 10;
char *output = new char[outsize];
z_stream zstrm;
zstrm.zalloc = Z_NULL;
zstrm.zfree = Z_NULL;
zstrm.opaque = Z_NULL;
zstrm.avail_in = (uint)(streamend - streamstart + 1);
zstrm.avail_out = (uint)outsize;
zstrm.next_in = (Bytef *)(buffer + streamstart);
zstrm.next_out = (Bytef *)output;
int rsti = inflateInit(&zstrm);
if (rsti == Z_OK) {
int rst2 = inflate(&zstrm, Z_FINISH);
inflateEnd(&zstrm);
if (rst2 >= 0) {
size_t totout = zstrm.total_out;
//search and replace text code here
size_t coutsize = (streamend - streamstart + 1) * 10;
char *coutput = new char[coutsize];
z_stream c_stream;
c_stream.zalloc = Z_NULL;
c_stream.zfree = Z_NULL;
c_stream.opaque = Z_NULL;
c_stream.total_out = 0;
c_stream.avail_in = (uint)totout;
c_stream.avail_out = (uint)coutsize;
c_stream.next_in = (Bytef *)output;
c_stream.next_out = (Bytef *)coutput;
rsti = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
if (rsti == Z_OK) {
rsti = deflate(&c_stream, Z_FINISH);
deflateEnd(&c_stream);
if (rsti >= 0) {
[self saveFile:coutput len:c_stream.total_out fileName:[destFile cStringUsingEncoding:NSUTF8StringEncoding]];
}
}
delete [] coutput; coutput = 0;
[self saveFile:(char *)"nendstr" len:7 fileName:[destFile cStringUsingEncoding:NSUTF8StringEncoding]];
}
}
delete[] output; output = 0;
buffer += streamend + 7;
filelen = filelen - (streamend + 7);
} else {
morestreams = false;
}
}
[self saveFile:buffer len:filelen fileName:[destFile cStringUsingEncoding:NSUTF8StringEncoding]];
}
fclose(pdfFile);
你认为文本可以在内容流中找到的假设是错误的。
假设您有一个内容为Hello World的PDF。然后你可以有一个像这样的流:
q
BT
36 806 Td
0 -18 Td
/F1 12 Tf
(Hello World!)Tj
0 0 Td
ET
Q
但是它也可以是这样的:
Q
BT
/F1 12 Tf
88.66 367 Td
(ld) Tj
-22 0 Td
(Wor) Tj
-15.33 0 Td
(llo) Tj
-15.33 0 Td
(He) Tj
ET
q
您的代码将在前一个流中检测到单词"Hello",但在后一个流中将忽略它。
PDF查看器将以完全相同的方式呈现两个流:您将在完全相同的位置看到"Hello World"。
有时字符串被分成更小的部分,你经常会发现文本数组来引入字距,等等…这是PDF中的所有标准做法。
PDF不是一种适合编辑的格式。我并不是说这是不可能的,但是如果你想要满足PDF流中一个字符串替换为另一个字符串的要求,你需要花费几周的额外编程时间。
您的代码中有多个问题,其影响在您在Bruno的回答的注释中提供的示例newpdf.pdf中可见:
-
将重新压缩的流写入输出文件后,在输入缓冲区中添加"nendstr"并继续这个字符串的大小,7个字符,超出源流的末尾,最有可能防止看到"endstream"中的"stream"作为下一个流的开始:
[self saveFile:(char *)"nendstr" len:7 fileName:[destFile cStringUsingEncoding:NSUTF8StringEncoding]]; [...] buffer += streamend + 7;
添加该字符串的问题是,您假设输入缓冲区中的"结束流"前面正好有一个NEWLINE (0x0A)字节。这个假设是错误的,因为
。在PDF中,有三种类型的有效行结束标记,单个换行(0x0A),单个回车(0x0D)或回车和换行对(0x0D 0x0A),并且这些行结束标记中的任何一种都可以位于输入缓冲区中的"结束流"之前;在上面计算压缩流结束的代码中,您忽略了单个CARRIAGE RETURN变量,这里您忽略了2字节的变量;此外:
b。PDF规范甚至不要求,而只是建议在流的结尾和"endstream"关键字之间添加一个结束行,参见章节7.3.8.1:
数据之后和endstream之前应该有一个行结束标记
这已经中断了示例文件中的第一个流,其中源文件在那里没有行结束标记,因此您的结果将原始的"endstream"替换为" nendstream"。这实际上在您的示例中经常发生。
-
你完全忽略了PDF流在它的字典中包含一个包含流长度的条目,参见PDF规范的第7.3.8.2节:
每个流字典都有一个长度指示PDF文件中有多少字节用于流的数据的条目。
您的操作,即使您只是解压缩和重新压缩,也可能改变压缩流的长度。因此,您必须更新Length条目。不可否认,这使您的任务变得更加困难,因为该字典位于流之前。此外,在像源文件这样的情况下,该条目甚至可能不直接包含值,而是引用文件中某处的间接对象。
这打破了文件中的第二个流,它声称它是8150字节长,但实际上是200字节长。任何PDF查看器都可以假设文件中该流的内容只有8150字节长,因此忽略后面200字节的内容。这很可能就是为什么您观察到
部分文字或图形缺失
-
你完全忽略了PDF有一个交叉引用表或流(甚至可能是它们的链),参见PDF规范中的7.5.4节:
交叉引用表包含允许随机访问文件中的间接对象的信息,这样就不需要读取整个文件来定位任何特定的对象。该表应为每个间接对象包含一行条目,指定该对象在文件主体中的字节偏移量。(从PDF 1.5开始,部分或全部的交叉引用信息可以包含在交叉引用流中;参见7.5.8,"交叉引用流")
您的操作,即使您只是解压缩和重新压缩,也可能改变压缩流的长度。因此,您必须更新交叉引用表中所有以下对象的偏移量。
由于结果文件中第二个流的大小已经不同,因此该文件中只有极少数交叉引用项是正确的。
-
您假设每个PDF流都被压缩。这个假设是错误的,参见PDF规范中的表5。
你的代码基本上放弃了所有它不能膨胀的流。这也可能是您观察到
部分文字或图形缺失
-
您假设PDF中的序列"stream"明确表示流的开始。这是错误的,该序列也可以很容易地在其他上下文中使用。
-
您假设PDF中流开始后的第一个序列"endstream"明确表示该流的结束。这是错误的,该序列也可能是流内容的一部分。必须使用Length的值流字典中的条目。
此外,您似乎假设您遇到的每个流仍然用于生成的PDF。事实并非如此。特别是在增量更新的情况下(参见PDF规范中的第7.5.6节),文件中可能会有许多对象不再使用。虽然这并不一定会破坏结果文件的语法,但您的更改(如果它们相互依赖)在语义上是不正确的。
我想你必须阅读文本如何存储在PDF文件中,
这里是一个链接到规范http://www.adobe.com/devnet/pdf/pdf_reference.html
第九节文本是理解的关键。
- 如何从也包含C++字母的文本文件中提取某些数字?
- 模式匹配文本并提取C++中的数据
- 遍历对象向量,并找到与从文本文件中提取的对象匹配的变量
- 将文本文件数据读入字符数组时提取运算符的歧义
- 使用 substr 提取文本文件时出现问题
- 接受多个输入(如 +、- 和平方数字)的计算器.从文本文件中提取信息
- 长方程加法/减法计算器,从文本文件中提取运算符和数字
- 从 PDF 中提取第一行文本
- 在特定字段中关键字搜索后,从文本文件中提取多个记录
- 如何用c++打印从文本文件中提取的非英文字符
- 程序未正确从文本文件中提取信息
- 从文本中抓取句子,将所有句子分别存储在某个数据结构中
- 从C 中的文本提取数字---分割故障
- 从文本文件中读取输入并将其提取 - C++
- 从C++中的文本文件中提取并使用特定数据
- 从二进制文件中抓取文本时,为什么 xdg_vtnr=8 是我的结果
- 有没有任何方法可以从C++中的文本中提取URL
- 可视化从文本C++中提取字符串数据
- 从Windows应用程序的文本框中抓取文本
- 如何在纯c++中从MS word文档文件中提取纯文本