0

我正在使用该函数在 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);
4

3 回答 3

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 流中用另一个字符串替换一个字符串的要求,那么您正在考虑几个星期的额外编程。

于 2013-06-10T15:42:59.597 回答
2

您的代码中有多个问题,其影响在您在对布鲁诺的回答的评论中提供的示例newpdf.pdf中可见:

  1. 将重新压缩的流写入输出文件后,添加“\nendstr”并继续输入缓冲区中源流末尾之外的 7 个字符的大小,最有可能阻止看到“流" 在 "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 规范甚至不要求,而只是建议在流的结尾和“endstream”关键字之间添加一个行尾,参见。第 7.3.8.1 节:

    在数据之后和endstream之前应该有一个行尾标记

    这已经破坏了示例文件中的第一个流,其中源文件在那里没有行尾标记,因此您的结果将原始的“endstream”替换为“\nendstram”。这实际上在您的样本中经常发生。

  2. 您完全忽略了其字典中的 PDF 流包含包含流长度的条目,参见。PDF 规范中的第 7.3.8.2 节:

    每个流字典都应有一个Length条目,指示 PDF 文件的多少字节用于流数据。

    您的操作,即使您只是解压缩和重新压缩,也可能会更改压缩流的长度。因此,您必须更新该长度条目。诚然,这会使您的任务更加困难,因为该字典位于流之前。此外,在像您的源文件这样的情况下,该条目甚至可能不直接包含值,而是引用文件中其他位置的间接对象。

    这会破坏文件中的第二个流,该流声称它长 8150 字节,但长了大约 200 字节。任何 PDF 查看器都可能假定文件中该流的内容只有 8150 字节长,因此忽略后面 200 字节的内容。这很可能是您观察到的原因

    缺少一些文字或图形。

  3. 您完全忽略了 PDF 具有交叉引用表或流(甚至可能是它们的链),参见。PDF 规范中的第 7.5.4 节:

    交叉引用表包含允许随机访问文件内的间接对象的信息,因此不需要读取整个文件来定位任何特定对象。该表应包含每个间接对象的一行条目,指定该对象在文件主体内的字节偏移量。(从 PDF 1.5 开始,部分或全部交叉引用信息可能包含在交叉引用流中;参见 7.5.8,“交叉引用流”。)

    您的操作,即使您只是解压缩和重新压缩,也可能会更改压缩流的长度。因此,您必须更新交叉引用表中所有后续对象的偏移量。

    由于结果文件中第二个流的大小已经不同,因此该文件中只有很少的交叉引用条目是正确的。

  4. 您假设每个 PDF 流都被压缩了。这个假设是错误的,参见。PDF 规范中的表 5 。

    您的代码基本上会丢弃所有无法膨胀的流。这也可能是您观察到的原因

    缺少一些文字或图形。

  5. 您假设 PDF 中的序列“流”明确表示流的开始。这是错误的,该序列也可以很容易地在其他情况下使用。

  6. 您假设 PDF 中流开始后的第一个序列“endstream”明确指示该流的结束。这是错误的,该序列也可能是流内容的一部分。您必须使用流字典中Length条目的值。

此外,您似乎假设您遇到的每个流仍然在生成的 PDF 中使用。不必如此。特别是在增量更新的情况下(参见PDF 规范中的第 7.5.6 节),文件中可能有许多对象不再使用。虽然这不一定会破坏结果文件的语法,但您的更改(如果它们相互依赖)在语义上是不正确的。

于 2013-06-13T14:05:05.137 回答
1

我认为您必须阅读有关文本如何存储在 PDF 文件中的信息,

这是规范http://www.adobe.com/devnet/pdf/pdf_reference.html的链接

第 9 节 文本是理解的关键。

于 2013-06-10T17:54:50.530 回答