Commit 84353451a33171b587c0bdeaae745dbc0e553fff
1 parent
71f17135
refactor to isolate pad calculation
git-svn-id: svn+q:///qpdf/trunk@956 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing
2 changed files
with
44 additions
and
48 deletions
include/qpdf/QPDFWriter.hh
| @@ -183,6 +183,7 @@ class QPDFWriter | @@ -183,6 +183,7 @@ class QPDFWriter | ||
| 183 | void writeBuffer(PointerHolder<Buffer>&); | 183 | void writeBuffer(PointerHolder<Buffer>&); |
| 184 | void writeStringQDF(std::string const& str); | 184 | void writeStringQDF(std::string const& str); |
| 185 | void writeStringNoQDF(std::string const& str); | 185 | void writeStringNoQDF(std::string const& str); |
| 186 | + void writePad(int nspaces); | ||
| 186 | void assignCompressedObjectNumbers(int objid); | 187 | void assignCompressedObjectNumbers(int objid); |
| 187 | void enqueueObject(QPDFObjectHandle object); | 188 | void enqueueObject(QPDFObjectHandle object); |
| 188 | void writeObjectStreamOffsets(std::vector<int>& offsets, int first_obj); | 189 | void writeObjectStreamOffsets(std::vector<int>& offsets, int first_obj); |
| @@ -242,6 +243,7 @@ class QPDFWriter | @@ -242,6 +243,7 @@ class QPDFWriter | ||
| 242 | int hint_offset, | 243 | int hint_offset, |
| 243 | int hint_length, | 244 | int hint_length, |
| 244 | bool skip_compression); | 245 | bool skip_compression); |
| 246 | + int calculateXrefStreamPadding(int xref_bytes); | ||
| 245 | 247 | ||
| 246 | // When filtering subsections, push additional pipelines to the | 248 | // When filtering subsections, push additional pipelines to the |
| 247 | // stack. When ready to switch, activate the pipeline stack. | 249 | // stack. When ready to switch, activate the pipeline stack. |
libqpdf/QPDFWriter.cc
| @@ -498,6 +498,15 @@ QPDFWriter::writeStringNoQDF(std::string const& str) | @@ -498,6 +498,15 @@ QPDFWriter::writeStringNoQDF(std::string const& str) | ||
| 498 | } | 498 | } |
| 499 | } | 499 | } |
| 500 | 500 | ||
| 501 | +void | ||
| 502 | +QPDFWriter::writePad(int nspaces) | ||
| 503 | +{ | ||
| 504 | + for (int i = 0; i < nspaces; ++i) | ||
| 505 | + { | ||
| 506 | + writeString(" "); | ||
| 507 | + } | ||
| 508 | +} | ||
| 509 | + | ||
| 501 | Pipeline* | 510 | Pipeline* |
| 502 | QPDFWriter::pushPipeline(Pipeline* p) | 511 | QPDFWriter::pushPipeline(Pipeline* p) |
| 503 | { | 512 | { |
| @@ -770,10 +779,7 @@ QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream, int prev) | @@ -770,10 +779,7 @@ QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream, int prev) | ||
| 770 | writeString(QUtil::int_to_string(prev)); | 779 | writeString(QUtil::int_to_string(prev)); |
| 771 | int nspaces = pos + 11 - this->pipeline->getCount(); | 780 | int nspaces = pos + 11 - this->pipeline->getCount(); |
| 772 | assert(nspaces >= 0); | 781 | assert(nspaces >= 0); |
| 773 | - for (int i = 0; i < nspaces; ++i) | ||
| 774 | - { | ||
| 775 | - writeString(" "); | ||
| 776 | - } | 782 | + writePad(nspaces); |
| 777 | } | 783 | } |
| 778 | } | 784 | } |
| 779 | else | 785 | else |
| @@ -1855,6 +1861,20 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, int max_offset, | @@ -1855,6 +1861,20 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, int max_offset, | ||
| 1855 | return space_before_zero; | 1861 | return space_before_zero; |
| 1856 | } | 1862 | } |
| 1857 | 1863 | ||
| 1864 | +int | ||
| 1865 | +QPDFWriter::calculateXrefStreamPadding(int xref_bytes) | ||
| 1866 | +{ | ||
| 1867 | + // This routine is called right after a linearization first pass | ||
| 1868 | + // xref stream has been written without compression. Calculate | ||
| 1869 | + // the amount of padding that would be required in the worst case, | ||
| 1870 | + // assuming the number of uncompressed bytes remains the same. | ||
| 1871 | + // The worst case for zlib is that the output is larger than the | ||
| 1872 | + // input by 6 bytes plus 5 bytes per 16K, and then we'll add 10 | ||
| 1873 | + // extra bytes for number length increases. | ||
| 1874 | + | ||
| 1875 | + return 16 + (5 * ((xref_bytes + 16383) / 16384)); | ||
| 1876 | +} | ||
| 1877 | + | ||
| 1858 | void | 1878 | void |
| 1859 | QPDFWriter::writeLinearized() | 1879 | QPDFWriter::writeLinearized() |
| 1860 | { | 1880 | { |
| @@ -2016,10 +2036,7 @@ QPDFWriter::writeLinearized() | @@ -2016,10 +2036,7 @@ QPDFWriter::writeLinearized() | ||
| 2016 | static int const pad = 150; | 2036 | static int const pad = 150; |
| 2017 | int spaces = (pos + pad - this->pipeline->getCount()); | 2037 | int spaces = (pos + pad - this->pipeline->getCount()); |
| 2018 | assert(spaces >= 0); | 2038 | assert(spaces >= 0); |
| 2019 | - for (int i = 0; i < spaces; ++i) | ||
| 2020 | - { | ||
| 2021 | - writeString(" "); | ||
| 2022 | - } | 2039 | + writePad(spaces); |
| 2023 | writeString("\n"); | 2040 | writeString("\n"); |
| 2024 | 2041 | ||
| 2025 | // Part 3: first page cross reference table and trailer. | 2042 | // Part 3: first page cross reference table and trailer. |
| @@ -2057,32 +2074,16 @@ QPDFWriter::writeLinearized() | @@ -2057,32 +2074,16 @@ QPDFWriter::writeLinearized() | ||
| 2057 | if (pass == 1) | 2074 | if (pass == 1) |
| 2058 | { | 2075 | { |
| 2059 | // Pad so we have enough room for the real xref | 2076 | // Pad so we have enough room for the real xref |
| 2060 | - // stream. We've written the stream without | ||
| 2061 | - // compression (but with all the stream dictionary | ||
| 2062 | - // parameters to enable it) and assuming a very | ||
| 2063 | - // generous allowance for writing file offsets. We | ||
| 2064 | - // need a little extra padding to allow for zlib's | ||
| 2065 | - // output to be larger than its input (6 bytes plus 5 | ||
| 2066 | - // bytes per 16K), and then we'll add 10 extra bytes | ||
| 2067 | - // for number length increases. | ||
| 2068 | - | ||
| 2069 | - unsigned int xref_bytes = endpos - pos; | ||
| 2070 | - int possible_extra = | ||
| 2071 | - 16 + (5 * ((xref_bytes + 16383) / 16384)); | ||
| 2072 | - for (int i = 0; i < possible_extra; ++i) | ||
| 2073 | - { | ||
| 2074 | - writeString(" "); | ||
| 2075 | - } | 2077 | + // stream. |
| 2078 | + writePad(calculateXrefStreamPadding(endpos - pos)); | ||
| 2076 | first_xref_end = this->pipeline->getCount(); | 2079 | first_xref_end = this->pipeline->getCount(); |
| 2077 | } | 2080 | } |
| 2078 | else | 2081 | else |
| 2079 | { | 2082 | { |
| 2080 | // Pad so that the next object starts at the same | 2083 | // Pad so that the next object starts at the same |
| 2081 | // place as in pass 1. | 2084 | // place as in pass 1. |
| 2082 | - for (int i = 0; i < first_xref_end - endpos; ++i) | ||
| 2083 | - { | ||
| 2084 | - writeString(" "); | ||
| 2085 | - } | 2085 | + writePad(first_xref_end - endpos); |
| 2086 | + | ||
| 2086 | // A failure of this insertion means we didn't allow | 2087 | // A failure of this insertion means we didn't allow |
| 2087 | // enough padding for the first pass xref stream. | 2088 | // enough padding for the first pass xref stream. |
| 2088 | assert(this->pipeline->getCount() == first_xref_end); | 2089 | assert(this->pipeline->getCount() == first_xref_end); |
| @@ -2139,27 +2140,23 @@ QPDFWriter::writeLinearized() | @@ -2139,27 +2140,23 @@ QPDFWriter::writeLinearized() | ||
| 2139 | second_xref_offset = this->pipeline->getCount(); | 2140 | second_xref_offset = this->pipeline->getCount(); |
| 2140 | if (need_xref_stream) | 2141 | if (need_xref_stream) |
| 2141 | { | 2142 | { |
| 2143 | + pos = this->pipeline->getCount(); | ||
| 2142 | space_before_zero = | 2144 | space_before_zero = |
| 2143 | writeXRefStream(second_half_xref, | 2145 | writeXRefStream(second_half_xref, |
| 2144 | second_half_end, second_xref_offset, | 2146 | second_half_end, second_xref_offset, |
| 2145 | t_lin_second, 0, second_half_end, | 2147 | t_lin_second, 0, second_half_end, |
| 2146 | - second_trailer_size); | 2148 | + second_trailer_size/*, |
| 2149 | + 0, 0, 0, 0, (pass == 1)*/); | ||
| 2150 | +/// int endpos = this->pipeline->getCount(); | ||
| 2151 | + | ||
| 2147 | if (pass == 1) | 2152 | if (pass == 1) |
| 2148 | { | 2153 | { |
| 2149 | - // Add some padding -- we need an accurate file_size | ||
| 2150 | - // number, and this could change if the pass 2 xref | ||
| 2151 | - // stream compresses differently. There shouldn't be | ||
| 2152 | - // much difference, so we'll just pad 100 characters. | ||
| 2153 | - // This is unscientific though, and may not always | ||
| 2154 | - // work. The only way we could really get around this | ||
| 2155 | - // would be to seek back to the beginning of the file | ||
| 2156 | - // and update /L in the linearization dictionary, but | ||
| 2157 | - // that would be the only thing in the design that | ||
| 2158 | - // would require the output file to be seekable. | ||
| 2159 | - for (int i = 0; i < 99; ++i) | ||
| 2160 | - { | ||
| 2161 | - writeString(" "); | ||
| 2162 | - } | 2154 | + // Pad so we have enough room for the real xref |
| 2155 | + // stream. See comments for previous xref stream on | ||
| 2156 | + // how we calculate the padding. | ||
| 2157 | + | ||
| 2158 | +/// writePad(calculateXrefStreamPadding(endpos - pos)); | ||
| 2159 | + writePad(99); | ||
| 2163 | writeString("\n"); | 2160 | writeString("\n"); |
| 2164 | second_xref_end = this->pipeline->getCount(); | 2161 | second_xref_end = this->pipeline->getCount(); |
| 2165 | } | 2162 | } |
| @@ -2167,12 +2164,9 @@ QPDFWriter::writeLinearized() | @@ -2167,12 +2164,9 @@ QPDFWriter::writeLinearized() | ||
| 2167 | { | 2164 | { |
| 2168 | // Make the file size the same. | 2165 | // Make the file size the same. |
| 2169 | int pos = this->pipeline->getCount(); | 2166 | int pos = this->pipeline->getCount(); |
| 2170 | - while (pos < second_xref_end + hint_length - 1) | ||
| 2171 | - { | ||
| 2172 | - ++pos; | ||
| 2173 | - writeString(" "); | ||
| 2174 | - } | 2167 | + writePad(second_xref_end + hint_length - 1 - pos); |
| 2175 | writeString("\n"); | 2168 | writeString("\n"); |
| 2169 | + | ||
| 2176 | // If this assertion fails, maybe we didn't have | 2170 | // If this assertion fails, maybe we didn't have |
| 2177 | // enough padding above. | 2171 | // enough padding above. |
| 2178 | assert(this->pipeline->getCount() == | 2172 | assert(this->pipeline->getCount() == |