Commit 05a6d9669d1ac20d3a86eb958506e247a5d91aa9
1 parent
3416c60f
redo padding calculation for first half xref stream; old calculation
failed to consider the effect of compressing the strema git-svn-id: svn+q:///qpdf/trunk@932 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing
1 changed file
with
35 additions
and
18 deletions
libqpdf/QPDFWriter.cc
| @@ -1734,14 +1734,15 @@ QPDFWriter::writeXRefStream(int objid, int max_id, int max_offset, | @@ -1734,14 +1734,15 @@ QPDFWriter::writeXRefStream(int objid, int max_id, int max_offset, | ||
| 1734 | trailer_e which, int first, int last, int size) | 1734 | trailer_e which, int first, int last, int size) |
| 1735 | { | 1735 | { |
| 1736 | return writeXRefStream(objid, max_id, max_offset, | 1736 | return writeXRefStream(objid, max_id, max_offset, |
| 1737 | - which, first, last, size, 0, 0, 0, 0); | 1737 | + which, first, last, size, 0, 0, 0, 0, false); |
| 1738 | } | 1738 | } |
| 1739 | 1739 | ||
| 1740 | int | 1740 | int |
| 1741 | QPDFWriter::writeXRefStream(int xref_id, int max_id, int max_offset, | 1741 | QPDFWriter::writeXRefStream(int xref_id, int max_id, int max_offset, |
| 1742 | trailer_e which, int first, int last, int size, | 1742 | trailer_e which, int first, int last, int size, |
| 1743 | int prev, int hint_id, | 1743 | int prev, int hint_id, |
| 1744 | - int hint_offset, int hint_length) | 1744 | + int hint_offset, int hint_length, |
| 1745 | + bool skip_compression) | ||
| 1745 | { | 1746 | { |
| 1746 | int xref_offset = this->pipeline->getCount(); | 1747 | int xref_offset = this->pipeline->getCount(); |
| 1747 | int space_before_zero = xref_offset - 1; | 1748 | int space_before_zero = xref_offset - 1; |
| @@ -1764,8 +1765,14 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, int max_offset, | @@ -1764,8 +1765,14 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, int max_offset, | ||
| 1764 | if (! ((this->stream_data_mode == qpdf_s_uncompress) || this->qdf_mode)) | 1765 | if (! ((this->stream_data_mode == qpdf_s_uncompress) || this->qdf_mode)) |
| 1765 | { | 1766 | { |
| 1766 | compressed = true; | 1767 | compressed = true; |
| 1767 | - p = pushPipeline( | ||
| 1768 | - new Pl_Flate("compress xref", p, Pl_Flate::a_deflate)); | 1768 | + if (! skip_compression) |
| 1769 | + { | ||
| 1770 | + // Write the stream dictionary for compression but don't | ||
| 1771 | + // actually compress. This helps us with computation of | ||
| 1772 | + // padding for pass 1 of linearization. | ||
| 1773 | + p = pushPipeline( | ||
| 1774 | + new Pl_Flate("compress xref", p, Pl_Flate::a_deflate)); | ||
| 1775 | + } | ||
| 1769 | p = pushPipeline( | 1776 | p = pushPipeline( |
| 1770 | new Pl_PNGFilter( | 1777 | new Pl_PNGFilter( |
| 1771 | "pngify xref", p, Pl_PNGFilter::a_encode, esize, 0)); | 1778 | "pngify xref", p, Pl_PNGFilter::a_encode, esize, 0)); |
| @@ -2024,12 +2031,15 @@ QPDFWriter::writeLinearized() | @@ -2024,12 +2031,15 @@ QPDFWriter::writeLinearized() | ||
| 2024 | // Must pad here too. | 2031 | // Must pad here too. |
| 2025 | if (pass == 1) | 2032 | if (pass == 1) |
| 2026 | { | 2033 | { |
| 2027 | - // first_half_max_obj_offset is very likely to fall | ||
| 2028 | - // within the first 64K of the document (thus | ||
| 2029 | - // requiring two bytes for offsets) since it is the | ||
| 2030 | - // offset of the last uncompressed object in page 1. | ||
| 2031 | - // We allow for it to do otherwise though. | ||
| 2032 | - first_half_max_obj_offset = 65535; | 2034 | + // Set first_half_max_obj_offset to a value large |
| 2035 | + // enough to force four bytes to be reserved for each | ||
| 2036 | + // file offset. This would provide adequate space for | ||
| 2037 | + // the xref stream as long as the last object in page | ||
| 2038 | + // 1 starts with in the first 4 GB of the file, which | ||
| 2039 | + // is extremely likely. In the second pass, we will | ||
| 2040 | + // know the actual value for this, but it's okay if | ||
| 2041 | + // it's smaller. | ||
| 2042 | + first_half_max_obj_offset = 1 << 25; | ||
| 2033 | } | 2043 | } |
| 2034 | pos = this->pipeline->getCount(); | 2044 | pos = this->pipeline->getCount(); |
| 2035 | writeXRefStream(first_half_xref, first_half_end, | 2045 | writeXRefStream(first_half_xref, first_half_end, |
| @@ -2037,19 +2047,24 @@ QPDFWriter::writeLinearized() | @@ -2037,19 +2047,24 @@ QPDFWriter::writeLinearized() | ||
| 2037 | t_lin_first, first_half_start, first_half_end, | 2047 | t_lin_first, first_half_start, first_half_end, |
| 2038 | first_trailer_size, | 2048 | first_trailer_size, |
| 2039 | hint_length + second_xref_offset, | 2049 | hint_length + second_xref_offset, |
| 2040 | - hint_id, hint_offset, hint_length); | 2050 | + hint_id, hint_offset, hint_length, |
| 2051 | + (pass == 1)); | ||
| 2041 | int endpos = this->pipeline->getCount(); | 2052 | int endpos = this->pipeline->getCount(); |
| 2042 | if (pass == 1) | 2053 | if (pass == 1) |
| 2043 | { | 2054 | { |
| 2044 | // Pad so we have enough room for the real xref | 2055 | // Pad so we have enough room for the real xref |
| 2045 | - // stream. In an extremely unlikely worst case, | ||
| 2046 | - // first_half_max_obj_offset could be enough larger to | ||
| 2047 | - // require two extra bytes beyond what we calculated | ||
| 2048 | - // in pass 1. This means we need to save two extra | ||
| 2049 | - // bytes for each xref entry. To that, we'll add 10 | ||
| 2050 | - // extra bytes for number length increases. | 2056 | + // stream. We've written the stream without |
| 2057 | + // compression (but with all the stream dictionary | ||
| 2058 | + // parameters to enable it) and assuming a very | ||
| 2059 | + // generous allowance for writing file offsets. We | ||
| 2060 | + // need a little extra padding to allow for zlib's | ||
| 2061 | + // output to be larger than its input (6 bytes plus 5 | ||
| 2062 | + // bytes per 16K), and then we'll add 10 extra bytes | ||
| 2063 | + // for number length increases. | ||
| 2064 | + | ||
| 2065 | + unsigned int xref_bytes = endpos - pos; | ||
| 2051 | int possible_extra = | 2066 | int possible_extra = |
| 2052 | - 10 + (2 * (first_half_end - first_half_start + 1)); | 2067 | + 16 + (5 * ((xref_bytes + 16383) / 16384)); |
| 2053 | for (int i = 0; i < possible_extra; ++i) | 2068 | for (int i = 0; i < possible_extra; ++i) |
| 2054 | { | 2069 | { |
| 2055 | writeString(" "); | 2070 | writeString(" "); |
| @@ -2064,6 +2079,8 @@ QPDFWriter::writeLinearized() | @@ -2064,6 +2079,8 @@ QPDFWriter::writeLinearized() | ||
| 2064 | { | 2079 | { |
| 2065 | writeString(" "); | 2080 | writeString(" "); |
| 2066 | } | 2081 | } |
| 2082 | + // A failure of this insertion means we didn't allow | ||
| 2083 | + // enough padding for the first pass xref stream. | ||
| 2067 | assert(this->pipeline->getCount() == first_xref_end); | 2084 | assert(this->pipeline->getCount() == first_xref_end); |
| 2068 | } | 2085 | } |
| 2069 | writeString("\n"); | 2086 | writeString("\n"); |