Commit ba5fb6916446d8bdf79cba25f08d759bc5595aec
1 parent
dadf8307
Make popping pipeline stack safer
Use destructors to pop the pipeline stack, and ensure that code that pops the stack is actually popping the intended thing.
Showing
4 changed files
with
168 additions
and
107 deletions
include/qpdf/Pipeline.hh
| @@ -70,6 +70,8 @@ class QPDF_DLL_CLASS Pipeline | @@ -70,6 +70,8 @@ class QPDF_DLL_CLASS Pipeline | ||
| 70 | virtual void write(unsigned char* data, size_t len) = 0; | 70 | virtual void write(unsigned char* data, size_t len) = 0; |
| 71 | QPDF_DLL | 71 | QPDF_DLL |
| 72 | virtual void finish() = 0; | 72 | virtual void finish() = 0; |
| 73 | + QPDF_DLL | ||
| 74 | + std::string getIdentifier() const; | ||
| 73 | 75 | ||
| 74 | protected: | 76 | protected: |
| 75 | Pipeline* getNext(bool allow_null = false); | 77 | Pipeline* getNext(bool allow_null = false); |
include/qpdf/QPDFWriter.hh
| @@ -473,6 +473,34 @@ class QPDFWriter | @@ -473,6 +473,34 @@ class QPDFWriter | ||
| 473 | 473 | ||
| 474 | enum trailer_e { t_normal, t_lin_first, t_lin_second }; | 474 | enum trailer_e { t_normal, t_lin_first, t_lin_second }; |
| 475 | 475 | ||
| 476 | + // An reference to a PipelinePopper instance is passed into | ||
| 477 | + // activatePipelineStack. When the PipelinePopper goes out of | ||
| 478 | + // scope, the pipeline stack is popped. PipelinePopper's | ||
| 479 | + // destructor calls finish on the current pipeline and pops the | ||
| 480 | + // pipeline stack until the top of stack is a previous active top | ||
| 481 | + // of stack, and restores the pipeline to that point. It deletes | ||
| 482 | + // any pipelines that it pops. If the bp argument is non-null and | ||
| 483 | + // any of the stack items are of type Pl_Buffer, the buffer is | ||
| 484 | + // retrieved. | ||
| 485 | + class PipelinePopper | ||
| 486 | + { | ||
| 487 | + friend class QPDFWriter; | ||
| 488 | + public: | ||
| 489 | + PipelinePopper(QPDFWriter* qw, | ||
| 490 | + PointerHolder<Buffer>* bp = 0) : | ||
| 491 | + qw(qw), | ||
| 492 | + bp(bp) | ||
| 493 | + { | ||
| 494 | + } | ||
| 495 | + ~PipelinePopper(); | ||
| 496 | + | ||
| 497 | + private: | ||
| 498 | + QPDFWriter* qw; | ||
| 499 | + PointerHolder<Buffer>* bp; | ||
| 500 | + std::string stack_id; | ||
| 501 | + }; | ||
| 502 | + friend class PipelinePopper; | ||
| 503 | + | ||
| 476 | unsigned int bytesNeeded(long long n); | 504 | unsigned int bytesNeeded(long long n); |
| 477 | void writeBinary(unsigned long long val, unsigned int bytes); | 505 | void writeBinary(unsigned long long val, unsigned int bytes); |
| 478 | void writeString(std::string const& str); | 506 | void writeString(std::string const& str); |
| @@ -560,24 +588,17 @@ class QPDFWriter | @@ -560,24 +588,17 @@ class QPDFWriter | ||
| 560 | int calculateXrefStreamPadding(qpdf_offset_t xref_bytes); | 588 | int calculateXrefStreamPadding(qpdf_offset_t xref_bytes); |
| 561 | 589 | ||
| 562 | // When filtering subsections, push additional pipelines to the | 590 | // When filtering subsections, push additional pipelines to the |
| 563 | - // stack. When ready to switch, activate the pipeline stack. | ||
| 564 | - // Pipelines passed to pushPipeline are deleted when | ||
| 565 | - // clearPipelineStack is called. | 591 | + // stack. When ready to switch, activate the pipeline stack. When |
| 592 | + // the passed in PipelinePopper goes out of scope, the stack is | ||
| 593 | + // popped. | ||
| 566 | Pipeline* pushPipeline(Pipeline*); | 594 | Pipeline* pushPipeline(Pipeline*); |
| 567 | - void activatePipelineStack(); | 595 | + void activatePipelineStack(PipelinePopper&); |
| 568 | void initializePipelineStack(Pipeline *); | 596 | void initializePipelineStack(Pipeline *); |
| 569 | 597 | ||
| 570 | - // Calls finish on the current pipeline and pops the pipeline | ||
| 571 | - // stack until the top of stack is a previous active top of stack, | ||
| 572 | - // and restores the pipeline to that point. Deletes any pipelines | ||
| 573 | - // that it pops. If the bp argument is non-null and any of the | ||
| 574 | - // stack items are of type Pl_Buffer, the buffer is retrieved. | ||
| 575 | - void popPipelineStack(PointerHolder<Buffer>* bp = 0); | ||
| 576 | - | ||
| 577 | void adjustAESStreamLength(size_t& length); | 598 | void adjustAESStreamLength(size_t& length); |
| 578 | - void pushEncryptionFilter(); | ||
| 579 | - void pushDiscardFilter(); | ||
| 580 | - void pushMD5Pipeline(); | 599 | + void pushEncryptionFilter(PipelinePopper&); |
| 600 | + void pushDiscardFilter(PipelinePopper&); | ||
| 601 | + void pushMD5Pipeline(PipelinePopper&); | ||
| 581 | void computeDeterministicIDData(); | 602 | void computeDeterministicIDData(); |
| 582 | 603 | ||
| 583 | void discardGeneration(std::map<QPDFObjGen, int> const& in, | 604 | void discardGeneration(std::map<QPDFObjGen, int> const& in, |
| @@ -654,6 +675,7 @@ class QPDFWriter | @@ -654,6 +675,7 @@ class QPDFWriter | ||
| 654 | std::map<QPDFObjGen, int> object_to_object_stream; | 675 | std::map<QPDFObjGen, int> object_to_object_stream; |
| 655 | std::map<int, std::set<QPDFObjGen> > object_stream_to_objects; | 676 | std::map<int, std::set<QPDFObjGen> > object_stream_to_objects; |
| 656 | std::list<Pipeline*> pipeline_stack; | 677 | std::list<Pipeline*> pipeline_stack; |
| 678 | + unsigned long long next_stack_id; | ||
| 657 | bool deterministic_id; | 679 | bool deterministic_id; |
| 658 | Pl_MD5* md5_pipeline; | 680 | Pl_MD5* md5_pipeline; |
| 659 | std::string deterministic_id_data; | 681 | std::string deterministic_id_data; |
libqpdf/Pipeline.cc
libqpdf/QPDFWriter.cc
| @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) : | @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) : | ||
| 63 | cur_stream_length(0), | 63 | cur_stream_length(0), |
| 64 | added_newline(false), | 64 | added_newline(false), |
| 65 | max_ostream_index(0), | 65 | max_ostream_index(0), |
| 66 | + next_stack_id(0), | ||
| 66 | deterministic_id(false), | 67 | deterministic_id(false), |
| 67 | md5_pipeline(0), | 68 | md5_pipeline(0), |
| 68 | did_write_setup(false), | 69 | did_write_setup(false), |
| @@ -1049,36 +1050,51 @@ QPDFWriter::pushPipeline(Pipeline* p) | @@ -1049,36 +1050,51 @@ QPDFWriter::pushPipeline(Pipeline* p) | ||
| 1049 | void | 1050 | void |
| 1050 | QPDFWriter::initializePipelineStack(Pipeline *p) | 1051 | QPDFWriter::initializePipelineStack(Pipeline *p) |
| 1051 | { | 1052 | { |
| 1052 | - this->m->pipeline = new Pl_Count("qpdf count", p); | 1053 | + this->m->pipeline = new Pl_Count("pipeline stack base", p); |
| 1053 | this->m->to_delete.push_back(this->m->pipeline); | 1054 | this->m->to_delete.push_back(this->m->pipeline); |
| 1054 | this->m->pipeline_stack.push_back(this->m->pipeline); | 1055 | this->m->pipeline_stack.push_back(this->m->pipeline); |
| 1055 | } | 1056 | } |
| 1056 | 1057 | ||
| 1057 | void | 1058 | void |
| 1058 | -QPDFWriter::activatePipelineStack() | 1059 | +QPDFWriter::activatePipelineStack(PipelinePopper& pp) |
| 1059 | { | 1060 | { |
| 1060 | - Pl_Count* c = new Pl_Count("count", this->m->pipeline_stack.back()); | 1061 | + std::string stack_id( |
| 1062 | + "stack " + QUtil::uint_to_string(this->m->next_stack_id)); | ||
| 1063 | + Pl_Count* c = new Pl_Count(stack_id.c_str(), | ||
| 1064 | + this->m->pipeline_stack.back()); | ||
| 1065 | + ++this->m->next_stack_id; | ||
| 1061 | this->m->pipeline_stack.push_back(c); | 1066 | this->m->pipeline_stack.push_back(c); |
| 1062 | this->m->pipeline = c; | 1067 | this->m->pipeline = c; |
| 1068 | + pp.stack_id = stack_id; | ||
| 1063 | } | 1069 | } |
| 1064 | 1070 | ||
| 1065 | -void | ||
| 1066 | -QPDFWriter::popPipelineStack(PointerHolder<Buffer>* bp) | 1071 | +QPDFWriter::PipelinePopper::~PipelinePopper() |
| 1067 | { | 1072 | { |
| 1068 | - assert(this->m->pipeline_stack.size() >= 2); | ||
| 1069 | - this->m->pipeline->finish(); | ||
| 1070 | - assert(dynamic_cast<Pl_Count*>(this->m->pipeline_stack.back()) == | ||
| 1071 | - this->m->pipeline); | ||
| 1072 | - delete this->m->pipeline_stack.back(); | ||
| 1073 | - this->m->pipeline_stack.pop_back(); | ||
| 1074 | - while (dynamic_cast<Pl_Count*>(this->m->pipeline_stack.back()) == 0) | ||
| 1075 | - { | ||
| 1076 | - Pipeline* p = this->m->pipeline_stack.back(); | ||
| 1077 | - if (dynamic_cast<Pl_MD5*>(p) == this->m->md5_pipeline) | 1073 | + if (stack_id.empty()) |
| 1074 | + { | ||
| 1075 | + return; | ||
| 1076 | + } | ||
| 1077 | + assert(qw->m->pipeline_stack.size() >= 2); | ||
| 1078 | + qw->m->pipeline->finish(); | ||
| 1079 | + assert(dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()) == | ||
| 1080 | + qw->m->pipeline); | ||
| 1081 | + // It might be possible for this assertion to fail if | ||
| 1082 | + // writeLinearized exits by exception when deterministic ID, but I | ||
| 1083 | + // don't think so. As of this writing, this is the only case in | ||
| 1084 | + // which two dynamically allocated PipelinePopper objects ever | ||
| 1085 | + // exist at the same time, so the assertion will fail if they get | ||
| 1086 | + // popped out of order from automatic destruction. | ||
| 1087 | + assert(qw->m->pipeline->getIdentifier() == stack_id); | ||
| 1088 | + delete qw->m->pipeline_stack.back(); | ||
| 1089 | + qw->m->pipeline_stack.pop_back(); | ||
| 1090 | + while (dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()) == 0) | ||
| 1091 | + { | ||
| 1092 | + Pipeline* p = qw->m->pipeline_stack.back(); | ||
| 1093 | + if (dynamic_cast<Pl_MD5*>(p) == qw->m->md5_pipeline) | ||
| 1078 | { | 1094 | { |
| 1079 | - this->m->md5_pipeline = 0; | 1095 | + qw->m->md5_pipeline = 0; |
| 1080 | } | 1096 | } |
| 1081 | - this->m->pipeline_stack.pop_back(); | 1097 | + qw->m->pipeline_stack.pop_back(); |
| 1082 | Pl_Buffer* buf = dynamic_cast<Pl_Buffer*>(p); | 1098 | Pl_Buffer* buf = dynamic_cast<Pl_Buffer*>(p); |
| 1083 | if (bp && buf) | 1099 | if (bp && buf) |
| 1084 | { | 1100 | { |
| @@ -1086,7 +1102,7 @@ QPDFWriter::popPipelineStack(PointerHolder<Buffer>* bp) | @@ -1086,7 +1102,7 @@ QPDFWriter::popPipelineStack(PointerHolder<Buffer>* bp) | ||
| 1086 | } | 1102 | } |
| 1087 | delete p; | 1103 | delete p; |
| 1088 | } | 1104 | } |
| 1089 | - this->m->pipeline = dynamic_cast<Pl_Count*>(this->m->pipeline_stack.back()); | 1105 | + qw->m->pipeline = dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()); |
| 1090 | } | 1106 | } |
| 1091 | 1107 | ||
| 1092 | void | 1108 | void |
| @@ -1103,7 +1119,7 @@ QPDFWriter::adjustAESStreamLength(size_t& length) | @@ -1103,7 +1119,7 @@ QPDFWriter::adjustAESStreamLength(size_t& length) | ||
| 1103 | } | 1119 | } |
| 1104 | 1120 | ||
| 1105 | void | 1121 | void |
| 1106 | -QPDFWriter::pushEncryptionFilter() | 1122 | +QPDFWriter::pushEncryptionFilter(PipelinePopper& pp) |
| 1107 | { | 1123 | { |
| 1108 | if (this->m->encrypted && (! this->m->cur_data_key.empty())) | 1124 | if (this->m->encrypted && (! this->m->cur_data_key.empty())) |
| 1109 | { | 1125 | { |
| @@ -1125,18 +1141,18 @@ QPDFWriter::pushEncryptionFilter() | @@ -1125,18 +1141,18 @@ QPDFWriter::pushEncryptionFilter() | ||
| 1125 | } | 1141 | } |
| 1126 | // Must call this unconditionally so we can call popPipelineStack | 1142 | // Must call this unconditionally so we can call popPipelineStack |
| 1127 | // to balance pushEncryptionFilter(). | 1143 | // to balance pushEncryptionFilter(). |
| 1128 | - activatePipelineStack(); | 1144 | + activatePipelineStack(pp); |
| 1129 | } | 1145 | } |
| 1130 | 1146 | ||
| 1131 | void | 1147 | void |
| 1132 | -QPDFWriter::pushDiscardFilter() | 1148 | +QPDFWriter::pushDiscardFilter(PipelinePopper& pp) |
| 1133 | { | 1149 | { |
| 1134 | pushPipeline(new Pl_Discard()); | 1150 | pushPipeline(new Pl_Discard()); |
| 1135 | - activatePipelineStack(); | 1151 | + activatePipelineStack(pp); |
| 1136 | } | 1152 | } |
| 1137 | 1153 | ||
| 1138 | void | 1154 | void |
| 1139 | -QPDFWriter::pushMD5Pipeline() | 1155 | +QPDFWriter::pushMD5Pipeline(PipelinePopper& pp) |
| 1140 | { | 1156 | { |
| 1141 | if (! this->m->id2.empty()) | 1157 | if (! this->m->id2.empty()) |
| 1142 | { | 1158 | { |
| @@ -1153,7 +1169,7 @@ QPDFWriter::pushMD5Pipeline() | @@ -1153,7 +1169,7 @@ QPDFWriter::pushMD5Pipeline() | ||
| 1153 | // Special case code in popPipelineStack clears this->m->md5_pipeline | 1169 | // Special case code in popPipelineStack clears this->m->md5_pipeline |
| 1154 | // upon deletion. | 1170 | // upon deletion. |
| 1155 | pushPipeline(this->m->md5_pipeline); | 1171 | pushPipeline(this->m->md5_pipeline); |
| 1156 | - activatePipelineStack(); | 1172 | + activatePipelineStack(pp); |
| 1157 | } | 1173 | } |
| 1158 | 1174 | ||
| 1159 | void | 1175 | void |
| @@ -1769,8 +1785,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, | @@ -1769,8 +1785,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, | ||
| 1769 | for (int attempt = 1; attempt <= 2; ++attempt) | 1785 | for (int attempt = 1; attempt <= 2; ++attempt) |
| 1770 | { | 1786 | { |
| 1771 | pushPipeline(new Pl_Buffer("stream data")); | 1787 | pushPipeline(new Pl_Buffer("stream data")); |
| 1772 | - activatePipelineStack(); | ||
| 1773 | - | 1788 | + PipelinePopper pp_stream_data(this, &stream_data); |
| 1789 | + activatePipelineStack(pp_stream_data); | ||
| 1774 | filtered = | 1790 | filtered = |
| 1775 | object.pipeStreamData( | 1791 | object.pipeStreamData( |
| 1776 | this->m->pipeline, | 1792 | this->m->pipeline, |
| @@ -1779,7 +1795,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, | @@ -1779,7 +1795,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, | ||
| 1779 | (filter | 1795 | (filter |
| 1780 | ? (uncompress ? qpdf_dl_all : this->m->stream_decode_level) | 1796 | ? (uncompress ? qpdf_dl_all : this->m->stream_decode_level) |
| 1781 | : qpdf_dl_none), false, (attempt == 1)); | 1797 | : qpdf_dl_none), false, (attempt == 1)); |
| 1782 | - popPipelineStack(&stream_data); | ||
| 1783 | if (filter && (! filtered)) | 1798 | if (filter && (! filtered)) |
| 1784 | { | 1799 | { |
| 1785 | // Try again | 1800 | // Try again |
| @@ -1808,11 +1823,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, | @@ -1808,11 +1823,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, | ||
| 1808 | adjustAESStreamLength(this->m->cur_stream_length); | 1823 | adjustAESStreamLength(this->m->cur_stream_length); |
| 1809 | unparseObject(stream_dict, 0, flags, | 1824 | unparseObject(stream_dict, 0, flags, |
| 1810 | this->m->cur_stream_length, compress); | 1825 | this->m->cur_stream_length, compress); |
| 1826 | + unsigned char last_char = '\0'; | ||
| 1811 | writeString("\nstream\n"); | 1827 | writeString("\nstream\n"); |
| 1812 | - pushEncryptionFilter(); | ||
| 1813 | - writeBuffer(stream_data); | ||
| 1814 | - unsigned char last_char = this->m->pipeline->getLastChar(); | ||
| 1815 | - popPipelineStack(); | 1828 | + { |
| 1829 | + PipelinePopper pp_enc(this); | ||
| 1830 | + pushEncryptionFilter(pp_enc); | ||
| 1831 | + writeBuffer(stream_data); | ||
| 1832 | + last_char = this->m->pipeline->getLastChar(); | ||
| 1833 | + } | ||
| 1816 | 1834 | ||
| 1817 | if (this->m->newline_before_endstream || | 1835 | if (this->m->newline_before_endstream || |
| 1818 | (this->m->qdf_mode && (last_char != '\n'))) | 1836 | (this->m->qdf_mode && (last_char != '\n'))) |
| @@ -1911,9 +1929,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | @@ -1911,9 +1929,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | ||
| 1911 | bool compressed = false; | 1929 | bool compressed = false; |
| 1912 | for (int pass = 1; pass <= 2; ++pass) | 1930 | for (int pass = 1; pass <= 2; ++pass) |
| 1913 | { | 1931 | { |
| 1932 | + // stream_buffer will be initialized only for pass 2 | ||
| 1933 | + PipelinePopper pp_ostream(this, &stream_buffer); | ||
| 1914 | if (pass == 1) | 1934 | if (pass == 1) |
| 1915 | { | 1935 | { |
| 1916 | - pushDiscardFilter(); | 1936 | + pushDiscardFilter(pp_ostream); |
| 1917 | } | 1937 | } |
| 1918 | else | 1938 | else |
| 1919 | { | 1939 | { |
| @@ -1928,10 +1948,12 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | @@ -1928,10 +1948,12 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | ||
| 1928 | 1948 | ||
| 1929 | // Take one pass at writing pairs of numbers so we can get | 1949 | // Take one pass at writing pairs of numbers so we can get |
| 1930 | // their size information | 1950 | // their size information |
| 1931 | - pushDiscardFilter(); | ||
| 1932 | - writeObjectStreamOffsets(offsets, first_obj); | ||
| 1933 | - first += this->m->pipeline->getCount(); | ||
| 1934 | - popPipelineStack(); | 1951 | + { |
| 1952 | + PipelinePopper pp_discard(this); | ||
| 1953 | + pushDiscardFilter(pp_discard); | ||
| 1954 | + writeObjectStreamOffsets(offsets, first_obj); | ||
| 1955 | + first += this->m->pipeline->getCount(); | ||
| 1956 | + } | ||
| 1935 | 1957 | ||
| 1936 | // Set up a stream to write the stream data into a buffer. | 1958 | // Set up a stream to write the stream data into a buffer. |
| 1937 | Pipeline* next = pushPipeline(new Pl_Buffer("object stream")); | 1959 | Pipeline* next = pushPipeline(new Pl_Buffer("object stream")); |
| @@ -1944,7 +1966,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | @@ -1944,7 +1966,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | ||
| 1944 | new Pl_Flate("compress object stream", next, | 1966 | new Pl_Flate("compress object stream", next, |
| 1945 | Pl_Flate::a_deflate)); | 1967 | Pl_Flate::a_deflate)); |
| 1946 | } | 1968 | } |
| 1947 | - activatePipelineStack(); | 1969 | + activatePipelineStack(pp_ostream); |
| 1948 | writeObjectStreamOffsets(offsets, first_obj); | 1970 | writeObjectStreamOffsets(offsets, first_obj); |
| 1949 | } | 1971 | } |
| 1950 | 1972 | ||
| @@ -1994,9 +2016,6 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | @@ -1994,9 +2016,6 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | ||
| 1994 | 2016 | ||
| 1995 | this->m->xref[new_obj] = QPDFXRefEntry(2, new_id, count); | 2017 | this->m->xref[new_obj] = QPDFXRefEntry(2, new_id, count); |
| 1996 | } | 2018 | } |
| 1997 | - | ||
| 1998 | - // stream_buffer will be initialized only for pass 2 | ||
| 1999 | - popPipelineStack(&stream_buffer); | ||
| 2000 | } | 2019 | } |
| 2001 | 2020 | ||
| 2002 | // Write the object | 2021 | // Write the object |
| @@ -2037,9 +2056,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | @@ -2037,9 +2056,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) | ||
| 2037 | { | 2056 | { |
| 2038 | QTC::TC("qpdf", "QPDFWriter encrypt object stream"); | 2057 | QTC::TC("qpdf", "QPDFWriter encrypt object stream"); |
| 2039 | } | 2058 | } |
| 2040 | - pushEncryptionFilter(); | ||
| 2041 | - writeBuffer(stream_buffer); | ||
| 2042 | - popPipelineStack(); | 2059 | + { |
| 2060 | + PipelinePopper pp_enc(this); | ||
| 2061 | + pushEncryptionFilter(pp_enc); | ||
| 2062 | + writeBuffer(stream_buffer); | ||
| 2063 | + } | ||
| 2043 | if (this->m->newline_before_endstream) | 2064 | if (this->m->newline_before_endstream) |
| 2044 | { | 2065 | { |
| 2045 | writeString("\n"); | 2066 | writeString("\n"); |
| @@ -2779,10 +2800,13 @@ QPDFWriter::writeHintStream(int hint_id) | @@ -2779,10 +2800,13 @@ QPDFWriter::writeHintStream(int hint_id) | ||
| 2779 | { | 2800 | { |
| 2780 | QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); | 2801 | QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); |
| 2781 | } | 2802 | } |
| 2782 | - pushEncryptionFilter(); | ||
| 2783 | - writeBuffer(hint_buffer); | ||
| 2784 | - unsigned char last_char = this->m->pipeline->getLastChar(); | ||
| 2785 | - popPipelineStack(); | 2803 | + unsigned char last_char = '\0'; |
| 2804 | + { | ||
| 2805 | + PipelinePopper pp_enc(this); | ||
| 2806 | + pushEncryptionFilter(pp_enc); | ||
| 2807 | + writeBuffer(hint_buffer); | ||
| 2808 | + last_char = this->m->pipeline->getLastChar(); | ||
| 2809 | + } | ||
| 2786 | 2810 | ||
| 2787 | if (last_char != '\n') | 2811 | if (last_char != '\n') |
| 2788 | { | 2812 | { |
| @@ -2896,46 +2920,48 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, qpdf_offset_t max_offset, | @@ -2896,46 +2920,48 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, qpdf_offset_t max_offset, | ||
| 2896 | new Pl_PNGFilter( | 2920 | new Pl_PNGFilter( |
| 2897 | "pngify xref", p, Pl_PNGFilter::a_encode, esize)); | 2921 | "pngify xref", p, Pl_PNGFilter::a_encode, esize)); |
| 2898 | } | 2922 | } |
| 2899 | - activatePipelineStack(); | ||
| 2900 | - for (int i = first; i <= last; ++i) | 2923 | + PointerHolder<Buffer> xref_data; |
| 2901 | { | 2924 | { |
| 2902 | - QPDFXRefEntry& e = this->m->xref[i]; | ||
| 2903 | - switch (e.getType()) | ||
| 2904 | - { | ||
| 2905 | - case 0: | ||
| 2906 | - writeBinary(0, 1); | ||
| 2907 | - writeBinary(0, f1_size); | ||
| 2908 | - writeBinary(0, f2_size); | ||
| 2909 | - break; | 2925 | + PipelinePopper pp_xref(this, &xref_data); |
| 2926 | + activatePipelineStack(pp_xref); | ||
| 2927 | + for (int i = first; i <= last; ++i) | ||
| 2928 | + { | ||
| 2929 | + QPDFXRefEntry& e = this->m->xref[i]; | ||
| 2930 | + switch (e.getType()) | ||
| 2931 | + { | ||
| 2932 | + case 0: | ||
| 2933 | + writeBinary(0, 1); | ||
| 2934 | + writeBinary(0, f1_size); | ||
| 2935 | + writeBinary(0, f2_size); | ||
| 2936 | + break; | ||
| 2910 | 2937 | ||
| 2911 | - case 1: | ||
| 2912 | - { | ||
| 2913 | - qpdf_offset_t offset = e.getOffset(); | ||
| 2914 | - if ((hint_id != 0) && | ||
| 2915 | - (i != hint_id) && | ||
| 2916 | - (offset >= hint_offset)) | ||
| 2917 | - { | ||
| 2918 | - offset += hint_length; | ||
| 2919 | - } | ||
| 2920 | - writeBinary(1, 1); | ||
| 2921 | - writeBinary(QIntC::to_ulonglong(offset), f1_size); | ||
| 2922 | - writeBinary(0, f2_size); | ||
| 2923 | - } | ||
| 2924 | - break; | 2938 | + case 1: |
| 2939 | + { | ||
| 2940 | + qpdf_offset_t offset = e.getOffset(); | ||
| 2941 | + if ((hint_id != 0) && | ||
| 2942 | + (i != hint_id) && | ||
| 2943 | + (offset >= hint_offset)) | ||
| 2944 | + { | ||
| 2945 | + offset += hint_length; | ||
| 2946 | + } | ||
| 2947 | + writeBinary(1, 1); | ||
| 2948 | + writeBinary(QIntC::to_ulonglong(offset), f1_size); | ||
| 2949 | + writeBinary(0, f2_size); | ||
| 2950 | + } | ||
| 2951 | + break; | ||
| 2925 | 2952 | ||
| 2926 | - case 2: | ||
| 2927 | - writeBinary(2, 1); | ||
| 2928 | - writeBinary(QIntC::to_ulonglong(e.getObjStreamNumber()), f1_size); | ||
| 2929 | - writeBinary(QIntC::to_ulonglong(e.getObjStreamIndex()), f2_size); | ||
| 2930 | - break; | 2953 | + case 2: |
| 2954 | + writeBinary(2, 1); | ||
| 2955 | + writeBinary(QIntC::to_ulonglong(e.getObjStreamNumber()), f1_size); | ||
| 2956 | + writeBinary(QIntC::to_ulonglong(e.getObjStreamIndex()), f2_size); | ||
| 2957 | + break; | ||
| 2931 | 2958 | ||
| 2932 | - default: | ||
| 2933 | - throw std::logic_error("invalid type writing xref stream"); | ||
| 2934 | - break; | ||
| 2935 | - } | 2959 | + default: |
| 2960 | + throw std::logic_error("invalid type writing xref stream"); | ||
| 2961 | + break; | ||
| 2962 | + } | ||
| 2963 | + } | ||
| 2936 | } | 2964 | } |
| 2937 | - PointerHolder<Buffer> xref_data; | ||
| 2938 | - popPipelineStack(&xref_data); | ||
| 2939 | 2965 | ||
| 2940 | openObject(xref_id); | 2966 | openObject(xref_id); |
| 2941 | writeString("<<"); | 2967 | writeString("<<"); |
| @@ -3156,6 +3182,8 @@ QPDFWriter::writeLinearized() | @@ -3156,6 +3182,8 @@ QPDFWriter::writeLinearized() | ||
| 3156 | // Write file in two passes. Part numbers refer to PDF spec 1.4. | 3182 | // Write file in two passes. Part numbers refer to PDF spec 1.4. |
| 3157 | 3183 | ||
| 3158 | FILE* lin_pass1_file = 0; | 3184 | FILE* lin_pass1_file = 0; |
| 3185 | + PointerHolder<PipelinePopper> pp_pass1 = new PipelinePopper(this); | ||
| 3186 | + PointerHolder<PipelinePopper> pp_md5 = new PipelinePopper(this); | ||
| 3159 | for (int pass = 1; pass <= 2; ++pass) | 3187 | for (int pass = 1; pass <= 2; ++pass) |
| 3160 | { | 3188 | { |
| 3161 | if (pass == 1) | 3189 | if (pass == 1) |
| @@ -3167,15 +3195,15 @@ QPDFWriter::writeLinearized() | @@ -3167,15 +3195,15 @@ QPDFWriter::writeLinearized() | ||
| 3167 | this->m->lin_pass1_filename.c_str(), "wb"); | 3195 | this->m->lin_pass1_filename.c_str(), "wb"); |
| 3168 | pushPipeline( | 3196 | pushPipeline( |
| 3169 | new Pl_StdioFile("linearization pass1", lin_pass1_file)); | 3197 | new Pl_StdioFile("linearization pass1", lin_pass1_file)); |
| 3170 | - activatePipelineStack(); | 3198 | + activatePipelineStack(*pp_pass1); |
| 3171 | } | 3199 | } |
| 3172 | else | 3200 | else |
| 3173 | { | 3201 | { |
| 3174 | - pushDiscardFilter(); | 3202 | + pushDiscardFilter(*pp_pass1); |
| 3175 | } | 3203 | } |
| 3176 | if (this->m->deterministic_id) | 3204 | if (this->m->deterministic_id) |
| 3177 | { | 3205 | { |
| 3178 | - pushMD5Pipeline(); | 3206 | + pushMD5Pipeline(*pp_md5); |
| 3179 | } | 3207 | } |
| 3180 | } | 3208 | } |
| 3181 | 3209 | ||
| @@ -3392,23 +3420,25 @@ QPDFWriter::writeLinearized() | @@ -3392,23 +3420,25 @@ QPDFWriter::writeLinearized() | ||
| 3392 | QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", | 3420 | QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", |
| 3393 | need_xref_stream ? 0 : 1); | 3421 | need_xref_stream ? 0 : 1); |
| 3394 | computeDeterministicIDData(); | 3422 | computeDeterministicIDData(); |
| 3395 | - popPipelineStack(); | 3423 | + pp_md5 = 0; |
| 3396 | assert(this->m->md5_pipeline == 0); | 3424 | assert(this->m->md5_pipeline == 0); |
| 3397 | } | 3425 | } |
| 3398 | 3426 | ||
| 3399 | // Close first pass pipeline | 3427 | // Close first pass pipeline |
| 3400 | file_size = this->m->pipeline->getCount(); | 3428 | file_size = this->m->pipeline->getCount(); |
| 3401 | - popPipelineStack(); | 3429 | + pp_pass1 = 0; |
| 3402 | 3430 | ||
| 3403 | // Save hint offset since it will be set to zero by | 3431 | // Save hint offset since it will be set to zero by |
| 3404 | // calling openObject. | 3432 | // calling openObject. |
| 3405 | qpdf_offset_t hint_offset = this->m->xref[hint_id].getOffset(); | 3433 | qpdf_offset_t hint_offset = this->m->xref[hint_id].getOffset(); |
| 3406 | 3434 | ||
| 3407 | // Write hint stream to a buffer | 3435 | // Write hint stream to a buffer |
| 3408 | - pushPipeline(new Pl_Buffer("hint buffer")); | ||
| 3409 | - activatePipelineStack(); | ||
| 3410 | - writeHintStream(hint_id); | ||
| 3411 | - popPipelineStack(&hint_buffer); | 3436 | + { |
| 3437 | + pushPipeline(new Pl_Buffer("hint buffer")); | ||
| 3438 | + PipelinePopper pp_hint(this, &hint_buffer); | ||
| 3439 | + activatePipelineStack(pp_hint); | ||
| 3440 | + writeHintStream(hint_id); | ||
| 3441 | + } | ||
| 3412 | hint_length = QIntC::to_offset(hint_buffer->getSize()); | 3442 | hint_length = QIntC::to_offset(hint_buffer->getSize()); |
| 3413 | 3443 | ||
| 3414 | // Restore hint offset | 3444 | // Restore hint offset |
| @@ -3541,9 +3571,10 @@ QPDFWriter::registerProgressReporter(PointerHolder<ProgressReporter> pr) | @@ -3541,9 +3571,10 @@ QPDFWriter::registerProgressReporter(PointerHolder<ProgressReporter> pr) | ||
| 3541 | void | 3571 | void |
| 3542 | QPDFWriter::writeStandard() | 3572 | QPDFWriter::writeStandard() |
| 3543 | { | 3573 | { |
| 3574 | + PointerHolder<PipelinePopper> pp_md5 = new PipelinePopper(this); | ||
| 3544 | if (this->m->deterministic_id) | 3575 | if (this->m->deterministic_id) |
| 3545 | { | 3576 | { |
| 3546 | - pushMD5Pipeline(); | 3577 | + pushMD5Pipeline(*pp_md5); |
| 3547 | } | 3578 | } |
| 3548 | 3579 | ||
| 3549 | // Start writing | 3580 | // Start writing |
| @@ -3597,7 +3628,7 @@ QPDFWriter::writeStandard() | @@ -3597,7 +3628,7 @@ QPDFWriter::writeStandard() | ||
| 3597 | { | 3628 | { |
| 3598 | QTC::TC("qpdf", "QPDFWriter standard deterministic ID", | 3629 | QTC::TC("qpdf", "QPDFWriter standard deterministic ID", |
| 3599 | this->m->object_stream_to_objects.empty() ? 0 : 1); | 3630 | this->m->object_stream_to_objects.empty() ? 0 : 1); |
| 3600 | - popPipelineStack(); | 3631 | + pp_md5 = 0; |
| 3601 | assert(this->m->md5_pipeline == 0); | 3632 | assert(this->m->md5_pipeline == 0); |
| 3602 | } | 3633 | } |
| 3603 | } | 3634 | } |