我正在使用该函数在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);
答案 0 :(得分:3)
您认为可以在内容流中找到文本的假设是错误的。
假设您有一个包含内容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流中用另一个String替换一个String,你就会看几周的额外编程。
答案 1 :(得分:2)
您的代码中存在多个问题,其效果在您对Bruno回答的评论中提供的示例newpdf.pdf中可见:
将重新压缩的流写入输出文件后,添加" \ nendstr"并继续输入缓冲区中源字符串末尾之外的字符串7的大小,最有可能阻止查看" stream" in" endstream"作为下一个流的开始:
[self saveFile:(char *)"\nendstr" len:7 fileName:[destFile cStringUsingEncoding:NSUTF8StringEncoding]];
[...]
buffer += streamend + 7;
添加该字符串的问题在于您假设" endstream"在输入缓冲区中,前面只有一个NEWLINE(0x0A)字节。这个假设是错误的,因为
一个。在PDF中有三种类型的有效行尾标记,一个LINE FEED(0x0A),一个CARRIAGE RETURN(0x0D),或CARRIAGE RETURN和LINE FEED对(0x0D 0x0A),以及这些中的任何一个结束行标记可以在" endstream"之前。在输入缓冲区;在上面进一步计算压缩流结束的代码中,忽略单个CARRIAGE RETURN变量,在这里忽略2字节变量;而且:
湾PDF specification甚至不需要,只是建议在流的结尾和" endstream"之间添加一个行尾。关键字,cf。第7.3.8.1节:
数据之后和 endstream
之前应该有一个行尾标记
这已经破坏了示例文件中的第一个流,在该流文件中源文件没有行尾标记,因此,您的结果将替换原始" endstream"用" \ nendstram"。这实际上经常在您的样本中发生。
您完全忽略其字典中的PDF流包含一个包含流长度的条目,参见PDF specification中的第7.3.8.2节:
每个流字典都应有一个长度条目,指示PDF文件的字节数用于流的数据。
即使您只进行解压缩和重新压缩,您的操作也可能会改变压缩流的长度。因此,您必须更新长度条目。这无疑使你的任务变得更加困难,因为该字典在之前是流。此外,在类似源文件的情况下,该条目甚至可能不直接包含该值,而是在文件中引用间接对象其他地方。
这会破坏文件中的第二个流,声称它的长度为8150字节,但相差大约200个字节。任何PDF查看器都可能认为文件中该流的内容只有8150字节长,因此忽略那些尾随200字节的内容。这很可能是你观察到
的原因缺少某些文字或图片。
您完全忽略PDF具有交叉引用表或流(或者甚至可能是它们的链),参见PDF specification中的第7.5.4节:
交叉引用表包含允许随机访问文件中的间接对象的信息,以便无需读取整个文件即可找到任何特定对象。该表应包含每个间接对象的单行条目,指定该对象在文件正文中的字节偏移量。 (从PDF 1.5开始,部分或全部交叉引用信息也可以包含在交叉引用流中;参见7.5.8,"交叉引用流。")
即使您只进行解压缩和重新压缩,您的操作也可能会改变压缩流的长度。因此,您必须更新交叉引用表中所有后续对象的偏移量。
由于结果文件中第二个流的大小已经不同,因此该文件中只有极少数交叉引用条目是正确的。
您假设每个PDF流都已缩减。这种假设是错误的,参见PDF specification中的表格5。
您的代码基本上会丢弃所有无法通知的流。这也可能是您观察到
的原因缺少某些文字或图片。
您假设序列" stream"在PDF中明确指示流的开始。这是错误的,该序列也可以在其他环境中轻松使用。
您假设第一个序列" endstream"在流开始后的PDF中明确指示该流的结束。这是错误的,该序列也可能是流内容的一部分。您必须使用流字典中长度条目的值。
此外,您似乎假设您出现的每个流仍然在生成的PDF中使用。这不一定是这种情况。特别是在增量更新的情况下(参见PDF specification中的第7.5.6节),文件中可能有许多对象不再使用。虽然这不一定会破坏结果文件的语法,但是您的更改(如果它们相互依赖)在语义上是不正确的。
答案 2 :(得分:1)